use crate::utils::config::Config;
use crate::utils::transaction::ProfileTransaction;
use crate::utils::validation::ProfileName;
use anyhow::{Context as _, Result};
use colored::Colorize as _;
use std::process::Stdio;
use tokio::process::Command;
pub async fn execute(
config: Config,
profile: String,
command: Vec<String>,
quiet: bool,
) -> Result<()> {
let profile_name = ProfileName::try_from(profile.as_str())
.with_context(|| format!("Invalid profile name '{profile}'"))?;
let profile_dir = config.profile_path_validated(&profile_name)?;
let codex_dir = config.codex_dir();
if !profile_dir.exists() {
anyhow::bail!("Profile '{profile}' not found");
}
if command.is_empty() {
anyhow::bail!("No command specified to run");
}
let mut txn =
ProfileTransaction::new(codex_dir).context("Failed to initialise profile transaction")?;
txn.stage_profile(&profile_dir, crate::utils::files::get_critical_files())
.context("Failed to stage profile")?;
txn.commit()
.context("Failed to atomically load profile for run")?;
let cmd = &command[0];
let args = &command[1..];
if !quiet {
println!(
"{} Running with profile {}: {}",
"▶".cyan(),
profile.green(),
command.join(" ").dimmed()
);
}
let _ = crate::commands::history::log_command(&config, &profile, &command.join(" ")).await;
let status = Command::new(cmd)
.args(args)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.stdin(Stdio::inherit())
.status()
.await
.with_context(|| format!("Failed to execute command: {cmd}"))?;
if let Err(e) = txn.rollback()
&& !quiet
{
eprintln!(
"{} Warning: Could not fully restore original profile: {}",
"⚠".yellow(),
e
);
}
if !quiet {
if status.success() {
println!(
"\n{} Command completed, restored original profile",
"✓".green()
);
} else {
println!(
"\n{} Command exited with code {:?}",
"!".yellow(),
status.code()
);
}
}
Ok(())
}