feat: add proxyctl daemon manager, fix standalone LS cleanup

- Add proxyctl CLI script for systemd service management
- Add systemd user service file for background operation
- Fix standalone LS kill: properly track real LS PID via pgrep
  and use sudo kill for cross-user cleanup on shutdown
- Remove deprecated scripts (dns-redirect, iptables-redirect,
  mitm-wrapper, standalone-ls, parse-snapshot)
- Disable tool stripping in MITM for tool call investigation
- Update GEMINI.md with CLI tools documentation
This commit is contained in:
Nikketryhard
2026-02-14 22:14:00 -06:00
parent f64f007421
commit 3e3af85798
9 changed files with 221 additions and 1425 deletions

View File

@@ -9,7 +9,8 @@ use serde_json::Value;
use tracing::info;
/// Strip ALL tool definitions.
const STRIP_ALL_TOOLS: bool = true;
/// Set to false to allow tools through (for tool call testing).
const STRIP_ALL_TOOLS: bool = false;
/// Modify a streamGenerateContent request body in-place.
/// Returns the modified JSON bytes, or None if modification wasn't possible.

View File

@@ -29,6 +29,10 @@ const LS_USER: &str = "antigravity-ls";
/// A running standalone LS process.
pub struct StandaloneLS {
child: Child,
/// The actual LS process PID (may differ from child PID when spawned via sudo).
ls_pid: Option<u32>,
/// Whether the LS was spawned via sudo (needs sudo kill).
use_sudo: bool,
pub port: u16,
pub csrf: String,
}
@@ -196,8 +200,25 @@ impl StandaloneLS {
info!(pid = child.id(), port, "Standalone LS spawned");
// When spawned via sudo, the child is the sudo process which exits after
// launching the LS as the target user. We need the actual LS PID for cleanup.
let ls_pid = if use_sudo {
// Give sudo a moment to spawn the real process
std::thread::sleep(std::time::Duration::from_millis(500));
// Find the LS process owned by antigravity-ls user
find_ls_pid_for_user(LS_USER).ok()
} else {
Some(child.id())
};
if let Some(pid) = ls_pid {
info!(ls_pid = pid, sudo = use_sudo, "Discovered actual LS process");
}
Ok(StandaloneLS {
child,
ls_pid,
use_sudo,
port,
csrf: main_config.csrf.clone(),
})
@@ -239,8 +260,25 @@ impl StandaloneLS {
/// Kill the standalone LS process.
pub fn kill(&mut self) {
info!("Killing standalone LS");
let _ = self.child.kill();
let _ = self.child.wait();
if self.use_sudo {
// The child is sudo which already exited. Kill the actual LS via sudo.
if let Some(pid) = self.ls_pid {
info!(pid, "Killing LS process via sudo");
let _ = std::process::Command::new("sudo")
.args(["-n", "kill", "-TERM", &pid.to_string()])
.status();
// Give it a moment to exit gracefully
std::thread::sleep(std::time::Duration::from_millis(500));
// Force kill if still alive
let _ = std::process::Command::new("sudo")
.args(["-n", "kill", "-KILL", &pid.to_string()])
.status();
}
} else {
let _ = self.child.kill();
let _ = self.child.wait();
}
}
}
@@ -366,6 +404,23 @@ fn has_ls_user() -> bool {
.unwrap_or(false)
}
/// Find the PID of a language_server process owned by a specific user.
///
/// Used to discover the actual LS process after sudo spawns it as a different user.
fn find_ls_pid_for_user(user: &str) -> Result<u32, String> {
let output = Command::new("pgrep")
.args(["-u", user, "-f", "language_server_linux"])
.output()
.map_err(|e| format!("pgrep failed: {e}"))?;
let stdout = String::from_utf8_lossy(&output.stdout);
stdout
.lines()
.next()
.and_then(|line| line.trim().parse::<u32>().ok())
.ok_or_else(|| format!("No LS process found for user {user}"))
}
#[cfg(test)]
mod tests {
use super::*;