'use strict'; const { readFileTool } = require('./read_file'); const { editFileTool } = require('./edit_file'); const { runCommandTool } = require('./run_command'); const { webSearchTool, extractWebpageTool } = require('./web_search'); const { searchWorkspaceTool } = require('./search_workspace'); const { readMediafileTool } = require('./read_mediafile'); const path = require('path'); const MAX_RUN_COMMAND_CHARS = 10000; const MAX_EXTRACT_WEBPAGE_CHARS = 80000; function formatFailure(err) { return `失败: ${err}`; } function formatReadFile(result) { if (!result.success) return formatFailure(result.error || '读取失败'); if (result.type === 'read') { return result.content || ''; } if (result.type === 'search') { const parts = []; for (const match of result.matches || []) { const hits = (match.hits || []).join(',') || ''; parts.push(`[${match.id}] L${match.line_start}-${match.line_end} hits:${hits}`); parts.push(match.snippet || ''); parts.push(''); } return parts.join('\n').trim(); } if (result.type === 'extract') { const parts = []; for (const seg of result.segments || []) { const label = seg.label || 'segment'; parts.push(`[${label}] L${seg.line_start}-${seg.line_end}`); parts.push(seg.content || ''); parts.push(''); } return parts.join('\n').trim(); } return ''; } function formatEditFile(result) { if (!result.success) return formatFailure(result.error || '修改失败'); const count = typeof result.replacements === 'number' ? result.replacements : 0; return `已替换 ${count} 处: ${result.path}`; } function formatWebSearch(result) { if (!result.success) return formatFailure(result.error || '搜索失败'); const time = result.searched_at || new Date().toISOString(); const summary = result.answer || ''; const lines = []; lines.push(`🔍 搜索查询: ${result.query}`); lines.push(`📅 搜索时间: ${time}`); lines.push(''); lines.push('📝 AI摘要:'); lines.push(summary || ''); lines.push(''); lines.push('---'); lines.push(''); lines.push('📊 搜索结果:'); const results = result.results || []; results.forEach((item, idx) => { lines.push(`${idx + 1}. ${item.title || ''}`); lines.push(` 🔗 ${item.url || ''}`); lines.push(` 📄 ${item.content || ''}`); }); return lines.join('\n').trim(); } function formatExtractWebpage(result, mode, url, targetPath) { if (!result.success) return formatFailure(result.error || '提取失败'); if (mode === 'save') { return `已保存: ${targetPath} (chars=${result.chars}, bytes=${result.bytes})`; } let content = result.content || ''; if (content.length > MAX_EXTRACT_WEBPAGE_CHARS) content = content.slice(0, MAX_EXTRACT_WEBPAGE_CHARS); return `URL: ${url}\n${content}`; } function formatRunCommand(result) { if (!result.success) { if (result.status === 'timeout') { const output = result.output ? result.output.slice(-MAX_RUN_COMMAND_CHARS) : ''; return `[timeout after ${result.timeout}s]\n${output}`.trim(); } return `[error rc=${result.return_code ?? '1'}] ${result.error || ''}\n${result.output || ''}`.trim(); } let output = result.output || ''; if (output.length > MAX_RUN_COMMAND_CHARS) output = output.slice(-MAX_RUN_COMMAND_CHARS); return output || '[no_output]'; } function formatSearchWorkspace(result) { if (!result.success) return formatFailure(result.error || '搜索失败'); if (result.mode === 'file') { const lines = [`命中文件(${result.matches.length}):`]; result.matches.forEach((p, idx) => lines.push(`${idx + 1}) ${p}`)); return lines.join('\n'); } if (result.mode === 'content') { const parts = []; for (const item of result.results || []) { parts.push(item.file); for (const m of item.matches || []) { parts.push(`- L${m.line}: ${m.snippet}`); } parts.push(''); } return parts.join('\n').trim(); } return ''; } function formatReadMediafile(result) { if (!result.success) return formatFailure(result.error || '读取失败'); if (result.type === 'image') return `已附加图片: ${result.path}`; return `已附加视频: ${result.path}`; } function buildToolContent(result) { if (result.type === 'image' || result.type === 'video') { const payload = { type: result.type === 'image' ? 'image_url' : 'video_url', [result.type === 'image' ? 'image_url' : 'video_url']: { url: `data:${result.mime};base64,${result.b64}`, }, }; return [ { type: 'text', text: formatReadMediafile(result) }, payload, ]; } return null; } async function executeTool({ workspace, config, allowMode, toolCall, abortSignal }) { const name = toolCall.function.name; let args = {}; try { args = JSON.parse(toolCall.function.arguments || '{}'); } catch (err) { return { success: false, tool: name, error: `JSON解析失败: ${err.message || err}`, formatted: formatFailure('参数解析失败'), }; } if (allowMode === 'read_only' && (name === 'edit_file' || name === 'run_command')) { const note = '当前为只读模式,已禁用 edit_file、run_command。若需使用,请告知当前无权限需要用户输入 /allow 切换权限。'; return { success: false, tool: name, error: note, formatted: note, }; } if (abortSignal && abortSignal.aborted) { return { success: false, tool: name, error: '任务被用户取消', formatted: '任务被用户取消', raw: { success: false, error: '任务被用户取消', cancelled: true }, }; } let raw; if (name === 'read_file') raw = readFileTool(workspace, args); else if (name === 'edit_file') raw = editFileTool(workspace, args); else if (name === 'run_command') raw = await runCommandTool(workspace, args, abortSignal); else if (name === 'web_search') raw = await webSearchTool(config, args, abortSignal); else if (name === 'extract_webpage') { if (!args.mode) args.mode = 'read'; const targetPath = args.target_path ? (path.isAbsolute(args.target_path) ? args.target_path : path.join(workspace, args.target_path)) : null; if (args.mode === 'save') { if (!targetPath || !targetPath.toLowerCase().endsWith('.md')) { raw = { success: false, error: 'target_path 必须是 .md 文件' }; } else { raw = await extractWebpageTool(config, args, targetPath, abortSignal); } } else { raw = await extractWebpageTool(config, args, targetPath, abortSignal); } } else if (name === 'search_workspace') raw = await searchWorkspaceTool(workspace, args); else if (name === 'read_mediafile') raw = readMediafileTool(workspace, args); else raw = { success: false, error: '未知工具' }; let formatted = ''; if (name === 'read_file') formatted = formatReadFile(raw); else if (name === 'edit_file') formatted = formatEditFile(raw); else if (name === 'run_command') formatted = formatRunCommand(raw); else if (name === 'web_search') formatted = formatWebSearch(raw); else if (name === 'extract_webpage') formatted = formatExtractWebpage(raw, args.mode, args.url, args.target_path); else if (name === 'search_workspace') formatted = formatSearchWorkspace(raw); else if (name === 'read_mediafile') formatted = formatReadMediafile(raw); else formatted = raw.success ? '' : formatFailure(raw.error || '失败'); const toolContent = buildToolContent(raw); return { success: raw.success, tool: name, raw, formatted, tool_content: toolContent, }; } module.exports = { executeTool };