import path from 'path'; import os from 'os'; import fs from 'fs'; import { execSync } from 'child_process'; import { PIPELINE_SCRIPT, PROJECT_ROOT } from './shared'; import type { ToolDefinition } from './types'; export const createManifest: ToolDefinition = { name: 'create_manifest', description: '初始化新的 manifest.json,调用 pipeline.js init 创建输出目录和项目骨架。这是视频创作 pipeline 的起点。', input_schema: { type: 'object', properties: { accountId: { type: 'string', description: '账号ID,决定模型、参考图等配置来源' }, mode: { type: 'string', description: '创作模式: single (单图模式) 或 framePair (首尾帧模式)' }, items: { type: 'string', description: '分镜数据 JSON 数组字符串。每个元素需包含 shotDesc (分镜描述) 和 script (旁白文案),可选 imagePrompt、videoPrompt、directorRef、keyword、duration' }, imageModel: { type: 'string', description: '生图模型(可选,默认继承账号配置): gemini, mj, gpt-image, kling' }, videoModel: { type: 'string', description: '视频模型(可选,默认继承账号配置): veo3-fast, veo3-fast-frames, kling, grok' }, format: { type: 'string', description: '画幅(可选,默认继承账号配置): 9:16, 16:9, 1:1' }, }, required: ['accountId', 'mode', 'items'], }, execute: async (params) => { const { accountId, mode, items, imageModel, videoModel, format } = params as Record; // Validate items JSON let parsed: unknown; try { parsed = JSON.parse(items); } catch { return '错误: items 不是合法的 JSON 字符串'; } if (!Array.isArray(parsed) || (parsed as unknown[]).length === 0) return '错误: items 必须是非空 JSON 数组'; // Write items to temp file to avoid shell escaping issues const tmpFile = path.join(os.tmpdir(), `pipeline-items-${Date.now()}.json`); fs.writeFileSync(tmpFile, items, 'utf-8'); try { const args = [ `"${PIPELINE_SCRIPT}"`, 'init', `--account "${accountId}"`, `--mode ${mode}`, `--items-file "${tmpFile}"`, imageModel ? `--image-model ${imageModel}` : '', videoModel ? `--video-model ${videoModel}` : '', format ? `--format ${format}` : '', ].filter(Boolean).join(' '); const output = execSync(`node ${args}`, { cwd: PROJECT_ROOT, encoding: 'utf-8' }); const match = output.match(/Manifest 已创建: (.+)/); const manifestPath = match ? match[1].trim() : '(unable to parse path)'; return `Manifest 已创建: ${manifestPath}\n\n${output}`; } finally { try { fs.unlinkSync(tmpFile); } catch { /* ignore */ } } }, };