//! Background bash control — list, cancel, and inspect background processes. use std::collections::HashMap; use std::sync::{Arc, Mutex, OnceLock}; use tracing::error; use super::job::BashJob; /// Global accessor for the shared BashControl singleton. /// /// Used by the Bash tool (to register jobs) and BashKill (to look them up). pub fn bash_control() -> &'static BashControl { static BASH_CONTROL: OnceLock = OnceLock::new(); BASH_CONTROL.get_or_init(BashControl::new) } /// Central registry of all running background bash jobs. pub struct BashControl { jobs: Mutex>>, } impl BashControl { pub fn new() -> Self { BashControl { jobs: Mutex::new(HashMap::new()), } } } impl Default for BashControl { fn default() -> Self { Self::new() } } impl BashControl { /// Register a new background job. pub fn register(&self, job: Arc) { if let Ok(mut guard) = self.jobs.lock() { guard.insert(job.id.clone(), job); } } /// Cancel a job by ID. pub fn cancel(&self, id: &str) -> bool { if let Ok(mut guard) = self.jobs.lock() { if let Some(job) = guard.remove(id) { job.cancel(); return true; } } false } /// List all active jobs. pub fn list(&self) -> Vec<(String, String, bool)> { let mut guard = match self.jobs.lock() { Ok(g) => g, Err(poisoned) => { error!("bgbash jobs mutex poisoned, recovering"); poisoned.into_inner() } }; guard.retain(|_, j| j.is_running()); guard .iter() .map(|(id, job)| (id.clone(), job.command.clone(), job.is_running())) .collect() } /// Clean up completed jobs. pub fn prune(&self) { if let Ok(mut guard) = self.jobs.lock() { guard.retain(|_, j| j.is_running()); } } }