feat: 重构为 Monorepo 架构并实现 HTTP Server
架构变更: - 采用 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 - 配置管理
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
/**
|
||||
* 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('');
|
||||
}
|
||||
Reference in New Issue
Block a user