use std::path::PathBuf;
use std::process::Stdio;
pub const BRAIN_SERVICE_LABEL: &str = "com.oxi.oxibrain";
pub fn brain_plist_path() -> Option<PathBuf> {
let home = std::env::var_os("HOME")?;
let mut p = PathBuf::from(home);
p.push("Library");
p.push("LaunchAgents");
p.push(format!("{BRAIN_SERVICE_LABEL}.plist"));
Some(p)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReviveAction {
BootstrapPlist(PathBuf),
KickstartService,
SpawnDetached(PathBuf),
InstallNeeded,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BrainControlReport {
pub binary: Option<PathBuf>,
pub plist: Option<PathBuf>,
pub service_loaded: bool,
}
pub fn revive_plan(report: &BrainControlReport) -> ReviveAction {
let Some(binary) = report.binary.clone() else {
return ReviveAction::InstallNeeded;
};
match &report.plist {
Some(plist) if !report.service_loaded => ReviveAction::BootstrapPlist(plist.clone()),
Some(_) => ReviveAction::KickstartService,
None => ReviveAction::SpawnDetached(binary),
}
}
pub fn find_oxibrain_binary() -> Option<PathBuf> {
if let Some(home) = std::env::var_os("HOME") {
let mut p = PathBuf::from(home);
p.push(".cargo");
p.push("bin");
p.push("oxibrain");
if p.is_file() {
return Some(p);
}
}
let path = std::env::var_os("PATH")?;
for dir in std::env::split_paths(&path) {
let candidate = dir.join("oxibrain");
if candidate.is_file() {
return Some(candidate);
}
}
None
}
pub fn service_loaded() -> bool {
if !cfg!(target_os = "macos") {
return false;
}
let uid = std::process::id();
let _ = uid;
let user = whoami_uid();
let out = std::process::Command::new("launchctl")
.args(["print", &format!("gui/{user}/{BRAIN_SERVICE_LABEL}")])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
matches!(out, Ok(st) if st.success())
}
pub fn should_auto_revive(
memory_enabled: bool,
daemon_down: bool,
already_attempted: bool,
) -> bool {
memory_enabled && daemon_down && !already_attempted
}
fn whoami_uid() -> String {
std::process::Command::new("id")
.arg("-u")
.output()
.ok()
.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
.unwrap_or_else(|| "501".to_string())
}
pub fn probe_control() -> BrainControlReport {
BrainControlReport {
binary: find_oxibrain_binary(),
plist: brain_plist_path().filter(|p| p.is_file()),
service_loaded: service_loaded(),
}
}
pub async fn revive() -> Result<String, String> {
let report = probe_control();
let plan = revive_plan(&report);
match plan {
ReviveAction::InstallNeeded => Err(
"oxibrain binary not found — install it first (e.g. `cargo install oxibrain` \
from the oxibrain repo), then run /brain again"
.to_string(),
),
ReviveAction::BootstrapPlist(plist) => {
let user = whoami_uid();
let domain = format!("gui/{user}");
let status = std::process::Command::new("launchctl")
.args(["bootstrap", &domain])
.arg(&plist)
.status()
.map_err(|e| format!("launchctl bootstrap failed: {e}"))?;
if !status.success() {
return Err(format!(
"launchctl bootstrap exited {} — check `launchctl print {domain}/{BRAIN_SERVICE_LABEL}`",
status.code().unwrap_or(-1)
));
}
wait_for_ping("bootstrapped the launchd service").await
}
ReviveAction::KickstartService => {
let user = whoami_uid();
let target = format!("gui/{user}/{BRAIN_SERVICE_LABEL}");
let status = std::process::Command::new("launchctl")
.args(["kickstart", &target])
.status()
.map_err(|e| format!("launchctl kickstart failed: {e}"))?;
if !status.success() {
return Err(format!(
"launchctl kickstart exited {} — check `launchctl print {target}`",
status.code().unwrap_or(-1)
));
}
wait_for_ping("kicked the launchd service").await
}
ReviveAction::SpawnDetached(binary) => {
let socket = crate::foundation::brain::default_socket_path();
let mut cmd = std::process::Command::new(&binary);
cmd.args(["serve", "--daemon"])
.arg("--socket")
.arg(&socket)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
cmd.process_group(0);
}
cmd.spawn()
.map_err(|e| format!("spawning oxibrain failed: {e}"))?;
wait_for_ping("spawned a detached daemon").await
}
}
}
async fn wait_for_ping(action: &str) -> Result<String, String> {
let backend = crate::foundation::brain::BrainMemoryBackend::new(
crate::foundation::brain::default_socket_path(),
);
for _ in 0..20 {
if backend.ping().await.is_ok() {
return Ok(format!("{action} — daemon is answering pings"));
}
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
}
Err(format!(
"{action}, but the daemon did not answer within 5s — check the log at \
~/.oxi/brain/daemon.log"
))
}
#[cfg(test)]
mod tests {
use super::*;
fn bin() -> PathBuf {
PathBuf::from("/usr/local/bin/oxibrain")
}
#[test]
fn plan_without_binary_requires_install() {
let report = BrainControlReport {
binary: None,
plist: Some(PathBuf::from("/Library/LaunchAgents/x.plist")),
service_loaded: false,
};
assert_eq!(revive_plan(&report), ReviveAction::InstallNeeded);
}
#[test]
fn plan_bootstraps_an_unloaded_service() {
let report = BrainControlReport {
binary: Some(bin()),
plist: Some(PathBuf::from("/Library/LaunchAgents/x.plist")),
service_loaded: false,
};
assert_eq!(
revive_plan(&report),
ReviveAction::BootstrapPlist(PathBuf::from("/Library/LaunchAgents/x.plist"))
);
}
#[test]
fn plan_kickstarts_a_loaded_service() {
let report = BrainControlReport {
binary: Some(bin()),
plist: Some(PathBuf::from("/Library/LaunchAgents/x.plist")),
service_loaded: true,
};
assert_eq!(revive_plan(&report), ReviveAction::KickstartService);
}
#[test]
fn plan_spawns_detached_without_launchd() {
let report = BrainControlReport {
binary: Some(bin()),
plist: None,
service_loaded: false,
};
assert_eq!(revive_plan(&report), ReviveAction::SpawnDetached(bin()));
}
#[test]
fn auto_revive_gates() {
assert!(should_auto_revive(true, true, false), "enabled+down → go");
assert!(
!should_auto_revive(false, true, false),
"memory disabled → never"
);
assert!(
!should_auto_revive(true, true, true),
"one attempt per session — no retry loops"
);
assert!(
!should_auto_revive(true, false, false),
"healthy daemon → nothing to do"
);
}
}