import { exec, spawn } from "child_process"; import { promisify } from "util"; import { existsSync, readFileSync } from "fs"; import { join } from "path"; const execAsync = promisify(exec); // 进程管理器 - 跟踪所有启动的开发服务器 const managedProcesses = new Map(); export const devServerTool = { name: "dev_server", description: "开发服务器管理。启动/停止/重启开发服务器,查看运行状态和实时日志。支持自动检测项目的 dev 命令。", inputSchema: { type: "object", properties: { project_path: { type: "string", description: "项目根目录(绝对路径)", }, action: { type: "string", description: "操作类型", enum: ["start", "stop", "restart", "status", "logs", "list"], }, command: { type: "string", description: "自定义启动命令(可选,默认自动检测 npm run dev 等)", }, name: { type: "string", description: "服务器名称/标识(可选,默认使用目录名)", }, tail: { type: "number", description: "显示最近几行日志(默认 30)", }, }, required: ["action"], }, }; function getServerName(projectPath, name) { if (name) return name; if (projectPath) return projectPath.split(/[/\\]/).pop() || "server"; return "default"; } function detectDevCommand(projectPath) { const pkgPath = join(projectPath, "package.json"); if (existsSync(pkgPath)) { try { const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")); const scripts = pkg.scripts || {}; // 按优先级检测 for (const name of ["dev", "start:dev", "serve", "start"]) { if (scripts[name]) return `npm run ${name}`; } } catch { } } if (existsSync(join(projectPath, "manage.py"))) { return "python manage.py runserver"; } if (existsSync(join(projectPath, "main.py"))) { return "python main.py"; } if (existsSync(join(projectPath, "app.py"))) { return "python app.py"; } return null; } export async function executeDevServer(args) { const { project_path, action, command, name, tail = 30 } = args; switch (action) { case "list": { if (managedProcesses.size === 0) { return "# 开发服务器\n\n_没有正在运行的服务器_"; } const output = ["# 运行中的开发服务器", ""]; for (const [key, info] of managedProcesses) { const running = !info.process.killed && info.process.exitCode === null; const uptime = Math.round((Date.now() - info.startTime) / 1000); output.push(`## ${running ? "🟢" : "🔴"} ${key}`, `- 命令: \`${info.command}\``, `- 目录: ${info.cwd}`, `- PID: ${info.process.pid}`, `- 运行时间: ${uptime}s`, `- 状态: ${running ? "运行中" : "已停止"}`, ``); } return output.join("\n"); } case "start": { if (!project_path) return "❌ start 需要 project_path 参数"; const serverName = getServerName(project_path, name); const existing = managedProcesses.get(serverName); if (existing && !existing.process.killed && existing.process.exitCode === null) { return `⚠️ 服务器 "${serverName}" 已在运行中 (PID: ${existing.process.pid})\n\n使用 action=restart 重启,或 action=stop 先停止`; } const startCmd = command || detectDevCommand(project_path); if (!startCmd) { return `❌ 未检测到启动命令,请手动指定 command 参数\n\n常见命令:\n- npm run dev\n- npm start\n- python app.py`; } // 解析命令 const isWin = process.platform === "win32"; const child = spawn(isWin ? "cmd" : "sh", [isWin ? "/c" : "-c", startCmd], { cwd: project_path, stdio: ["ignore", "pipe", "pipe"], detached: false, }); const logs = []; const maxLogs = 200; const addLog = (data, stream) => { const lines = data.toString().split("\n").filter(Boolean); for (const line of lines) { logs.push(`[${stream}] ${line}`); if (logs.length > maxLogs) logs.shift(); } }; child.stdout?.on("data", (data) => addLog(data, "out")); child.stderr?.on("data", (data) => addLog(data, "err")); managedProcesses.set(serverName, { process: child, command: startCmd, cwd: project_path, startTime: Date.now(), logs, }); // 等待一会检查是否立即崩溃 await new Promise((r) => setTimeout(r, 2000)); const crashed = child.exitCode !== null; if (crashed) { const output = logs.join("\n"); managedProcesses.delete(serverName); return `# ❌ 服务器启动失败\n\n命令: \`${startCmd}\`\n退出码: ${child.exitCode}\n\n\`\`\`\n${output.slice(0, 3000)}\n\`\`\``; } return [ `# ✅ 服务器已启动`, ``, `- 名称: ${serverName}`, `- 命令: \`${startCmd}\``, `- PID: ${child.pid}`, `- 目录: ${project_path}`, ``, `最近日志:`, "```", logs.slice(-10).join("\n") || "(等待输出...)", "```", ``, `💡 使用 \`dev_server action=logs\` 查看实时日志`, ].join("\n"); } case "stop": { const serverName = getServerName(project_path, name); const info = managedProcesses.get(serverName); if (!info) { return `❌ 未找到服务器 "${serverName}"\n\n使用 action=list 查看所有运行中的服务器`; } const pid = info.process.pid; try { // Windows 需要 taskkill 杀进程树 if (process.platform === "win32" && pid) { await execAsync(`taskkill /PID ${pid} /T /F`).catch(() => { }); } else { info.process.kill("SIGTERM"); } } catch { } managedProcesses.delete(serverName); return `# ✅ 服务器已停止\n\n- 名称: ${serverName}\n- PID: ${pid}`; } case "restart": { const serverName = getServerName(project_path, name); const info = managedProcesses.get(serverName); if (info) { try { if (process.platform === "win32" && info.process.pid) { await execAsync(`taskkill /PID ${info.process.pid} /T /F`).catch(() => { }); } else { info.process.kill("SIGTERM"); } } catch { } managedProcesses.delete(serverName); await new Promise((r) => setTimeout(r, 1000)); } // 重新启动 return executeDevServer({ project_path: info?.cwd || project_path, action: "start", command: command || info?.command, name: serverName, }); } case "status": { const serverName = getServerName(project_path, name); const info = managedProcesses.get(serverName); if (!info) { return `❌ 未找到服务器 "${serverName}"`; } const running = !info.process.killed && info.process.exitCode === null; const uptime = Math.round((Date.now() - info.startTime) / 1000); return [ `# ${running ? "🟢" : "🔴"} ${serverName}`, ``, `- 状态: ${running ? "运行中" : `已停止 (退出码: ${info.process.exitCode})`}`, `- 命令: \`${info.command}\``, `- PID: ${info.process.pid}`, `- 运行时间: ${uptime}s`, `- 目录: ${info.cwd}`, ].join("\n"); } case "logs": { const serverName = getServerName(project_path, name); const info = managedProcesses.get(serverName); if (!info) { return `❌ 未找到服务器 "${serverName}"`; } const recentLogs = info.logs.slice(-tail); return [ `# 📋 ${serverName} 日志(最近 ${tail} 行)`, ``, "```", recentLogs.join("\n") || "(无日志)", "```", ].join("\n"); } default: return `❌ 未知操作: ${action}`; } } //# sourceMappingURL=devServer.js.map