release: v0.1.3

This commit is contained in:
2026-08-17 21:01:02 +08:00
parent 67f684d988
commit cbcca32a2b
8 changed files with 114 additions and 12 deletions

View File

@@ -26,6 +26,15 @@ impl DshNotFound {
fn new(msg: impl Into<String>) -> Self {
Self::Message(msg.into())
}
/// True when resolution successfully analyzed every candidate and found no
/// usable dsh at all (as opposed to an invalid user-supplied override).
pub fn is_missing(&self) -> bool {
matches!(
self,
DshNotFound::Message(message) if message.contains("找不到")
)
}
}
pub fn strip_ansi(text: &str) -> String {
@@ -59,6 +68,18 @@ pub fn find_npx_dsh_bins() -> Vec<PathBuf> {
if let Some(globbed) = pattern.to_str() {
if let Ok(paths) = glob_simple(globbed) {
for path in paths {
// npm also drops an extensionless POSIX shim (`dsh`) next to
// `dsh.cmd`; Windows cannot CreateProcess that file, so prefer
// the cmd wrapper when present.
#[cfg(windows)]
let path = {
let cmd = path.with_extension("cmd");
if cmd.is_file() {
cmd
} else {
path
}
};
if is_executable(&path) {
bins.push(path);
}
@@ -105,7 +126,7 @@ impl DshLauncher {
}
}
pub fn resolve(&self) -> Result<Vec<String>, DshNotFound> {
fn collect_candidates(&self) -> Result<Vec<Vec<String>>, DshNotFound> {
let mut candidates: Vec<Vec<String>> = Vec::new();
if let Ok(env_override) = std::env::var(ENV_BIN_OVERRIDE) {
@@ -163,6 +184,11 @@ impl DshLauncher {
}
}
Ok(candidates)
}
pub fn resolve(&self) -> Result<Vec<String>, DshNotFound> {
let candidates = self.collect_candidates()?;
let chosen = candidates.into_iter().next().ok_or_else(|| {
if self.in_flatpak {
DshNotFound::new(
@@ -170,13 +196,22 @@ impl DshLauncher {
)
} else {
DshNotFound::new(
"找不到 DeepSeek Harness (dsh),也没有可用的 npx\n安装npm install -g @deepseek-ai/dsh\n或用 DSH_DESKTOP_DSH_BIN 指定 dsh 的完整路径。",
"找不到 DeepSeek Harness (dsh)。\n请安装 Node.js/npm首次启动会自动安装 dsh或用 DSH_DESKTOP_DSH_BIN 指定 dsh 的完整路径。",
)
}
})?;
Ok(chosen)
}
/// True when a real dsh binary is available without relying on the
/// transient `npx --yes @deepseek-ai/dsh` fallback.
pub fn has_installed_dsh(&self) -> Result<bool, DshNotFound> {
let candidates = self.collect_candidates()?;
Ok(candidates
.iter()
.any(|candidate| candidate.first().map(String::as_str) != Some("npx")))
}
pub fn web_argv(&self) -> Result<Vec<String>, DshNotFound> {
let mut argv = self.resolve()?;
argv.extend([
@@ -406,6 +441,15 @@ mod tests {
assert_eq!(argv, vec!["echo"]);
}
#[test]
fn has_installed_dsh_with_override() {
let _guard = ENV_LOCK.lock().unwrap();
std::env::set_var(ENV_BIN_OVERRIDE, "echo");
let launcher = DshLauncher::new(None, None);
assert!(launcher.has_installed_dsh().unwrap());
std::env::remove_var(ENV_BIN_OVERRIDE);
}
#[test]
fn invalid_env_override_raises() {
let _guard = ENV_LOCK.lock().unwrap();