chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,365 @@
# qiming-agent_diff Windows CMD 窗口问题验证报告
## 测试环境
- **版本**: qiming-agent_diff (tag: win-v0.1.2, v0.1.26)
- **mcp-proxy 版本**: 0.1.37 → 0.1.39
- **测试日志**: `/Users/apple/Downloads/qiming-agent.log.2026-02-12`
## 测试结果 ❌
### 问题 1: CMD 窗口仍然弹出
**预期**: 所有服务启动时不弹出 CMD 窗口
**实际**: CMD 窗口仍然弹出
**状态**: ❌ **未解决**
### 问题 2: mcp-proxy 启动失败
**预期**: mcp-proxy 正常启动并通过健康检查
**实际**: 健康检查超时15秒后仍未就绪
**状态**: ❌ **未解决**
### 问题 3: Node.js 重复安装
**预期**: 检测到已安装的 Node.js直接使用
**实际**: 重复尝试安装 10 次后才使用自动安装
**状态**: ❌ **未解决**
---
## 代码验证
### qiming-agent_diff 中的实现 ✅ 代码正确
**文件**: `crates/qiming-agent-core/src/service/mod.rs`
```rust
// Windows 下正确使用了 CreationFlags + JobObject
#[cfg(target_os = "windows")]
let child = cmd
.wrap(process_wrap::tokio::KillOnDrop)
.wrap(CreationFlags(CREATE_NO_WINDOW | DETACHED_PROCESS)) // ✅ 正确
.wrap(JobObject) // ✅ 正确
.spawn()?;
```
**应用位置**:
1.`run_command_with_timeout()` - 通用命令执行
2.`file_server_start()` - 文件服务器启动
3.`lanproxy_start()` - LAN 代理启动
4.`mcp_proxy_start()` - MCP 代理启动
**Cargo.toml 特性**:
```toml
process-wrap = { version = "9", features = ["tokio1", "process-group", "job-object"] }
```
✅ 已正确添加 `job-object` 特性
---
## 为什么代码正确但仍然弹出窗口?
### 可能原因 1: `.cmd` 批处理文件本身的问题 ⭐ 最可能
**分析**:
Windows 上的 npm 全局包会生成 `.cmd` 包装文件:
```
C:\Users\MECHREVO\.local\bin\
├── mcp-proxy.cmd ← 批处理文件
├── qiming-file-server.cmd
└── ...
```
**问题**: 即使父进程设置了 `CREATE_NO_WINDOW``.cmd` 文件启动时仍然会:
1. 启动 `cmd.exe` 解释器
2. `cmd.exe` 可能会**忽略**父进程的窗口标志
3. `.cmd` 内部再启动 Node.js 进程
**层级关系**:
```
qiming-agent.exe (设置 CREATE_NO_WINDOW)
└─ cmd.exe (解释 .cmd 文件) ← 可能弹窗!
└─ node.exe (实际的服务)
```
**验证方法**:
1. 直接运行 `.cmd` 文件看是否弹窗:
```powershell
# 测试 1: 直接运行
C:\Users\MECHREVO\.local\bin\mcp-proxy.cmd
# 测试 2: 通过 Rust 程序运行(设置 CREATE_NO_WINDOW
# 如果仍然弹窗,说明问题在 .cmd 文件本身
```
2. 查看 `.cmd` 文件内容:
```powershell
type C:\Users\MECHREVO\.local\bin\mcp-proxy.cmd
```
---
### 可能原因 2: process-wrap 的 JobObject 实现问题
**分析**:
`process-wrap``JobObject` 可能没有正确传递 `CREATE_NO_WINDOW` 标志给子进程。
**验证代码**:
```rust
// 当前实现
.wrap(CreationFlags(CREATE_NO_WINDOW | DETACHED_PROCESS))
.wrap(JobObject)
// 可能的问题JobObject 重新创建了进程,覆盖了 CreationFlags
```
**解决方案**: 检查 process-wrap 的源码,确认 JobObject 是否保留了 CreationFlags。
---
### 可能原因 3: Windows 版本或系统设置
某些 Windows 版本或系统设置可能忽略 `CREATE_NO_WINDOW` 标志。
**验证**:
```powershell
# 检查 Windows 版本
systeminfo | findstr /B /C:"OS Name" /C:"OS Version"
# 检查是否有组策略限制
gpedit.msc
```
---
## 解决方案建议
### 方案 A: 绕过 `.cmd` 文件,直接调用 Node.js ⭐ 推荐
**原理**: 不使用 `.cmd` 包装文件,直接调用 node.exe
**实现**:
```rust
// 修改前
let bin_path = "C:\\Users\\...\\mcp-proxy.cmd";
let mut cmd = CommandWrap::with_new(&bin_path, |cmd| {
// ...
});
// 修改后
let node_exe = "C:\\Users\\...\\node.exe";
let mcp_proxy_js = "C:\\Users\\...\\node_modules\\mcp-stdio-proxy\\dist\\index.js";
let mut cmd = CommandWrap::with_new(node_exe, |cmd| {
cmd.arg(mcp_proxy_js) // 直接调用 JS 文件
.env("PATH", ...)
// ...
});
#[cfg(windows)]
let child = cmd
.wrap(CreationFlags(CREATE_NO_WINDOW | DETACHED_PROCESS))
.wrap(JobObject)
.spawn()?;
```
**优点**:
- ✅ 完全绕过 `.cmd` 文件
- ✅ 更可控的进程启动
- ✅ 更清晰的参数传递
**缺点**:
- ⚠️ 需要找到 `.cmd` 对应的 `.js` 文件路径
- ⚠️ 需要处理 npm 包的路径解析
---
### 方案 B: 使用 `conhost.exe` 隐藏控制台
**原理**: 使用 Windows 的 `conhost.exe` 工具隐藏控制台窗口
**实现**:
```rust
#[cfg(windows)]
fn run_hidden(cmd_path: &str, args: &[&str]) -> Result<Child> {
use std::process::{Command, Stdio};
use std::os::windows::process::CommandExt;
// 方法 1: 使用 CREATE_NO_WINDOW + STARTUPINFOEXW
const CREATE_NO_WINDOW: u32 = 0x08000000;
const DETACHED_PROCESS: u32 = 0x00000008;
let mut cmd = Command::new(cmd_path);
cmd.args(args);
cmd.creation_flags(CREATE_NO_WINDOW | DETACHED_PROCESS);
cmd.stdout(Stdio::null());
cmd.stderr(Stdio::null());
// 方法 2: 使用 wscript 启动(更隐蔽)
// let mut cmd = Command::new("wscript.exe");
// cmd.arg("//B") // Batch mode (no UI)
// .arg("//NoLogo")
// .arg(vbs_script_path); // 需要生成 VBS 脚本
cmd.spawn()
}
```
---
### 方案 C: 打包成 Windows 服务或使用隐藏启动器
**原理**: 将服务打包成 Windows 服务或使用专门的隐藏启动器
**实现**:
1. **Windows 服务**:
```rust
// 使用 windows-service crate
// 将 qiming-agent 注册为 Windows 服务
```
2. **隐藏启动器**:
```rust
// 创建一个小的 C++ 启动器
// 使用 ShellExecuteEx + SW_HIDE
```
---
## 调试步骤
### 1. 验证 `.cmd` 文件是否是根本原因
```powershell
# 查看 mcp-proxy.cmd 内容
type C:\Users\MECHREVO\.local\bin\mcp-proxy.cmd
# 尝试直接运行 node.exe而不是 .cmd
C:\Users\MECHREVO\.local\bin\node.exe C:\Users\MECHREVO\.local\bin\node_modules\mcp-stdio-proxy\dist\index.js --help
```
### 2. 使用 Process Explorer 查看进程树
下载 Process Explorer: https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer
启动 qiming-agent 后,查看进程树:
```
qiming-agent.exe
└─ cmd.exe ← 如果看到这个,说明 .cmd 文件启动了 cmd.exe
└─ conhost.exe ← 控制台宿主
└─ node.exe
```
### 3. 添加详细日志
修改 `service/mod.rs`,添加日志:
```rust
#[cfg(windows)]
{
tracing::info!("Windows: 设置 CREATE_NO_WINDOW | DETACHED_PROCESS");
tracing::info!("命令路径: {}", bin_path);
tracing::info!("是否是 .cmd 文件: {}", bin_path.ends_with(".cmd"));
}
```
---
## 测试 mcp-proxy v0.1.39 的改进
根据 mcp-proxy 的更新日志v0.1.39 添加了:
1. ✅ 环境变量日志配置支持
2. ✅ 增强的启动日志
3. ✅ stderr 输出关键信息
**验证**:
```powershell
# 设置日志目录
$env:MCP_PROXY_LOG_DIR = "C:\Users\MECHREVO\AppData\Roaming\qiming-agent\logs\mcp-proxy"
$env:MCP_PROXY_LOG_LEVEL = "debug"
$env:MCP_PROXY_PORT = "18099"
# 手动启动 mcp-proxy
C:\Users\MECHREVO\.local\bin\mcp-proxy.cmd
# 查看日志
type C:\Users\MECHREVO\AppData\Roaming\qiming-agent\logs\mcp-proxy\log.2026-02-12
```
---
## Node.js 检测问题修复
日志显示的问题:
```
[resolve_node_bin] npm -> fallback to PATH # 10 次失败
[NodeInstall] 开始自动安装 Node.js... # 最终触发安装
```
**根本原因**: `resolve_node_bin` 没有真正验证 npm 命令是否可用
**修复方案** (已在 mcp-proxy 仓库的分析文档中提供):
```rust
async fn is_nodejs_available() -> bool {
tokio::process::Command::new("node")
.arg("--version")
.status()
.await
.map(|s| s.success())
.unwrap_or(false)
}
async fn ensure_npm_package(package_name: &str) -> Result<()> {
// 1. 先检查 Node.js 是否可用
if !is_nodejs_available().await {
install_nodejs().await?;
}
// 2. 检查包是否已安装
if is_npm_package_installed(package_name).await? {
return Ok(());
}
// 3. 安装包(带重试限制)
// ...
}
```
---
## 总结
### ✅ qiming-agent_diff 代码是正确的
代码层面的修改是正确的:
- ✅ 使用了 `CreationFlags(CREATE_NO_WINDOW | DETACHED_PROCESS)`
- ✅ 使用了 `JobObject` 管理进程
- ✅ 添加了 `process-group``job-object` 特性
### ❌ 但仍然弹出 CMD 窗口
**最可能的原因**: `.cmd` 批处理文件本身启动了 `cmd.exe`,导致窗口弹出
**推荐的解决方案**:
1.**方案 A**: 绕过 `.cmd` 文件,直接调用 `node.exe`
2. 🔧 **方案 B**: 验证 process-wrap 的 JobObject 实现
3. 📊 **调试**: 使用 Process Explorer 查看进程树
### 📋 下一步行动
1. **立即**: 查看 `.cmd` 文件内容,确认是否是根本原因
2. **验证**: 尝试直接调用 `node.exe` 而不是 `.cmd`
3. **测试**: 使用 Process Explorer 查看进程树
4. **应用**: 如果方案 A 有效,修改 qiming-agent 代码
### 📝 相关文档
- mcp-proxy LOG_CONFIGURATION.md - 日志配置指南
- QIMING_AGENT_WINDOWS_CMD_FIX.md - CMD 窗口修复方案
- MCP_PROXY_STARTUP_FAILURE_ANALYSIS.md - 启动失败分析