mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 03:46:22 +00:00
fix(windows): hide subprocess console windows
This commit is contained in:
@@ -1325,7 +1325,7 @@ fn db_type_for_native_offline_entry(registry: &AgentRegistry, platform: &str, na
|
||||
|
||||
fn extract_tar_gz(archive: &Path, dest: &Path) -> Result<(), String> {
|
||||
std::fs::create_dir_all(dest).map_err(|e| e.to_string())?;
|
||||
let status = std::process::Command::new("tar")
|
||||
let status = crate::process::new_std_command("tar")
|
||||
.args(["xzf", &archive.to_string_lossy(), "-C", &dest.to_string_lossy(), "--strip-components=1"])
|
||||
.status()
|
||||
.map_err(|e| format!("Failed to extract archive: {e}"))?;
|
||||
|
||||
@@ -8,9 +8,6 @@ use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::Notify;
|
||||
|
||||
#[cfg(windows)]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CliAgentRunOptions {
|
||||
pub connection_id: String,
|
||||
@@ -103,19 +100,7 @@ pub fn model_infos(ids: &[&str]) -> Vec<AiModelInfo> {
|
||||
}
|
||||
|
||||
pub fn cli_command(program: impl AsRef<OsStr>) -> Command {
|
||||
let command = Command::new(program);
|
||||
configure_cli_command(command)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn configure_cli_command(mut command: Command) -> Command {
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
command
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn configure_cli_command(command: Command) -> Command {
|
||||
command
|
||||
crate::process::new_tokio_command(program)
|
||||
}
|
||||
|
||||
pub async fn list_json_models_or_default(
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::env;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
use std::time::Instant;
|
||||
use tokio::process::Command;
|
||||
use tokio::sync::Notify;
|
||||
|
||||
const DEFAULT_CODEX_MODELS: &[&str] = &["default", "gpt-5.5", "gpt-5.4-mini"];
|
||||
@@ -186,7 +185,7 @@ fn program_path_candidates(dir: &Path, program: &str) -> Vec<PathBuf> {
|
||||
#[cfg(not(windows))]
|
||||
async fn shell_program_path(program: &str) -> Option<String> {
|
||||
let script = shell_resolve_script(program);
|
||||
let mut command = Command::new(user_shell());
|
||||
let mut command = cli_command(user_shell());
|
||||
command.args(user_shell_args(&script));
|
||||
command.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::null());
|
||||
let output = command.output().await.ok()?;
|
||||
@@ -204,7 +203,7 @@ async fn shell_program_path(program: &str) -> Option<String> {
|
||||
#[cfg(windows)]
|
||||
async fn shell_program_path(program: &str) -> Option<String> {
|
||||
let script = format!("(Get-Command -All {} -ErrorAction SilentlyContinue).Source", windows_shell_quote(program));
|
||||
let mut command = Command::new("powershell.exe");
|
||||
let mut command = cli_command("powershell.exe");
|
||||
command.args(["-NoProfile", "-Command", &script]);
|
||||
command.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::null());
|
||||
let output = command.output().await.ok()?;
|
||||
|
||||
@@ -19,8 +19,6 @@ const AGENT_EXIT_DIAGNOSTIC_POLL_MS: u64 = 10;
|
||||
const AGENT_JAVA_OPTS_ENV: &str = "DBX_AGENT_JAVA_OPTS";
|
||||
const AGENT_JAVA_TOO_OLD_MESSAGE: &str =
|
||||
"Agent requires Java 21, but DBX started it with an older Java runtime. Use DBX managed JRE 21 or select a Java 21 executable in Driver Manager.";
|
||||
#[cfg(windows)]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
pub struct AgentDriverClient {
|
||||
child: Child,
|
||||
@@ -360,19 +358,13 @@ impl AgentDriverClient {
|
||||
/// they speak the DBX stdin/stdout JSON-RPC protocol.
|
||||
/// Blocks (async) until the agent writes `{"ready":true}` to stdout.
|
||||
pub async fn spawn(launch: AgentLaunchSpec) -> Result<Self, String> {
|
||||
let mut command = Command::new(&launch.program);
|
||||
let mut command = crate::process::new_std_command(&launch.program);
|
||||
command.args(&launch.args).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped());
|
||||
if let Some(working_dir) = &launch.working_dir {
|
||||
command.current_dir(working_dir);
|
||||
}
|
||||
remove_agent_proxy_env(&mut command);
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
|
||||
let mut child =
|
||||
command.spawn().map_err(|e| format!("Failed to spawn agent process {}: {e}", launch_display(&launch)))?;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::sync::{Arc, Mutex as StdMutex, OnceLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::process::{Child, ChildStdin, Command};
|
||||
use tokio::process::{Child, ChildStdin};
|
||||
use tokio::sync::{oneshot, Mutex, OwnedSemaphorePermit, Semaphore};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
@@ -389,7 +389,7 @@ impl DuckDbWorkerClient {
|
||||
.map_err(|_| "DuckDB worker process limiter is closed".to_string())?;
|
||||
|
||||
log::info!("[duckdb-worker:start] executable={}", self.inner.executable.display());
|
||||
let mut child = Command::new(&self.inner.executable)
|
||||
let mut child = crate::process::new_tokio_command(&self.inner.executable)
|
||||
.arg("--duckdb-worker")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::update::{fetch_latest_release, is_newer_version, JdbcPluginLatest};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::process::Command;
|
||||
|
||||
const JDBC_PLUGIN_DOWNLOAD_URL: &str =
|
||||
"https://github.com/t8y2/dbx/releases/latest/download/dbx-jdbc-plugin-latest.zip";
|
||||
@@ -153,7 +152,7 @@ pub async fn install_jdbc_driver_from_maven(
|
||||
let local_repo = plugin_dir.join("maven-cache");
|
||||
std::fs::create_dir_all(&local_repo).map_err(|err| err.to_string())?;
|
||||
|
||||
let mut command = Command::new(&resolver);
|
||||
let mut command = crate::process::new_tokio_command(&resolver);
|
||||
env.apply_to(&mut command);
|
||||
command.arg("resolve").arg("--coordinate").arg(&coordinate).arg("--local-repo").arg(&local_repo);
|
||||
for repo in &repositories {
|
||||
|
||||
@@ -34,6 +34,7 @@ pub mod nacos;
|
||||
pub mod object_source_sql;
|
||||
pub mod path_utils;
|
||||
pub mod plugins;
|
||||
pub mod process;
|
||||
pub mod query;
|
||||
pub mod query_cancel;
|
||||
pub mod query_execution_sql;
|
||||
|
||||
@@ -12,8 +12,6 @@ use tokio::sync::Mutex;
|
||||
use tokio::time::timeout;
|
||||
|
||||
const PLUGIN_REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
#[cfg(windows)]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
pub const SUPPORTED_PLUGIN_PROTOCOL_VERSION: u32 = 1;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -412,7 +410,7 @@ fn spawn_plugin_child(plugin: &InstalledPlugin, env: &PluginRuntimeEnv) -> Resul
|
||||
.ok_or_else(|| format!("Plugin '{}' does not declare an executable", plugin.manifest.id))?;
|
||||
let executable_path = resolve_plugin_executable(&plugin.path, executable);
|
||||
|
||||
let mut command = Command::new(&executable_path);
|
||||
let mut command = crate::process::new_tokio_command(&executable_path);
|
||||
command
|
||||
.current_dir(&plugin.path)
|
||||
.stdin(Stdio::piped())
|
||||
@@ -421,13 +419,6 @@ fn spawn_plugin_child(plugin: &InstalledPlugin, env: &PluginRuntimeEnv) -> Resul
|
||||
.kill_on_drop(true);
|
||||
env.apply_to(&mut command);
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use std::os::windows::process::CommandExt;
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
|
||||
command.spawn().map_err(|err| format!("Failed to start plugin '{}': {err}", plugin.manifest.id))
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
use std::ffi::OsStr;
|
||||
|
||||
#[cfg(windows)]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
pub fn new_std_command(program: impl AsRef<OsStr>) -> std::process::Command {
|
||||
let mut command = std::process::Command::new(program);
|
||||
hide_std_console_window(&mut command);
|
||||
command
|
||||
}
|
||||
|
||||
pub fn hide_std_console_window(command: &mut std::process::Command) {
|
||||
#[cfg(not(windows))]
|
||||
let _ = command;
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
// DBX is a GUI app; console subprocesses should not flash a window.
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_tokio_command(program: impl AsRef<OsStr>) -> tokio::process::Command {
|
||||
let mut command = tokio::process::Command::new(program);
|
||||
hide_tokio_console_window(&mut command);
|
||||
command
|
||||
}
|
||||
|
||||
pub fn hide_tokio_console_window(command: &mut tokio::process::Command) {
|
||||
#[cfg(not(windows))]
|
||||
let _ = command;
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// Keep async child processes consistent with std::process::Command.
|
||||
command.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
}
|
||||
@@ -113,7 +113,7 @@ pub fn system_proxy_url() -> Option<String> {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn system_proxy_url_from_platform() -> Option<String> {
|
||||
let output = std::process::Command::new("scutil").arg("--proxy").output().ok()?;
|
||||
let output = crate::process::new_std_command("scutil").arg("--proxy").output().ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
@@ -123,20 +123,11 @@ fn system_proxy_url_from_platform() -> Option<String> {
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn system_proxy_url_from_platform() -> Option<String> {
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
let key = r"HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings";
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
let proxy_enable = std::process::Command::new("reg")
|
||||
.args(["query", key, "/v", "ProxyEnable"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.ok()?;
|
||||
let proxy_server = std::process::Command::new("reg")
|
||||
.args(["query", key, "/v", "ProxyServer"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.ok()?;
|
||||
let proxy_enable =
|
||||
crate::process::new_std_command("reg").args(["query", key, "/v", "ProxyEnable"]).output().ok()?;
|
||||
let proxy_server =
|
||||
crate::process::new_std_command("reg").args(["query", key, "/v", "ProxyServer"]).output().ok()?;
|
||||
if !proxy_enable.status.success() || !proxy_server.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use dbx_core::path_utils::expand_tilde;
|
||||
pub fn reveal_in_file_manager(path: &Path) -> Result<(), String> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
std::process::Command::new("open")
|
||||
dbx_core::process::new_std_command("open")
|
||||
.arg("-R")
|
||||
.arg(path)
|
||||
.spawn()
|
||||
@@ -30,7 +30,7 @@ pub fn reveal_in_file_manager(path: &Path) -> Result<(), String> {
|
||||
// after the comma. `Command::arg` does not invoke a shell, so the path
|
||||
// is forwarded as-is — spaces and non-ASCII characters survive.
|
||||
let arg = format!("/select,{}", path.display());
|
||||
std::process::Command::new("explorer")
|
||||
dbx_core::process::new_std_command("explorer")
|
||||
.arg(arg)
|
||||
.spawn()
|
||||
.map(|_| ())
|
||||
@@ -40,7 +40,7 @@ pub fn reveal_in_file_manager(path: &Path) -> Result<(), String> {
|
||||
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
|
||||
{
|
||||
let target: PathBuf = path.parent().map(|p| p.to_path_buf()).unwrap_or_else(|| path.to_path_buf());
|
||||
std::process::Command::new("xdg-open")
|
||||
dbx_core::process::new_std_command("xdg-open")
|
||||
.arg(&target)
|
||||
.spawn()
|
||||
.map(|_| ())
|
||||
|
||||
@@ -18,7 +18,7 @@ pub async fn read_keychain_password(service: String, account: Option<String>) ->
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn read_keychain_password_blocking(service: String, account: Option<String>) -> Result<String, String> {
|
||||
let mut cmd = std::process::Command::new("security");
|
||||
let mut cmd = dbx_core::process::new_std_command("security");
|
||||
cmd.args(["find-generic-password", "-s", &service, "-w"]);
|
||||
if let Some(ref acct) = account {
|
||||
cmd.args(["-a", acct]);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#[cfg(not(windows))]
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -10,8 +9,6 @@ const MCP_PACKAGE_NAME: &str = "@dbx-app/mcp-server";
|
||||
const MCP_LATEST_URL: &str = "https://registry.npmjs.org/@dbx-app%2fmcp-server/latest";
|
||||
const MCP_INSTALL_COMMAND: &str = "npm install -g @dbx-app/mcp-server@latest --registry=https://registry.npmjs.org";
|
||||
const SHELL_COMMAND_MARKER: &str = "__DBX_MCP_COMMAND_OUTPUT_START__";
|
||||
#[cfg(windows)]
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct McpServerStatus {
|
||||
@@ -238,13 +235,8 @@ fn command_output(command: &str, args: &[&str]) -> Result<CommandOutput, String>
|
||||
}
|
||||
|
||||
fn run_command(command: &str, args: &[&str]) -> Result<CommandOutput, String> {
|
||||
let mut cmd = Command::new(command);
|
||||
let mut cmd = dbx_core::process::new_std_command(command);
|
||||
cmd.args(args);
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::process::CommandExt;
|
||||
cmd.creation_flags(CREATE_NO_WINDOW);
|
||||
}
|
||||
let output = cmd.output().map_err(|e| e.to_string())?;
|
||||
Ok(CommandOutput {
|
||||
success: output.status.success(),
|
||||
|
||||
@@ -206,21 +206,21 @@ fn unique_file_path(dir: &Path, file_name: &str) -> PathBuf {
|
||||
fn open_path(path: &Path) -> Result<(), String> {
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut command = {
|
||||
let mut command = std::process::Command::new("open");
|
||||
let mut command = dbx_core::process::new_std_command("open");
|
||||
command.arg(path);
|
||||
command
|
||||
};
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let mut command = {
|
||||
let mut command = std::process::Command::new("explorer");
|
||||
let mut command = dbx_core::process::new_std_command("explorer");
|
||||
command.arg(path);
|
||||
command
|
||||
};
|
||||
|
||||
#[cfg(all(not(target_os = "macos"), not(target_os = "windows")))]
|
||||
let mut command = {
|
||||
let mut command = std::process::Command::new("xdg-open");
|
||||
let mut command = dbx_core::process::new_std_command("xdg-open");
|
||||
command.arg(path);
|
||||
command
|
||||
};
|
||||
|
||||
@@ -127,7 +127,9 @@ fn platform_product_version() -> Option<String> {
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows"))]
|
||||
fn command_first_line(command: &str, args: &[&str]) -> Option<String> {
|
||||
let output = std::process::Command::new(command).args(args).output().ok()?;
|
||||
let mut process = dbx_core::process::new_std_command(command);
|
||||
process.args(args);
|
||||
let output = process.output().ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user