use crate::auto_task::container_session::{ContainerSession, TerminalOutput}; use crate::core::shared::state::{AppState, TaskProgressEvent}; use log::error; use std::sync::Arc; use tokio::sync::mpsc; pub struct AgentExecutor { pub state: Arc, pub session_id: String, pub task_id: String, container: Option, } impl AgentExecutor { pub fn new(state: Arc, session_id: &str, task_id: &str) -> Self { Self { state, session_id: session_id.to_string(), task_id: task_id.to_string(), container: None, } } pub async fn initialize(&mut self) -> Result<(), String> { self.broadcast_step("Initializing Agent Environment", 1, 10); let mut session = ContainerSession::new(&self.session_id).await?; let (tx, mut rx) = mpsc::channel(100); session.start_terminal(tx).await?; self.container = Some(session); // Spawn a task to listen to terminal output and broadcast it let state_clone = self.state.clone(); let task_id_clone = self.task_id.clone(); tokio::spawn(async move { while let Some(output) = rx.recv().await { let (line, stream) = match output { TerminalOutput::Stdout(l) => (l, "stdout"), TerminalOutput::Stderr(l) => (l, "stderr"), }; let mut event = TaskProgressEvent::new( &task_id_clone, "terminal_output", &line, ) .with_event_type("terminal_output"); // The JS on the frontend expects { type: "terminal_output", line: "...", stream: "..." } // So we hijack details for stream event.details = Some(stream.to_string()); event.text = Some(line.clone()); state_clone.broadcast_task_progress(event); } }); self.broadcast_browser_ready("http://localhost:8000", 8000); self.broadcast_step("Agent Ready", 2, 10); Ok(()) } pub async fn execute_shell_command(&mut self, cmd: &str) -> Result<(), String> { if let Some(container) = &mut self.container { container.send_command(cmd).await?; Ok(()) } else { Err("Container not initialized".into()) } } pub fn broadcast_thought(&self, thought: &str) { let mut event = TaskProgressEvent::new( &self.task_id, "thought_process", thought, ) .with_event_type("thought_process"); event.text = Some(thought.to_string()); self.state.broadcast_task_progress(event); } pub fn broadcast_step(&self, label: &str, current: u8, total: u8) { let event = TaskProgressEvent::new( &self.task_id, "step_progress", label, ) .with_event_type("step_progress") .with_progress(current, total); self.state.broadcast_task_progress(event); } pub fn broadcast_browser_ready(&self, url: &str, port: u16) { let mut event = TaskProgressEvent::new( &self.task_id, "browser_ready", url, ) .with_event_type("browser_ready"); event.details = Some(port.to_string()); self.state.broadcast_task_progress(event); } pub async fn cleanup(&mut self) { if let Some(mut container) = self.container.take() { if let Err(e) = container.stop().await { error!("Error stopping container session: {}", e); } } } }