use super::types::{CronTask, TaskStatus, TaskType};
use crate::agent::AgentManager;
use crate::skill::ShellRunner;
use anyhow::Result;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
#[derive(Debug, Clone)]
pub struct ExecutionResult {
pub success: bool,
pub output: Arc<str>,
pub error: Option<Arc<str>>,
pub duration_ms: u64,
}
pub struct TaskExecutor {
agent_manager: Arc<RwLock<AgentManager>>,
skills_dir: std::path::PathBuf,
}
impl TaskExecutor {
pub fn new(agent_manager: Arc<RwLock<AgentManager>>, skills_dir: std::path::PathBuf) -> Self {
Self {
agent_manager,
skills_dir,
}
}
pub async fn execute(&self, task: &CronTask) -> Result<ExecutionResult> {
let start = Instant::now();
let result = match task.task_type {
TaskType::Agent => self.execute_agent(task).await,
TaskType::Skill => self.execute_skill(task).await,
TaskType::Command => self.execute_command(task).await,
};
let duration = start.elapsed();
match result {
Ok(output) => Ok(ExecutionResult {
success: true,
output,
error: None,
duration_ms: duration.as_millis() as u64,
}),
Err(e) => Ok(ExecutionResult {
success: false,
output: Arc::from(""),
error: Some(Arc::from(e.to_string().as_str())),
duration_ms: duration.as_millis() as u64,
}),
}
}
async fn execute_agent(&self, task: &CronTask) -> Result<Arc<str>> {
let manager = self.agent_manager.read().await;
let agent = manager.get(&task.target).await
.ok_or_else(|| anyhow::anyhow!("Agent '{}' not found", task.target))?;
let provider = manager.provider()
.ok_or_else(|| anyhow::anyhow!("No LLM provider configured"))?;
let response = agent.chat(&task.description, provider).await?;
Ok(Arc::from(response.as_str()))
}
async fn execute_skill(&self, task: &CronTask) -> Result<Arc<str>> {
let mut loader = crate::skill::SkillLoader::new(self.skills_dir.clone());
loader.load_all()?;
let skill = loader.get(&task.target)
.ok_or_else(|| anyhow::anyhow!("Skill '{}' not found", task.target))?;
let runner = ShellRunner::new(task.timeout_secs);
let result = runner.run(&skill.path, &[]).await?;
if result.success {
Ok(result.stdout)
} else {
anyhow::bail!("Skill execution failed: {}", result.stderr);
}
}
async fn execute_command(&self, task: &CronTask) -> Result<Arc<str>> {
let timeout = Duration::from_secs(task.timeout_secs);
let output = tokio::time::timeout(
timeout,
tokio::process::Command::new("sh")
.arg("-c")
.arg(task.target.as_ref())
.output()
).await??;
if output.status.success() {
Ok(Arc::from(String::from_utf8_lossy(&output.stdout).as_ref()))
} else {
anyhow::bail!("Command failed: {}", String::from_utf8_lossy(&output.stderr));
}
}
}