use axum::{routing::get, Json, Router};
use serde_json::json;
use tracing::info;
pub async fn start_health_server(port: u16, bind_addr: &str) -> anyhow::Result<()> {
let app = Router::new().route("/health", get(health_handler));
let ip: std::net::IpAddr = bind_addr
.parse()
.unwrap_or(std::net::IpAddr::V4(std::net::Ipv4Addr::LOCALHOST));
let addr = std::net::SocketAddr::new(ip, port);
info!("Health server listening on {}", addr);
let listener = tokio::net::TcpListener::bind(addr).await?;
axum::serve(listener, app).await?;
Ok(())
}
async fn health_handler() -> Json<serde_json::Value> {
Json(json!({"status": "ok"}))
}
#[cfg(target_os = "linux")]
pub fn install_service() -> anyhow::Result<()> {
let exe = std::env::current_exe()?;
let working_dir = std::env::current_dir()?;
let unit = format!(
r#"[Unit]
Description=aidaemon - AI personal daemon
After=network.target
[Service]
Type=simple
ExecStart={}
WorkingDirectory={}
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
"#,
exe.display(),
working_dir.display()
);
let path = "/etc/systemd/system/aidaemon.service";
std::fs::write(path, unit)?;
println!("Service file written to {}", path);
println!("Run: sudo systemctl daemon-reload && sudo systemctl enable --now aidaemon");
Ok(())
}
#[cfg(target_os = "macos")]
pub fn install_service() -> anyhow::Result<()> {
let exe = std::env::current_exe()?;
let working_dir = std::env::current_dir()?;
let home = std::env::var("HOME")?;
let log_dir = format!("{}/Library/Logs/aidaemon", home);
std::fs::create_dir_all(&log_dir)?;
let plist = format!(
r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>ai.aidaemon</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/caffeinate</string>
<string>-i</string>
<string>{}</string>
</array>
<key>WorkingDirectory</key>
<string>{}</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>{}/stdout.log</string>
<key>StandardErrorPath</key>
<string>{}/stderr.log</string>
</dict>
</plist>
"#,
exe.display(),
working_dir.display(),
log_dir,
log_dir
);
let path = format!("{}/Library/LaunchAgents/ai.aidaemon.plist", home);
std::fs::write(&path, plist)?;
println!("Plist written to {}", path);
println!("Logs will be written to {}/", log_dir);
println!("Run: launchctl load {}", path);
Ok(())
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
pub fn install_service() -> anyhow::Result<()> {
anyhow::bail!("Service installation is only supported on Linux and macOS");
}