Skip to main content

pitboard_core/switch/
uninstall.rs

1//! Taking pitboard off a machine without leaving credentials behind.
2
3use super::{Result, Settled, purge};
4use crate::context::Context;
5use crate::{home, state};
6
7/// What was removed, for the report.
8pub struct Removed {
9    /// Parked logins deleted from the keychain or the vault.
10    pub parks: usize,
11    /// Parked logins that could not be deleted, which is why the home was kept.
12    pub pending: usize,
13    /// Whether ~/.pitboard itself is gone.
14    pub home_removed: bool,
15}
16
17/// Deletes every parked login, then pitboard's own directory. Claude Code's login is left
18/// exactly as it is: whoever is signed in stays signed in.
19///
20/// The directory is removed last and only when every parked login is gone, because
21/// state.json is the only index of those keychain items. Deleting it first would leave live
22/// refresh tokens on the machine with no way left to name them.
23pub fn uninstall(settled: Settled) -> Result<Removed> {
24    let Settled {
25        _exclusive,
26        mut state,
27        ctx,
28    } = settled;
29    let parks = state.accounts.iter().filter(|a| a.parked.is_some()).count();
30    for label in state
31        .accounts
32        .iter()
33        .map(|a| a.label.clone())
34        .collect::<Vec<_>>()
35    {
36        state.remove(&label);
37    }
38    state.active = None;
39    state::save(&ctx, &state)?;
40    let pending = purge(&ctx, &mut state);
41    let home_removed = pending == 0 && remove_home(&ctx);
42    Ok(Removed {
43        parks: parks - pending,
44        pending,
45        home_removed,
46    })
47}
48
49/// The lock file this run holds lives in here too; on Unix an open file goes on existing
50/// until the last handle closes, so removing the directory now is safe.
51fn remove_home(ctx: &Context) -> bool {
52    std::fs::remove_dir_all(home::dir(ctx)).is_ok()
53}