Skip to main content

objectiveai_cli/command/
kill_all.rs

1//! `kill-all` — terminate every process holding a lock anywhere under
2//! the configured `OBJECTIVEAI_DIR`.
3//!
4//! The blunt counterpart to the targeted `{api,db,mcp,viewer} kill`:
5//! rather than resolving one server by its known lock key, this asks
6//! [`objectiveai_sdk::lockfile::owners_in_tree`] for every process
7//! holding any `*.lock` anywhere in the dir tree and kills the lot —
8//! the api server, per-state db supervisors (which take their
9//! postmasters with them), viewers, mcp servers, and any
10//! agent-holding cli processes.
11//!
12//! Two sweeps: killing a lock owner can orphan children that only
13//! surface as lock owners once their parent is gone (a hard-killed
14//! supervisor's plugin servers), and on Windows a dead process's lock
15//! handle is released asynchronously — so the first pass exposes
16//! stragglers the second pass then catches. PIDs actually terminated
17//! are de-duplicated across both sweeps, so the reported count is
18//! distinct processes killed. Idempotent: a count of zero is not an
19//! error.
20
21use std::collections::HashSet;
22
23use objectiveai_sdk::cli::command::kill_all::{Request, Response};
24
25use crate::context::Context;
26use crate::error::Error;
27
28pub async fn execute(ctx: &Context, _request: Request) -> Result<Response, Error> {
29    let dir = ctx.filesystem.dir().clone();
30    let me = std::process::id();
31    let mut killed: HashSet<u32> = HashSet::new();
32    for _ in 0..2 {
33        let pids = objectiveai_sdk::lockfile::owners_in_tree(&dir)
34            .await
35            .map_err(|e| Error::Spawn("read lock owners in tree".to_string(), e))?;
36        for pid in pids {
37            // Never signal ourselves (this very `kill-all` process may
38            // hold a lock under the tree) or the kernel's pid 0.
39            if pid == me || pid == 0 {
40                continue;
41            }
42            if crate::spawn::kill_pid(pid) == 1 {
43                killed.insert(pid);
44            }
45        }
46    }
47    Ok(Response { killed: killed.len() })
48}
49
50pub mod request_schema {
51    use objectiveai_sdk::cli::command::kill_all as sdk;
52    use objectiveai_sdk::cli::command::kill_all::request_schema::{Request, Response};
53
54    use crate::context::Context;
55    use crate::error::Error;
56
57    pub async fn execute(_ctx: &Context, _request: Request) -> Result<Response, Error> {
58        Ok(objectiveai_sdk::cli::command::ResponseSchema(schemars::schema_for!(sdk::Request)))
59    }
60}
61
62pub mod response_schema {
63    use objectiveai_sdk::cli::command::kill_all as sdk;
64    use objectiveai_sdk::cli::command::kill_all::response_schema::{Request, Response};
65
66    use crate::context::Context;
67    use crate::error::Error;
68
69    pub async fn execute(_ctx: &Context, _request: Request) -> Result<Response, Error> {
70        Ok(objectiveai_sdk::cli::command::ResponseSchema(schemars::schema_for!(sdk::Response)))
71    }
72}