diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-16 20:16:23 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-07-16 20:16:23 +0300 |
| commit | e907e6a3664608b34351344dfdfbc0431dab8760 (patch) | |
| tree | dd941b378612b44032effdec68f42116fdd837f9 | |
| parent | 8915f01c6577b5507d1dd1a7851ec3ecb9154189 (diff) | |
fix(windows): hide agent manifest curl windows
refs #1468
| -rw-r--r-- | docs/next/CHANGELOG.md | 1 | ||||
| -rw-r--r-- | src/detect/manifest_update.rs | 4 | ||||
| -rw-r--r-- | src/platform/mod.rs | 7 | ||||
| -rw-r--r-- | src/platform/windows.rs | 56 |
4 files changed, 47 insertions, 21 deletions
diff --git a/docs/next/CHANGELOG.md b/docs/next/CHANGELOG.md index 3f75e127..2e83fd02 100644 --- a/docs/next/CHANGELOG.md +++ b/docs/next/CHANGELOG.md @@ -7,6 +7,7 @@ - Added macOS support for the `HERDR_AGENT=<agent>` foreground-process hint, allowing agents hidden behind host-visible wrappers such as `nono` to use the named agent's screen manifest. (#679) ### Fixed +- Background agent-detection manifest checks no longer flash curl console windows on Windows. (#1468) - Live handoff now preserves installed plugins and no longer lets the next plugin installation overwrite the existing registry. (#893) - `herdr wait agent-status` now returns `pane_not_found` promptly when its target pane closes instead of waiting for the full timeout. (#1439) diff --git a/src/detect/manifest_update.rs b/src/detect/manifest_update.rs index ac06ec97..02efa3bc 100644 --- a/src/detect/manifest_update.rs +++ b/src/detect/manifest_update.rs @@ -470,7 +470,9 @@ fn catalog_url() -> String { fn fetch_text(url: &str) -> Result<String, String> { let max_fetch_bytes = MAX_FETCH_BYTES.to_string(); - let mut child = Command::new("curl") + let mut command = Command::new("curl"); + crate::platform::configure_background_command(&mut command); + let mut child = command .args([ "-sfL", "--retry", diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 3583df5e..42b9e128 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -33,6 +33,13 @@ pub(crate) fn pane_custom_command_pty_builder(command: &str) -> portable_pty::Co pane_custom_command_pty_builder_platform(command) } +pub(crate) fn configure_background_command(command: &mut std::process::Command) { + configure_background_command_platform(command); +} + +#[cfg(not(windows))] +fn configure_background_command_platform(_command: &mut std::process::Command) {} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct PlatformCapabilities { pub(crate) live_handoff: bool, diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 209ec6d6..46c75141 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -28,8 +28,8 @@ use windows_sys::{ Ole::CF_UNICODETEXT, Threading::{ GetCurrentProcess, GetExitCodeProcess, OpenProcess, TerminateProcess, - DETACHED_PROCESS, PROCESS_BASIC_INFORMATION, PROCESS_QUERY_LIMITED_INFORMATION, - PROCESS_VM_READ, + CREATE_NO_WINDOW, DETACHED_PROCESS, PROCESS_BASIC_INFORMATION, + PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_READ, }, }, UI::Shell::{CommandLineToArgvW, ShellExecuteW}, @@ -129,6 +129,12 @@ fn scrollback_editor_argv_with_env( Ok(argv) } +pub(crate) fn configure_background_command_platform(command: &mut std::process::Command) { + use std::os::windows::process::CommandExt; + + command.creation_flags(CREATE_NO_WINDOW); +} + pub fn detach_server_daemon_command(command: &mut std::process::Command) { use std::os::windows::process::CommandExt; @@ -662,12 +668,16 @@ mod tests { use windows_sys::Win32::System::Console::{AllocConsole, FreeConsole, GetConsoleWindow}; - const DETACHED_CONSOLE_TEST_CHILD_ENV: &str = "HERDR_TEST_DETACHED_CONSOLE_CHILD"; + const CONSOLE_TEST_CHILD_ENV: &str = "HERDR_TEST_CONSOLE_CHILD_MODE"; #[test] - fn server_daemon_command_does_not_inherit_console() { - if std::env::var_os(DETACHED_CONSOLE_TEST_CHILD_ENV).is_some() { - assert!(unsafe { GetConsoleWindow() }.is_null()); + fn background_and_server_daemon_commands_do_not_have_consoles() { + if let Some(mode) = std::env::var_os(CONSOLE_TEST_CHILD_ENV) { + assert!( + unsafe { GetConsoleWindow() }.is_null(), + "{} child opened or inherited a console", + mode.to_string_lossy() + ); return; } @@ -679,26 +689,32 @@ mod tests { }; let test_exe = std::env::current_exe().expect("resolve test executable"); - let mut child = Command::new(test_exe); - child - .arg("server_daemon_command_does_not_inherit_console") - .env(DETACHED_CONSOLE_TEST_CHILD_ENV, "1") - .stdin(Stdio::null()) - .stdout(Stdio::null()) - .stderr(Stdio::null()); - super::detach_server_daemon_command(&mut child); + let configurations: [(&str, fn(&mut Command)); 2] = [ + ("background", super::configure_background_command_platform), + ("server daemon", super::detach_server_daemon_command), + ]; + for (mode, configure) in configurations { + let mut child = Command::new(&test_exe); + child + .arg("background_and_server_daemon_commands_do_not_have_consoles") + .env(CONSOLE_TEST_CHILD_ENV, mode) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()); + configure(&mut child); + + let status = child.status().expect("spawn console isolation test child"); + assert!( + status.success(), + "{mode} child opened or inherited a console" + ); + } - let status = child.status().expect("spawn detached test child"); if allocated_console { unsafe { FreeConsole(); } } - - assert!( - status.success(), - "detached child inherited the test console" - ); } fn argv_strings(argv: &[std::ffi::OsString]) -> Vec<String> { |
