use anyhow::Result;
use colored::Colorize;
pub(crate) fn addr_file_path() -> Option<std::path::PathBuf> {
trusty_common::resolve_data_dir("trusty-memory")
.ok()
.map(|d| d.join("http_addr"))
}
pub(crate) fn start_lock_path() -> Option<std::path::PathBuf> {
trusty_common::resolve_data_dir("trusty-memory")
.ok()
.map(|d| d.join("start.lock"))
}
pub(crate) fn daemon_start_config() -> trusty_common::mcp::DaemonBridgeConfig {
trusty_common::mcp::DaemonBridgeConfig {
service_name: "trusty-memory".to_string(),
spawn_args: vec!["serve".to_string(), "--foreground".to_string()],
health_path: "/health".to_string(),
base_url_fn: Box::new(crate::commands::daemon_guard::daemon_base_url),
startup_timeout: None,
poll_interval: None,
no_spawn: false,
no_spawn_hint: None,
}
}
pub async fn handle_start() -> Result<()> {
if let Some(path) = addr_file_path() {
if let Some(url) = trusty_common::check_already_running(&path, "/health").await {
eprintln!("{} trusty-memory is already running at {url}", "◉".green());
return Ok(());
}
}
let lock_path = start_lock_path()
.ok_or_else(|| anyhow::anyhow!("could not resolve the trusty-memory data directory"))?;
let config = daemon_start_config();
let url = trusty_common::mcp::ensure_daemon_up_single_flight(&config, &lock_path).await?;
eprintln!("{} trusty-memory daemon ready at {url}", "✓".green());
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spawn_args_never_bind_port_zero() {
let config = daemon_start_config();
let joined = config.spawn_args.join(" ");
assert!(
!joined.contains(":0"),
"spawn args must never bind port 0 (#1152); got: {joined}"
);
assert!(
!config.spawn_args.iter().any(|a| a == "--http"),
"the canonical daemon takes the dynamic walker from 7070, not an \
explicit --http (#1152); got: {joined}"
);
assert_eq!(
config.spawn_args,
vec!["serve".to_string(), "--foreground".to_string()],
"the canonical daemon command must stay `serve --foreground`"
);
}
#[test]
fn start_lock_lives_beside_the_addr_file() {
let (Some(lock), Some(addr)) = (start_lock_path(), addr_file_path()) else {
return; };
assert_eq!(
lock.parent(),
addr.parent(),
"the start lock must live in the same data dir as http_addr"
);
assert_eq!(lock.file_name().unwrap(), "start.lock");
}
#[test]
fn start_config_relies_on_the_single_flight_lock() {
let config = daemon_start_config();
assert!(
!config.no_spawn,
"start-if-not-running requires spawning to be permitted (#5267)"
);
assert!(
start_lock_path().is_some(),
"a spawning config must have a lock path to serialise on"
);
}
}