//! Shared bootstrap helpers for injected agent binaries. use crate::agent_runtime_detection::{ detect_runtimes, format_as_env_value, print_detection_report_for_env, DetectedRuntime, }; use tracing::{info, warn}; #[derive(Debug, Clone)] pub struct RuntimeBootstrapResult { pub runtimes_override: Option, pub detected_runtimes: Option>, } /// Detect runtimes and populate the agent runtime environment variable when needed. /// /// This must run before the Tokio runtime starts because it may mutate process /// environment variables. pub fn bootstrap_runtime_env(env_var_name: &str) -> RuntimeBootstrapResult { let runtimes_override = std::env::var(env_var_name).ok(); let mut detected_runtimes = None; if let Some(ref override_value) = runtimes_override { info!( "{} already set (override): {}", env_var_name, override_value ); info!("Running auto-detection for override-specified runtimes..."); let detected = detect_runtimes(); let override_names: Vec<&str> = override_value.split(',').map(|s| s.trim()).collect(); let filtered: Vec<_> = detected .into_iter() .filter(|rt| { let lower_name = rt.name.to_ascii_lowercase(); override_names .iter() .any(|ov| ov.to_ascii_lowercase() == lower_name) }) .collect(); if filtered.is_empty() { warn!( "None of the override runtimes ({}) were found on this system", override_value ); } else { info!( "Matched {} override runtime(s) to detected interpreters:", filtered.len() ); for rt in &filtered { match &rt.version { Some(ver) => info!(" ✓ {} — {} ({})", rt.name, rt.path, ver), None => info!(" ✓ {} — {}", rt.name, rt.path), } } detected_runtimes = Some(filtered); } } else { info!("No {} override — running auto-detection...", env_var_name); let detected = detect_runtimes(); if detected.is_empty() { warn!("No runtimes detected! The agent may not be able to execute any work."); } else { info!("Detected {} runtime(s):", detected.len()); for rt in &detected { match &rt.version { Some(ver) => info!(" ✓ {} — {} ({})", rt.name, rt.path, ver), None => info!(" ✓ {} — {}", rt.name, rt.path), } } let runtime_csv = format_as_env_value(&detected); info!("Setting {}={}", env_var_name, runtime_csv); std::env::set_var(env_var_name, &runtime_csv); detected_runtimes = Some(detected); } } RuntimeBootstrapResult { runtimes_override, detected_runtimes, } } pub fn print_detect_only_report(env_var_name: &str, result: &RuntimeBootstrapResult) { if result.runtimes_override.is_some() { info!("--detect-only: re-running detection to show what is available on this system..."); println!( "NOTE: {} is set — auto-detection was skipped during normal startup.", env_var_name ); println!(" Showing what auto-detection would find on this system:"); println!(); let detected = detect_runtimes(); print_detection_report_for_env(env_var_name, &detected); } else if let Some(ref detected) = result.detected_runtimes { print_detection_report_for_env(env_var_name, detected); } else { let detected = detect_runtimes(); print_detection_report_for_env(env_var_name, &detected); } }