From 3a3ec9232f701adc29958b9417f8828bb4699352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E6=94=BF=E7=BF=B0?= Date: Tue, 14 Jul 2026 23:56:49 +0800 Subject: [PATCH 1/2] fix: parse --https_server_port for Antigravity 2.0 detection Antigravity 2.0 language_server.exe uses --https_server_port instead of --extension_server_port. The old regex only matched the latter, causing process detection to fail even though the server is running and has valid CSRF tokens. Add HTTPS_PORT_RE fallback regex and make extension_port optional so detection works with both old and new argument formats. Fixes #192 --- rust/src/providers/antigravity/mod.rs | 53 +++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/rust/src/providers/antigravity/mod.rs b/rust/src/providers/antigravity/mod.rs index 16eb6bf3c6..ead2a7f7ce 100755 --- a/rust/src/providers/antigravity/mod.rs +++ b/rust/src/providers/antigravity/mod.rs @@ -77,6 +77,7 @@ impl AntigravityProvider { static CSRF_RE: OnceLock = OnceLock::new(); static EXT_CSRF_RE: OnceLock = OnceLock::new(); static PORT_RE: OnceLock = OnceLock::new(); + static HTTPS_PORT_RE: OnceLock = OnceLock::new(); let csrf_regex = CSRF_RE .get_or_init(|| Regex::new(r"--csrf_token\s+([a-f0-9-]+)").expect("valid regex")); let ext_csrf_regex = EXT_CSRF_RE.get_or_init(|| { @@ -84,6 +85,9 @@ impl AntigravityProvider { }); let port_regex = PORT_RE .get_or_init(|| Regex::new(r"--extension_server_port\s+(\d+)").expect("valid regex")); + let https_port_regex = HTTPS_PORT_RE.get_or_init(|| { + Regex::new(r"--https_server_port\s+(\d+)").expect("valid regex") + }); for line in stdout.lines() { if line.contains("--csrf_token") { @@ -107,13 +111,19 @@ impl AntigravityProvider { let port = port_regex .captures(line) .and_then(|c| c.get(1)) - .and_then(|m| m.as_str().parse::().ok()); + .and_then(|m| m.as_str().parse::().ok()) + .or_else(|| { + https_port_regex + .captures(line) + .and_then(|c| c.get(1)) + .and_then(|m| m.as_str().parse::().ok()) + }); - if let (Some(token), Some(p)) = (csrf_token, port) { + if let Some(token) = csrf_token { return Some(ProcessInfo { csrf_token: token, extension_server_csrf_token: ext_csrf_token, - extension_port: p, + extension_port: port, pid, }); } @@ -124,7 +134,7 @@ impl AntigravityProvider { } /// Find the actual API port by probing the language server's candidate ports. - async fn find_api_port(extension_port: u16, pid: Option) -> Result { + async fn find_api_port(extension_port: Option, pid: Option) -> Result { // The language server binds a RANDOM localhost port at startup; --extension_server_port // is only a reference point (and belongs to a separate HTTP extension server), so the // real gRPC/Connect API port is not guaranteed to be within a small window above it. @@ -149,7 +159,9 @@ impl AntigravityProvider { if let Some(pid) = pid { candidates.extend(Self::listening_ports_for_pid(pid)); } - candidates.extend((0..20u16).map(|offset| extension_port.saturating_add(offset))); + if let Some(ep) = extension_port.filter(|&p| p > 0) { + candidates.extend((0..20u16).map(|offset| ep.saturating_add(offset))); + } candidates.extend([53835, 53836, 53837, 53838, 53845, 53849]); let mut probed: Vec = Vec::new(); @@ -445,7 +457,7 @@ impl Provider for AntigravityProvider { struct ProcessInfo { csrf_token: String, extension_server_csrf_token: Option, - extension_port: u16, + extension_port: Option, pid: Option, } @@ -702,10 +714,37 @@ mod tests { let process = AntigravityProvider::parse_process_info(output).expect("process info"); assert_eq!(process.pid, Some(4242)); - assert_eq!(process.extension_port, 54123); + assert_eq!(process.extension_port, Some(54123)); assert_eq!(process.csrf_token, "11111111-2222-3333-4444-555555555555"); } + #[test] + fn parses_language_server_without_extension_server_port() { + let output = "34564\tC:\\Users\\test\\AppData\\Local\\Programs\\Antigravity\\resources\\bin\\language_server.exe --standalone --override_ide_name antigravity --subclient_type hub --override_ide_version 2.0.11 --https_server_port 0 --csrf_token 68dda2fb-6b26-40c0-aeef-b9a628615714 --app_data_dir antigravity"; + + let process = AntigravityProvider::parse_process_info(output) + .expect("process info should be detected"); + + assert_eq!(process.pid, Some(34564)); + assert_eq!(process.extension_port, Some(0)); + assert_eq!( + process.csrf_token, + "68dda2fb-6b26-40c0-aeef-b9a628615714" + ); + } + + #[test] + fn parses_language_server_without_any_port_arg() { + let output = "34564\tC:\\Users\\test\\AppData\\Local\\Programs\\Antigravity\\resources\\bin\\language_server.exe --standalone --csrf_token aabbccdd-1122-3344-5566-778899001122 --app_data_dir antigravity"; + + let process = AntigravityProvider::parse_process_info(output) + .expect("process info should be detected"); + + assert_eq!(process.pid, Some(34564)); + assert_eq!(process.extension_port, None); + assert_eq!(process.csrf_token, "aabbccdd-1122-3344-5566-778899001122"); + } + fn make_response(models: Vec<(&str, f64)>) -> UserStatusResponse { let json = serde_json::json!({ "userStatus": { From e2f6bafce8a508b31aee21f171b6e80cc2f8405c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B3=E6=94=BF=E7=BF=B0?= Date: Wed, 15 Jul 2026 16:32:00 +0800 Subject: [PATCH 2/2] refactor: share flag parser across csrf/port args Use one flag_re helper for all --csrf_token, --extension_server_* and --https_server_* args so the parser accepts both --flag VALUE and --flag=VALUE spellings (e.g. Chromium-style). Antigravity 2.0 may emit the = form, which previously left extension_port None and fell through to heuristic probing. Add regression test for --csrf_token=... --https_server_port=... --- rust/src/providers/antigravity/mod.rs | 46 ++++++++++++++++----------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/rust/src/providers/antigravity/mod.rs b/rust/src/providers/antigravity/mod.rs index ead2a7f7ce..61fff25b89 100755 --- a/rust/src/providers/antigravity/mod.rs +++ b/rust/src/providers/antigravity/mod.rs @@ -24,6 +24,12 @@ pub struct AntigravityProvider { metadata: ProviderMetadata, } +/// Return a regex that matches `-- ` or `--=`. +fn flag_re(flag: &str) -> Regex { + Regex::new(&format!(r"--{f}(?:\s+|\s*=\s*)(\S+)", f = flag)) + .expect("valid flag pattern") +} + impl AntigravityProvider { pub fn new() -> Self { Self { @@ -73,21 +79,11 @@ impl AntigravityProvider { } fn parse_process_info(stdout: &str) -> Option { - // Parse command line for CSRF token and port — compiled once - static CSRF_RE: OnceLock = OnceLock::new(); - static EXT_CSRF_RE: OnceLock = OnceLock::new(); - static PORT_RE: OnceLock = OnceLock::new(); - static HTTPS_PORT_RE: OnceLock = OnceLock::new(); - let csrf_regex = CSRF_RE - .get_or_init(|| Regex::new(r"--csrf_token\s+([a-f0-9-]+)").expect("valid regex")); - let ext_csrf_regex = EXT_CSRF_RE.get_or_init(|| { - Regex::new(r"--extension_server_csrf_token\s+([a-f0-9-]+)").expect("valid regex") - }); - let port_regex = PORT_RE - .get_or_init(|| Regex::new(r"--extension_server_port\s+(\d+)").expect("valid regex")); - let https_port_regex = HTTPS_PORT_RE.get_or_init(|| { - Regex::new(r"--https_server_port\s+(\d+)").expect("valid regex") - }); + // Shared argument parser: handles `--flag value` and `--flag=value` forms + let csrf_re = flag_re("csrf_token"); + let ext_csrf_re = flag_re("extension_server_csrf_token"); + let port_re = flag_re("extension_server_port"); + let https_port_re = flag_re("https_server_port"); for line in stdout.lines() { if line.contains("--csrf_token") { @@ -98,22 +94,22 @@ impl AntigravityProvider { None => (None, line), }; - let csrf_token = csrf_regex + let csrf_token = csrf_re .captures(line) .and_then(|c| c.get(1)) .map(|m| m.as_str().to_string()); - let ext_csrf_token = ext_csrf_regex + let ext_csrf_token = ext_csrf_re .captures(line) .and_then(|c| c.get(1)) .map(|m| m.as_str().to_string()); - let port = port_regex + let port = port_re .captures(line) .and_then(|c| c.get(1)) .and_then(|m| m.as_str().parse::().ok()) .or_else(|| { - https_port_regex + https_port_re .captures(line) .and_then(|c| c.get(1)) .and_then(|m| m.as_str().parse::().ok()) @@ -745,6 +741,18 @@ mod tests { assert_eq!(process.csrf_token, "aabbccdd-1122-3344-5566-778899001122"); } + #[test] + fn parses_equals_form_args() { + let output = "34564\tC:\\Users\\test\\AppData\\Local\\Programs\\Antigravity\\resources\\bin\\language_server.exe --csrf_token=68dda2fb-6b26-40c0-aeef-b9a628615714 --https_server_port=61999"; + + let process = AntigravityProvider::parse_process_info(output) + .expect("process info should be detected"); + + assert_eq!(process.pid, Some(34564)); + assert_eq!(process.extension_port, Some(61999)); + assert_eq!(process.csrf_token, "68dda2fb-6b26-40c0-aeef-b9a628615714"); + } + fn make_response(models: Vec<(&str, f64)>) -> UserStatusResponse { let json = serde_json::json!({ "userStatus": {