rsclaw 0.0.1-alpha.1

rsclaw: High-performance AI agent (BETA). Optimized for M4 Max and 2GB VPS. 100% compatible with openclaw
Documentation
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;

/// Task execution result.
#[derive(Debug, Clone)]
pub struct ExecutionResult {
    pub success: bool,
    pub output: Arc<str>,
    pub error: Option<Arc<str>>,
    pub duration_ms: u64,
}

/// Task executor.
pub struct TaskExecutor {
    agent_manager: Arc<RwLock<AgentManager>>,
    skills_dir: std::path::PathBuf,
}

impl TaskExecutor {
    /// Create a new task executor.
    pub fn new(agent_manager: Arc<RwLock<AgentManager>>, skills_dir: std::path::PathBuf) -> Self {
        Self {
            agent_manager,
            skills_dir,
        }
    }

    /// Execute a task.
    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,
            }),
        }
    }

    /// Execute an agent task.
    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()))
    }

    /// Execute a skill task.
    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);
        }
    }

    /// Execute a command task.
    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));
        }
    }
}