use anyhow::{anyhow, Result};
use colored::Colorize;
use trusty_common::daemon_guard::{probe_once, spin_until_ready, DaemonGuardConfig};
pub async fn probe_health(base: &str) -> bool {
probe_once(&format!("{base}/api/v1/health")).await
}
fn spawn_daemon() -> Result<u32> {
trusty_common::daemon_guard::spawn_current_exe(&["serve", "--foreground"])
.map_err(|e| anyhow!("trusty-memory daemon spawn failed: {e}"))
}
pub fn daemon_base_url() -> String {
match trusty_common::read_daemon_addr("trusty-memory") {
Ok(Some(addr)) if !addr.is_empty() => {
if addr.starts_with("http://") || addr.starts_with("https://") {
addr
} else {
format!("http://{addr}")
}
}
_ => "http://127.0.0.1:7070".to_string(),
}
}
pub async fn ensure_daemon_running(base: &str) -> Result<()> {
if probe_health(base).await {
return Ok(());
}
eprintln!("{} Starting trusty-memory daemon…", "◉".cyan());
spawn_daemon()?;
let cfg = DaemonGuardConfig {
health_url: format!("{base}/api/v1/health"),
service_name: "trusty-memory".to_string(),
startup_timeout: std::time::Duration::from_secs(30),
poll_interval: std::time::Duration::from_millis(500),
timeout_hint: "try `trusty-memory start` manually to see the error".to_string(),
};
spin_until_ready(&cfg).await
}
pub async fn open_web_dashboard() -> Result<()> {
let base = daemon_base_url();
ensure_daemon_running(&base).await?;
let live_base = daemon_base_url();
let url = format!("{live_base}/ui");
eprintln!("{} Opening {} …", "◉".green(), url.cyan());
if let Err(e) = open::that(&url) {
eprintln!(
"{} could not launch browser ({e}). Open this URL manually: {}",
"⚠".yellow(),
url
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, Instant};
#[tokio::test]
async fn probe_health_returns_false_on_connection_refused() {
let base = "http://127.0.0.1:65535";
let started = Instant::now();
let ok = probe_health(base).await;
assert!(!ok, "probe should fail against an unbound port");
assert!(
started.elapsed() < Duration::from_secs(6),
"probe took too long: {:?}",
started.elapsed()
);
}
#[tokio::test]
async fn probe_health_returns_false_on_bad_url() {
let ok = probe_health("not-a-valid-url").await;
assert!(!ok);
}
#[test]
fn daemon_base_url_always_has_http_scheme() {
let url = daemon_base_url();
assert!(
url.starts_with("http://") || url.starts_with("https://"),
"daemon_base_url must start with http(s)://; got: {url}"
);
}
}