app / src-tauri /src /modules /process.rs
AZILS's picture
Upload 323 files
a21c316 verified
use std::process::Command;
use std::thread;
use std::time::Duration;
use sysinfo::System;
#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;
/// Get normalized path of the current running executable
fn get_current_exe_path() -> Option<std::path::PathBuf> {
std::env::current_exe()
.ok()
.and_then(|p| p.canonicalize().ok())
}
/// Check if Antigravity is running
pub fn is_antigravity_running() -> bool {
let mut system = System::new();
system.refresh_processes(sysinfo::ProcessesToUpdate::All);
let current_exe = get_current_exe_path();
let current_pid = std::process::id();
// Recognition ref 1: Load manual config path (moved outside loop for performance)
let manual_path = crate::modules::config::load_app_config()
.ok()
.and_then(|c| c.antigravity_executable)
.and_then(|p| std::path::PathBuf::from(p).canonicalize().ok());
for (pid, process) in system.processes() {
let pid_u32 = pid.as_u32();
if pid_u32 == current_pid {
continue;
}
let name = process.name().to_string_lossy().to_lowercase();
let exe_path = process
.exe()
.and_then(|p| p.to_str())
.unwrap_or("")
.to_lowercase();
// Exclude own path (handles case where manager is mistaken for Antigravity on Linux)
if let (Some(ref my_path), Some(p_exe)) = (&current_exe, process.exe()) {
if let Ok(p_path) = p_exe.canonicalize() {
if my_path == &p_path {
continue;
}
}
}
// Recognition ref 2: Priority check for manual path match
if let (Some(ref m_path), Some(p_exe)) = (&manual_path, process.exe()) {
if let Ok(p_path) = p_exe.canonicalize() {
// macOS: Check if within the same .app bundle
#[cfg(target_os = "macos")]
{
let m_path_str = m_path.to_string_lossy();
let p_path_str = p_path.to_string_lossy();
if let (Some(m_idx), Some(p_idx)) =
(m_path_str.find(".app"), p_path_str.find(".app"))
{
if m_path_str[..m_idx + 4] == p_path_str[..p_idx + 4] {
// Even if path matches, must confirm via name and args that it's not a Helper
let args = process.cmd();
let is_helper_by_args = args
.iter()
.any(|arg| arg.to_string_lossy().contains("--type="));
let is_helper_by_name = name.contains("helper")
|| name.contains("plugin")
|| name.contains("renderer")
|| name.contains("gpu")
|| name.contains("crashpad")
|| name.contains("utility")
|| name.contains("audio")
|| name.contains("sandbox");
if !is_helper_by_args && !is_helper_by_name {
return true;
}
}
}
}
#[cfg(not(target_os = "macos"))]
if m_path == &p_path {
return true;
}
}
}
// Common helper process exclusion logic
// Common helper process exclusion logic
let args = process.cmd();
let args_str = args
.iter()
.map(|arg| arg.to_string_lossy().to_lowercase())
.collect::<Vec<String>>()
.join(" ");
let is_helper = args_str.contains("--type=")
|| name.contains("helper")
|| name.contains("plugin")
|| name.contains("renderer")
|| name.contains("gpu")
|| name.contains("crashpad")
|| name.contains("utility")
|| name.contains("audio")
|| name.contains("sandbox")
|| exe_path.contains("crashpad");
#[cfg(target_os = "macos")]
{
if exe_path.contains("antigravity.app") && !is_helper {
return true;
}
}
#[cfg(target_os = "windows")]
{
if name == "antigravity.exe" && !is_helper {
return true;
}
}
#[cfg(target_os = "linux")]
{
if (name.contains("antigravity") || exe_path.contains("/antigravity"))
&& !name.contains("tools")
&& !is_helper
{
return true;
}
}
}
false
}
#[cfg(target_os = "linux")]
/// Get PID set of current process and all direct relatives (ancestors + descendants)
fn get_self_family_pids(system: &sysinfo::System) -> std::collections::HashSet<u32> {
let current_pid = std::process::id();
let mut family_pids = std::collections::HashSet::new();
family_pids.insert(current_pid);
// 1. Look up all ancestors (Ancestors) - prevent killing the launcher
let mut next_pid = current_pid;
// Prevent infinite loop, max depth 10
for _ in 0..10 {
let pid_val = sysinfo::Pid::from_u32(next_pid);
if let Some(process) = system.process(pid_val) {
if let Some(parent) = process.parent() {
let parent_id = parent.as_u32();
// Avoid cycles or duplicates
if !family_pids.insert(parent_id) {
break;
}
next_pid = parent_id;
} else {
break;
}
} else {
break;
}
}
// 2. Look down all descendants (Descendants)
// Build parent-child relationship map (Parent -> Children)
let mut adj: std::collections::HashMap<u32, Vec<u32>> = std::collections::HashMap::new();
for (pid, process) in system.processes() {
if let Some(parent) = process.parent() {
adj.entry(parent.as_u32()).or_default().push(pid.as_u32());
}
}
// BFS traversal to find all descendants
let mut queue = std::collections::VecDeque::new();
queue.push_back(current_pid);
while let Some(pid) = queue.pop_front() {
if let Some(children) = adj.get(&pid) {
for &child in children {
if family_pids.insert(child) {
queue.push_back(child);
}
}
}
}
family_pids
}
/// Get PIDs of all Antigravity processes (including main and helper processes)
fn get_antigravity_pids() -> Vec<u32> {
let mut system = System::new();
system.refresh_processes(sysinfo::ProcessesToUpdate::All);
// Linux: Enable family process tree exclusion
#[cfg(target_os = "linux")]
let family_pids = get_self_family_pids(&system);
let mut pids = Vec::new();
let current_pid = std::process::id();
let current_exe = get_current_exe_path();
// Load manual config path as auxiliary reference
let manual_path = crate::modules::config::load_app_config()
.ok()
.and_then(|c| c.antigravity_executable)
.and_then(|p| std::path::PathBuf::from(p).canonicalize().ok());
for (pid, process) in system.processes() {
let pid_u32 = pid.as_u32();
// Exclude own PID
if pid_u32 == current_pid {
continue;
}
// Exclude own executable path (hardened against broad name matching)
if let (Some(ref my_path), Some(p_exe)) = (&current_exe, process.exe()) {
if let Ok(p_path) = p_exe.canonicalize() {
if my_path == &p_path {
continue;
}
}
}
let _name = process.name().to_string_lossy().to_lowercase();
#[cfg(target_os = "linux")]
{
// 1. Exclude family processes (self, children, parents)
if family_pids.contains(&pid_u32) {
continue;
}
// 2. Extra protection: match "tools" likely manager if not a child
if _name.contains("tools") {
continue;
}
}
#[cfg(not(target_os = "linux"))]
{
// Other platforms: exclude only self
if pid_u32 == current_pid {
continue;
}
}
// Recognition ref 3: Check manual config path match
if let (Some(ref m_path), Some(p_exe)) = (&manual_path, process.exe()) {
if let Ok(p_path) = p_exe.canonicalize() {
#[cfg(target_os = "macos")]
{
let m_path_str = m_path.to_string_lossy();
let p_path_str = p_path.to_string_lossy();
if let (Some(m_idx), Some(p_idx)) =
(m_path_str.find(".app"), p_path_str.find(".app"))
{
if m_path_str[..m_idx + 4] == p_path_str[..p_idx + 4] {
let args = process.cmd();
let is_helper_by_args = args
.iter()
.any(|arg| arg.to_string_lossy().contains("--type="));
let is_helper_by_name = _name.contains("helper")
|| _name.contains("plugin")
|| _name.contains("renderer")
|| _name.contains("gpu")
|| _name.contains("crashpad")
|| _name.contains("utility")
|| _name.contains("audio")
|| _name.contains("sandbox");
if !is_helper_by_args && !is_helper_by_name {
pids.push(pid_u32);
continue;
}
}
}
}
#[cfg(not(target_os = "macos"))]
if m_path == &p_path {
pids.push(pid_u32);
continue;
}
}
}
// Get executable path
let exe_path = process
.exe()
.and_then(|p| p.to_str())
.unwrap_or("")
.to_lowercase();
// Common helper process exclusion logic
let args = process.cmd();
let args_str = args
.iter()
.map(|arg| arg.to_string_lossy().to_lowercase())
.collect::<Vec<String>>()
.join(" ");
let is_helper = args_str.contains("--type=")
|| _name.contains("helper")
|| _name.contains("plugin")
|| _name.contains("renderer")
|| _name.contains("gpu")
|| _name.contains("crashpad")
|| _name.contains("utility")
|| _name.contains("audio")
|| _name.contains("sandbox")
|| exe_path.contains("crashpad");
#[cfg(target_os = "macos")]
{
// Match processes within Antigravity main app bundle, excluding Helper/Plugin/Renderer etc.
if exe_path.contains("antigravity.app") && !is_helper {
pids.push(pid_u32);
}
}
#[cfg(target_os = "windows")]
{
let name = process.name().to_string_lossy().to_lowercase();
if name == "antigravity.exe" && !is_helper {
pids.push(pid_u32);
}
}
#[cfg(target_os = "linux")]
{
let name = process.name().to_string_lossy().to_lowercase();
if (name == "antigravity" || exe_path.contains("/antigravity"))
&& !name.contains("tools")
&& !is_helper
{
pids.push(pid_u32);
}
}
}
if !pids.is_empty() {
crate::modules::logger::log_info(&format!(
"Found {} Antigravity processes: {:?}",
pids.len(),
pids
));
}
pids
}
/// Close Antigravity processes
pub fn close_antigravity(#[allow(unused_variables)] timeout_secs: u64) -> Result<(), String> {
crate::modules::logger::log_info("Closing Antigravity...");
#[cfg(target_os = "windows")]
{
// Windows: Precise kill by PID to support multiple versions or custom filenames
let pids = get_antigravity_pids();
if !pids.is_empty() {
crate::modules::logger::log_info(&format!(
"Precisely closing {} identified processes on Windows...",
pids.len()
));
for pid in pids {
let _ = Command::new("taskkill")
.args(["/F", "/PID", &pid.to_string()])
.creation_flags(0x08000000) // CREATE_NO_WINDOW
.output();
}
// Give some time for system to clean up PIDs
thread::sleep(Duration::from_millis(200));
}
}
#[cfg(target_os = "macos")]
{
// macOS: Optimize closing strategy to avoid "Window terminated unexpectedly" popups
// Strategy: SEND SIGTERM to main process only, let it coordinate closing children
let pids = get_antigravity_pids();
if !pids.is_empty() {
// 1. Identify main process (PID)
// Strategy: Principal processes of Electron/Tauri do not have the `--type` parameter, while Helper processes have `--type=renderer/gpu/utility`, etc.
let mut system = System::new();
system.refresh_processes(sysinfo::ProcessesToUpdate::All);
let mut main_pid = None;
// Load manual configuration path as highest priority reference
let manual_path = crate::modules::config::load_app_config()
.ok()
.and_then(|c| c.antigravity_executable)
.and_then(|p| std::path::PathBuf::from(p).canonicalize().ok());
crate::modules::logger::log_info("Analyzing process list to identify main process:");
for pid_u32 in &pids {
let pid = sysinfo::Pid::from_u32(*pid_u32);
if let Some(process) = system.process(pid) {
let name = process.name().to_string_lossy();
let args = process.cmd();
let args_str = args
.iter()
.map(|arg| arg.to_string_lossy().into_owned())
.collect::<Vec<String>>()
.join(" ");
crate::modules::logger::log_info(&format!(
" - PID: {} | Name: {} | Args: {}",
pid_u32, name, args_str
));
// 1. Priority to manual path matching
if let (Some(ref m_path), Some(p_exe)) = (&manual_path, process.exe()) {
if let Ok(p_path) = p_exe.canonicalize() {
let m_path_str = m_path.to_string_lossy();
let p_path_str = p_path.to_string_lossy();
if let (Some(m_idx), Some(p_idx)) =
(m_path_str.find(".app"), p_path_str.find(".app"))
{
if m_path_str[..m_idx + 4] == p_path_str[..p_idx + 4] {
// Deep validation: even if path matches, must exclude Helper keywords and arguments
let is_helper_by_args = args_str.contains("--type=");
let is_helper_by_name = name.to_lowercase().contains("helper")
|| name.to_lowercase().contains("plugin")
|| name.to_lowercase().contains("renderer")
|| name.to_lowercase().contains("gpu")
|| name.to_lowercase().contains("crashpad")
|| name.to_lowercase().contains("utility")
|| name.to_lowercase().contains("audio")
|| name.to_lowercase().contains("sandbox")
|| name.to_lowercase().contains("language_server");
if !is_helper_by_args && !is_helper_by_name {
main_pid = Some(pid_u32);
crate::modules::logger::log_info(&format!(
" => Identified as main process (manual path match)"
));
break;
}
}
}
}
}
// 2. Feature analysis matching (fallback)
let is_helper_by_name = name.to_lowercase().contains("helper")
|| name.to_lowercase().contains("crashpad")
|| name.to_lowercase().contains("utility")
|| name.to_lowercase().contains("audio")
|| name.to_lowercase().contains("sandbox")
|| name.to_lowercase().contains("language_server")
|| name.to_lowercase().contains("plugin")
|| name.to_lowercase().contains("renderer");
let is_helper_by_args = args_str.contains("--type=");
if !is_helper_by_name && !is_helper_by_args {
if main_pid.is_none() {
main_pid = Some(pid_u32);
crate::modules::logger::log_info(&format!(
" => Identified as main process (Name/Args analysis)"
));
}
} else {
crate::modules::logger::log_info(&format!(
" => Identified as helper process (Helper/Args)"
));
}
}
}
// Phase 1: Graceful exit (SIGTERM)
if let Some(pid) = main_pid {
crate::modules::logger::log_info(&format!(
"Sending SIGTERM to main process PID: {}",
pid
));
let output = Command::new("kill")
.args(["-15", &pid.to_string()])
.output();
if let Ok(result) = output {
if !result.status.success() {
let error = String::from_utf8_lossy(&result.stderr);
crate::modules::logger::log_warn(&format!(
"Main process SIGTERM failed: {}",
error
));
}
}
} else {
crate::modules::logger::log_warn(
"No clear main process identified, attempting SIGTERM for all processes (may cause popups)",
);
for pid in &pids {
let _ = Command::new("kill")
.args(["-15", &pid.to_string()])
.output();
}
}
// Wait for graceful exit (max 70% of timeout_secs)
let graceful_timeout = (timeout_secs * 7) / 10;
let start = std::time::Instant::now();
while start.elapsed() < Duration::from_secs(graceful_timeout) {
if !is_antigravity_running() {
crate::modules::logger::log_info("All Antigravity processes gracefully closed");
return Ok(());
}
thread::sleep(Duration::from_millis(500));
}
// Phase 2: Force kill (SIGKILL) - targeting all remaining processes (Helpers)
if is_antigravity_running() {
let remaining_pids = get_antigravity_pids();
if !remaining_pids.is_empty() {
crate::modules::logger::log_warn(&format!(
"Graceful exit timeout, force killing {} remaining processes (SIGKILL)",
remaining_pids.len()
));
for pid in &remaining_pids {
let output = Command::new("kill").args(["-9", &pid.to_string()]).output();
if let Ok(result) = output {
if !result.status.success() {
let error = String::from_utf8_lossy(&result.stderr);
if !error.contains("No such process") {
// "No matching processes" for killall, "No such process" for kill
crate::modules::logger::log_error(&format!(
"SIGKILL process {} failed: {}",
pid, error
));
}
}
}
}
thread::sleep(Duration::from_secs(1));
}
// Final check
if !is_antigravity_running() {
crate::modules::logger::log_info("All processes exited after forced cleanup");
return Ok(());
}
} else {
crate::modules::logger::log_info("All processes exited after SIGTERM");
return Ok(());
}
} else {
// Only consider not running when pids is empty, don't error here as it might already be closed
crate::modules::logger::log_info("Antigravity not running, no need to close");
return Ok(());
}
}
#[cfg(target_os = "linux")]
{
// Linux: Also attempt to identify main process and delegate exit
let pids = get_antigravity_pids();
if !pids.is_empty() {
let mut system = System::new();
system.refresh_processes(sysinfo::ProcessesToUpdate::All);
let mut main_pid = None;
// Load manual configuration path as highest priority reference
let manual_path = crate::modules::config::load_app_config()
.ok()
.and_then(|c| c.antigravity_executable)
.and_then(|p| std::path::PathBuf::from(p).canonicalize().ok());
crate::modules::logger::log_info("Analyzing Linux process list to identify main process:");
for pid_u32 in &pids {
let pid = sysinfo::Pid::from_u32(*pid_u32);
if let Some(process) = system.process(pid) {
let name = process.name().to_string_lossy().to_lowercase();
let args = process.cmd();
let args_str = args
.iter()
.map(|arg| arg.to_string_lossy().into_owned())
.collect::<Vec<String>>()
.join(" ");
crate::modules::logger::log_info(&format!(
" - PID: {} | Name: {} | Args: {}",
pid_u32, name, args_str
));
// 1. Priority to manual path matching
if let (Some(ref m_path), Some(p_exe)) = (&manual_path, process.exe()) {
if let Ok(p_path) = p_exe.canonicalize() {
if &p_path == m_path {
// Confirm not a Helper
let is_helper_by_args = args_str.contains("--type=");
let is_helper_by_name = name.contains("helper")
|| name.contains("renderer")
|| name.contains("gpu")
|| name.contains("crashpad")
|| name.contains("utility")
|| name.contains("audio")
|| name.contains("sandbox");
if !is_helper_by_args && !is_helper_by_name {
main_pid = Some(pid_u32);
crate::modules::logger::log_info(&format!(
" => Identified as main process (manual path match)"
));
break;
}
}
}
}
// 2. Feature analysis matching
let is_helper_by_args = args_str.contains("--type=");
let is_helper_by_name = name.contains("helper")
|| name.contains("renderer")
|| name.contains("gpu")
|| name.contains("crashpad")
|| name.contains("utility")
|| name.contains("audio")
|| name.contains("sandbox")
|| name.contains("plugin")
|| name.contains("language_server");
if !is_helper_by_args && !is_helper_by_name {
if main_pid.is_none() {
main_pid = Some(pid_u32);
crate::modules::logger::log_info(&format!(
" => Identified as main process (Feature analysis)"
));
}
} else {
crate::modules::logger::log_info(&format!(
" => Identified as helper process (Helper/Args)"
));
}
}
}
// Phase 1: Graceful exit (SIGTERM)
if let Some(pid) = main_pid {
crate::modules::logger::log_info(&format!("Attempting to gracefully close main process {} (SIGTERM)", pid));
let _ = Command::new("kill")
.args(["-15", &pid.to_string()])
.output();
} else {
crate::modules::logger::log_warn(
"No clear Linux main process identified, sending SIGTERM to all associated processes",
);
for pid in &pids {
let _ = Command::new("kill")
.args(["-15", &pid.to_string()])
.output();
}
}
// Wait for graceful exit
let graceful_timeout = (timeout_secs * 7) / 10;
let start = std::time::Instant::now();
while start.elapsed() < Duration::from_secs(graceful_timeout) {
if !is_antigravity_running() {
crate::modules::logger::log_info("Antigravity gracefully closed");
return Ok(());
}
thread::sleep(Duration::from_millis(500));
}
// Phase 2: Force kill (SIGKILL) - targeting all remaining processes
if is_antigravity_running() {
let remaining_pids = get_antigravity_pids();
if !remaining_pids.is_empty() {
crate::modules::logger::log_warn(&format!(
"Graceful exit timeout, force killing {} remaining processes (SIGKILL)",
remaining_pids.len()
));
for pid in &remaining_pids {
let _ = Command::new("kill").args(["-9", &pid.to_string()]).output();
}
thread::sleep(Duration::from_secs(1));
}
}
} else {
// pids is empty, meaning no process detected or all excluded by logic
crate::modules::logger::log_info(
"No Antigravity processes found to close (possibly filtered or not running)",
);
}
}
// Final check
if is_antigravity_running() {
return Err("Unable to close Antigravity process, please close manually and retry".to_string());
}
crate::modules::logger::log_info("Antigravity closed successfully");
Ok(())
}
/// Start Antigravity
#[allow(unused_mut)]
pub fn start_antigravity() -> Result<(), String> {
crate::modules::logger::log_info("Starting Antigravity...");
// Prefer manually specified path and args from configuration
let config = crate::modules::config::load_app_config().ok();
let manual_path = config
.as_ref()
.and_then(|c| c.antigravity_executable.clone());
let args = config.and_then(|c| c.antigravity_args.clone());
if let Some(mut path_str) = manual_path {
let mut path = std::path::PathBuf::from(&path_str);
#[cfg(target_os = "macos")]
{
// Fault tolerance: If path is inside .app bundle (e.g. misselected Helper), auto-correct to .app directory
if let Some(app_idx) = path_str.find(".app") {
let corrected_app = &path_str[..app_idx + 4];
if corrected_app != path_str {
crate::modules::logger::log_info(&format!(
"Detected macOS path inside .app bundle, auto-correcting to: {}",
corrected_app
));
path_str = corrected_app.to_string();
path = std::path::PathBuf::from(&path_str);
}
}
}
if path.exists() {
crate::modules::logger::log_info(&format!("Starting with manual configuration path: {}", path_str));
#[cfg(target_os = "macos")]
{
// macOS: if .app directory, use open
if path_str.ends_with(".app") || path.is_dir() {
let mut cmd = Command::new("open");
cmd.arg("-a").arg(&path_str);
// Add startup arguments
if let Some(ref args) = args {
for arg in args {
cmd.arg(arg);
}
}
cmd.spawn().map_err(|e| format!("Startup failed (open): {}", e))?;
} else {
let mut cmd = Command::new(&path_str);
// Add startup arguments
if let Some(ref args) = args {
for arg in args {
cmd.arg(arg);
}
}
cmd.spawn()
.map_err(|e| format!("Startup failed (direct): {}", e))?;
}
}
#[cfg(not(target_os = "macos"))]
{
let mut cmd = Command::new(&path_str);
// Add startup arguments
if let Some(ref args) = args {
for arg in args {
cmd.arg(arg);
}
}
cmd.spawn().map_err(|e| format!("Startup failed: {}", e))?;
}
crate::modules::logger::log_info(&format!(
"Antigravity startup command sent (manual path: {}, args: {:?})",
path_str, args
));
return Ok(());
} else {
crate::modules::logger::log_warn(&format!(
"Manual configuration path does not exist: {}, falling back to auto-detection",
path_str
));
}
}
#[cfg(target_os = "macos")]
{
// Improvement: Use output() to wait for open command completion and capture "app not found" error
let mut cmd = Command::new("open");
cmd.args(["-a", "Antigravity"]);
// Add startup arguments
if let Some(ref args) = args {
for arg in args {
cmd.arg(arg);
}
}
let output = cmd
.output()
.map_err(|e| format!("Unable to execute open command: {}", e))?;
if !output.status.success() {
let error = String::from_utf8_lossy(&output.stderr);
return Err(format!(
"Startup failed (open exited with {}): {}",
output.status, error
));
}
}
#[cfg(target_os = "windows")]
{
let has_args = args.as_ref().map_or(false, |a| !a.is_empty());
if has_args {
if let Some(detected_path) = get_antigravity_executable_path() {
let path_str = detected_path.to_string_lossy().to_string();
crate::modules::logger::log_info(&format!(
"Starting with auto-detected path (has args): {}",
path_str
));
use crate::utils::command::CommandExtWrapper;
let mut cmd = Command::new(&path_str);
cmd.creation_flags_windows();
if let Some(ref args) = args {
for arg in args {
cmd.arg(arg);
}
}
cmd.spawn().map_err(|e| format!("Startup failed: {}", e))?;
} else {
return Err("Startup arguments configured but cannot find Antigravity executable path. Please set the executable path manually in Settings.".to_string());
}
} else {
use crate::utils::command::CommandExtWrapper;
let mut cmd = Command::new("cmd");
cmd.creation_flags_windows();
cmd.args(["/C", "start", "antigravity://"]);
let result = cmd.spawn();
if result.is_err() {
return Err("Startup failed, please open Antigravity manually".to_string());
}
}
}
#[cfg(target_os = "linux")]
{
let mut cmd = Command::new("antigravity");
// Add startup arguments
if let Some(ref args) = args {
for arg in args {
cmd.arg(arg);
}
}
cmd.spawn().map_err(|e| format!("Startup failed: {}", e))?;
}
crate::modules::logger::log_info(&format!(
"Antigravity startup command sent (default detection, args: {:?})",
args
));
Ok(())
}
/// Get Antigravity executable path and startup arguments from running processes
///
/// This is the most reliable method to find installations and startup args anywhere
fn get_process_info() -> (Option<std::path::PathBuf>, Option<Vec<String>>) {
let mut system = System::new_all();
system.refresh_all();
let current_exe = get_current_exe_path();
let current_pid = std::process::id();
for (pid, process) in system.processes() {
let pid_u32 = pid.as_u32();
if pid_u32 == current_pid {
continue;
}
// Exclude manager process itself
if let (Some(ref my_path), Some(p_exe)) = (&current_exe, process.exe()) {
if let Ok(p_path) = p_exe.canonicalize() {
if my_path == &p_path {
continue;
}
}
}
let name = process.name().to_string_lossy().to_lowercase();
// Get executable path and command line arguments
if let Some(exe) = process.exe() {
let mut args = process.cmd().iter();
let exe_path = args
.next()
.map_or(exe.to_string_lossy(), |arg| arg.to_string_lossy())
.to_lowercase();
// Extract actual arguments from command line (skipping exe path)
let args = args
.map(|arg| arg.to_string_lossy().to_lowercase())
.collect::<Vec<String>>();
let args_str = args.join(" ");
// Common helper process exclusion logic
let is_helper = args_str.contains("--type=")
|| args_str.contains("node-ipc")
|| args_str.contains("nodeipc")
|| args_str.contains("max-old-space-size")
|| args_str.contains("node_modules")
|| name.contains("helper")
|| name.contains("plugin")
|| name.contains("renderer")
|| name.contains("gpu")
|| name.contains("crashpad")
|| name.contains("utility")
|| name.contains("audio")
|| name.contains("sandbox")
|| exe_path.contains("crashpad");
let path = Some(exe.to_path_buf());
let args = Some(args);
#[cfg(target_os = "macos")]
{
// macOS: Exclude helper processes, match main app only, and check Frameworks
if exe_path.contains("antigravity.app")
&& !is_helper
&& !exe_path.contains("frameworks")
{
// Try to extract .app path for better open command support
if let Some(app_idx) = exe_path.find(".app") {
let app_path_str = &exe.to_string_lossy()[..app_idx + 4];
let path = Some(std::path::PathBuf::from(app_path_str));
return (path, args);
}
return (path, args);
}
}
#[cfg(target_os = "windows")]
{
// Windows: Strictly match process name and exclude helpers
if name == "antigravity.exe" && !is_helper {
return (path, args);
}
}
#[cfg(target_os = "linux")]
{
// Linux: Check process name or path for antigravity, excluding helpers and manager
if (name == "antigravity" || exe_path.contains("/antigravity"))
&& !name.contains("tools")
&& !is_helper
{
return (path, args);
}
}
}
}
(None, None)
}
/// Get Antigravity executable path from running processes
///
/// Most reliable method to find installation anywhere
pub fn get_path_from_running_process() -> Option<std::path::PathBuf> {
let (path, _) = get_process_info();
path
}
/// Get Antigravity startup arguments from running processes
pub fn get_args_from_running_process() -> Option<Vec<String>> {
let (_, args) = get_process_info();
args
}
/// Get --user-data-dir argument value (if exists)
pub fn get_user_data_dir_from_process() -> Option<std::path::PathBuf> {
// Prefer getting startup arguments from config
if let Ok(config) = crate::modules::config::load_app_config() {
if let Some(args) = config.antigravity_args {
// Check arguments in config
for i in 0..args.len() {
if args[i] == "--user-data-dir" && i + 1 < args.len() {
// Next argument is the path
let path = std::path::PathBuf::from(&args[i + 1]);
if path.exists() {
return Some(path);
}
} else if args[i].starts_with("--user-data-dir=") {
// Argument and value in same string, e.g. --user-data-dir=/path/to/data
let parts: Vec<&str> = args[i].splitn(2, '=').collect();
if parts.len() == 2 {
let path_str = parts[1];
let path = std::path::PathBuf::from(path_str);
if path.exists() {
return Some(path);
}
}
}
}
}
}
// If not in config, get arguments from running process
if let Some(args) = get_args_from_running_process() {
for i in 0..args.len() {
if args[i] == "--user-data-dir" && i + 1 < args.len() {
// Next argument is the path
let path = std::path::PathBuf::from(&args[i + 1]);
if path.exists() {
return Some(path);
}
} else if args[i].starts_with("--user-data-dir=") {
// Argument and value in same string, e.g. --user-data-dir=/path/to/data
let parts: Vec<&str> = args[i].splitn(2, '=').collect();
if parts.len() == 2 {
let path_str = parts[1];
let path = std::path::PathBuf::from(path_str);
if path.exists() {
return Some(path);
}
}
}
}
}
None
}
/// Get Antigravity executable path (cross-platform)
///
/// Search strategy (highest to lowest priority):
/// 1. Get path from running process (most reliable, supports any location)
/// 2. Iterate standard installation locations
/// 3. Return None
pub fn get_antigravity_executable_path() -> Option<std::path::PathBuf> {
// Strategy 1: Get from running process (supports any location)
if let Some(path) = get_path_from_running_process() {
return Some(path);
}
// Strategy 2: Check standard installation locations
check_standard_locations()
}
/// Check standard installation locations
fn check_standard_locations() -> Option<std::path::PathBuf> {
#[cfg(target_os = "macos")]
{
let path = std::path::PathBuf::from("/Applications/Antigravity.app");
if path.exists() {
return Some(path);
}
}
#[cfg(target_os = "windows")]
{
use std::env;
// Get environment variables
let local_appdata = env::var("LOCALAPPDATA").ok();
let program_files =
env::var("ProgramFiles").unwrap_or_else(|_| "C:\\Program Files".to_string());
let program_files_x86 =
env::var("ProgramFiles(x86)").unwrap_or_else(|_| "C:\\Program Files (x86)".to_string());
let mut possible_paths = Vec::new();
// User installation location (preferred)
if let Some(local) = local_appdata {
possible_paths.push(
std::path::PathBuf::from(&local)
.join("Programs")
.join("Antigravity")
.join("Antigravity.exe"),
);
}
// System installation location
possible_paths.push(
std::path::PathBuf::from(&program_files)
.join("Antigravity")
.join("Antigravity.exe"),
);
// 32-bit compatibility location
possible_paths.push(
std::path::PathBuf::from(&program_files_x86)
.join("Antigravity")
.join("Antigravity.exe"),
);
// Return the first existing path
for path in possible_paths {
if path.exists() {
return Some(path);
}
}
}
#[cfg(target_os = "linux")]
{
let possible_paths = vec![
std::path::PathBuf::from("/usr/bin/antigravity"),
std::path::PathBuf::from("/opt/Antigravity/antigravity"),
std::path::PathBuf::from("/usr/share/antigravity/antigravity"),
];
// User local installation
if let Some(home) = dirs::home_dir() {
let user_local = home.join(".local/bin/antigravity");
if user_local.exists() {
return Some(user_local);
}
}
for path in possible_paths {
if path.exists() {
return Some(path);
}
}
}
None
}