use super::ProcessService;
use crate::paths;
use anyhow::{Context, Result};
use mecha10_cli_core::utils::strip_node_scope;
use std::collections::HashMap;
use std::path::Path;
use std::process::{Command, Stdio};
impl ProcessService {
pub fn spawn_node(&mut self, name: &str, binary_path: &str, args: &[&str]) -> Result<u32> {
let child = Command::new(binary_path)
.args(args)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()
.with_context(|| format!("Failed to spawn process: {}", binary_path))?;
let pid = child.id();
self.manager.track(name.to_string(), child);
Ok(pid)
}
pub fn spawn_with_output(&mut self, name: &str, binary_path: &str, args: &[&str]) -> Result<u32> {
let child = Command::new(binary_path)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.with_context(|| format!("Failed to spawn process: {}", binary_path))?;
let pid = child.id();
self.manager.track(name.to_string(), child);
Ok(pid)
}
pub fn spawn_with_env(
&mut self,
name: &str,
binary_path: &str,
args: &[&str],
env: HashMap<String, String>,
) -> Result<u32> {
let disk_logging_enabled = self.disk_logging_enabled();
tracing::debug!("🔧 spawn_with_env for '{}': received {} env vars", name, env.len());
if !env.is_empty() {
for (key, value) in &env {
if key == "ROBOT_API_KEY" {
tracing::debug!(" {} = <redacted>", key);
} else {
tracing::debug!(" {} = {}", key, value);
}
}
} else {
tracing::warn!("⚠️ No environment variables to inject!");
}
let mut cmd = Command::new(binary_path);
cmd.args(args).envs(&env);
cmd.env("MECHA10_DISK_LOGGING_ENABLED", disk_logging_enabled.to_string());
if disk_logging_enabled {
let logs_dir = std::path::PathBuf::from(paths::project::LOGS_DIR);
if !logs_dir.exists() {
std::fs::create_dir_all(&logs_dir)?;
}
let log_name = strip_node_scope(name);
let log_file_path = logs_dir.join(format!("{}.log", log_name));
let log_file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&log_file_path)
.with_context(|| format!("Failed to create log file: {}", log_file_path.display()))?;
let log_file_stderr = log_file.try_clone().context("Failed to clone log file handle")?;
cmd.stdout(log_file) .stderr(log_file_stderr); } else {
tracing::debug!(
"📴 Disk logging disabled via project config; discarding stdout/stderr for '{}'",
name
);
cmd.stdout(Stdio::null()).stderr(Stdio::null());
}
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
cmd.process_group(0); }
let child = cmd
.spawn()
.with_context(|| format!("Failed to spawn process: {}", binary_path))?;
let pid = child.id();
self.manager.track(name.to_string(), child);
Ok(pid)
}
pub fn spawn_in_dir(
&mut self,
name: &str,
binary_path: &str,
args: &[&str],
working_dir: impl AsRef<Path>,
) -> Result<u32> {
let child = Command::new(binary_path)
.args(args)
.current_dir(working_dir)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.spawn()
.with_context(|| format!("Failed to spawn process: {}", binary_path))?;
let pid = child.id();
self.manager.track(name.to_string(), child);
Ok(pid)
}
pub fn spawn_nodes(&mut self, nodes: Vec<(&str, &str, Vec<&str>)>) -> Result<HashMap<String, u32>> {
let mut pids = HashMap::new();
for (name, binary_path, args) in nodes {
match self.spawn_node(name, binary_path, &args) {
Ok(pid) => {
pids.insert(name.to_string(), pid);
}
Err(e) => {
eprintln!("Failed to spawn {}: {}", name, e);
}
}
}
Ok(pids)
}
}