fix(ssh): hide the console for ProxyCommand children too

The daemon is spawned detached with no console of its own, so a
ProxyCommand launched from it (`ssh -W`, `connect.exe`, `cloudflared`)
had Windows allocate one — not a flash but a black window that stayed
up for the whole session.

`hide_console` takes `std::process::Command`; this site builds a
`tokio::process::Command`, which is a distinct type with its own
`creation_flags`. Add `hide_console_tokio` alongside it so the module
comment's claim that every non-PTY Command goes through this file holds
again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
thomas
2026-07-19 18:46:52 +08:00
co-authored by Claude Opus 4.8
parent 3231d315bf
commit 87f865d55a
2 changed files with 21 additions and 4 deletions
+17 -4
View File
@@ -8,19 +8,32 @@
//!
//! `CREATE_NO_WINDOW` suppresses the console entirely; stdout/stderr pipes are
//! unaffected, so output capture keeps working. Every non-PTY `Command` in the
//! app should go through [`hide_console`] before it runs. PTY children are not
//! in scope — the daemon owns those and passes its own flags (see
//! [`crate::daemon::spawn`]).
//! app should go through [`hide_console`] (or [`hide_console_tokio`] for the
//! async flavor) before it runs. PTY children are not in scope — the daemon
//! owns those and passes its own flags (see [`crate::daemon::spawn`]).
use std::process::Command;
#[cfg(windows)]
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
/// Suppress the console window Windows would otherwise allocate for a console
/// subsystem child. No-op on Unix, so callers stay `cfg`-free.
pub fn hide_console(cmd: &mut Command) -> &mut Command {
#[cfg(windows)]
{
use std::os::windows::process::CommandExt as _;
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
cmd.creation_flags(CREATE_NO_WINDOW);
}
cmd
}
/// [`hide_console`] for `tokio::process::Command`. Separate because tokio's
/// builder is a distinct type with its own `creation_flags`, not a `Deref` to
/// the std one.
pub fn hide_console_tokio(cmd: &mut tokio::process::Command) -> &mut tokio::process::Command {
#[cfg(windows)]
{
cmd.creation_flags(CREATE_NO_WINDOW);
}
cmd
+4
View File
@@ -173,6 +173,10 @@ fn spawn_proxy_command(
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::inherit())
.kill_on_drop(true);
// The daemon is detached and has no console to lend this child, so without
// the flag a `ProxyCommand` (`ssh -W`, `connect.exe`, `cloudflared`) gets a
// console of its own that stays up for the whole session.
crate::core::proc::hide_console_tokio(&mut cmd);
let mut child = cmd
.spawn()
.map_err(|e| anyhow::anyhow!("spawn ProxyCommand failed: {e}"))?;