feat(daemon): launchd user-agent install with KeepAlive
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,68 @@
|
||||
use anyhow::Result;
|
||||
pub fn install_agent() -> Result<()> {
|
||||
anyhow::bail!("install-agent implemented in Task 16")
|
||||
use anyhow::{Context, Result};
|
||||
use std::path::PathBuf;
|
||||
|
||||
const LABEL: &str = "xyz.spacesh.daemon";
|
||||
|
||||
fn plist_path() -> Result<PathBuf> {
|
||||
let home = dirs::home_dir().context("no home")?;
|
||||
Ok(home.join("Library").join("LaunchAgents").join(format!("{LABEL}.plist")))
|
||||
}
|
||||
|
||||
/// Render the launchd plist. `run_at_load` defaults to false in this slice.
|
||||
pub fn render_plist(exe: &str, run_at_load: bool) -> String {
|
||||
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>{LABEL}</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>{exe}</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<{run_at_load}/>
|
||||
</dict>
|
||||
</plist>
|
||||
"#,
|
||||
run_at_load = if run_at_load { "true" } else { "false" }
|
||||
)
|
||||
}
|
||||
|
||||
pub fn install_agent() -> Result<()> {
|
||||
let exe = std::env::current_exe()?;
|
||||
let plist = render_plist(&exe.to_string_lossy(), false);
|
||||
let path = plist_path()?;
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
std::fs::write(&path, plist)?;
|
||||
// Load it (best-effort; ignore "already loaded").
|
||||
let _ = std::process::Command::new("launchctl")
|
||||
.arg("load")
|
||||
.arg(&path)
|
||||
.status();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn plist_has_label_keepalive_and_exe() {
|
||||
let p = render_plist("/usr/local/bin/spaceshd", false);
|
||||
assert!(p.contains("xyz.spacesh.daemon"));
|
||||
assert!(p.contains("/usr/local/bin/spaceshd"));
|
||||
assert!(p.contains("<key>KeepAlive</key>\n <true/>"));
|
||||
assert!(p.contains("<key>RunAtLoad</key>\n <false/>"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn run_at_load_toggles() {
|
||||
assert!(render_plist("x", true).contains("<key>RunAtLoad</key>\n <true/>"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user