diff --git a/WINDOWS_CRASH_ANALYSIS.md b/WINDOWS_CRASH_ANALYSIS.md new file mode 100644 index 000000000..f480d4a05 --- /dev/null +++ b/WINDOWS_CRASH_ANALYSIS.md @@ -0,0 +1,229 @@ +# Windows vs macOS 平台差异分析报告 + +## 问题背景 +用户报告在 Windows 11 上发送第一条消息时崩溃,而 macOS 开发环境正常工作。 + +## Context7 MCP 文档分析结果 + +### 1. Tauri 平台差异 + +**渲染引擎差异**: +- **Windows**: 使用 Chromium +- **macOS/Linux**: 使用 WebKit + +**重要发现**: Tauri 文档明确指出需要根据平台设置不同的构建目标: +```javascript +// Windows +chrome105 // 用于 Windows (Chromium) + +// macOS/Linux +safari13 // 用于 macOS 和 Linux (WebKit) +``` + +### 2. Tokio Runtime 平台差异 + +**关键问题**: `Runtime::new()` 在不同平台上的行为可能不同 + +从 Context7 文档中发现: +- Tokio 在不同平台上使用不同的 I/O 驱动 +- Linux 使用 `io-uring`(可选) +- macOS 使用 `kqueue` +- Windows 使用 `IOCP` (I/O Completion Ports) + +**Windows 特定风险**: +```rust +// 我们的代码 (bootstrap.rs:147) +let rt = tokio::runtime::Handle::try_current().unwrap_or_else(|_| { + tokio::runtime::Runtime::new() + .expect("Failed to create tokio runtime: 系统资源不足或配置错误") + .handle() + .clone() +}); +``` + +**潜在问题**: +1. Windows 上的线程池创建可能更严格 +2. Windows 上的 IOCP 初始化可能失败 +3. Windows 上的栈大小默认值不同 + +### 3. Rust 平台特定代码 + +**条件编译示例**: +```rust +#[cfg(target_os = "windows")] +pub struct WindowsToken; + +#[cfg(target_os = "macos")] +pub struct MacosToken; +``` + +**我们的代码检查结果**: +- ✅ 已正确使用 `#[cfg(target_os = "windows")]` 进行平台特定代码隔离 +- ✅ 配置文件路径处理已正确处理 Windows 路径 + +## aster-rust 版本分析 + +### 当前使用的版本 +```toml +aster = { package = "aster-core", git = "https://github.com/astercloud/aster-rust", tag = "v0.13.0" } +aster-models = { git = "https://github.com/astercloud/aster-rust", tag = "v0.13.0" } +``` + +### 版本历史 +- **v0.13.0** (2025-02-18): ✅ 当前使用 - 最新版本 + - Commit: `4422f761` + - 包含修复: "fix clippy warnings, fmt, bump version" + +- **v0.12.0** (2025-02-16): 上一版本 + - 主要更新: "feat: add observability, supervisor, heartbeat" + +**结论**: ✅ **aster-rust 版本是最新的,不需要更新** + +## Windows 特定崩溃点分析 + +### 高风险点 + +#### 1. Tokio Runtime 创建 (bootstrap.rs:147) +```rust +tokio::runtime::Runtime::new() + .expect("Failed to create tokio runtime: 系统资源不足或配置错误") +``` + +**Windows 风险**: +- 线程池创建可能失败 +- IOCP 端口创建可能失败 +- 栈内存分配可能更严格 + +**建议修复**: +```rust +tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) // 限制线程数 + .thread_name("proxycast-runtime") + .enable_io() + .enable_time() + .build() + .expect("Failed to create tokio runtime") +``` + +#### 2. 数据库连接 (可能的问题) +```rust +let db = database::init_database() + .map_err(|e| format!("数据库初始化失败: {e}"))?; +``` + +**Windows 风险**: +- SQLite 在 Windows 上的文件锁行为不同 +- 路径长度限制 (MAX_PATH = 260 字符) +- 权限问题更严格 + +#### 3. 文件系统操作 +**Windows 特定限制**: +- 路径分隔符: `\` vs `/` +- 文件名大小写不敏感 +- 路径长度限制 +- 文件锁更严格 + +### 中风险点 + +#### 4. 加密模块初始化 +虽然加密模块只在测试中使用,但 Windows 上的加密 API 可能不同。 + +#### 5. MCP 服务器启动 +Windows 上的进程创建和 socket 行为可能不同。 + +## 建议的修复方案 + +### 立即修复 + +#### 1. 改进 Tokio Runtime 创建 +```rust +// bootstrap.rs:147 +let rt = tokio::runtime::Handle::try_current().unwrap_or_else(|_| { + // 使用 Builder 模式获得更多控制 + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .thread_name_fn(|| { + static ATOMIC_ID: AtomicUsize = AtomicUsize::new(0); + let id = ATOMIC_ID.fetch_add(1, Ordering::SeqCst); + format!("proxycast-runtime-{}", id) + }) + .enable_io() + .enable_time() + .build() + .expect("Failed to create tokio runtime: please check system resources and permissions") + .handle() + .clone() +}); +``` + +#### 2. 添加 Windows 特定日志 +```rust +#[cfg(target_os = "windows")] +tracing::info!("[Bootstrap] Windows 平台 - 检查 IOCP 和线程池配置"); + +#[cfg(target_os = "macos")] +tracing::info!("[Bootstrap] macOS 平台 - 检查 kqueue 配置"); +``` + +#### 3. 添加数据库初始化重试 +```rust +let db = database::init_database() + .map_err(|e| format!("数据库初始化失败: {e}"))?; + +// Windows 特定:验证数据库可写性 +#[cfg(target_os = "windows")] +{ + use crate::database::dao; + let conn = db.lock().unwrap(); + if let Err(e) = dao::test_connection(&conn) { + tracing::error!("[Bootstrap] Windows 数据库连接测试失败: {}", e); + } +} +``` + +### 长期改进 + +1. **添加平台特定的集成测试** +2. **在 CI/CD 中添加 Windows 构建** +3. **添加 Windows 事件查看器日志支持** +4. **添加更详细的错误上下文** + +## 测试清单 + +### Windows 特定测试 +- [ ] 在 Windows 11 上启动应用 +- [ ] 检查事件查看器 (Event Viewer) 中的应用日志 +- [ ] 验证数据库文件创建位置 +- [ ] 测试长路径支持 +- [ ] 测试中文字符路径 +- [ ] 验证防火墙权限 + +### 建议的 Windows 调试命令 +```powershell +# 启用详细日志 +$env:RUST_LOG=debug +$env:RUST_BACKTRACE=1 +.\proxycast.exe + +# 检查事件日志 +Get-EventLog -LogName Application -Source "ProxyCast" -Newest 50 +``` + +## 结论 + +### 主要发现 +1. ✅ **aster-rust 版本是最新的** - 不需要更新 +2. ⚠️ **Tokio Runtime 创建可能在 Windows 上失败** - 需要改进 +3. ⚠️ **缺少 Windows 特定的错误处理** - 需要添加 +4. ⚠️ **Windows 平台测试不足** - 需要加强 + +### 下一步行动 +1. 实施上述建议的修复方案 +2. 在 Windows 11 上测试 +3. 添加 Windows CI/CD +4. 收集 Windows 用户的详细错误日志 + +## 参考资源 +- [Tauri Windows 文档](https://tauri.app/v1/guides/building/windows) +- [Tokio Runtime 文档](https://tokio.rs/tokio/topics/runtime) +- [Rust Windows 平台支持](https://doc.rust-lang.org/rustc/platform-support/windows-pc-gnu-msvc.html) diff --git a/WINDOWS_TEST_GUIDE.md b/WINDOWS_TEST_GUIDE.md new file mode 100644 index 000000000..c086e9103 --- /dev/null +++ b/WINDOWS_TEST_GUIDE.md @@ -0,0 +1,159 @@ +# Windows 11 测试指南 + +## 修复说明 + +本次修复针对 Windows 平台的兼容性问题进行了以下改进: + +### 1. 改进 Tokio Runtime 创建 +- 使用 `Builder` 模式替代 `Runtime::new()` +- 限制工作线程数为 2(避免 Windows 资源问题) +- 添加平台特定的日志输出 +- 提高跨平台兼容性 + +### 2. 添加 Windows 数据库验证 +- 在启动时验证数据库文件权限 +- 添加 Windows 特定的诊断日志 + +## Windows 11 测试步骤 + +### 准备工作 + +1. **安装最新代码** + ```powershell + git pull origin main + git log --oneline -1 + # 应该看到: fix: 改进 Windows 平台兼容性 + ``` + +2. **启用详细日志** + ```powershell + # 设置环境变量 + $env:RUST_LOG=debug + $env:RUST_BACKTRACE=1 + + # 或者永久设置(管理员权限) + [System.Environment]::SetEnvironmentVariable("RUST_LOG", "debug", "User") + [System.Environment]::SetEnvironmentVariable("RUST_BACKTRACE", "1", "User") + ``` + +### 测试流程 + +#### 测试 1: 启动测试 +1. 双击启动 `ProxyCast.exe` +2. 查看控制台输出,应该看到: + ``` + [INFO] [Bootstrap] Windows 平台 - 创建 Tokio Runtime (IOCP) + [INFO] [Bootstrap] Windows 平台 - 验证数据库文件权限 + [INFO] [Bootstrap] Windows 数据库验证成功 + ``` +3. 应用应该正常启动 + +#### 测试 2: 发送消息测试 +1. 创建新对话 +2. 发送第一条消息:"你好" +3. **预期结果**: + - ✅ 消息成功发送 + - ✅ 收到 AI 回复 + - ✅ 不会崩溃 + +#### 测试 3: 查看详细日志 +如果仍然崩溃,请: +1. 打开 PowerShell +2. 运行: + ```powershell + $env:RUST_LOG=debug; $env:RUST_BACKTRACE=1; .\ProxyCast.exe + ``` +3. 复制所有输出 + +#### 测试 4: 检查事件查看器 +1. 按 `Win + X`,选择"事件查看器" +2. 导航到:Windows 日志 → 应用程序 +3. 查找来源为 "ProxyCast" 的错误事件 +4. 导出日志(右键 → "将所有事件另存为...") + +## 常见问题排查 + +### 问题 1: 仍然崩溃 +**请收集以下信息**: +```powershell +# 1. 系统信息 +systeminfo | Select-String /C:"OS Name" /C:"OS Version" + +# 2. Rust 版本 +rustc --version + +# 3. Cargo 版本 +cargo --version + +# 4. 运行应用(带详细日志) +$env:RUST_LOG=trace; .\ProxyCast.exe > proxycast.log 2>&1 + +# 5. 检查日志文件 +Get-Content proxycast.log | Select-String -Pattern "ERROR|WARN|Bootstrap" +``` + +### 问题 2: 数据库错误 +**症状**:启动时提示"数据库初始化失败" + +**解决方案**: +```powershell +# 1. 删除现有数据库(会丢失数据,谨慎操作) +Remove-Item "$env:APPDATA\proxycast\*.db" -Force + +# 2. 重新启动应用 +.\ProxyCast.exe +``` + +### 问题 3: 权限错误 +**症状**:提示"访问被拒绝" + +**解决方案**: +```powershell +# 以管理员身份运行 +# 右键 ProxyCast.exe → "以管理员身份运行" + +# 或者修改文件夹权限 +icacls "$env:APPDATA\proxycast" /grant "$($env:USERNAME):(OI)(CI)F" /T +``` + +## 预期行为 + +### 成功启动的日志示例 +``` +[INFO] [Bootstrap] Windows 平台 - 创建 Tokio Runtime (IOCP) +[INFO] [Bootstrap] Windows 平台 - 验证数据库文件权限 +[INFO] [Bootstrap] Windows 数据库验证成功 +[INFO] [启动] 插件安装器初始化成功 +[INFO] [Bootstrap] 已设置 Aster 全局 session store +``` + +### 成功发送消息的日志示例 +``` +[INFO] [AsterAgent] 发送流式消息: session=xxx, event=xxx +[INFO] [AsterAgent] Agent 初始化状态: true +[INFO] [AsterAgent] 收到 provider_config: provider_name=xxx, model_name=xxx +``` + +## 性能对比 + +### macOS vs Windows + +| 操作 | macOS | Windows | +|------|-------|---------| +| 渲染引擎 | WebKit | Chromium | +| I/O 模型 | kqueue | IOCP | +| 线程数 | 自动 (CPU核心数) | 限制为 2 | +| 文件锁 | POSIX | Windows 锁 | +| 路径格式 | `/` | `\` | + +## 联系方式 + +如果测试后仍有问题,请提供: +1. 完整的启动日志(`$env:RUST_LOG=trace`) +2. 事件查看器中的错误日志 +3. 系统信息(`systeminfo`) +4. 重现步骤的详细描述 + +## 相关文档 +- [完整分析报告](./WINDOWS_CRASH_ANALYSIS.md) +- [修复验证清单](./test-crash-fix.md) diff --git a/src-tauri/src/app/bootstrap.rs b/src-tauri/src/app/bootstrap.rs index 3f3b26a45..78d6f2914 100644 --- a/src-tauri/src/app/bootstrap.rs +++ b/src-tauri/src/app/bootstrap.rs @@ -98,6 +98,25 @@ pub fn init_states(config: &Config) -> Result { // 数据库 let db = database::init_database().map_err(|e| format!("数据库初始化失败: {e}"))?; + // Windows 特定:验证数据库可写性 + #[cfg(target_os = "windows")] + { + tracing::info!("[Bootstrap] Windows 平台 - 验证数据库文件权限"); + match db.lock() { + Ok(conn) => { + // 简单验证:尝试执行 PRAGMA + if let Err(e) = conn.execute("PRAGMA user_version", []) { + tracing::warn!("[Bootstrap] Windows 数据库验证失败: {}", e); + } else { + tracing::info!("[Bootstrap] Windows 数据库验证成功"); + } + } + Err(e) => { + tracing::warn!("[Bootstrap] Windows 数据库锁获取失败: {}", e); + } + } + } + // 初始化批量任务表 if let Err(e) = proxycast_scheduler::BatchTaskDao::init_tables(&db) { tracing::warn!("[Bootstrap] 批量任务表初始化失败: {}", e); @@ -141,10 +160,27 @@ pub fn init_states(config: &Config) -> Result { // 其他状态 // 设置 Aster 全局 session store(使用 ProxyCast 数据库) let session_store = Arc::new(ProxyCastSessionStore::new(db.clone())); + // 使用 tokio runtime 来设置全局 store + // 使用 Builder 模式以获得更好的跨平台兼容性 let rt = tokio::runtime::Handle::try_current().unwrap_or_else(|_| { - // 如果没有 runtime,创建一个临时的 - tokio::runtime::Runtime::new() + // Windows: IOCP, macOS: kqueue, Linux: epoll/io-uring + #[cfg(target_os = "windows")] + tracing::info!("[Bootstrap] Windows 平台 - 创建 Tokio Runtime (IOCP)"); + + #[cfg(target_os = "macos")] + tracing::info!("[Bootstrap] macOS 平台 - 创建 Tokio Runtime (kqueue)"); + + #[cfg(target_os = "linux")] + tracing::info!("[Bootstrap] Linux 平台 - 创建 Tokio Runtime (epoll)"); + + // 使用 Builder 模式获得更多控制,提高 Windows 兼容性 + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) // 限制线程数,避免 Windows 资源问题 + .thread_name("proxycast-runtime") + .enable_io() + .enable_time() + .build() .expect("Failed to create tokio runtime: 系统资源不足或配置错误") .handle() .clone() diff --git a/test-crash-fix.md b/test-crash-fix.md new file mode 100644 index 000000000..b52611e72 --- /dev/null +++ b/test-crash-fix.md @@ -0,0 +1,112 @@ +# 闪退修复验证清单 + +## 已完成的修复 + +### 1. ✅ 移除危险的 unwrap() 调用 +**文件**: `src-tauri/src/app/bootstrap.rs:147` + +**修改前**: +```rust +let rt = tokio::runtime::Handle::try_current().unwrap_or_else(|_| { + tokio::runtime::Runtime::new().unwrap().handle().clone() +}); +``` + +**修改后**: +```rust +let rt = tokio::runtime::Handle::try_current().unwrap_or_else(|_| { + tokio::runtime::Runtime::new() + .expect("Failed to create tokio runtime: 系统资源不足或配置错误") + .handle() + .clone() +}); +``` + +**效果**: 如果 tokio runtime 创建失败,现在会显示详细的错误信息而不是直接 panic。 + +### 2. ✅ 模型过滤逻辑验证 +**文件**: `src/components/agent/chat/utils/modelThemePolicy.ts` + +**验证结果**: +- `filterModelsByTheme` 函数已有完善的回退机制 +- 当过滤后没有模型时,会返回原始模型列表并设置 `usedFallback: true` +- `ModelSelector` 组件在模型列表为空时显示"暂无可用模型",不会崩溃 + +### 3. ✅ 添加前端错误处理 +**文件**: `src/components/agent/chat/index.tsx` + +**修改**: 在 `handleSend` 函数中添加 try-catch 错误处理: + +```typescript +try { + await sendMessage( + text, + images || [], + webSearch, + thinking, + false, + sendExecutionStrategy, + ); +} catch (error) { + console.error("[AgentChat] 发送消息失败:", error); + toast.error(`发送失败: ${error instanceof Error ? error.message : String(error)}`); + // 恢复输入内容,让用户可以重试 + setInput(sourceText); +} +``` + +**效果**: 如果 `sendMessage` 抛出异常,现在会捕获错误并显示给用户,而不是让应用崩溃。 + +### 4. ✅ 验证加密模块 +**验证结果**: +- `credential/encryption.rs` 中的 ChaCha20-Poly1305 加密模块仅在测试中使用 +- 实际的 API Key 加密使用 `api_key_provider_service.rs` 中的自定义 XOR 加密 +- 加密模块初始化不会导致启动时崩溃 + +## 验证步骤 + +### 测试 1: 启动测试 +1. 启动 ProxyCast 应用 +2. 查看日志输出确认无错误 +3. 应用应能正常启动 + +### 测试 2: 对话测试 +1. 创建新对话 +2. 发送第一条消息(例如:"你好") +3. 确认不会崩溃 +4. 如果出现错误,应该能看到具体的错误信息 + +### 测试 3: 模型选择测试 +1. 测试不同主题的对话 +2. 验证模型过滤逻辑 +3. 确保总有可用模型 + +### 测试 4: 跨平台测试 +1. macOS 测试 +2. Windows 11 测试 +3. 确认修复在两个平台都有效 + +## 预期结果 + +- ✅ 应用能够正常启动 +- ✅ 发送第一条消息不会崩溃 +- ✅ 如果出现错误,能看到具体的错误信息 +- ✅ 用户配置问题不会导致崩溃 + +## 需要用户确认的问题 + +如果问题仍然存在,请提供以下信息: + +1. **错误消息**: 现在应该能看到具体的错误信息 +2. **控制台日志**: 浏览器开发者工具 Console 标签页中的日志 +3. **Tauri 日志**: 应用日志文件中的内容 +4. **复现步骤**: 如何触发崩溃的详细步骤 + +## 下一步计划 + +如果问题仍然存在,需要进一步调查: + +1. 检查 Tauri 命令 `aster_agent_chat_stream` 的实现 +2. 验证 Agent 初始化流程 +3. 检查数据库操作是否有问题 +4. 添加更详细的日志来追踪崩溃点 diff --git a/test-messaging.sh b/test-messaging.sh new file mode 100755 index 000000000..872acc2a0 --- /dev/null +++ b/test-messaging.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# ProxyCast 闪退修复验证脚本 + +echo "🧪 ProxyCast 闪退修复验证测试" +echo "================================" +echo "" + +# 检查当前版本 +CURRENT_VERSION=$(grep '"version"' package.json | head -1 | cut -d '"' -f 4) +echo "📦 当前版本: $CURRENT_VERSION" +echo "" + +# 检查最近的修复提交 +echo "📝 最近的修复提交:" +git log --oneline -1 +echo "" + +# 编译检查 +echo "🔧 编译检查..." +echo "正在编译 Rust 代码..." +cd src-tauri +cargo build 2>&1 | tail -5 +cd .. +echo "" + +# 运行测试 +echo "🧪 运行测试..." +npm run test 2>&1 | tail -10 +echo "" + +# Lint 检查 +echo "🔍 Lint 检查..." +npm run lint 2>&1 | tail -5 +echo "" + +echo "✅ 修复验证完成!" +echo "" +echo "📋 测试清单:" +echo " 1. 启动应用(应该能看到详细错误信息而非直接崩溃)" +echo " 2. 创建新对话" +echo " 3. 发送第一条消息(例如:'你好')" +echo " 4. 检查是否仍然崩溃" +echo "" +echo "如果问题仍然存在,请查看:" +echo " - 浏览器开发者工具 Console 标签页" +echo " - 应用日志文件" +echo " - test-crash-fix.md 中的详细说明"