/** * 工具执行器 * 接收后端的 tool_call 事件,执行本地工具,返回结果 */ import * as vscode from 'vscode'; import * as path from 'path'; import * as os from 'os'; import * as fs from 'fs'; import { readFileContent, readDirectory } from '../utils/readFiles'; import { createOrOverwriteFile } from '../utils/createFiles'; import { generateVCD, checkIverilogAvailable } from '../utils/iverilogRunner'; import { submitToolResult, createSuccessResult, createBusinessErrorResult, createSystemErrorResult } from './apiClient'; import type { ToolCallRequest, ToolName, FileReadArgs, FileWriteArgs, FileListArgs, SyntaxCheckArgs, SimulationArgs, WaveformSummaryArgs } from '../types/api'; /** * 工具执行器上下文 */ export interface ToolExecutorContext { /** 扩展路径(用于 iverilog) */ extensionPath: string; /** 工作区路径 */ workspacePath: string; } /** * 执行工具调用 * @param request 工具调用请求 * @param context 执行上下文 */ export async function executeToolCall( request: ToolCallRequest, context: ToolExecutorContext ): Promise { const toolName = request.params.name as ToolName; const args = request.params.arguments; const callId = request.id; console.log(`[ToolExecutor] 执行工具: ${toolName}, callId=${callId}`, args); try { let resultText: string; switch (toolName) { case 'file_read': resultText = await executeFileRead(args as unknown as FileReadArgs); break; case 'file_write': resultText = await executeFileWrite(args as unknown as FileWriteArgs); break; case 'file_list': resultText = await executeFileList(args as unknown as FileListArgs); break; case 'syntax_check': resultText = await executeSyntaxCheck(args as unknown as SyntaxCheckArgs, context); break; case 'simulation': resultText = await executeSimulation(args as unknown as SimulationArgs, context); break; case 'waveform_summary': resultText = await executeWaveformSummary(args as unknown as WaveformSummaryArgs); break; default: throw new Error(`未知工具: ${toolName}`); } // 提交成功结果 const result = createSuccessResult(callId, resultText); await submitToolResult(result); console.log(`[ToolExecutor] 工具执行成功: ${toolName}, callId=${callId}`); } catch (error) { const errorMessage = error instanceof Error ? error.message : '未知错误'; console.error(`[ToolExecutor] 工具执行失败: ${toolName}, callId=${callId}`, error); // 提交错误结果 const result = createBusinessErrorResult(callId, errorMessage); await submitToolResult(result); } } /** * 执行 file_read 工具 */ async function executeFileRead(args: FileReadArgs): Promise { const content = await readFileContent(args.path); return content; } /** * 执行 file_write 工具 */ async function executeFileWrite(args: FileWriteArgs): Promise { await createOrOverwriteFile(args.path, args.content); return `文件已写入: ${args.path}`; } /** * 执行 file_list 工具 */ async function executeFileList(args: FileListArgs): Promise { const dirPath = args.path || '.'; const extensions = args.extension ? [args.extension] : undefined; const files = await readDirectory(dirPath, extensions); const fileList = files.map(f => f.path).join('\n'); return fileList || '(目录为空)'; } /** * 执行 syntax_check 工具 * 将代码写入临时文件,调用 iverilog 检查语法 */ async function executeSyntaxCheck( args: SyntaxCheckArgs, context: ToolExecutorContext ): Promise { // 检查 iverilog 是否可用 const iverilogCheck = await checkIverilogAvailable(context.extensionPath); if (!iverilogCheck.available) { throw new Error(`iverilog 不可用: ${iverilogCheck.message}`); } // 创建临时文件 const tempDir = os.tmpdir(); const tempFile = path.join(tempDir, `iccoder_syntax_${Date.now()}.v`); try { // 写入代码到临时文件 fs.writeFileSync(tempFile, args.code, 'utf-8'); // 调用 iverilog 进行语法检查 const { spawn } = require('child_process'); const iverilogPath = getIverilogPath(context.extensionPath); return new Promise((resolve, reject) => { const child = spawn(iverilogPath, ['-t', 'null', tempFile], { cwd: tempDir, env: { ...process.env, IVERILOG_ROOT: path.join(context.extensionPath, 'tools', 'iverilog') } }); let stdout = ''; let stderr = ''; child.stdout.on('data', (data: Buffer) => { stdout += data.toString(); }); child.stderr.on('data', (data: Buffer) => { stderr += data.toString(); }); child.on('close', (code: number) => { // 清理临时文件 try { fs.unlinkSync(tempFile); } catch (e) { // 忽略清理错误 } if (code === 0) { resolve('语法检查通过,无错误。'); } else { resolve(`语法检查发现错误:\n${stderr || stdout}`); } }); child.on('error', (error: Error) => { try { fs.unlinkSync(tempFile); } catch (e) { // 忽略清理错误 } reject(error); }); }); } catch (error) { // 确保清理临时文件 try { fs.unlinkSync(tempFile); } catch (e) { // 忽略 } throw error; } } /** * 执行 simulation 工具 */ async function executeSimulation( args: SimulationArgs, context: ToolExecutorContext ): Promise { // 获取工作区路径 const workspaceFolders = vscode.workspace.workspaceFolders; if (!workspaceFolders || workspaceFolders.length === 0) { throw new Error('请先打开一个工作区'); } const projectPath = workspaceFolders[0].uri.fsPath; // 调用现有的 generateVCD 函数 const result = await generateVCD(projectPath, context.extensionPath); if (result.success) { let message = result.message; if (result.stdout) { message += `\n\n仿真输出:\n${result.stdout}`; } return message; } else { let errorMessage = result.message; if (result.stderr) { errorMessage += `\n\n错误输出:\n${result.stderr}`; } throw new Error(errorMessage); } } /** * 执行 waveform_summary 工具 * TODO: 实现 VCD 波形分析 */ async function executeWaveformSummary(args: WaveformSummaryArgs): Promise { // TODO: 使用 vcdrom/vcd-stream 解析 VCD 文件 // 目前返回一个占位响应 return `波形分析功能暂未实现。\n请求参数:\n- VCD文件: ${args.vcdPath}\n- 信号: ${args.signals}\n- 检查点: ${args.checkpoints || '无'}`; } /** * 获取 iverilog 路径 */ function getIverilogPath(extensionPath: string): string { const platform = process.platform; if (platform === 'win32') { return path.join(extensionPath, 'tools', 'iverilog', 'bin', 'iverilog.exe'); } else { return path.join(extensionPath, 'tools', 'iverilog', 'bin', 'iverilog'); } } /** * 创建工具执行器上下文 */ export function createToolExecutorContext(extensionPath: string): ToolExecutorContext { const workspaceFolders = vscode.workspace.workspaceFolders; const workspacePath = workspaceFolders?.[0]?.uri.fsPath || ''; return { extensionPath, workspacePath }; }