5e32375f0e
架构变更: - 采用 pnpm workspaces 实现 Monorepo 结构 - 将现有代码迁移到 packages/core - 新增 packages/server HTTP 服务层 Server 功能: - REST API: 会话管理、工具管理、配置管理 - WebSocket: 实时双向通信支持 - SSE: 服务端事件推送 - Hono + Bun 作为运行时 API 端点: - GET/POST /api/sessions - 会话 CRUD - GET/POST /api/sessions/:id/messages - 消息管理 - GET /api/sessions/:id/events - SSE 事件流 - WS /api/ws/:sessionId - WebSocket 连接 - GET/POST /api/tools - 工具管理 - GET/PUT /api/config - 配置管理
285 lines
7.0 KiB
TypeScript
285 lines
7.0 KiB
TypeScript
/**
|
|
* LSP CLI 命令
|
|
* 提供语言服务器的查询和安装功能
|
|
*/
|
|
|
|
import { execSync, spawnSync } from 'child_process';
|
|
import { getUniqueServers, type ServerConfig, type InstallConfig } from './server.js';
|
|
import type { LanguageId } from './language.js';
|
|
|
|
// 服务器状态
|
|
export interface ServerStatus {
|
|
id: string;
|
|
displayName: string;
|
|
description: string;
|
|
command: string;
|
|
installed: boolean;
|
|
languages: LanguageId[];
|
|
install: InstallConfig;
|
|
}
|
|
|
|
/**
|
|
* 检查命令是否存在
|
|
*/
|
|
function commandExists(command: string): boolean {
|
|
try {
|
|
execSync(`which ${command}`, { stdio: 'ignore' });
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 检查包管理器是否可用
|
|
*/
|
|
function hasPackageManager(manager: string): boolean {
|
|
return commandExists(manager);
|
|
}
|
|
|
|
/**
|
|
* 获取所有服务器状态
|
|
*/
|
|
export function listServers(): ServerStatus[] {
|
|
const servers = getUniqueServers();
|
|
|
|
return servers.map((server) => ({
|
|
id: server.id,
|
|
displayName: server.config.displayName,
|
|
description: server.config.description,
|
|
command: server.config.command,
|
|
installed: commandExists(server.config.command),
|
|
languages: server.languages,
|
|
install: server.config.install,
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* 打印服务器列表
|
|
*/
|
|
export function printServerList(): void {
|
|
const servers = listServers();
|
|
|
|
console.log('\n语言服务器状态:\n');
|
|
console.log(' 状态 | 服务器 | 支持语言');
|
|
console.log(' ------+--------------------------------+------------------');
|
|
|
|
for (const server of servers) {
|
|
const status = server.installed ? ' ✓ ' : ' ✗ ';
|
|
const statusColor = server.installed ? '\x1b[32m' : '\x1b[31m';
|
|
const reset = '\x1b[0m';
|
|
const name = server.displayName.padEnd(30);
|
|
const langs = server.languages.slice(0, 3).join(', ') + (server.languages.length > 3 ? '...' : '');
|
|
|
|
console.log(` ${statusColor}${status}${reset} | ${name} | ${langs}`);
|
|
}
|
|
|
|
const installed = servers.filter((s) => s.installed).length;
|
|
console.log(`\n 已安装: ${installed}/${servers.length}\n`);
|
|
}
|
|
|
|
/**
|
|
* 获取安装命令
|
|
*/
|
|
function getInstallCommand(install: InstallConfig): { command: string; description: string } | null {
|
|
// 优先使用 npm
|
|
if (install.npm && hasPackageManager('npm')) {
|
|
return {
|
|
command: `npm install -g ${install.npm}`,
|
|
description: 'npm',
|
|
};
|
|
}
|
|
|
|
// pip
|
|
if (install.pip && hasPackageManager('pip3')) {
|
|
return {
|
|
command: `pip3 install ${install.pip}`,
|
|
description: 'pip3',
|
|
};
|
|
}
|
|
if (install.pip && hasPackageManager('pip')) {
|
|
return {
|
|
command: `pip install ${install.pip}`,
|
|
description: 'pip',
|
|
};
|
|
}
|
|
|
|
// go
|
|
if (install.go && hasPackageManager('go')) {
|
|
return {
|
|
command: `go install ${install.go}`,
|
|
description: 'go install',
|
|
};
|
|
}
|
|
|
|
// rustup
|
|
if (install.rustup && hasPackageManager('rustup')) {
|
|
return {
|
|
command: `rustup component add ${install.rustup}`,
|
|
description: 'rustup',
|
|
};
|
|
}
|
|
|
|
// cargo
|
|
if (install.cargo && hasPackageManager('cargo')) {
|
|
return {
|
|
command: `cargo install ${install.cargo}`,
|
|
description: 'cargo',
|
|
};
|
|
}
|
|
|
|
// brew
|
|
if (install.brew && hasPackageManager('brew')) {
|
|
return {
|
|
command: `brew install ${install.brew}`,
|
|
description: 'Homebrew',
|
|
};
|
|
}
|
|
|
|
// gem
|
|
if (install.gem && hasPackageManager('gem')) {
|
|
return {
|
|
command: `gem install ${install.gem}`,
|
|
description: 'RubyGems',
|
|
};
|
|
}
|
|
|
|
// custom
|
|
if (install.custom) {
|
|
return {
|
|
command: install.custom,
|
|
description: '自定义命令',
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 安装服务器
|
|
*/
|
|
export async function installServer(serverId: string): Promise<boolean> {
|
|
const servers = listServers();
|
|
const server = servers.find(
|
|
(s) => s.id === serverId || s.displayName.toLowerCase() === serverId.toLowerCase()
|
|
);
|
|
|
|
if (!server) {
|
|
console.error(`\x1b[31m错误: 未找到服务器 "${serverId}"\x1b[0m`);
|
|
console.log('\n可用的服务器:');
|
|
servers.forEach((s) => console.log(` - ${s.displayName} (${s.id})`));
|
|
return false;
|
|
}
|
|
|
|
if (server.installed) {
|
|
console.log(`\x1b[32m✓ ${server.displayName} 已安装\x1b[0m`);
|
|
return true;
|
|
}
|
|
|
|
const installCmd = getInstallCommand(server.install);
|
|
|
|
if (!installCmd) {
|
|
console.error(`\x1b[31m无法自动安装 ${server.displayName}\x1b[0m`);
|
|
if (server.install.manual) {
|
|
console.log('\n手动安装说明:');
|
|
console.log(server.install.manual);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
console.log(`\n正在安装 ${server.displayName}...`);
|
|
console.log(`使用 ${installCmd.description}: ${installCmd.command}\n`);
|
|
|
|
try {
|
|
const result = spawnSync(installCmd.command, {
|
|
shell: true,
|
|
stdio: 'inherit',
|
|
});
|
|
|
|
if (result.status === 0) {
|
|
console.log(`\n\x1b[32m✓ ${server.displayName} 安装成功\x1b[0m`);
|
|
return true;
|
|
} else {
|
|
console.error(`\n\x1b[31m✗ ${server.displayName} 安装失败\x1b[0m`);
|
|
if (server.install.manual) {
|
|
console.log('\n手动安装说明:');
|
|
console.log(server.install.manual);
|
|
}
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
console.error(`\n\x1b[31m安装出错: ${error}\x1b[0m`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 安装所有服务器
|
|
*/
|
|
export async function installAllServers(): Promise<void> {
|
|
const servers = listServers();
|
|
const notInstalled = servers.filter((s) => !s.installed);
|
|
|
|
if (notInstalled.length === 0) {
|
|
console.log('\x1b[32m所有语言服务器都已安装\x1b[0m');
|
|
return;
|
|
}
|
|
|
|
console.log(`\n将安装 ${notInstalled.length} 个语言服务器:\n`);
|
|
notInstalled.forEach((s) => console.log(` - ${s.displayName}`));
|
|
console.log('');
|
|
|
|
let success = 0;
|
|
let failed = 0;
|
|
|
|
for (const server of notInstalled) {
|
|
const result = await installServer(server.id);
|
|
if (result) {
|
|
success++;
|
|
} else {
|
|
failed++;
|
|
}
|
|
console.log('');
|
|
}
|
|
|
|
console.log(`\n安装完成: ${success} 成功, ${failed} 失败`);
|
|
}
|
|
|
|
/**
|
|
* 显示服务器详细信息
|
|
*/
|
|
export function showServerInfo(serverId: string): void {
|
|
const servers = listServers();
|
|
const server = servers.find(
|
|
(s) => s.id === serverId || s.displayName.toLowerCase() === serverId.toLowerCase()
|
|
);
|
|
|
|
if (!server) {
|
|
console.error(`\x1b[31m错误: 未找到服务器 "${serverId}"\x1b[0m`);
|
|
return;
|
|
}
|
|
|
|
const status = server.installed ? '\x1b[32m已安装\x1b[0m' : '\x1b[31m未安装\x1b[0m';
|
|
|
|
console.log(`\n${server.displayName}`);
|
|
console.log('='.repeat(40));
|
|
console.log(`状态: ${status}`);
|
|
console.log(`命令: ${server.command}`);
|
|
console.log(`描述: ${server.description}`);
|
|
console.log(`支持语言: ${server.languages.join(', ')}`);
|
|
|
|
if (!server.installed) {
|
|
const installCmd = getInstallCommand(server.install);
|
|
if (installCmd) {
|
|
console.log(`\n安装命令 (${installCmd.description}):`);
|
|
console.log(` ${installCmd.command}`);
|
|
}
|
|
if (server.install.manual) {
|
|
console.log('\n手动安装说明:');
|
|
console.log(server.install.manual);
|
|
}
|
|
}
|
|
|
|
console.log('');
|
|
}
|