claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
131 lines • 4.4 kB
JavaScript
/**
* Auto-install utility for optional MCP tool dependencies
*
* When an MCP tool requires an optional package that isn't installed,
* this utility attempts to install it automatically on first use.
*/
import { spawnSync } from 'child_process';
// Track which packages we've attempted to install this session
const installAttempts = new Set();
/**
* Auto-install a package if not available
*
* @param packageName - npm package name to install
* @param options - Installation options
* @returns true if installed successfully or already attempted
*/
export async function autoInstallPackage(packageName, options = {}) {
const { timeout = 60000, save = false, silent = false } = options;
// Validate package name to prevent command injection (CVE fix)
// Valid npm package names: @scope/name or name, alphanumeric with - . _ ~
const validPackageName = /^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*(@[a-z0-9-._~]+)?$/i;
if (!validPackageName.test(packageName)) {
if (!silent) {
console.error(`[claude-flow] Invalid package name: ${packageName}`);
}
return false;
}
// Only attempt once per session
if (installAttempts.has(packageName)) {
return false;
}
installAttempts.add(packageName);
try {
if (!silent) {
console.error(`[claude-flow] Auto-installing ${packageName}...`);
}
// Use spawn with array args to prevent shell injection
const args = ['install', packageName, save ? '--save' : '--no-save'];
const result = spawnSync('npm', args, {
stdio: silent ? 'pipe' : ['pipe', 'pipe', 'pipe'],
timeout,
shell: false, // Explicitly disable shell
});
if (result.status !== 0) {
throw new Error(result.stderr?.toString() || 'Installation failed');
}
if (!silent) {
console.error(`[claude-flow] Successfully installed ${packageName}`);
}
return true;
}
catch (error) {
if (!silent) {
console.error(`[claude-flow] Failed to auto-install ${packageName}: ${error}`);
}
return false;
}
}
/**
* Try to import a package, auto-install if not found, and retry
*
* @param packageName - npm package name
* @param options - Installation options
* @returns The imported module or null if failed
*/
export async function tryImportOrInstall(packageName, options = {}) {
try {
// First try to import
return await import(packageName);
}
catch {
// Package not found, try to install
const installed = await autoInstallPackage(packageName, options);
if (installed) {
try {
// ESM caches failed imports, so we need to bust the cache
// Add a timestamp query parameter to force a fresh import
const cacheBuster = `?t=${Date.now()}`;
return await import(`${packageName}${cacheBuster}`);
}
catch {
console.error(`[claude-flow] ${packageName} installed but failed to load. Restart MCP server.`);
return null;
}
}
return null;
}
}
/**
* Check if a package is available without installing
*/
export async function isPackageAvailable(packageName) {
try {
await import(packageName);
return true;
}
catch {
return false;
}
}
/**
* Reset install attempts (useful for testing)
*/
export function resetInstallAttempts() {
installAttempts.clear();
}
/**
* Optional package dependencies and their purposes
*/
export const OPTIONAL_PACKAGES = {
'@claude-flow/aidefence': {
description: 'AI manipulation defense (prompt injection, PII detection)',
tools: ['aidefence_scan', 'aidefence_analyze', 'aidefence_stats', 'aidefence_learn'],
},
'@claude-flow/embeddings': {
description: 'Vector embeddings with ONNX support',
tools: ['embeddings_generate', 'embeddings_search', 'embeddings_batch'],
},
'onnxruntime-node': {
description: 'ONNX runtime for neural network inference',
tools: ['neural_*'],
},
};
export default {
autoInstallPackage,
tryImportOrInstall,
isPackageAvailable,
resetInstallAttempts,
OPTIONAL_PACKAGES,
};
//# sourceMappingURL=auto-install.js.map