diff --git a/app/agent/tools/manager.py b/app/agent/tools/manager.py index 3c97b3f1..5ea24a19 100644 --- a/app/agent/tools/manager.py +++ b/app/agent/tools/manager.py @@ -149,6 +149,19 @@ class MoviePilotToolsManager: return True return value + @staticmethod + def _parse_array_string(value: str, key: str, item_type: str = "string") -> list: + """ + 将逗号分隔的字符串解析为列表,并根据 item_type 转换元素类型 + """ + trimmed = value.strip() + if not trimmed: + return [] + return [ + MoviePilotToolsManager._normalize_scalar_value(item_type, item.strip(), key) + for item in trimmed.split(",") if item.strip() + ] + @staticmethod def _normalize_arguments(tool_instance: Any, arguments: Dict[str, Any]) -> Dict[str, Any]: """ @@ -185,6 +198,12 @@ class MoviePilotToolsManager: field_info = MoviePilotToolsManager._resolve_field_schema(properties[key]) field_type = field_info.get("type") + # 数组类型:将字符串解析为列表 + if field_type == "array" and isinstance(value, str): + item_type = field_info.get("items", {}).get("type", "string") + normalized[key] = MoviePilotToolsManager._parse_array_string(value, key, item_type) + continue + # 根据类型进行转换 normalized[key] = MoviePilotToolsManager._normalize_scalar_value(field_type, value, key) diff --git a/skills/moviepilot-cli/scripts/mp-cli.js b/skills/moviepilot-cli/scripts/mp-cli.js index 5f0f6337..024b0213 100755 --- a/skills/moviepilot-cli/scripts/mp-cli.js +++ b/skills/moviepilot-cli/scripts/mp-cli.js @@ -149,7 +149,7 @@ function normalizeCommand(tool = {}) { }; } -function request(method, targetUrl, headers = {}, body) { +function request(method, targetUrl, headers = {}, body, timeout = 120000) { return new Promise((resolve, reject) => { let url; try { @@ -180,6 +180,10 @@ function request(method, targetUrl, headers = {}, body) { } ); + req.setTimeout(timeout, () => { + req.destroy(new Error(`Request timed out after ${timeout}ms`)); + }); + req.on('error', reject); if (body !== undefined) { @@ -225,7 +229,7 @@ async function loadCommandJson(commandName) { if (statusCode === '404') { console.error(`Error: command '${commandName}' not found`); - console.error(`Run '${SCRIPT_NAME} list' to see available commands`); + console.error(`Run 'node ${SCRIPT_NAME} list' to see available commands`); process.exit(1); } @@ -345,80 +349,7 @@ async function cmdShow(commandName) { process.stdout.write(`\n${usageLabel} ${usageLine}${reqPart}${optPart}\n`); } -function parseBoolean(value) { - return value === 'true' || value === '1' || value === 'yes'; -} - -function parseNumber(value, key) { - if (value === '') { - fail(`Error: invalid numeric value for '${key}'`); - } - - const result = Number(value); - if (Number.isNaN(result)) { - fail(`Error: invalid numeric value for '${key}': '${value}'`); - } - return result; -} - -function parseScalarValue(value, key, type = 'string') { - if (type === 'integer' || type === 'number') { - return parseNumber(value, key); - } - - if (type === 'boolean') { - return parseBoolean(value); - } - - return value; -} - -function parseArrayValue(value, key, itemType = 'string') { - const trimmed = value.trim(); - if (!trimmed) { - return []; - } - - if (trimmed.startsWith('[')) { - let parsed; - try { - parsed = JSON.parse(trimmed); - } catch { - fail(`Error: invalid array value for '${key}': '${value}'`); - } - - if (!Array.isArray(parsed)) { - fail(`Error: invalid array value for '${key}': '${value}'`); - } - - return parsed.map((item) => { - if (typeof item === 'string') { - return parseScalarValue(item.trim(), key, itemType); - } - if (itemType === 'integer' || itemType === 'number') { - if (typeof item !== 'number') { - fail(`Error: invalid numeric value for '${key}': '${item}'`); - } - return item; - } - if (itemType === 'boolean') { - if (typeof item !== 'boolean') { - fail(`Error: invalid boolean value for '${key}': '${item}'`); - } - return item; - } - return item; - }); - } - - return trimmed - .split(',') - .map((item) => item.trim()) - .filter(Boolean) - .map((item) => parseScalarValue(item, key, itemType)); -} - -function buildArguments(command, pairs) { +function buildArguments(pairs) { const args = { explanation: 'CLI invocation' }; for (const kv of pairs) { @@ -427,40 +358,20 @@ function buildArguments(command, pairs) { } const index = kv.indexOf('='); - const key = kv.slice(0, index); - const value = kv.slice(index + 1); - const field = command.fields.find((item) => item.name === key); - const fieldType = field?.type || 'string'; - const itemType = field?.item_type || 'string'; - - if (fieldType === 'array') { - args[key] = parseArrayValue(value, key, itemType); - continue; - } - - args[key] = parseScalarValue(value, key, fieldType); + args[kv.slice(0, index)] = kv.slice(index + 1); } return args; } async function cmdRun(commandName, pairs) { - await loadCommandsJson(); - if (!commandName) { fail(`Usage: ${SCRIPT_NAME} [key=value ...]`); } - const command = commandsJson.find((item) => item.name === commandName); - if (!command) { - console.error(`Error: command '${commandName}' not found`); - console.error(`Run 'node ${SCRIPT_NAME} list' to see available commands`); - process.exit(1); - } - const requestBody = JSON.stringify({ tool_name: commandName, - arguments: buildArguments(command, pairs), + arguments: buildArguments(pairs), }); const { statusCode, body } = await request( @@ -490,7 +401,7 @@ async function cmdRun(commandName, pairs) { try { printValue(JSON.parse(parsed.result)); } catch { - printValue({ result: parsed.result }); + printValue(parsed.result); } } else { printValue(parsed.result);