Strictly verify UI attachment during fallback pipe discovery

This commit is contained in:
Riz Ashraf committed 2026-09-26 21:14:10 +01:00
1 parent 8a67249ddb
commit bffc8896f1
1 file changed
+30 -2
+30 -2
View File
@@ -59,6 +59,28 @@ pub async fn send_error(id: Value, code: i32, message: &str) {
.await; .await;
} }
async fn is_interactive(socket_path: &str) -> bool {
// If it's our explicitly named headless fallback instance, skip the shell-out and reject it immediately
if socket_path.contains("agy-headless-nvim") {
return false;
}
if let Ok(output) = tokio::process::Command::new("nvim")
.arg("--server")
.arg(socket_path)
.arg("--headless")
.arg("--remote-expr")
.arg("empty(nvim_list_uis())")
.output()
.await
{
let stdout = String::from_utf8_lossy(&output.stdout);
// empty(nvim_list_uis()) returns 1 if it IS headless/empty, and 0 if it has a UI
return stdout.trim() == "0";
}
false
}
#[cfg(windows)] #[cfg(windows)]
async fn get_socket_path() -> Result<String, String> { async fn get_socket_path() -> Result<String, String> {
let profile = let profile =
@@ -83,7 +105,10 @@ async fn get_socket_path() -> Result<String, String> {
let name = entry.file_name(); let name = entry.file_name();
let name_str = name.to_string_lossy(); let name_str = name.to_string_lossy();
if name_str.starts_with("nvim.") { if name_str.starts_with("nvim.") {
return Ok(format!(r"\\.\pipe\{name_str}")); let candidate = format!(r"\\.\pipe\{name_str}");
if is_interactive(&candidate).await {
return Ok(candidate);
}
} }
} }
} }
@@ -108,7 +133,10 @@ async fn get_socket_path() -> Result<String, String> {
if name.starts_with("nvim.") { if name.starts_with("nvim.") {
let path = entry.path().join("0"); let path = entry.path().join("0");
if tokio::fs::try_exists(&path).await.unwrap_or(false) { if tokio::fs::try_exists(&path).await.unwrap_or(false) {
return Ok(path.to_string_lossy().to_string()); let candidate = path.to_string_lossy().to_string();
if is_interactive(&candidate).await {
return Ok(candidate);
}
} }
} }
} }