Files
qiming/qiming-run_code_rmcp/src/main.rs
2026-06-01 13:43:09 +08:00

195 lines
5.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<PathBuf>,
/// Code content as string
#[arg(short, long)]
code: Option<String>,
/// Parameters to pass to the script (JSON format)
#[arg(short, long)]
params: Option<String>,
}
#[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<String> {
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<String>) -> Result<Option<Value>> {
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}");
}
}