Skip to main content

aster/chrome_mcp/
native_host.rs

1//! Chrome Native Messaging Host 安装和管理
2
3use std::path::PathBuf;
4use tokio::fs;
5
6use super::types::*;
7
8/// 获取当前平台
9pub fn get_platform() -> Platform {
10    #[cfg(target_os = "macos")]
11    {
12        Platform::MacOS
13    }
14
15    #[cfg(target_os = "windows")]
16    {
17        Platform::Windows
18    }
19
20    #[cfg(target_os = "linux")]
21    {
22        // 检查是否在 WSL 中
23        if let Ok(release) = std::fs::read_to_string("/proc/version") {
24            if release.to_lowercase().contains("microsoft")
25                || release.to_lowercase().contains("wsl")
26            {
27                return Platform::Wsl;
28            }
29        }
30        Platform::Linux
31    }
32
33    #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
34    {
35        Platform::Unknown
36    }
37}
38
39/// 获取 Chrome Native Messaging Hosts 目录路径
40pub fn get_native_hosts_directory() -> Option<PathBuf> {
41    let home = dirs::home_dir()?;
42
43    match get_platform() {
44        Platform::MacOS => Some(
45            home.join("Library")
46                .join("Application Support")
47                .join("Google")
48                .join("Chrome")
49                .join("NativeMessagingHosts"),
50        ),
51        Platform::Linux => Some(
52            home.join(".config")
53                .join("google-chrome")
54                .join("NativeMessagingHosts"),
55        ),
56        Platform::Windows => {
57            let app_data = std::env::var("APPDATA")
58                .map(PathBuf::from)
59                .unwrap_or_else(|_| home.join("AppData").join("Local"));
60            Some(app_data.join("Claude Code").join("ChromeNativeHost"))
61        }
62        _ => None,
63    }
64}
65
66/// 获取 Claude 配置目录
67pub fn get_claude_config_dir() -> PathBuf {
68    dirs::home_dir()
69        .unwrap_or_else(|| PathBuf::from("."))
70        .join(".aster")
71}
72
73/// 获取 Socket 路径
74pub fn get_socket_path() -> String {
75    let username = std::env::var("USER")
76        .or_else(|_| std::env::var("USERNAME"))
77        .unwrap_or_else(|_| "unknown".to_string());
78    let socket_name = format!("aster-mcp-browser-bridge-{}", username);
79
80    #[cfg(windows)]
81    return format!("\\\\.\\pipe\\{}", socket_name);
82
83    #[cfg(not(windows))]
84    return std::env::temp_dir()
85        .join(socket_name)
86        .to_string_lossy()
87        .to_string();
88}
89
90/// 生成 Native Host Manifest
91pub fn generate_native_host_manifest(wrapper_script_path: &str) -> serde_json::Value {
92    serde_json::json!({
93        "name": NATIVE_HOST_NAME,
94        "description": "Aster Browser Extension Native Host",
95        "path": wrapper_script_path,
96        "type": "stdio",
97        "allowed_origins": [
98            format!("chrome-extension://{}/", CHROME_EXTENSION_ID)
99        ]
100    })
101}
102
103/// 生成 Native Host Wrapper Script
104pub fn generate_wrapper_script(command: &str) -> String {
105    match get_platform() {
106        Platform::Windows => format!(
107            "@echo off\nREM Chrome native host wrapper script\n{}\n",
108            command
109        ),
110        _ => format!(
111            "#!/bin/bash\n# Chrome native host wrapper script\nexec {}\n",
112            command
113        ),
114    }
115}
116
117/// 检查 Chrome 集成是否支持
118pub fn is_chrome_integration_supported() -> bool {
119    matches!(
120        get_platform(),
121        Platform::MacOS | Platform::Linux | Platform::Windows
122    )
123}
124
125/// 检查 Chrome 集成是否已配置
126pub async fn is_chrome_integration_configured() -> bool {
127    let hosts_dir = match get_native_hosts_directory() {
128        Some(d) => d,
129        None => return false,
130    };
131
132    let manifest_path = hosts_dir.join(format!("{}.json", NATIVE_HOST_NAME));
133    fs::metadata(&manifest_path).await.is_ok()
134}
135
136/// 获取所有 MCP 工具名称
137pub fn get_mcp_tool_names() -> Vec<String> {
138    vec![
139        "mcp__claude-in-chrome__javascript_tool".to_string(),
140        "mcp__claude-in-chrome__read_page".to_string(),
141        "mcp__claude-in-chrome__find".to_string(),
142        "mcp__claude-in-chrome__form_input".to_string(),
143        "mcp__claude-in-chrome__computer".to_string(),
144        "mcp__claude-in-chrome__navigate".to_string(),
145        "mcp__claude-in-chrome__resize_window".to_string(),
146        "mcp__claude-in-chrome__gif_creator".to_string(),
147        "mcp__claude-in-chrome__upload_image".to_string(),
148        "mcp__claude-in-chrome__get_page_text".to_string(),
149        "mcp__claude-in-chrome__tabs_context_mcp".to_string(),
150        "mcp__claude-in-chrome__tabs_create_mcp".to_string(),
151        "mcp__claude-in-chrome__update_plan".to_string(),
152        "mcp__claude-in-chrome__read_console_messages".to_string(),
153        "mcp__claude-in-chrome__read_network_requests".to_string(),
154        "mcp__claude-in-chrome__shortcuts_list".to_string(),
155        "mcp__claude-in-chrome__shortcuts_execute".to_string(),
156    ]
157}
158
159/// 检查是否应该启用 Chrome 集成
160pub fn should_enable_chrome_integration(cli_chrome_flag: Option<bool>) -> bool {
161    // 如果明确通过 --no-chrome 禁用
162    if cli_chrome_flag == Some(false) {
163        return false;
164    }
165
166    // 如果通过 --chrome 明确启用
167    if cli_chrome_flag == Some(true) {
168        return true;
169    }
170
171    // 检查环境变量
172    if let Ok(env_value) = std::env::var("ASTER_ENABLE_CHROME") {
173        if env_value == "1" || env_value == "true" {
174            return true;
175        }
176        if env_value == "0" || env_value == "false" {
177            return false;
178        }
179    }
180
181    false
182}
183
184/// 安装 Chrome Native Host 的结果
185#[derive(Debug)]
186pub struct SetupResult {
187    pub success: bool,
188    pub message: String,
189    pub manifest_path: Option<PathBuf>,
190    pub wrapper_path: Option<PathBuf>,
191}
192
193/// 安装 Chrome Native Host
194pub async fn setup_chrome_native_host(command: &str) -> Result<SetupResult, String> {
195    // 检查平台支持
196    if !is_chrome_integration_supported() {
197        return Ok(SetupResult {
198            success: false,
199            message: "Chrome integration is not supported on this platform".to_string(),
200            manifest_path: None,
201            wrapper_path: None,
202        });
203    }
204
205    // 获取 Native Hosts 目录
206    let hosts_dir = get_native_hosts_directory()
207        .ok_or_else(|| "Failed to get native hosts directory".to_string())?;
208
209    // 创建目录
210    fs::create_dir_all(&hosts_dir)
211        .await
212        .map_err(|e| format!("Failed to create native hosts directory: {}", e))?;
213
214    // 生成 wrapper script 路径
215    let wrapper_ext = if get_platform() == Platform::Windows {
216        "bat"
217    } else {
218        "sh"
219    };
220    let wrapper_path = hosts_dir.join(format!("{}.{}", NATIVE_HOST_NAME, wrapper_ext));
221
222    // 写入 wrapper script
223    let wrapper_content = generate_wrapper_script(command);
224    fs::write(&wrapper_path, &wrapper_content)
225        .await
226        .map_err(|e| format!("Failed to write wrapper script: {}", e))?;
227
228    // 设置执行权限 (非 Windows)
229    #[cfg(unix)]
230    {
231        use std::os::unix::fs::PermissionsExt;
232        let perms = std::fs::Permissions::from_mode(0o755);
233        std::fs::set_permissions(&wrapper_path, perms)
234            .map_err(|e| format!("Failed to set wrapper script permissions: {}", e))?;
235    }
236
237    // 生成并写入 manifest
238    let manifest_path = hosts_dir.join(format!("{}.json", NATIVE_HOST_NAME));
239    let manifest = generate_native_host_manifest(&wrapper_path.to_string_lossy());
240    let manifest_json = serde_json::to_string_pretty(&manifest)
241        .map_err(|e| format!("Failed to serialize manifest: {}", e))?;
242
243    fs::write(&manifest_path, &manifest_json)
244        .await
245        .map_err(|e| format!("Failed to write manifest: {}", e))?;
246
247    // Windows 需要注册表设置
248    #[cfg(windows)]
249    {
250        setup_windows_registry(&manifest_path)?;
251    }
252
253    Ok(SetupResult {
254        success: true,
255        message: "Chrome native host installed successfully".to_string(),
256        manifest_path: Some(manifest_path),
257        wrapper_path: Some(wrapper_path),
258    })
259}
260
261/// Windows 注册表设置
262#[cfg(windows)]
263fn setup_windows_registry(manifest_path: &PathBuf) -> Result<(), String> {
264    use winreg::enums::*;
265    use winreg::RegKey;
266
267    let hkcu = RegKey::predef(HKEY_CURRENT_USER);
268    let path = format!(
269        "Software\\Google\\Chrome\\NativeMessagingHosts\\{}",
270        NATIVE_HOST_NAME
271    );
272
273    let (key, _) = hkcu
274        .create_subkey(&path)
275        .map_err(|e| format!("Failed to create registry key: {}", e))?;
276
277    let manifest_str: String = manifest_path.to_string_lossy().to_string();
278    key.set_value("", &manifest_str)
279        .map_err(|e| format!("Failed to set registry value: {}", e))?;
280
281    Ok(())
282}
283
284/// 卸载 Chrome Native Host
285pub async fn uninstall_chrome_native_host() -> Result<(), String> {
286    let hosts_dir = get_native_hosts_directory()
287        .ok_or_else(|| "Failed to get native hosts directory".to_string())?;
288
289    // 删除 manifest
290    let manifest_path = hosts_dir.join(format!("{}.json", NATIVE_HOST_NAME));
291    if fs::metadata(&manifest_path).await.is_ok() {
292        fs::remove_file(&manifest_path)
293            .await
294            .map_err(|e| format!("Failed to remove manifest: {}", e))?;
295    }
296
297    // 删除 wrapper script
298    let wrapper_ext = if get_platform() == Platform::Windows {
299        "bat"
300    } else {
301        "sh"
302    };
303    let wrapper_path = hosts_dir.join(format!("{}.{}", NATIVE_HOST_NAME, wrapper_ext));
304    if fs::metadata(&wrapper_path).await.is_ok() {
305        fs::remove_file(&wrapper_path)
306            .await
307            .map_err(|e| format!("Failed to remove wrapper script: {}", e))?;
308    }
309
310    // Windows 清理注册表
311    #[cfg(windows)]
312    {
313        uninstall_windows_registry()?;
314    }
315
316    Ok(())
317}
318
319/// Windows 注册表清理
320#[cfg(windows)]
321fn uninstall_windows_registry() -> Result<(), String> {
322    use winreg::enums::*;
323    use winreg::RegKey;
324
325    let hkcu = RegKey::predef(HKEY_CURRENT_USER);
326    let path = format!(
327        "Software\\Google\\Chrome\\NativeMessagingHosts\\{}",
328        NATIVE_HOST_NAME
329    );
330
331    // 忽略删除失败(可能不存在)
332    let _ = hkcu.delete_subkey(&path);
333    Ok(())
334}