fix: explain dispatcher TUI spawn failures (#853)

This commit is contained in:
Hunter Bown
2026-05-06 05:41:40 -05:00
committed by GitHub
parent a6a5e3aee8
commit 2090792570
+37 -4
View File
@@ -6,7 +6,7 @@ use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{Context, Result, bail};
use anyhow::{Context, Result, anyhow, bail};
use clap::{Args, CommandFactory, Parser, Subcommand, ValueEnum};
use clap_complete::{Shell, generate};
use deepseek_agent::ModelRegistry;
@@ -1044,7 +1044,7 @@ fn delegate_to_tui(
) -> Result<()> {
let tui = locate_sibling_tui_binary()?;
let mut cmd = Command::new(tui);
let mut cmd = Command::new(&tui);
if let Some(config) = cli.config.as_ref() {
cmd.arg("--config").arg(config);
}
@@ -1115,7 +1115,9 @@ fn delegate_to_tui(
cmd.env("DEEPSEEK_BASE_URL", base_url);
}
let status = cmd.status().context("failed to spawn deepseek-tui")?;
let status = cmd
.status()
.map_err(|err| anyhow!("{}", tui_spawn_error(&tui, &err)))?;
match status.code() {
Some(code) => std::process::exit(code),
None => bail!("deepseek-tui terminated by signal"),
@@ -1124,13 +1126,33 @@ fn delegate_to_tui(
fn delegate_simple_tui(args: Vec<String>) -> Result<()> {
let tui = locate_sibling_tui_binary()?;
let status = Command::new(tui).args(args).status()?;
let status = Command::new(&tui)
.args(args)
.status()
.map_err(|err| anyhow!("{}", tui_spawn_error(&tui, &err)))?;
match status.code() {
Some(code) => std::process::exit(code),
None => bail!("deepseek-tui terminated by signal"),
}
}
fn tui_spawn_error(tui: &Path, err: &io::Error) -> String {
format!(
"failed to spawn companion TUI binary at {}: {err}\n\
\n\
The `deepseek` dispatcher found a `deepseek-tui` file, but the OS refused \
to execute it. Common fixes:\n\
- Reinstall with `npm install -g deepseek-tui`, or run `deepseek update`.\n\
- On Windows, run `where deepseek` and `where deepseek-tui`; both should \
come from the same install directory.\n\
- If you downloaded release assets manually, keep both `deepseek` and \
`deepseek-tui` binaries together and make sure the TUI binary is executable.\n\
- Set DEEPSEEK_TUI_BIN to the absolute path of a working `deepseek-tui` \
binary.",
tui.display()
)
}
/// Resolve the sibling `deepseek-tui` executable next to the running
/// dispatcher. Honours platform executable suffix (`.exe` on Windows) so
/// the npm-distributed Windows package — which ships
@@ -1965,6 +1987,17 @@ mod tests {
assert_eq!(found, target, "primary platform-correct name wins");
}
#[test]
fn dispatcher_spawn_error_names_path_and_recovery_checks() {
let err = io::Error::new(io::ErrorKind::PermissionDenied, "access is denied");
let message = tui_spawn_error(Path::new("C:/tools/deepseek-tui.exe"), &err);
assert!(message.contains("C:/tools/deepseek-tui.exe"));
assert!(message.contains("access is denied"));
assert!(message.contains("where deepseek"));
assert!(message.contains("DEEPSEEK_TUI_BIN"));
}
/// Windows-only fallback: the user from #247 manually renamed the
/// file to drop `.exe`. After the fix lands, that workaround must
/// still resolve via the suffix-less fallback so they don't have to