|
|
@@ -3,6 +3,11 @@ use serde::{Deserialize, Serialize};
|
|
|
use std::fs;
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
+#[cfg(target_os = "windows")]
|
|
|
+use winreg::enums::*;
|
|
|
+#[cfg(target_os = "windows")]
|
|
|
+use winreg::RegKey;
|
|
|
+
|
|
|
#[derive(Serialize, Deserialize)]
|
|
|
pub struct ClaudeCodeStatus {
|
|
|
pub installed: bool,
|
|
|
@@ -253,3 +258,211 @@ pub async fn open_claude_code_config() -> CommandResult {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+/// Claude Code 相关的环境变量名称(用于用户验证和服务器地址)
|
|
|
+const CLAUDE_ENV_VARS: &[&str] = &[
|
|
|
+ // 用户验证相关
|
|
|
+ "ANTHROPIC_AUTH_TOKEN",
|
|
|
+ "ANTHROPIC_API_KEY",
|
|
|
+ "CLAUDE_API_KEY",
|
|
|
+ // 服务器地址相关
|
|
|
+ "ANTHROPIC_BASE_URL",
|
|
|
+ "CLAUDE_BASE_URL",
|
|
|
+ "ANTHROPIC_API_BASE",
|
|
|
+ // 模型配置相关
|
|
|
+ "ANTHROPIC_MODEL",
|
|
|
+ "ANTHROPIC_DEFAULT_MODEL",
|
|
|
+ "ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
|
+ "ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
|
+ "ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
|
+];
|
|
|
+
|
|
|
+/// 删除 Claude Code 相关的环境变量
|
|
|
+#[tauri::command]
|
|
|
+pub async fn delete_claude_env_vars() -> CommandResult {
|
|
|
+ #[cfg(target_os = "windows")]
|
|
|
+ {
|
|
|
+ delete_claude_env_vars_windows()
|
|
|
+ }
|
|
|
+
|
|
|
+ #[cfg(target_os = "macos")]
|
|
|
+ {
|
|
|
+ delete_claude_env_vars_unix()
|
|
|
+ }
|
|
|
+
|
|
|
+ #[cfg(target_os = "linux")]
|
|
|
+ {
|
|
|
+ delete_claude_env_vars_unix()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Windows 平台删除环境变量
|
|
|
+#[cfg(target_os = "windows")]
|
|
|
+fn delete_claude_env_vars_windows() -> CommandResult {
|
|
|
+ let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
|
|
+
|
|
|
+ let env_key = match hkcu.open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) {
|
|
|
+ Ok(key) => key,
|
|
|
+ Err(e) => {
|
|
|
+ return CommandResult {
|
|
|
+ success: false,
|
|
|
+ error: Some(format!("Failed to open registry: {}", e)),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut deleted_vars = Vec::new();
|
|
|
+ let mut errors = Vec::new();
|
|
|
+
|
|
|
+ for var_name in CLAUDE_ENV_VARS {
|
|
|
+ // 检查环境变量是否存在
|
|
|
+ if env_key.get_value::<String, _>(*var_name).is_ok() {
|
|
|
+ match env_key.delete_value(*var_name) {
|
|
|
+ Ok(_) => {
|
|
|
+ deleted_vars.push(*var_name);
|
|
|
+ log::info!("Deleted environment variable: {}", var_name);
|
|
|
+ }
|
|
|
+ Err(e) => {
|
|
|
+ errors.push(format!("{}: {}", var_name, e));
|
|
|
+ log::error!("Failed to delete {}: {}", var_name, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 通知系统环境变量已更改
|
|
|
+ if !deleted_vars.is_empty() {
|
|
|
+ unsafe {
|
|
|
+ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
|
|
+ SendMessageTimeoutW, HWND_BROADCAST, SMTO_ABORTIFHUNG, WM_SETTINGCHANGE,
|
|
|
+ };
|
|
|
+ let param: Vec<u16> = "Environment\0".encode_utf16().collect();
|
|
|
+ SendMessageTimeoutW(
|
|
|
+ HWND_BROADCAST,
|
|
|
+ WM_SETTINGCHANGE,
|
|
|
+ 0,
|
|
|
+ param.as_ptr() as isize,
|
|
|
+ SMTO_ABORTIFHUNG,
|
|
|
+ 5000,
|
|
|
+ std::ptr::null_mut(),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if errors.is_empty() {
|
|
|
+ if deleted_vars.is_empty() {
|
|
|
+ CommandResult {
|
|
|
+ success: true,
|
|
|
+ error: None,
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ CommandResult {
|
|
|
+ success: true,
|
|
|
+ error: None,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ CommandResult {
|
|
|
+ success: false,
|
|
|
+ error: Some(errors.join("; ")),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// Unix 平台删除环境变量(macOS/Linux)
|
|
|
+#[cfg(any(target_os = "macos", target_os = "linux"))]
|
|
|
+fn delete_claude_env_vars_unix() -> CommandResult {
|
|
|
+ // 在 Unix 系统上,环境变量通常存储在 shell 配置文件中
|
|
|
+ // 我们需要从这些文件中移除相关的 export 语句
|
|
|
+ let home = match dirs::home_dir() {
|
|
|
+ Some(h) => h,
|
|
|
+ None => {
|
|
|
+ return CommandResult {
|
|
|
+ success: false,
|
|
|
+ error: Some("Cannot determine home directory".to_string()),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let shell_configs = [
|
|
|
+ ".bashrc",
|
|
|
+ ".bash_profile",
|
|
|
+ ".zshrc",
|
|
|
+ ".profile",
|
|
|
+ ];
|
|
|
+
|
|
|
+ let mut modified_files = Vec::new();
|
|
|
+ let mut errors = Vec::new();
|
|
|
+
|
|
|
+ for config_file in shell_configs {
|
|
|
+ let config_path = home.join(config_file);
|
|
|
+ if config_path.exists() {
|
|
|
+ match remove_env_vars_from_file(&config_path) {
|
|
|
+ Ok(modified) => {
|
|
|
+ if modified {
|
|
|
+ modified_files.push(config_file);
|
|
|
+ log::info!("Modified shell config: {}", config_file);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Err(e) => {
|
|
|
+ errors.push(format!("{}: {}", config_file, e));
|
|
|
+ log::error!("Failed to modify {}: {}", config_file, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if errors.is_empty() {
|
|
|
+ CommandResult {
|
|
|
+ success: true,
|
|
|
+ error: None,
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ CommandResult {
|
|
|
+ success: false,
|
|
|
+ error: Some(errors.join("; ")),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// 从文件中移除 Claude 相关的环境变量导出语句
|
|
|
+#[cfg(any(target_os = "macos", target_os = "linux"))]
|
|
|
+fn remove_env_vars_from_file(path: &PathBuf) -> Result<bool, String> {
|
|
|
+ let content = fs::read_to_string(path).map_err(|e| e.to_string())?;
|
|
|
+
|
|
|
+ let mut modified = false;
|
|
|
+ let mut new_lines = Vec::new();
|
|
|
+
|
|
|
+ for line in content.lines() {
|
|
|
+ let trimmed = line.trim();
|
|
|
+ let mut should_remove = false;
|
|
|
+
|
|
|
+ for var_name in CLAUDE_ENV_VARS {
|
|
|
+ // 匹配 export VAR_NAME= 或 VAR_NAME= 格式
|
|
|
+ if trimmed.starts_with(&format!("export {}=", var_name))
|
|
|
+ || trimmed.starts_with(&format!("{}=", var_name))
|
|
|
+ {
|
|
|
+ should_remove = true;
|
|
|
+ modified = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if !should_remove {
|
|
|
+ new_lines.push(line);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if modified {
|
|
|
+ let new_content = new_lines.join("\n");
|
|
|
+ // 保留原文件的换行符风格
|
|
|
+ let new_content = if content.ends_with('\n') {
|
|
|
+ format!("{}\n", new_content)
|
|
|
+ } else {
|
|
|
+ new_content
|
|
|
+ };
|
|
|
+ fs::write(path, new_content).map_err(|e| e.to_string())?;
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(modified)
|
|
|
+}
|