@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
239 lines (208 loc) • 7.52 kB
text/typescript
import { DeploymentOption, SystemDependency } from '@lobehub/market-sdk';
import debug from 'debug';
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
import { SystemDependencyCheckResult } from '@/types/plugins';
import { InstallationChecker, PackageInstallCheckResult } from './types';
const execPromise = promisify(exec);
const log = debug('lobe-mcp:deps-check');
// Helper function to get current platform install instructions
const getCurrentPlatformInstructions = (installInstructions: any) => {
if (!installInstructions) return undefined;
const platform = process.platform;
let current: string | undefined;
// Map platform to instruction key
switch (platform) {
case 'darwin': {
current = installInstructions.macos;
break;
}
case 'linux': {
current = installInstructions.linux_debian || installInstructions.linux;
break;
}
case 'win32': {
current = installInstructions.windows;
break;
}
}
return {
current,
manual: installInstructions.manual,
};
};
/**
* MCP System Dependency Check Service
*/
class MCPSystemDepsCheckService {
private checkers: Map<string, InstallationChecker> = new Map();
/**
* Register installation checker
*/
registerChecker(method: string, checker: InstallationChecker) {
this.checkers.set(method, checker);
log(`Installation checker registered: ${method}`);
}
/**
* Check system dependency version
*/
async checkSystemDependency(dependency: SystemDependency): Promise<SystemDependencyCheckResult> {
try {
// If check command not provided, use generic command
const checkCommand = dependency.checkCommand || `${dependency.name} --version`;
log(`Checking system dependency: ${dependency.name}, command: ${checkCommand}`);
const { stdout, stderr } = await execPromise(checkCommand);
if (stderr && !stdout) {
return {
error: stderr,
installInstructions: getCurrentPlatformInstructions(
(dependency as any).installInstructions,
),
installed: false,
meetRequirement: false,
name: dependency.name,
requiredVersion: (dependency as any).requiredVersion,
};
}
const output = stdout.trim();
let version = output;
// Process version parsing
if (dependency.versionParsingRequired) {
// Extract version number - usually in format vX.Y.Z or X.Y.Z
const versionMatch = output.match(/[Vv]?(\d+(\.\d+)*)/);
if (versionMatch) {
version = versionMatch[0];
}
}
let meetRequirement = true;
if (dependency.requiredVersion) {
// Extract numeric part
const currentVersion = version.replace(/^[Vv]/, ''); // Remove possible v prefix
const currentVersionNum = parseFloat(currentVersion);
// Extract condition and number from required version
const requirementMatch = dependency.requiredVersion.match(/([<=>]+)?(\d+(\.\d+)*)/);
if (requirementMatch) {
const [, operator = '=', requiredVersion] = requirementMatch;
const requiredNum = parseFloat(requiredVersion);
switch (operator) {
case '>=': {
meetRequirement = currentVersionNum >= requiredNum;
break;
}
case '>': {
meetRequirement = currentVersionNum > requiredNum;
break;
}
case '<=': {
meetRequirement = currentVersionNum <= requiredNum;
break;
}
case '<': {
meetRequirement = currentVersionNum < requiredNum;
break;
}
default: {
// Default equals
meetRequirement = currentVersionNum === requiredNum;
break;
}
}
}
}
log(
`System dependency check result: ${dependency.name}, installed: ${true}, meets requirement: ${meetRequirement}, version: ${version}`,
);
return {
installInstructions: getCurrentPlatformInstructions(
(dependency as any).installInstructions,
),
installed: true,
meetRequirement,
name: dependency.name,
requiredVersion: (dependency as any).requiredVersion,
version,
};
} catch (error) {
log(`System dependency check error: ${dependency.name}, ${error}`);
return {
error: error instanceof Error ? error.message : 'Unknown error',
installInstructions: getCurrentPlatformInstructions(
(dependency as any).installInstructions,
),
installed: false,
meetRequirement: false,
name: dependency.name,
requiredVersion: (dependency as any).requiredVersion,
};
}
}
/**
* Check deployment option
*/
async checkDeployOption(option: DeploymentOption): Promise<{
allDependenciesMet: boolean;
configSchema?: any;
connection: any;
isRecommended?: boolean;
needsConfig?: boolean;
packageInstalled: boolean;
systemDependencies: SystemDependencyCheckResult[];
}> {
const systemDependenciesResults: SystemDependencyCheckResult[] = [];
// Check system dependencies
if (option.systemDependencies && option.systemDependencies.length > 0) {
for (const dep of option.systemDependencies) {
const result = await this.checkSystemDependency(dep);
systemDependenciesResults.push(result);
}
}
// Get corresponding installation checker
const checker = this.checkers.get(option.installationMethod);
let packageInstalled = false;
let packageResult: PackageInstallCheckResult | null = null;
if (checker) {
// Use specific installation checker to check package installation status
packageResult = await checker.checkPackageInstalled(option.installationDetails);
packageInstalled = packageResult.installed;
} else {
log(`Installation checker not found: ${option.installationMethod}`);
}
// Check if all system dependencies meet requirements
const allDependenciesMet = systemDependenciesResults.every((dep) => dep.meetRequirement);
// Check if configuration is required (有必填项)
const configSchema = option.connection?.configSchema;
const needsConfig = Boolean(
configSchema &&
// 检查是否有 required 数组且不为空
((Array.isArray(configSchema.required) && configSchema.required.length > 0) ||
// 检查 properties 中是否有字段标记为 required
(configSchema.properties &&
Object.values(configSchema.properties).some((prop: any) => prop.required === true))),
);
log(
`Configuration check result: needsConfig=${needsConfig}, required=${configSchema?.required}, configSchema=%O`,
configSchema,
);
// Create connection info
const connection = option.connection.url
? {
...option.connection,
type: 'http',
}
: {
...option.connection,
type: 'stdio',
};
return {
allDependenciesMet,
configSchema,
connection,
isRecommended: option.isRecommended,
needsConfig,
packageInstalled,
systemDependencies: systemDependenciesResults,
};
}
}
// Create singleton instance
export const mcpSystemDepsCheckService = new MCPSystemDepsCheckService();