/** * WordPress Tools - OpenClaw 插件 * 提供 WordPress 发布和 AI 生图工具 * * 修复说明: * 1. register 改为同步方法(解决 "register must be synchronous" 错误) * 2. wp_publish 新增 images 参数(支持传入图片路径上传到媒体库) * 3. 新增 wp_publish_with_image 工具(一步完成生图+发布) */ import { spawn } from 'child_process'; // WordPress 发布脚本路径 const WP_PUBLISH_SCRIPT = '/www/wwwroot/wp-publish/scripts/wp_publish_text.py'; const WP_GENERATE_SCRIPT = '/www/wwwroot/wp-publish/scripts/wp_generate_image.py'; const PYTHON_PATH = '/usr/bin/python3'; /** * 执行 Python 脚本 */ function executePythonScript(scriptPath, args) { return new Promise((resolve, reject) => { const process = spawn(PYTHON_PATH, [scriptPath, ...args], { env: { ...process.env }, stdio: ['pipe', 'pipe', 'pipe'] }); let stdout = ''; let stderr = ''; process.stdout.on('data', (data) => { stdout += data.toString(); }); process.stderr.on('data', (data) => { stderr += data.toString(); }); process.on('close', (code) => { if (code === 0) { resolve(stdout); } else { reject(new Error(`脚本执行失败: ${stderr}`)); } }); process.on('error', (error) => { reject(error); }); }); } /** * 定义插件入口 */ export default { id: 'wp-tools', name: 'WordPress Tools', description: 'WordPress 发布和 AI 生图工具', // 修复1:去掉 async,改为同步注册 register(api) { // ========== 工具 1:WordPress 发布 ========== api.registerTool({ name: 'wp_publish', description: '发布文章到 WordPress 网站。支持文字内容和图片上传。', parameters: { type: 'object', properties: { text: { type: 'string', description: '文章内容(必填)' }, title: { type: 'string', description: '文章标题(可选,默认从内容提取)' }, category: { type: 'string', description: '分类名称(如:ai, ai-kepu, jishu 等)' }, tags: { type: 'string', description: '标签(可选)' }, status: { type: 'string', description: '发布状态(publish/draft)', enum: ['publish', 'draft'] }, images: { type: 'string', description: '图片文件路径(可选),多个路径用逗号分隔。AI 生图完成后,将返回的图片路径传入此参数即可自动上传到 WordPress 媒体库。' } }, required: ['text'] }, async execute(_id, params) { try { const args = [ params.text, '--title', params.title || '', '--instruction', params.category ? `#分类 ${params.category}` : '', '--status', params.status || 'publish' ]; // 修复2:支持传入图片路径 if (params.images) { const imagePaths = params.images.split(',').map(p => p.trim()).filter(Boolean); for (const imgPath of imagePaths) { args.push('--images', imgPath); } } const result = await executePythonScript(WP_PUBLISH_SCRIPT, args); return { content: [{ type: 'text', text: `✅ 文章发布成功!\n\n${result}` }] }; } catch (error) { return { content: [{ type: 'text', text: `❌ 发布失败:${error.message}` }] }; } } }); // ========== 工具 2:AI 生图 ========== api.registerTool({ name: 'wp_generate_image', description: '使用 AI 生成图片。支持通义万相模型,可指定尺寸、格式和数量。生成后的图片可通过 wp_publish 的 images 参数上传到 WordPress 媒体库。', parameters: { type: 'object', properties: { prompt: { type: 'string', description: '图片描述(必填,如:一只可爱的猫咪在草地上晒太阳)' }, model: { type: 'string', description: '模型名称(默认:wanx-v1)', enum: ['wanx-v1', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus'] }, size: { type: 'string', description: '图片尺寸(默认:1024*1024)', enum: ['1024*1024', '720*1280', '1280*720', '512*512'] }, format: { type: 'string', description: '图片格式(默认:webp)', enum: ['webp', 'jpg', 'png'] }, count: { type: 'number', description: '生成数量(1-4,默认:1)', minimum: 1, maximum: 4 }, style: { type: 'string', description: '图片风格(如:写实、动漫、水彩等)' } }, required: ['prompt'] }, async execute(_id, params) { try { const args = [ params.prompt, '--model', params.model || 'wanx-v1', '--size', params.size || '1024*1024', '--format', params.format || 'webp', '--count', params.count || 1 ]; if (params.style) { args.push('--style', params.style); } const result = await executePythonScript(WP_GENERATE_SCRIPT, args); return { content: [{ type: 'text', text: `✅ AI 生图完成!\n\n${result}` }] }; } catch (error) { return { content: [{ type: 'text', text: `❌ AI 生图失败:${error.message}` }] }; } } }); // ========== 工具 3:生图并发布(新增) ========== api.registerTool({ name: 'wp_publish_with_image', description: '一步完成 AI 生图并发布到 WordPress。自动执行:生图 → 上传到媒体库 → 发布文章。', parameters: { type: 'object', properties: { title: { type: 'string', description: '文章标题(必填)' }, text: { type: 'string', description: '文章内容(必填)' }, image_prompt: { type: 'string', description: '图片描述(必填,用于 AI 生图)' }, image_model: { type: 'string', description: '生图模型(默认:wanx-v1)', enum: ['wanx-v1', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus'] }, image_size: { type: 'string', description: '图片尺寸(默认:1024*1024)', enum: ['1024*1024', '720*1280', '1280*720', '512*512'] }, category: { type: 'string', description: '分类名称' }, tags: { type: 'string', description: '标签' }, status: { type: 'string', description: '发布状态(publish/draft)', enum: ['publish', 'draft'] } }, required: ['title', 'text', 'image_prompt'] }, async execute(_id, params) { try { // 步骤1:先调用生图 const genArgs = [ params.image_prompt, '--model', params.image_model || 'wanx-v1', '--size', params.image_size || '1024*1024', '--format', 'jpg', '--count', '1' ]; const genResult = await executePythonScript(WP_GENERATE_SCRIPT, genArgs); // 从生图结果中提取图片路径 let imagePath = ''; try { // 尝试从输出中提取 JSON 中的 paths const jsonMatch = genResult.match(/\{[\s\S]*"paths"[\s\S]*\}/); if (jsonMatch) { const genJson = JSON.parse(jsonMatch[0]); if (genJson.paths && genJson.paths.length > 0) { imagePath = genJson.paths[0]; } } } catch (e) { // 如果解析失败,尝试从文本中提取路径 const pathMatch = genResult.match(/\/www\/wwwroot\/wp-publish\/temp\/[\w.]+/); if (pathMatch) { imagePath = pathMatch[0]; } } if (!imagePath) { return { content: [{ type: 'text', text: `⚠️ 图片生成完成但未找到路径,直接发布文字内容。\n\n生图结果:${genResult}` }] }; } // 步骤2:发布文章并上传图片 const pubArgs = [ params.text, '--title', params.title, '--instruction', params.category ? `#分类 ${params.category}` : '', '--status', params.status || 'publish', '--images', imagePath ]; const pubResult = await executePythonScript(WP_PUBLISH_SCRIPT, pubArgs); return { content: [{ type: 'text', text: `✅ 生图并发布成功!\n\n生图结果:${genResult}\n\n发布结果:${pubResult}` }] }; } catch (error) { return { content: [{ type: 'text', text: `❌ 操作失败:${error.message}` }] }; } } }); console.log('[wp-tools] WordPress 工具已注册'); } };