1pub mod cli;
2pub mod config;
3pub mod daemon;
4pub mod db;
5pub mod hook;
6pub mod models;
7pub mod recovery;
8pub mod state_machine;
9pub mod tui;
10
11use std::process::Command;
12
13pub fn build_version() -> String {
14 let version = env!("CARGO_PKG_VERSION");
15 if cfg!(debug_assertions) {
16 format!("{}-dev+{}", version, env!("BUILD_TIMESTAMP"))
17 } else {
18 version.to_string()
19 }
20}
21
22fn kill_daemon() {
23 let client = reqwest::blocking::Client::new();
24 let _ = client
25 .post(format!("{}/shutdown", hook::DAEMON_URL))
26 .timeout(std::time::Duration::from_secs(1))
27 .send();
28 std::thread::sleep(std::time::Duration::from_millis(500));
29}
30
31fn spawn_daemon() {
32 let exe = std::env::current_exe().unwrap_or_else(|_| "sp".into());
33 Command::new(&exe)
34 .arg("daemon")
35 .stdout(std::process::Stdio::null())
36 .stderr(std::process::Stdio::null())
37 .spawn()
38 .ok();
39 std::thread::sleep(std::time::Duration::from_secs(1));
40}
41
42pub fn ensure_daemon_running() {
43 let client = reqwest::blocking::Client::new();
44 let my_version = build_version();
45
46 let response = client
47 .get(format!("{}/health", hook::DAEMON_URL))
48 .timeout(std::time::Duration::from_secs(1))
49 .send()
50 .ok()
51 .and_then(|r| r.json::<serde_json::Value>().ok());
52
53 match response {
54 Some(json) => {
55 let daemon_version = json.get("version").and_then(|v| v.as_str());
56 if daemon_version != Some(&my_version) {
57 kill_daemon();
58 spawn_daemon();
59 }
60 }
61 None => {
62 spawn_daemon();
63 }
64 }
65}