use anyhow::{Context, Result}; use clap::{Args, Parser, Subcommand}; use log::{error, info}; use serde_json::Value; use std::{fs, path::PathBuf}; mod app_error; mod cache; mod deno_runner; #[cfg(feature = "mcp")] mod mcp; mod model; mod python_runner; use crate::cache::CodeFileCache; use crate::model::{CodeExecutor, CodeScriptExecutionResult, LanguageScript}; #[derive(Parser)] #[command(name = "run_code_rmcp")] #[command(about = "Execute JavaScript and Python code using MCP SDK", long_about = None)] struct Cli { #[command(subcommand)] command: Commands, /// Show execution logs #[arg(short, long)] show_logs: bool, /// Use MCP SDK integration #[cfg(feature = "mcp")] #[arg(short, long)] use_mcp: bool, /// Clear cache before execution #[arg(short = 'c', long)] clear_cache: bool, } #[derive(Subcommand)] enum Commands { /// Execute JavaScript code Js(CodeArgs), /// Execute TypeScript code Ts(CodeArgs), /// Execute Python code Python(CodeArgs), /// Clear cache files ClearCache { /// Language to clear cache for (js, ts, python, or all) #[arg(short, long)] language: String, }, } #[derive(Args)] struct CodeArgs { /// Path to the code file #[arg(short, long)] file: Option, /// Code content as string #[arg(short, long)] code: Option, /// Parameters to pass to the script (JSON format) #[arg(short, long)] params: Option, } #[tokio::main] async fn main() -> Result<()> { // 解析命令行参数 let cli = Cli::parse(); // 初始化日志 let mut builder = env_logger::Builder::from_default_env(); if cli.show_logs { // 如果--show-logs参数为true,则至少显示info级别的日志 builder.filter_level(log::LevelFilter::Info); } builder.init(); if let Commands::ClearCache { language } = &cli.command { match language.to_lowercase().as_str() { "js" => { CodeFileCache::clear_cache_by_language(&LanguageScript::Js).await?; info!("已清除 JavaScript 缓存"); } "ts" => { CodeFileCache::clear_cache_by_language(&LanguageScript::Ts).await?; info!("已清除 TypeScript 缓存"); } "python" => { CodeFileCache::clear_cache_by_language(&LanguageScript::Python).await?; info!("已清除 Python 缓存"); } "all" => { CodeFileCache::clear_all_cache().await?; info!("已清除所有缓存"); } _ => { info!("无效的语言类型,可选项: js, ts, python, all"); return Ok(()); } } return Ok(()); } // 解析传递给脚本的参数 let params = match &cli.command { Commands::Js(args) => parse_params(&args.params)?, Commands::Ts(args) => parse_params(&args.params)?, Commands::Python(args) => parse_params(&args.params)?, _ => None, }; // 从参数获取代码 let (code, language) = match &cli.command { Commands::Js(args) => (get_code(args)?, LanguageScript::Js), Commands::Ts(args) => (get_code(args)?, LanguageScript::Ts), Commands::Python(args) => (get_code(args)?, LanguageScript::Python), _ => unreachable!(), }; // 如果指定了清除缓存选项,则清除对应语言的缓存 if cli.clear_cache { CodeFileCache::clear_cache_by_language(&language).await?; info!("已清除 {language:?} 缓存"); } // 执行代码 #[cfg(feature = "mcp")] let result = if cli.use_mcp { // 使用MCP SDK集成 CodeExecutor::execute_with_params_compat(&code, language, params).await? } else { // 直接执行 CodeExecutor::execute_with_params_compat(&code, language, params).await? }; #[cfg(not(feature = "mcp"))] let result = CodeExecutor::execute_with_params_compat(&code, language, params).await?; // 打印结果 print_result(result); Ok(()) } fn get_code(args: &CodeArgs) -> Result { if let Some(file) = &args.file { fs::read_to_string(file).context("Failed to read code file") } else if let Some(code) = &args.code { Ok(code.clone()) } else { anyhow::bail!("Either file or code must be provided") } } fn parse_params(params_str: &Option) -> Result> { match params_str { Some(s) if !s.is_empty() => { let parsed: Value = serde_json::from_str(s).context("Failed to parse parameters as JSON")?; Ok(Some(parsed)) } _ => Ok(None), } } fn print_result(result: CodeScriptExecutionResult) { if !result.logs.is_empty() { info!("--- Logs ---"); for log in result.logs { info!("{log}"); } info!("------------"); } if let Some(result_val) = result.result { // 使用 serde_json 序列化结果,确保所有类型都能正确显示 match serde_json::to_string_pretty(&result_val) { Ok(json_str) => info!("Result: {json_str}"), Err(_) => info!("Result: {result_val}"), } } if let Some(error) = result.error { error!("Error: {error}"); } }