From 013aadf2109ea6760532677b876e1bb3f4fdd28a Mon Sep 17 00:00:00 2001 From: Minidoracat Date: Thu, 5 Jun 2025 08:48:34 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E6=96=B0=E5=A2=9E=20WSL=20?= =?UTF-8?q?=E7=92=B0=E5=A2=83=E6=AA=A2=E6=B8=AC=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E4=B8=A6=E5=9C=A8=E7=9B=B8=E9=97=9C=E5=87=BD=E6=95=B8=E4=B8=AD?= =?UTF-8?q?=E6=95=B4=E5=90=88=20WSL=20=E6=94=AF=E6=8F=B4=EF=BC=8C=E6=8F=90?= =?UTF-8?q?=E5=8D=87=E8=B7=A8=E5=B9=B3=E5=8F=B0=E4=BD=BF=E7=94=A8=E9=AB=94?= =?UTF-8?q?=E9=A9=97=E3=80=82=E6=9B=B4=E6=96=B0=E6=B8=AC=E8=A9=A6=E7=94=A8?= =?UTF-8?q?=E4=BE=8B=E4=BB=A5=E6=B6=B5=E8=93=8B=E6=96=B0=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mcp_feedback_enhanced/server.py | 68 +++++++++-- src/mcp_feedback_enhanced/test_web_ui.py | 37 +++--- .../web/utils/browser.py | 113 +++++++++++++++++- 3 files changed, 189 insertions(+), 29 deletions(-) diff --git a/src/mcp_feedback_enhanced/server.py b/src/mcp_feedback_enhanced/server.py index fc598ee..c0002e0 100644 --- a/src/mcp_feedback_enhanced/server.py +++ b/src/mcp_feedback_enhanced/server.py @@ -114,42 +114,83 @@ mcp = FastMCP(SERVER_NAME, version=__version__, **fastmcp_settings) # ===== 工具函數 ===== +def is_wsl_environment() -> bool: + """ + 檢測是否在 WSL (Windows Subsystem for Linux) 環境中運行 + + Returns: + bool: True 表示 WSL 環境,False 表示其他環境 + """ + try: + # 檢查 /proc/version 文件是否包含 WSL 標識 + if os.path.exists('/proc/version'): + with open('/proc/version', 'r') as f: + version_info = f.read().lower() + if 'microsoft' in version_info or 'wsl' in version_info: + debug_log("偵測到 WSL 環境(通過 /proc/version)") + return True + + # 檢查 WSL 相關環境變數 + wsl_env_vars = ['WSL_DISTRO_NAME', 'WSL_INTEROP', 'WSLENV'] + for env_var in wsl_env_vars: + if os.getenv(env_var): + debug_log(f"偵測到 WSL 環境變數: {env_var}") + return True + + # 檢查是否存在 WSL 特有的路徑 + wsl_paths = ['/mnt/c', '/mnt/d', '/proc/sys/fs/binfmt_misc/WSLInterop'] + for path in wsl_paths: + if os.path.exists(path): + debug_log(f"偵測到 WSL 特有路徑: {path}") + return True + + except Exception as e: + debug_log(f"WSL 檢測過程中發生錯誤: {e}") + + return False + + def is_remote_environment() -> bool: """ 檢測是否在遠端環境中運行 - + Returns: bool: True 表示遠端環境,False 表示本地環境 """ + # WSL 不應被視為遠端環境,因為它可以訪問 Windows 瀏覽器 + if is_wsl_environment(): + debug_log("WSL 環境不被視為遠端環境") + return False + # 檢查 SSH 連線指標 for env_var in SSH_ENV_VARS: if os.getenv(env_var): debug_log(f"偵測到 SSH 環境變數: {env_var}") return True - + # 檢查遠端開發環境 for env_var in REMOTE_ENV_VARS: if os.getenv(env_var): debug_log(f"偵測到遠端開發環境: {env_var}") return True - + # 檢查 Docker 容器 if os.path.exists('/.dockerenv'): debug_log("偵測到 Docker 容器環境") return True - + # Windows 遠端桌面檢查 if sys.platform == 'win32': session_name = os.getenv('SESSIONNAME', '') if session_name and 'RDP' in session_name: debug_log(f"偵測到 Windows 遠端桌面: {session_name}") return True - - # Linux 無顯示環境檢查 - if sys.platform.startswith('linux') and not os.getenv('DISPLAY'): + + # Linux 無顯示環境檢查(但排除 WSL) + if sys.platform.startswith('linux') and not os.getenv('DISPLAY') and not is_wsl_environment(): debug_log("偵測到 Linux 無顯示環境") return True - + return False @@ -567,16 +608,18 @@ async def launch_web_ui_with_timeout(project_dir: str, summary: str, timeout: in def get_system_info() -> str: """ 獲取系統環境資訊 - + Returns: str: JSON 格式的系統資訊 """ is_remote = is_remote_environment() + is_wsl = is_wsl_environment() can_gui = can_use_gui() - + system_info = { "平台": sys.platform, "Python 版本": sys.version.split()[0], + "WSL 環境": is_wsl, "遠端環境": is_remote, "GUI 可用": can_gui, "建議介面": "Web UI" if is_remote or not can_gui else "Qt GUI", @@ -586,9 +629,12 @@ def get_system_info() -> str: "DISPLAY": os.getenv("DISPLAY"), "VSCODE_INJECTION": os.getenv("VSCODE_INJECTION"), "SESSIONNAME": os.getenv("SESSIONNAME"), + "WSL_DISTRO_NAME": os.getenv("WSL_DISTRO_NAME"), + "WSL_INTEROP": os.getenv("WSL_INTEROP"), + "WSLENV": os.getenv("WSLENV"), } } - + return json.dumps(system_info, ensure_ascii=False, indent=2) diff --git a/src/mcp_feedback_enhanced/test_web_ui.py b/src/mcp_feedback_enhanced/test_web_ui.py index a72fbf2..5664917 100644 --- a/src/mcp_feedback_enhanced/test_web_ui.py +++ b/src/mcp_feedback_enhanced/test_web_ui.py @@ -162,23 +162,27 @@ def test_environment_detection(): """Test environment detection logic""" debug_log("🔍 測試環境檢測功能") debug_log("-" * 30) - + try: - from .server import is_remote_environment, can_use_gui - + from .server import is_remote_environment, is_wsl_environment, can_use_gui + + wsl_detected = is_wsl_environment() remote_detected = is_remote_environment() gui_available = can_use_gui() - + + debug_log(f"WSL 環境檢測: {'是' if wsl_detected else '否'}") debug_log(f"遠端環境檢測: {'是' if remote_detected else '否'}") debug_log(f"GUI 可用性: {'是' if gui_available else '否'}") - - if remote_detected: + + if wsl_detected: + debug_log("✅ 檢測到 WSL 環境,將使用 Web UI 並支援 Windows 瀏覽器啟動") + elif remote_detected: debug_log("✅ 將使用 Web UI (適合遠端開發環境)") else: debug_log("✅ 將使用 Qt GUI (本地環境)") - + return True - + except Exception as e: debug_log(f"❌ 環境檢測失敗: {e}") return False @@ -245,27 +249,30 @@ def test_environment_web_ui_mode(): """Test environment-based Web UI mode""" debug_log("\n🌐 測試環境變數控制 Web UI 模式") debug_log("-" * 30) - + try: - from .server import interactive_feedback, is_remote_environment, can_use_gui + from .server import interactive_feedback, is_remote_environment, is_wsl_environment, can_use_gui import os - + # 顯示當前環境狀態 + is_wsl = is_wsl_environment() is_remote = is_remote_environment() gui_available = can_use_gui() force_web_env = os.getenv("FORCE_WEB", "").lower() - - debug_log(f"當前環境 - 遠端: {is_remote}, GUI 可用: {gui_available}") + + debug_log(f"當前環境 - WSL: {is_wsl}, 遠端: {is_remote}, GUI 可用: {gui_available}") debug_log(f"FORCE_WEB 環境變數: {force_web_env or '未設定'}") - + if force_web_env in ("true", "1", "yes", "on"): debug_log("✅ FORCE_WEB 已啟用,將強制使用 Web UI") + elif is_wsl: + debug_log("✅ WSL 環境,將使用 Web UI 並支援 Windows 瀏覽器啟動") elif not is_remote and gui_available: debug_log("ℹ️ 本地 GUI 環境,將使用 Qt GUI") debug_log("💡 可設定 FORCE_WEB=true 強制使用 Web UI 進行測試") else: debug_log("ℹ️ 將自動使用 Web UI(遠端環境或 GUI 不可用)") - + return True except Exception as e: diff --git a/src/mcp_feedback_enhanced/web/utils/browser.py b/src/mcp_feedback_enhanced/web/utils/browser.py index 6fa091d..a52dedd 100644 --- a/src/mcp_feedback_enhanced/web/utils/browser.py +++ b/src/mcp_feedback_enhanced/web/utils/browser.py @@ -4,18 +4,125 @@ 瀏覽器工具函數 ============== -提供瀏覽器相關的工具函數。 +提供瀏覽器相關的工具函數,包含 WSL 環境的特殊處理。 """ +import os +import sys +import subprocess import webbrowser from typing import Callable +# 導入調試功能 +from ...debug import server_debug_log as debug_log + + +def is_wsl_environment() -> bool: + """ + 檢測是否在 WSL 環境中運行 + + Returns: + bool: True 表示 WSL 環境,False 表示其他環境 + """ + try: + # 檢查 /proc/version 文件是否包含 WSL 標識 + if os.path.exists('/proc/version'): + with open('/proc/version', 'r') as f: + version_info = f.read().lower() + if 'microsoft' in version_info or 'wsl' in version_info: + return True + + # 檢查 WSL 相關環境變數 + wsl_env_vars = ['WSL_DISTRO_NAME', 'WSL_INTEROP', 'WSLENV'] + for env_var in wsl_env_vars: + if os.getenv(env_var): + return True + + # 檢查是否存在 WSL 特有的路徑 + wsl_paths = ['/mnt/c', '/mnt/d', '/proc/sys/fs/binfmt_misc/WSLInterop'] + for path in wsl_paths: + if os.path.exists(path): + return True + + except Exception: + pass + + return False + + +def open_browser_in_wsl(url: str) -> None: + """ + 在 WSL 環境中開啟 Windows 瀏覽器 + + Args: + url: 要開啟的 URL + """ + try: + # 嘗試使用 cmd.exe 啟動瀏覽器 + cmd = ['cmd.exe', '/c', 'start', url] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) + + if result.returncode == 0: + debug_log(f"成功使用 cmd.exe 啟動瀏覽器: {url}") + return + else: + debug_log(f"cmd.exe 啟動失敗,返回碼: {result.returncode}, 錯誤: {result.stderr}") + + except Exception as e: + debug_log(f"使用 cmd.exe 啟動瀏覽器失敗: {e}") + + try: + # 嘗試使用 powershell.exe 啟動瀏覽器 + cmd = ['powershell.exe', '-c', f'Start-Process "{url}"'] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) + + if result.returncode == 0: + debug_log(f"成功使用 powershell.exe 啟動瀏覽器: {url}") + return + else: + debug_log(f"powershell.exe 啟動失敗,返回碼: {result.returncode}, 錯誤: {result.stderr}") + + except Exception as e: + debug_log(f"使用 powershell.exe 啟動瀏覽器失敗: {e}") + + try: + # 最後嘗試使用 wslview(如果安裝了 wslu 套件) + cmd = ['wslview', url] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=10) + + if result.returncode == 0: + debug_log(f"成功使用 wslview 啟動瀏覽器: {url}") + return + else: + debug_log(f"wslview 啟動失敗,返回碼: {result.returncode}, 錯誤: {result.stderr}") + + except Exception as e: + debug_log(f"使用 wslview 啟動瀏覽器失敗: {e}") + + # 如果所有方法都失敗,拋出異常 + raise Exception("無法在 WSL 環境中啟動 Windows 瀏覽器") + + +def smart_browser_open(url: str) -> None: + """ + 智能瀏覽器開啟函數,根據環境選擇最佳方式 + + Args: + url: 要開啟的 URL + """ + if is_wsl_environment(): + debug_log("檢測到 WSL 環境,使用 WSL 專用瀏覽器啟動方式") + open_browser_in_wsl(url) + else: + debug_log("使用標準瀏覽器啟動方式") + webbrowser.open(url) + def get_browser_opener() -> Callable[[str], None]: """ 獲取瀏覽器開啟函數 - + Returns: Callable: 瀏覽器開啟函數 """ - return webbrowser.open \ No newline at end of file + return smart_browser_open \ No newline at end of file