修复导航栏claude版本不显示
Some checks failed
Build Linux / Build Linux x86_64 (push) Has been cancelled
Build Test / Build Test (Linux) (push) Has been cancelled
Build Test / Build Test (Windows) (push) Has been cancelled
Build Test / Build Test (macOS) (push) Has been cancelled
Build Test / Build Test Summary (push) Has been cancelled
Some checks failed
Build Linux / Build Linux x86_64 (push) Has been cancelled
Build Test / Build Test (Linux) (push) Has been cancelled
Build Test / Build Test (Windows) (push) Has been cancelled
Build Test / Build Test (macOS) (push) Has been cancelled
Build Test / Build Test Summary (push) Has been cancelled
This commit is contained in:
@@ -202,7 +202,16 @@ fn find_which_installations() -> Vec<ClaudeInstallation> {
|
||||
|
||||
let mut installations = Vec::new();
|
||||
|
||||
match Command::new(command_name).arg("claude").output() {
|
||||
// Create command with enhanced PATH for production environments
|
||||
let mut cmd = Command::new(command_name);
|
||||
cmd.arg("claude");
|
||||
|
||||
// In production (DMG), we need to ensure proper PATH is set
|
||||
let enhanced_path = build_enhanced_path();
|
||||
debug!("Using enhanced PATH for {}: {}", command_name, enhanced_path);
|
||||
cmd.env("PATH", enhanced_path);
|
||||
|
||||
match cmd.output() {
|
||||
Ok(output) if output.status.success() => {
|
||||
let output_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
|
||||
@@ -401,7 +410,11 @@ fn find_standard_installations() -> Vec<ClaudeInstallation> {
|
||||
}
|
||||
|
||||
// Also check if claude is available in PATH (without full path)
|
||||
if let Ok(output) = Command::new("claude").arg("--version").output() {
|
||||
let mut path_cmd = Command::new("claude");
|
||||
path_cmd.arg("--version");
|
||||
path_cmd.env("PATH", build_enhanced_path());
|
||||
|
||||
if let Ok(output) = path_cmd.output() {
|
||||
if output.status.success() {
|
||||
debug!("claude is available in PATH");
|
||||
// Combine stdout and stderr for robust version extraction
|
||||
@@ -427,7 +440,11 @@ fn find_standard_installations() -> Vec<ClaudeInstallation> {
|
||||
|
||||
/// Get Claude version by running --version command
|
||||
fn get_claude_version(path: &str) -> Result<Option<String>, String> {
|
||||
match Command::new(path).arg("--version").output() {
|
||||
// Use the helper function to create command with proper environment
|
||||
let mut cmd = create_command_with_env(path);
|
||||
cmd.arg("--version");
|
||||
|
||||
match cmd.output() {
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
// Combine stdout and stderr for robust version extraction
|
||||
@@ -556,11 +573,15 @@ pub fn create_command_with_env(program: &str) -> Command {
|
||||
|
||||
info!("Creating command for: {}", program);
|
||||
|
||||
// Build enhanced PATH for production environments (DMG/App Bundle)
|
||||
let enhanced_path = build_enhanced_path();
|
||||
debug!("Enhanced PATH: {}", enhanced_path);
|
||||
cmd.env("PATH", enhanced_path.clone());
|
||||
|
||||
// Inherit essential environment variables from parent process
|
||||
for (key, value) in std::env::vars() {
|
||||
// Pass through PATH and other essential environment variables
|
||||
if key == "PATH"
|
||||
|| key == "HOME"
|
||||
// Pass through essential environment variables (excluding PATH which we set above)
|
||||
if key == "HOME"
|
||||
|| key == "USER"
|
||||
|| key == "SHELL"
|
||||
|| key == "LANG"
|
||||
@@ -595,7 +616,12 @@ pub fn create_command_with_env(program: &str) -> Command {
|
||||
if program.contains("/.nvm/versions/node/") {
|
||||
if let Some(node_bin_dir) = std::path::Path::new(program).parent() {
|
||||
// Ensure the Node.js bin directory is in PATH
|
||||
let current_path = std::env::var("PATH").unwrap_or_default();
|
||||
let current_path = cmd.get_envs()
|
||||
.find(|(k, _)| k.to_str() == Some("PATH"))
|
||||
.and_then(|(_, v)| v)
|
||||
.and_then(|v| v.to_str())
|
||||
.unwrap_or(&enhanced_path)
|
||||
.to_string();
|
||||
let node_bin_str = node_bin_dir.to_string_lossy();
|
||||
if !current_path.contains(&node_bin_str.as_ref()) {
|
||||
let new_path = format!("{}:{}", node_bin_str, current_path);
|
||||
@@ -607,3 +633,73 @@ pub fn create_command_with_env(program: &str) -> Command {
|
||||
|
||||
cmd
|
||||
}
|
||||
|
||||
/// Build an enhanced PATH that includes all possible Claude installation locations
|
||||
/// This is especially important for DMG/packaged applications where PATH may be limited
|
||||
fn build_enhanced_path() -> String {
|
||||
let mut paths = Vec::new();
|
||||
|
||||
// Start with current PATH
|
||||
if let Ok(current_path) = std::env::var("PATH") {
|
||||
paths.push(current_path);
|
||||
}
|
||||
|
||||
// Add standard system paths that might be missing in packaged apps
|
||||
let system_paths = vec![
|
||||
"/usr/local/bin",
|
||||
"/usr/bin",
|
||||
"/bin",
|
||||
"/opt/homebrew/bin",
|
||||
"/opt/homebrew/sbin",
|
||||
];
|
||||
|
||||
for path in system_paths {
|
||||
if PathBuf::from(path).exists() {
|
||||
paths.push(path.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// Add user-specific paths
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
let user_paths = vec![
|
||||
format!("{}/.local/bin", home),
|
||||
format!("{}/.claude/local", home),
|
||||
format!("{}/.npm-global/bin", home),
|
||||
format!("{}/.yarn/bin", home),
|
||||
format!("{}/.bun/bin", home),
|
||||
format!("{}/bin", home),
|
||||
format!("{}/.config/yarn/global/node_modules/.bin", home),
|
||||
format!("{}/node_modules/.bin", home),
|
||||
];
|
||||
|
||||
for path in user_paths {
|
||||
if PathBuf::from(&path).exists() {
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Add all NVM node versions
|
||||
let nvm_dir = PathBuf::from(&home).join(".nvm/versions/node");
|
||||
if nvm_dir.exists() {
|
||||
if let Ok(entries) = std::fs::read_dir(&nvm_dir) {
|
||||
for entry in entries.flatten() {
|
||||
if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
|
||||
let bin_path = entry.path().join("bin");
|
||||
if bin_path.exists() {
|
||||
paths.push(bin_path.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove duplicates while preserving order
|
||||
let mut seen = std::collections::HashSet::new();
|
||||
let unique_paths: Vec<String> = paths
|
||||
.into_iter()
|
||||
.filter(|path| seen.insert(path.clone()))
|
||||
.collect();
|
||||
|
||||
unique_paths.join(":")
|
||||
}
|
||||
|
@@ -623,102 +623,40 @@ pub async fn get_system_prompt() -> Result<String, String> {
|
||||
|
||||
/// Checks if Claude Code is installed and gets its version
|
||||
#[tauri::command]
|
||||
pub async fn check_claude_version(app: AppHandle) -> Result<ClaudeVersionStatus, String> {
|
||||
pub async fn check_claude_version(_app: AppHandle) -> Result<ClaudeVersionStatus, String> {
|
||||
log::info!("Checking Claude Code version");
|
||||
|
||||
let claude_path = match find_claude_binary(&app) {
|
||||
Ok(path) => path,
|
||||
Err(e) => {
|
||||
return Ok(ClaudeVersionStatus {
|
||||
is_installed: false,
|
||||
version: None,
|
||||
output: e,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
use log::debug;debug!("Claude path: {}", claude_path);
|
||||
|
||||
// In production builds, we can't check the version directly
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
log::warn!("Cannot check claude version in production build");
|
||||
// If we found a path (either stored or in common locations), assume it's installed
|
||||
if claude_path != "claude" && PathBuf::from(&claude_path).exists() {
|
||||
return Ok(ClaudeVersionStatus {
|
||||
is_installed: true,
|
||||
version: None,
|
||||
output: "Claude binary found at: ".to_string() + &claude_path,
|
||||
});
|
||||
} else {
|
||||
return Ok(ClaudeVersionStatus {
|
||||
is_installed: false,
|
||||
version: None,
|
||||
output: "Cannot verify Claude installation in production build. Please ensure Claude Code is installed.".to_string(),
|
||||
});
|
||||
}
|
||||
// Try to find Claude installations with versions
|
||||
let installations = crate::claude_binary::discover_claude_installations();
|
||||
|
||||
if installations.is_empty() {
|
||||
return Ok(ClaudeVersionStatus {
|
||||
is_installed: false,
|
||||
version: None,
|
||||
output: "Claude Code not found. Please ensure it's installed.".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
let output = std::process::Command::new(claude_path)
|
||||
.arg("--version")
|
||||
.output();
|
||||
|
||||
match output {
|
||||
Ok(output) => {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
|
||||
|
||||
// Use regex to directly extract version pattern (e.g., "1.0.41")
|
||||
let version_regex = regex::Regex::new(r"(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?(?:\+[a-zA-Z0-9.-]+)?)").ok();
|
||||
|
||||
// Combine stdout and stderr for version extraction (some tools write version to stderr)
|
||||
let mut version_src = stdout.clone();
|
||||
if !stderr.is_empty() {
|
||||
version_src.push('\n');
|
||||
version_src.push_str(&stderr);
|
||||
}
|
||||
|
||||
let version = if let Some(regex) = version_regex {
|
||||
regex
|
||||
.captures(&version_src)
|
||||
.and_then(|captures| captures.get(1))
|
||||
.map(|m| m.as_str().to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let full_output = if stderr.is_empty() {
|
||||
stdout.clone()
|
||||
} else {
|
||||
format!("{}\n{}", stdout, stderr)
|
||||
};
|
||||
|
||||
// Check if the output matches the expected format
|
||||
// Expected format: "1.0.17 (Claude Code)" or similar
|
||||
let is_valid =
|
||||
stdout.contains("(Claude Code)")
|
||||
|| stdout.contains("Claude Code")
|
||||
|| stderr.contains("(Claude Code)")
|
||||
|| stderr.contains("Claude Code");
|
||||
|
||||
Ok(ClaudeVersionStatus {
|
||||
is_installed: is_valid && output.status.success(),
|
||||
version,
|
||||
output: full_output.trim().to_string(),
|
||||
})
|
||||
// Find the best installation (highest version or first found)
|
||||
let best_installation = installations
|
||||
.into_iter()
|
||||
.max_by(|a, b| {
|
||||
match (&a.version, &b.version) {
|
||||
(Some(v1), Some(v2)) => v1.cmp(v2),
|
||||
(Some(_), None) => std::cmp::Ordering::Greater,
|
||||
(None, Some(_)) => std::cmp::Ordering::Less,
|
||||
(None, None) => std::cmp::Ordering::Equal,
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to run claude command: {}", e);
|
||||
Ok(ClaudeVersionStatus {
|
||||
is_installed: false,
|
||||
version: None,
|
||||
output: format!("Command not found: {}", e),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap(); // Safe because we checked is_empty() above
|
||||
|
||||
log::info!("Found Claude installation: {:?}", best_installation);
|
||||
|
||||
Ok(ClaudeVersionStatus {
|
||||
is_installed: true,
|
||||
version: best_installation.version,
|
||||
output: format!("Claude binary found at: {}", best_installation.path),
|
||||
})
|
||||
}
|
||||
|
||||
/// Saves the CLAUDE.md system prompt file
|
||||
|
Reference in New Issue
Block a user