1//! Where the daemon keeps its socket, its log and its decisions. The mod 2//! computes the same socket path from the same two variables (`bridge.ts`). 3 4use std::path::PathBuf; 5 6/// `$XDG_STATE_HOME/jevhooks`, or `~/.local/state/jevhooks`. 7pub fn state_dir() -> PathBuf { 8 let base = std::env::var_os("XDG_STATE_HOME") 9 .filter(|v| !v.is_empty()) 10 .map(PathBuf::from) 11 .or_else(|| std::env::var_os("HOME").map(|home| PathBuf::from(home).join(".local/state"))) 12 .unwrap_or_else(|| PathBuf::from(".")); 13 base.join("jevhooks") 14} 15 16/// The one daemon's socket; every session's mod and MCP server talk to it. 17pub fn socket() -> PathBuf { 18 state_dir().join("daemon.sock") 19} 20 21/// Held for as long as a daemon runs, so two cannot serve at once. 22pub fn lock() -> PathBuf { 23 state_dir().join("daemon.lock") 24} 25 26/// The daemon's stderr, since nothing else is attached to it. 27pub fn daemon_log() -> PathBuf { 28 state_dir().join("daemon.log") 29} 30 31/// Every decision and what became of it, one JSON object per line. 32pub fn decisions() -> PathBuf { 33 state_dir().join("decisions.jsonl") 34} 35 36/// Which build this process is: the executable's device, inode, size and 37/// modification time. A rebuilt binary is a different identity, which is how 38/// a new session's MCP server knows the running daemon is stale. 39pub fn identity() -> String { 40 use std::os::unix::fs::MetadataExt; 41 match std::env::current_exe().and_then(std::fs::metadata) { 42 Ok(meta) => format!("{}:{}:{}:{}", meta.dev(), meta.ino(), meta.len(), meta.mtime_nsec() as i128 + meta.mtime() as i128 * 1_000_000_000), 43 Err(_) => "unknown".to_owned(), 44 } 45}