Files
qiming/qimingclaw/specs/agent-client/agent-client-spec.md

196 KiB
Raw Blame History

Agent Client 技术设计规格文档

版本v1.1.0 最后更新2026-01-29 状态:设计中

目录

  1. 功能需求总览
  2. UI 界面详细设计
  3. 组件选型与使用
  4. 核心模块设计
  5. 通信协议设计
  6. 系统架构
  7. 目录结构
  8. 阶段一验收测试用例

1. 功能需求总览

1.1 开发阶段划分

本项目采用分阶段开发策略,明确各阶段目标和范围:

阶段一:核心通信与 UI当前优先级

目标:验证 agent-server-adminagent-client 之间的双向通信能力,确保 data-server 的 P2P/TCP/WebSocket 通信正常工作。

功能模块 说明 优先级
基础运行能力 跨平台运行、系统托盘、开机自启动 P0
连接管理 连接 data-server、P2P/Relay 模式、心跳保活、重连 P0
客户端信息 显示 ID、显示/隐藏密码、复制功能、修改密码 P0
设置界面 data-server 地址配置 P0
权限管理 屏幕录制、辅助功能权限检测与引导 P0
依赖管理 Node.js 自动安装、npm 工具安装 P0
跨平台打包 Windows/macOS/Linux 安装包 P0

验证指标

  • agent-client 可在 macOS/Windows/Linux 成功打包安装
  • agent-client 能成功连接 data-serverhbbs/hbbr
  • agent-server-admin 能发现并连接 agent-client
  • 双向通信正常(消息可发送/接收)
  • 支持 P2P 直连(局域网)
  • 支持 Relay 中继(跨公网)
  • 支持 WebSocket 兼容模式(防火墙环境)

阶段二Agent 运行时集成(后续开发)

目标:集成 agent_runner实现 Agent 任务执行、进度回传等功能。

功能模块 说明 优先级
Agent 执行 任务执行、输出回传、任务取消 P1
Agent 状态 状态栏显示 agent 进程运行状态 P1
远程桌面 屏幕捕获、输入模拟 P1
文件传输 发送/接收文件 P1
日志调试 开发者模式日志、导出日志 P1
客户端升级 检查更新、下载安装 P2
聊天界面 Agent 聊天 UI P2

1.2 功能优先级分类

优先级 功能模块 说明
P0 (高优先级) 基础运行能力 跨平台运行、系统托盘、开机自启动、Tab 导航
P0 连接管理 连接 data-server、P2P/Relay 模式、心跳保活
P0 客户端信息 显示 ID、显示/隐藏密码、复制功能、修改密码
P0 设置界面 data-server 地址配置
P0 权限管理 屏幕录制、辅助功能权限检测与引导
P0 依赖管理 Node.js 自动安装、npm 工具安装
P1 (中优先级) 增强功能 主题切换、语言设置、工作目录、文件传输
P1 远程桌面 屏幕捕获、输入模拟
P1 Agent 状态 状态栏显示 agent 进程运行状态
P1 Agent 执行 任务执行、输出回传
P1 日志调试 开发者模式日志、导出日志
P1 关于界面 版本信息、链接
P2 (低优先级) 进阶功能 客户端升级、聊天界面

1.2 Feature 控制表

Feature 默认启用 控制功能
tray Yes 系统托盘图标
auto-launch Yes 开机自启动
chat-ui No Agent 聊天界面
remote-desktop No 远程桌面功能
dependency-management Yes 依赖管理界面
file-transfer Yes 文件传输功能
dev-mode No 开发者模式日志

1.3 功能与组件映射表

功能需求 依赖组件 使用模块
UI 框架 gpui + gpui-component Root, TabPanel, Input, Button, Switch, DataTable 等
系统托盘 tray-icon + muda TrayIconBuilder, Menu, MenuItem
开机自启动 auto-launch AutoLaunchBuilder
P2P/Relay 连接 qiming-rustdesk RendezvousMediator, ConnectionType
屏幕捕获 qiming-rustdesk (scrap) ScrapManager
输入模拟 enigo Enigo, Key, Button
Agent 运行时 rcoder AgentWorkerManager
跨平台打包 cargo-packager Config, PackageFormat
配置管理 serde + toml AppConfig

2. UI 界面详细设计

2.1 主窗口布局

┌─────────────────────────────────────────────────────────────────────────────┐
│  [qiming-agent ×]                                                           │
├─────────────────────────────────────────────────────────────────────────────┤
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                                                                       │  │
│  │    ┌─────────────┬─────────────┬─────────────┬─────────────┐        │  │
│  │    │  客户端     │  设置       │ 依赖管理     │  权限       │  关于  │  │
│  │    │  信息       │             │             │  设置       │        │  │
│  │    └─────────────┴─────────────┴─────────────┴─────────────┘        │  │
│  │                                                                       │  │
│  │    ┌─────────────────────────────────────────────────────────────┐   │  │
│  │    │                                                             │   │  │
│  │    │                      Tab 内容区域                           │   │  │
│  │    │                                                             │   │  │
│  │    └─────────────────────────────────────────────────────────────┘   │  │
│  │                                                                       │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
├─────────────────────────────────────────────────────────────────────────────┤
│  [ID: 12345678]  ● 已连接 (P2P) - 32ms  │  Agent: 空闲  │  依赖: ✓ 正常  │  10:30:45│
└─────────────────────────────────────────────────────────────────────────────┘

状态栏元素

  • 左侧:客户端 ID可点击复制| 连接状态(图标 + 文字 + 延迟)
  • 中左Agent 状态(空闲/运行中/错误)
  • 中右:依赖状态(绿色✓表示正常,红色⚠表示有问题)
  • 右侧:当前时间

状态栏布局

┌─────────────────────────────────────────────────────────────────────────────────────┐
│ [ID: 12345678]  ● 已连接 (P2P) - 32ms  │  Agent: 空闲  │  依赖: ✓ 正常  │  10:30:45  │
└─────────────────────────────────────────────────────────────────────────────────────┘

状态图标定义

状态 图标 颜色 说明
连接成功 绿色 P2P 直连
连接成功 黄色 Relay 中继
连接中 蓝色(闪烁) 正在连接
断开连接 红色 连接断开
Agent 空闲 灰色 空闲状态
Agent 运行中 蓝色(旋转) 任务执行中
Agent 错误 红色 任务出错
依赖正常 绿色 所有依赖就绪
依赖异常 红色 部分依赖缺失

2.2 客户端信息 TabID 和密码界面)

2.2.1 界面功能

核心功能

  1. 显示客户端唯一 ID只读服务器分配不可修改
  2. 显示连接密码(可切换显示/隐藏)
  3. 复制 ID 和密码到剪贴板
  4. 密码修改入口:点击按钮跳转到设置 Tab 的"安全"子页面

2.2.2 详细布局

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                  您的客户端 ID                           │   │
│   │                                                         │   │
│   │     ┌─────────────────────────────────────────────┐     │   │
│   │     │                                             │     │   │
│   │     │              12345678                       │     │   │
│   │     │                                             │     │   │
│   │     │           8 位数字 ID                       │     │   │
│   │     │                                             │     │   │
│   │     └─────────────────────────────────────────────┘     │   │
│   │                                                         │   │
│   │       [📋 复制 ID]                                      │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                  连接密码                               │   │
│   │                                                         │   │
│   │     ┌─────────────────────────────────────────────┐     │   │
│   │     │  🔒 **********●******                      │     │   │
│   │     └─────────────────────────────────────────────┘     │   │
│   │                                                         │   │
│   │       [📋 复制密码]   [👁 显示/隐藏]                    │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                                                         │   │
│   │       [🔐 修改密码]  →  跳转至 设置 → 安全              │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │   ⚠️  此密码用于远程连接验证,请妥善保管                 │   │
│   │       修改密码需要验证原密码                             │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.2.3 交互逻辑

交互 触发动作 使用组件
点击 "复制 ID" 将 ID 写入系统剪贴板 cx.write_to_clipboard()
点击 "复制密码" 将密码写入系统剪贴板 cx.write_to_clipboard()
点击 "显示/隐藏" 切换密码掩码状态 password_masked 状态
点击 "修改密码" 切换到设置 Tab激活"安全"子页面 cx.set_active_tab(Settings) + cx.set_settings_subtab(Security)

2.2.4 状态定义

/// 客户端信息状态
struct ClientInfoState {
    /// 客户端 ID只读
    client_id: String,

    /// 密码(掩码状态)
    password_masked: bool,
}

/// 密码修改状态(在设置 Tab - 安全子页面中管理)
struct PasswordChangeState {
    /// 原密码
    old_password: String,

    /// 新密码
    new_password: String,

    /// 确认密码
    confirm_password: String,

    /// 密码强度
    strength: PasswordStrength,

    /// 修改状态
    status: PasswordChangeStatus,
}

enum PasswordStrength {
    Weak,      // < 8 字符,红色
    Medium,    // >= 8 字符,无特殊字符,黄色
    Strong,    // >= 8 字符 + 特殊字符,绿色
}

enum PasswordChangeStatus {
    Idle,
    Validating,      // 验证原密码
    Changing,        // 修改中
    Success,         // 修改成功
    Error(String),   // 错误信息
}

2.2.5 修改密码弹框(设置 Tab - 安全子页面)

弹框触发:在设置 Tab → 安全子页面,点击"修改密码"按钮

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│                    ┌─────────────────────────┐                 │
│                    │       修改密码          │                 │
│                    └─────────────────────────┘                 │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                                                         │   │
│   │   原密码:     [🔒 ****************]  [👁 显示/隐藏]     │   │
│   │                                                         │   │
│   │   新密码:     [🔒 ****************]  [👁 显示/隐藏]     │   │
│   │              密码强度: [弱 ●○○] [中 ○●○] [强 ○○●]       │   │
│   │              至少 8 位,包含字母和数字                    │   │
│   │                                                         │   │
│   │   确认新密码: [🔒 ****************]  [👁 显示/隐藏]     │   │
│   │              两次输入不一致时显示红色提示                │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│   ┌─────────────────────────────────────────────────────────┐   │
│   │                                                         │   │
│   │   错误信息: 原密码验证失败                               │   │
│   │   成功信息: 密码修改成功!                               │   │
│   │                                                         │   │
│   └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│          [✗ 取消]                    [✓ 确认修改]              │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

验证规则

  1. 原密码必填,调用服务器验证
  2. 新密码长度 >= 8 位
  3. 新密码必须包含字母和数字(可选:特殊字符)
  4. 两次新密码输入必须一致
  5. 新密码不能与原密码相同

验证时机

  • 实时验证:输入时检查格式
  • 提交时验证:再次检查所有规则

2.3 设置 Tab

2.3.1 子页面结构

设置 Tab 包含以下子页面(左侧导航 + 右侧内容):

子页面 功能
服务器 配置 data-server 地址
安全 修改密码、安全设置
常规 开机自启动、语言
外观 主题、工作目录
日志 日志级别、导出日志

设置 Tab 布局

┌─────────────────────────────────────────────────────────────────┐
│  设置                                              [💾 保存]   │
├──────────────────────────┬──────────────────────────────────────┤
│                          │                                      │
│  [≡] 服务器              │   ┌─────────────────────────────┐   │
│                          │   │                              │   │
│  [🔒] 安全               │   │    服务器配置内容            │   │
│                          │   │
│  [⚙ │   │                             ] 常规               │   │                              │   │
│                          │   └─────────────────────────────┘   │
│  [🎨] 外观               │                                      │
│                          │                                      │
│  [📋] 日志               │                                      │
│                          │                                      │
├──────────────────────────┴──────────────────────────────────────┤
│  [← 返回上一级]  当前: 设置 / 服务器                           │
└─────────────────────────────────────────────────────────────────┘

面包屑导航:顶部显示当前路径,如 设置 / 服务器,支持点击返回上级

2.3.2 服务器配置子页面

┌─────────────────────────────────────────────────────────────────┐
│  设置 / 服务器配置                                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  信令服务器 (hbbs)                                       │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    地址: [______________________________]  [:21116]     │   │
│  │                                                         │   │
│  │    例: hbbs.example.com:21116                           │   │
│  │                                                         │   │
│  │    用于注册客户端 ID 和协商 P2P 连接                     │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  中继服务器 (hbbr)                                       │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    地址: [______________________________]  [:21117]     │   │
│  │                                                         │   │
│  │    例: relay.example.com:21117                          │   │
│  │                                                         │   │
│  │    当 P2P 连接失败时,数据将通过此服务器转发             │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  API 服务器                                              │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    地址: [______________________________]  [:8080]      │   │
│  │                                                         │   │
│  │    用于 HTTP API 调用和 SSE 消息推送                     │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│                      [💾 保存]  [↩️ 恢复默认]                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

服务器配置逻辑

配置项 说明 默认值 验证规则
信令服务器地址 hbbs 主机地址或域名 127.0.0.1 非空,支持域名和 IP
信令服务器端口 hbbs 监听端口 21116 1-65535
中继服务器地址 hbbr 主机地址或域名 127.0.0.1 非空,支持域名和 IP
中继服务器端口 hbbr 监听端口 21117 1-65535
API 服务器地址 API 服务主机地址 127.0.0.1 非空,支持域名和 IP
API 服务器端口 API 服务监听端口 8080 1-65535

连接测试逻辑

  1. 用户输入服务器地址后,可点击"连接测试"按钮验证连通性
  2. 测试顺序:
    • 测试 hbbs 连接TCP
    • 测试 hbbr 连接TCP
    • 测试 API 服务器连接HTTP
  3. 测试结果反馈:
    • 全部成功:显示"所有服务器连接正常"
    • 部分失败:显示失败的服务器列表及错误原因
    • 超时:显示"连接超时,请检查网络或服务器地址"

连接流程

客户端启动
    │
    ▼
┌─────────────────┐
│ 读取配置文件    │
└────────┬────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────┐
│ 1. 连接 hbbs (信令服务器)                                   │
│    - TCP 连接: hbbs_addr:hbbs_port                          │
│    - 发送 RegisterPeer 注册 ID                              │
│    - 建立 WebSocket 长连接用于信令协商                      │
└───────────────────────────┬─────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. 连接 hbbr (中继服务器)                                   │
│    - TCP 连接: hbbr_addr:hbbr_port                          │
│    - 用于 P2P 失败时的数据中继                              │
│    - 支持 WebSocket 连接 (兼容防火墙)                       │
└───────────────────────────┬─────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. 连接 API 服务器                                          │
│    - HTTP 连接: api_addr:api_port                           │
│    - 用于 SSE 实时消息推送                                  │
│    - 用于 HTTP API 调用                                     │
└───────────────────────────┬─────────────────────────────────┘
                            │
                            ▼
                    ┌───────┴───────┐
                    │   连接成功    │
                    │   开始工作    │
                    └───────────────┘

配置存储

  • 配置文件路径:~/Library/Application Support/qiming-agent/config/server.toml (macOS)
  • Windows: %LOCALAPPDATA%\qiming-agent\config\server.toml
  • Linux: ~/.local/share/qiming-agent/config/server.toml
# server.toml
[hbbs]
host = "hbbs.example.com"
port = 21116

[hbbr]
host = "relay.example.com"
port = 21117

[api]
host = "api.example.com"
port = 8080

恢复默认配置:将所有服务器地址重置为 127.0.0.1 和默认端口

2.3.3 安全设置子页面(密码修改)

┌─────────────────────────────────────────────────────────────────┐
│  设置 / 安全                                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  客户端密码                                               │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │  此密码用于管理端 (agent-server-admin) 连接时验证        │   │
│  │                                                         │   │
│  │  状态:      [✓ 已设置]      [🔐 修改密码]               │   │
│  │  最后修改:  2026-01-15 14:30:00                         │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  安全建议                                                 │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │  • 建议定期更换密码(每 90 天)                           │   │
│  │  • 使用强密码:至少 8 位,包含字母和数字                   │   │
│  │  • 不要与其他账户使用相同密码                             │   │
│  │  • 不要将密码告知他人                                    │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

修改密码流程

  1. 点击"修改密码"按钮 → 弹出密码修改对话框
  2. 用户输入:新密码 → 确认新密码
  3. 点击"确认修改" → 验证格式 → 保存新密码
  4. 修改成功后,管理端需要重新配置新密码才能连接

重要提示

  • 密码修改后,所有已连接的管理端会立即断开
  • 管理端需要更新配置文件中的密码才能重新连接

2.3.4 常规设置子页面

┌─────────────────────────────────────────────────────────────────┐
│  设置 / 常规                                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  开机启动                                                 │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    [✓] 开机时启动 qiming-agent                            │   │
│  │                                                         │   │
│  │    Windows: 使用注册表 Run 键                            │   │
│  │    macOS: 使用 LaunchAgent                               │   │
│  │    Linux: 使用 XDG Autostart                             │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  语言 / Language                                        │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    [简体中文                          ▼]                 │   │
│  │    [English                           ▼]                 │   │
│  │    [日本語                            ▼]                 │   │
│  │                                                         │   │
│  │    需要重启应用生效                                       │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  启动行为                                                 │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    [✓] 启动时最小化到系统托盘                            │   │
│  │    [ ] 启动时显示主窗口                                  │   │
│  │    [ ] 关闭主窗口时最小化到托盘(不退出)                 │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.3.5 外观设置子页面

┌─────────────────────────────────────────────────────────────────┐
│  设置 / 外观                                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  主题                                                     │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    (●) 浅色主题                                          │   │
│  │    ( ) 深色主题                                          │   │
│  │    ( ) 跟随系统                                          │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  工作目录                                                 │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    用于存储文件传输和 Agent 运行时文件                   │   │
│  │                                                         │   │
│  │    [____________________________]  [📁 浏览...]         │   │
│  │                                                         │   │
│  │    当前: ~/Documents/qiming-agent                        │   │
│  │    剩余空间: 256 GB                                      │   │
│  │                                                         │   │
│  │    ⚠️  修改后需要重启应用生效                             │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  文件传输默认目录                                         │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    (●) 使用工作目录                                      │   │
│  │    ( ) 每次询问保存位置                                  │   │
│  │    ( ) 指定目录: [________________]  [📁 浏览...]       │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.3.6 日志设置子页面

┌─────────────────────────────────────────────────────────────────┐
│  设置 / 日志                                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  日志级别                                                 │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    ( ) 错误 (Error)                                      │   │
│  │    ( ) 警告 (Warn)                                       │   │
│  │    (●) 信息 (Info)                                       │   │
│  │    ( ) 调试 (Debug) - 仅开发模式可用                     │   │
│  │    ( ) 跟踪 (Trace) - 仅开发模式可用                     │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  日志文件                                                 │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    [✓] 启用日志文件输出                                   │   │
│  │                                                         │   │
│  │    日志目录: [________________]  [📁 浏览...]           │   │
│  │                                                         │   │
│  │    单文件最大: [_____] MB   (默认: 10)                   │   │
│  │    保留数量:  [_____] 个    (默认: 5)                    │   │
│  │                                                         │   │
│  │    当前日志: qiming-agent_20260129.log                    │   │
│  │    文件大小: 2.5 MB                                      │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │       [📤 导出日志...]          [🗑 清空日志]           │   │
│  │                                                         │   │
│  │    导出时自动排除敏感信息密码、Token 等)               │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.4 依赖管理 Tab

2.4.1 依赖状态总览

┌─────────────────────────────────────────────────────────────────┐
│  依赖管理                                              [🔄 刷新] │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  总状态: 3/5 已安装  │  2 个需要关注                      │   │
│  │  ✓ Node.js v20.10.0 (系统)                               │   │
│  │  ✓ npm 10.2.0 (系统)                                     │   │
│  │  ✗ opencode 未安装 (建议安装)                            │   │
│  │  ↑ claude-code 1.0.0 -> 1.0.1 (可更新)                   │   │
│  │  ✓ docker 24.0.7 (系统)                                  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  依赖项      │ 版本       │ 来源    │ 状态      │ 操作   │   │
│  │  ───────────────────────────────────────────────────────│   │
│  │  [🟢] Node.js│ v20.10.0  │ 系统全局│ 已安装    │ ─ ─   │   │
│  │  [🟢] npm    │ 10.2.0    │ 系统全局│ 已安装    │ ─ ─   │   │
│  │  [🔴] opencode│ -         │ -       │ 未安装    │ [安装]│   │
│  │  [🟡] claude  │ 1.0.0     │ 客户端  │ 可更新    │ [更新]│   │
│  │  [🟢] docker  │ 24.0.7    │ 系统全局│ 已安装    │ ─ ─   │   │
│  │  [⚪] rustc   │ -         │ -       │ 可选      │ [安装]│   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  状态图标说明:                                                   │
│  🟢 已安装 - 正常可用                                            │
│  🟡 可更新 - 有新版本可用                                        │
│  🔴 未安装 - 尚未安装                                            │
│  🟠 安装失败 - 上次安装出错                                      │
│  🔵 安装中 - 正在下载/安装                                       │
│  ⚪ 可选 - 未安装但不影响核心功能                                 │
│                                                                 │
│  来源图标说明:                                                   │
│  [系统] 系统全局安装                                             │
│  [客户端] 客户端隔离目录                                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

改进点

  1. 顶部增加总状态卡片,一目了然
  2. 使用颜色圆点代替文字状态
  3. 显示来源类型(系统/客户端)
  4. 可选依赖用灰色显示,不影响总状态统计

2.4.2 详细依赖信息卡片

Node.js 详情

┌───────────────────────────────────────────────────────────────┐
│  Node.js                                                       │
├───────────────────────────────────────────────────────────────┤
│                                                               │
│  状态: ✓ 已安装 (v20.10.0)                                    │
│                                                               │
│  来源: 系统全局 (/usr/local/bin/node)                         │
│        已存在于系统 PATH无需安装到客户端目录                  │
│                                                               │
│  版本信息:                                                     │
│    当前版本: v20.10.0                                         │
│    最新版本: v20.11.1 (LTS)                                   │
│    检查时间: 2026-01-29 10:30:00                              │
│                                                               │
│  安装路径:                                                     │
│    macOS: /usr/local/bin/node                                 │
│    Windows: C:\Program Files\nodejs\node.exe                  │
│    Linux: /usr/bin/node                                       │
│                                                               │
│  支持的平台:                                                    │
│    ✓ Windows x64                                              │
│    ✓ macOS x64 (Intel)                                        │
│    ✓ macOS arm64 (Apple Silicon)                              │
│    ✓ Linux x64                                                │
│                                                               │
│  操作:                                                         │
│       [检查更新]  [卸载]           需要管理员权限              │
│                                                               │
└───────────────────────────────────────────────────────────────┘

未安装依赖详情

┌───────────────────────────────────────────────────────────────┐
│  opencode (ACP Agent 工具)                                     │
├───────────────────────────────────────────────────────────────┤
│                                                               │
│  状态: ✗ 未安装                                                │
│                                                               │
│  用途: 运行 ACP 协议的 AI Agent                               │
│                                                               │
│  安装方式:                                                     │
│    npm install -g opencode                                    │
│                                                               │
│  安装目录 (客户端隔离):                                        │
│    ~/Library/Application Support/qiming-agent/tools/npm-global │
│                                                               │
│  前置依赖:                                                     │
│    ✓ Node.js >= 18.0.0                                        │
│                                                               │
│  ───────────────────────────────────────────────────────────  │
│                                                               │
│  安装进度:                                                     │
│    [──────────────────────────] 0%                            │
│                                                               │
│  安装日志:                                                     │
│    > npm install -g opencode                                  │
│    > fetching...                                              │
│                                                               │
│  ───────────────────────────────────────────────────────────  │
│                                                               │
│  错误信息:                                                     │
│    ✗ 网络连接超时,请检查网络设置                              │
│                                                               │
│  操作:                                                         │
│       [重试安装]  [手动安装指引]  [忽略]                       │
│                                                               │
└───────────────────────────────────────────────────────────────┘

2.4.3 手动安装指引

┌───────────────────────────────────────────────────────────────┐
│  手动安装 opencode                                             │
├───────────────────────────────────────────────────────────────┤
│                                                               │
│  如果自动安装失败,您可以手动安装:                            │
│                                                               │
│  步骤 1: 安装 Node.js (如果尚未安装)                           │
│    - 下载地址: https://nodejs.org/dist/v20.10.0/              │
│    - macOS: node-v20.10.0.pkg                                 │
│    - Windows: node-v20.10.0-x64.msi                           │
│    - Linux: node-v20.10.0-linux-x64.tar.xz                    │
│                                                               │
│  步骤 2: 安装 opencode                                         │
│    打开终端,执行:                                             │
│                                                               │
│      npm install -g opencode                                   │
│                                                               │
│  或指定安装目录:                                               │
│                                                               │
│      cd ~/Library/Application\ Support/qiming-agent/tools/     │
│      mkdir -p npm-global                                       │
│      npm config set prefix './npm-global'                     │
│      npm install -g opencode                                   │
│                                                               │
│  步骤 3: 验证安装                                              │
│                                                               │
│      which opencode                                            │
│      opencode --version                                        │
│                                                               │
│  完成后,点击"刷新状态"按钮重新检测。                          │
│                                                               │
│  ───────────────────────────────────────────────────────────  │
│                                                               │
│       [✓ 我已手动安装完成]  [✗ 取消]                          │
│                                                               │
└───────────────────────────────────────────────────────────────┘


2.5 Agent 状态显示

在状态栏显示当前 agent 进程的运行状态:

状态 显示示例 说明
无 agent Agent: - 未检测到 agent 进程
运行中 Agent: 2 运行中 有 2 个 agent 进程在运行
空闲 Agent: 空闲 agent 进程正常运行,等待任务
执行中 Agent: 执行中 (1/3) 1 个任务执行中,共 3 个进程
错误 Agent: ⚠️ 错误 agent 进程异常

状态栏显示位置

┌─────────────────────────────────────────────────────────────────────────────┐
│  [ID: 12345678]  ● 已连接 (P2P) - 32ms  │  Agent: 2 运行中  │  10:30:45  │
└─────────────────────────────────────────────────────────────────────────────┘

2.6 权限设置 Tab

2.6.1 权限检查列表

┌─────────────────────────────────────────────────────────────────┐
│  权限设置                                            [🔄 检查] │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  ⚠️  部分权限未授权,部分功能可能无法正常使用             │   │
│  │      需要 2/4 个权限                                     │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  🟢 辅助功能权限                                         │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    用途: 用于远程控制时模拟键盘鼠标输入                  │   │
│  │    状态: ✓ 已授权                                         │   │
│  │    [详细信息...]                                         │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  🟢 磁盘访问权限 (macOS)                                  │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    用途: 用于访问磁盘文件进行文件传输                    │   │
│  │    状态: ✓ 已授权                                         │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  🔴 屏幕录制权限                                         │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │    影响: 远程桌面功能无法使用                            │   │
│  │    用途: 用于远程桌面实时画面传输                        │   │
│  │    状态: ⚠️ 未授权                                        │   │
│  │                                                         │   │
│  │    [🚀 立即授权]                                         │   │
│  │                                                         │   │
│  │    设置路径:                                             │   │
│  │    macOS: 系统设置 → 隐私与安全性 → 屏幕录制             │   │
│  │            勾选 "qiming-agent"                            │   │
│  │    Windows: 系统 → 隐私与安全性 → 屏幕录制               │   │
│  │            启用 "qiming-agent"                            │   │
│  │    Linux: 设置 → 隐私 → 屏幕录制 (Wayland)               │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  🟢 麦克风权限                                           │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │    影响: 语音通话功能(可选)                            │   │
│  │    用途: 用于语音通话功能                                │   │
│  │    状态: ✓ 已授权                                         │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

改进点

  1. 顶部显示总体统计(需要 X/Y 个权限)
  2. 权限卡片按影响程度排序:核心权限在前,可选权限在后
  3. 每个权限卡片标注"影响"(功能影响说明)
  4. 颜色区分状态:🟢 正常、🔴 缺失、🟡 部分

2.6.2 权限检测逻辑

/// 权限管理器
struct PermissionManager {
    /// 检测所有权限状态
    fn check_all(&self) -> Vec<PermissionCheck> {
        vec![
            self.check_screen_capture(),
            self.check_accessibility(),
            self.check_full_disk_access(),
            self.check_microphone(),
        ]
    }

    /// 屏幕录制权限
    fn check_screen_capture(&self) -> PermissionCheck {
        PermissionCheck {
            permission: PermissionType::ScreenCapture,
            status: self.platform_check_screen_capture(),
            description: "用于远程桌面实时画面传输".to_string(),
            setup_instruction: match current_platform() {
                Platform::MacOS => "系统设置 → 隐私与安全性 → 屏幕录制 → 勾选 qiming-agent",
                Platform::Windows => "系统 → 隐私与安全性 → 屏幕录制 → 启用 qiming-agent",
                Platform::Linux => "设置 → 隐私 → 屏幕录制 (Wayland)",
            }.to_string(),
        }
    }
}

2.7 关于 Tab

┌─────────────────────────────────────────────────────────────────┐
│  关于                                                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│                    ┌─────────────────────┐                     │
│                    │                     │                     │
│                    │    [应用图标]       │                     │
│                    │                     │                     │
│                    └─────────────────────┘                     │
│                                                                 │
│                     qiming-agent                                │
│                     版本 1.0.0 (Build 20260129)                │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  构建信息:                                                       │
│    • Rust 版本: 1.83.0                                         │
│    • 构建时间: 2026-01-29 10:30:45                             │
│    • 构建平台: macOS arm64                                     │
│    • Commit: abc1234                                           │
│                                                                 │
│  技术支持:                                                       │
│    • 文档: https://docs.example.com                            │
│    • 问题反馈: https://github.com/example/issues               │
│    • 官方网站: https://example.com                             │
│                                                                 │
│  许可证: MIT License                                            │
│                                                                 │
│  ─────────────────────────────────────────────────────────────  │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │       [📤 导出日志]  [🔄 检查更新]  [ 系统信息]       │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.8 远程桌面 Tab远程控制客户端

注意:此 Tab 通过 feature remote-desktop 控制,默认关闭

2.8.1 界面功能

核心功能

  1. 查看被管理端远程桌面画面
  2. 发送键盘鼠标输入到被管理端
  3. 画质调节(流畅/标准/高清)
  4. 全屏/窗口模式切换
  5. 文件传输入口
  6. 连接状态显示

2.8.2 远程桌面布局

┌─────────────────────────────────────────────────────────────────────────────┐
│  远程桌面                                                         [□ 全屏] │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                     │   │
│  │                                                                     │   │
│  │                      被管理端屏幕实时画面                            │   │
│  │                                                                     │   │
│  │                                                                     │   │
│  │                    ┌─────────────────────────────────┐             │   │
│  │                    │                                 │             │   │
│  │                    │         1920 x 1080             │             │   │
│  │                    │                                 │             │   │
│  │                    └─────────────────────────────────┘             │   │
│  │                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  [📊 画质: 高清 ▼]  [📁 传输文件]  [🔒 锁定客户端]  [⏸ 暂停]  [✕ 断开]  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  连接信息: ● 已连接 (P2P) - 延迟: 32ms  - 客户端: Client-A (ID: 12345678) │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

2.8.3 画质调节

画质 分辨率 帧率 适用场景
流畅 1024x768 15 fps 低带宽/高延迟
标准 1280x720 24 fps 普通网络
高清 1920x1080 30 fps 高带宽

2.8.4 键盘快捷键

快捷键 功能
Ctrl + Alt + End 发送 Ctrl+Alt+Del
Esc 退出全屏
Ctrl + Alt + F 切换全屏
Ctrl + Alt + S 切换画质

2.9 Agent 聊天界面(可选功能)

注意:此 Tab 通过 feature chat-ui 控制,默认关闭

2.9.1 界面功能

核心功能

  1. 发送 prompt 到 Agent 执行
  2. 查看 Agent 执行过程中的消息输出
  3. 中止正在执行的任务
  4. 查看执行历史

2.9.2 聊天界面布局

┌─────────────────────────────────────────────────────────────────┐
│  Agent 聊天                                              [📝 新建] │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  会话历史:                                                │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │  [📁 会话 1]  2026-01-29 10:30                          │   │
│  │  [📁 会话 2]  2026-01-29 09:15                          │   │
│  │  [📁 会话 3]  2026-01-28 16:45                          │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │  🤖 Assistant                                           │   │
│  │  你好!我是你的 Agent 助手,有什么可以帮你的?           │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │  👤 用户                                10:30:45         │   │
│  │  请帮我写一个 Rust HTTP 服务器                          │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │  🤖 Assistant                                           │   │
│  │  ⟳ 正在执行...                                          │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │  [🔴 中止任务]                                          │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │  [📎]  [🔒 ******************__________________]  [➤ 发送] │   │
│  │       附件      Prompt 输入框                            │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.9.3 执行状态展示

状态 图标 说明
等待中 任务排队中
执行中 正在运行 Agent
输出中 📤 正在回传消息
完成 任务完成
错误 执行出错
已中止 用户取消

2.10 文件传输界面

2.10.1 发送文件流程

┌─────────────────────────────────────────────────────────────────┐
│  发送文件                                              [✕ 关闭] │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  选择文件:  [____________________]  [📁 浏览...]        │   │
│  │  或拖拽文件到此处                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  文件列表:                                               │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │  📄 report.pdf (2.5 MB)                                 │   │
│  │  🖼 screenshot.png (1.2 MB)                             │   │
│  │  📦 data.zip (15.8 MB)                                  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  目标客户端:                                            │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │  [Client-A (ID: 12345678)                    ▼]         │   │
│  │                                                         │   │
│  │  预计传输时间: ~30 秒                                    │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│                    [📤 发送]  [✕ 取消]                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.10.2 接收文件提示

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│                    ┌─────────────────────────┐                 │
│                    │     📥 收到文件         │                 │
│                    └─────────────────────────┘                 │
│                                                                 │
│  发送方: admin@example.com                                      │
│  文件: qiming-agent-installer.dmg (125 MB)                      │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  保存位置:                                               │   │
│  │  [________________________]  [📁 浏览...]               │   │
│  │  ~/Downloads/                                           │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│          [✓ 接受并保存]  [✗ 拒绝]  [📂 打开文件夹]             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.11 客户端升级

2.11.1 检查更新流程

┌─────────────────────────────────────────────────────────────────┐
│  关于 / 检查更新                                        [🔄 检查] │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │                    ┌─────────────────────┐             │   │
│  │                    │                     │             │   │
│  │                    │    [应用图标]       │             │   │
│  │                    │                     │             │   │
│  │                    └─────────────────────┘             │   │
│  │                                                         │   │
│  │                     qiming-agent                        │   │
│  │                     当前版本: 1.0.0                     │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  检查更新                                                │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    [🔄 检查更新]                                         │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  更新信息 (发现新版本 v1.1.0)                            │   │
│  │  ─────────────────────────────────────────────────────  │   │
│  │                                                         │   │
│  │    最新版本: v1.1.0 (2026-02-01)                        │   │
│  │    发布日期: 2026-02-01                                 │   │
│  │    文件大小: 125 MB                                     │   │
│  │                                                         │   │
│  │  更新内容:                                               │   │
│  │    • 新增远程桌面画质调节功能                           │   │
│  │    • 优化文件传输速度                                   │   │
│  │    • 修复已知问题                                        │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│          [⬇️ 下载更新]  [⏰ 稍后提醒]  [❌ 忽略此版本]          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.11.2 下载进度

┌─────────────────────────────────────────────────────────────────┐
│  正在下载更新...                                      [✕ 后台下载] │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │  qiming-agent v1.1.0                                     │   │
│  │                                                         │   │
│  │  [████████████████████░░░░░░░░░░░░░] 75%                │   │
│  │                                                         │   │
│  │  93.8 MB / 125 MB   -   2.5 MB/s   -   剩余 12 秒        │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│                    [❌ 取消下载]                                │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.11.3 安装更新

┌─────────────────────────────────────────────────────────────────┐
│                                                                 │
│                    ┌─────────────────────────┐                 │
│                    │     ✅ 下载完成         │                 │
│                    └─────────────────────────┘                 │
│                                                                 │
│  qiming-agent v1.1.0 已下载完成                                  │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                                                         │   │
│  │  [⬆️ 立即安装并重启]                                     │   │
│  │                                                         │   │
│  │  [⏰ 退出时安装]                                         │   │
│  │                                                         │   │
│  │  [❌ 稍后提醒]                                           │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  安装后应用将自动重启                                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.12 消息通知系统

2.12.1 通知类型

类型 图标 用途
信息 一般信息提示
成功 操作成功
警告 ⚠️ 需要注意的事项
错误 操作失败
Agent 🤖 Agent 执行状态

2.12.2 通知示例

┌─────────────────────────────────────────────────────────────────┐
│  状态栏                                                        │
├─────────────────────────────────────────────────────────────────┤
│  [ID: 12345678] ● 已连接 (P2P) - 32ms  │  Agent: 空闲  │  10:30:45│
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  通知区域 (顶部/右下角)                                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  ✅ 文件传输完成                                          │   │
│  │     report.pdf 已保存到 ~/Downloads/                    │   │
│  │     [✕]                                                  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  🤖 Agent 任务完成                                        │   │
│  │     "帮助我写代码" - 执行完成                            │   │
│  │     [查看结果]  [✕]                                      │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  ⚠️  依赖需要更新                                         │   │
│  │     Node.js v20.10.0 → v20.11.1                         │   │
│  │     [立即更新]  [稍后]  [✕]                              │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

2.13 系统托盘菜单

┌─────────────────────────────────────────────────┐
│                    📌 qiming-agent                │
├─────────────────────────────────────────────────┤
│                                                 │
│  显示窗口              [Show Window]            │
│                                                 │
│  ─────────────────────────────────────────────  │
│                                                 │
│  状态: ● 已连接 (P2P)                           │
│  延迟: 32ms                                     │
│  ID: 12345678                                  │
│                                                 │
│  ─────────────────────────────────────────────  │
│                                                 │
│  Agent 状态: 空闲                                │
│                                                 │
│  ─────────────────────────────────────────────  │
│                                                 │
│  设置...              [Settings]                │
│  依赖管理             [Dependencies]            │
│  远程桌面             [Remote Desktop]          │
│                                                 │
│  ─────────────────────────────────────────────  │
│                                                 │
│  关于                 [About]                   │
│                                                 │
│  ─────────────────────────────────────────────  │
│                                                 │
│  退出                  [Quit]                   │
│                                                 │
└─────────────────────────────────────────────────┘

3. 组件选型与使用

3.1 组件使用总览表

功能需求 选用的 Rust 库 使用方式
UI 框架
窗口、渲染、事件 gpui git 依赖
基础 UI 组件 gpui-component path 依赖
系统托盘
托盘图标 tray-icon path 依赖
菜单系统 muda path 依赖
开机自启动
自启动管理 auto-launch path 依赖
通信协议
P2P/中继连接 qiming-rustdesk rlib 静态链接
信令服务 rustdesk-server 服务器端使用
远程桌面
屏幕捕获 scrap (qiming-rustdesk) rlib 静态链接
输入模拟 enigo path 依赖
Agent 运行时
Agent 生命周期 agent_runner (rcoder) path 依赖
打包发布
跨平台打包 cargo-packager path 依赖
工具库
异步运行时 tokio crates.io
配置管理 serde + toml crates.io
日志 tracing crates.io
错误处理 thiserror crates.io

3.2 各组件详细使用方式

3.2.1 qiming-rustdesk

Cargo.toml 配置

[dependencies]
librustdesk = { path = "../vendors/qiming-rustdesk" }

使用模块

模块 功能 关键类型/方法
rendezvous_mediator 连接管理 RendezvousMediator::new(), login(), logout()
hbb_common::conn_type 连接类型 ConnectionType::P2P, ConnectionType::Relay
hbb_common::latency 延迟检测 Latency::measure()
platform::input_service 输入模拟 InputService::new()
platform::scrap 屏幕捕获 Scrap::new(), Scrap::capture()

连接流程

// 1. 创建 rendezvous mediator
let mediator = RendezvousMediator::new();

// 2. 配置服务器地址
mediator.set_id_server(&config.id_server);
mediator.set_relay_server(&config.relay_servers);

// 3. 登录
let login_result = mediator.login().await?;

// 4. 处理连接结果
match login_result.conn_type {
    ConnectionType::P2P => {
        // P2P 直连成功
    }
    ConnectionType::Relay => {
        // 使用中继模式
    }
}

3.2.2 gpui-component

核心组件使用表

组件 用途 代码示例
Root 窗口根容器 Root::new(window)
TabPanel Tab 切换 TabPanel::new().tabs(...)
Input 文本输入 Input::new(text, on_change)
PasswordInput 密码输入 PasswordInput::new(text)
Button 按钮 Button::new("id", cx, handler)
IconButton 图标按钮 IconButton::new("id", IconName::Copy)
Switch 开关 Switch::new(checked, on_change)
Select 下拉选择 Select::new(items, selected)
DataTable 表格 DataTable::new().columns(...)
Modal 模态框 Modal::new(title, content)
Notification 通知 Notification::info(message)
Icon 图标 Icon::new(name)
Badge 标签 Badge::new(text).variant(...)
ProgressBar 进度条 ProgressBar::new(progress)
StatusBar 状态栏 StatusBar::new()
Clipboard 剪贴板 cx.write_to_clipboard()

3.2.3 tray-icon + muda

use tray_icon::{TrayIconBuilder, icon::Icon, TrayIconEvent};
use muda::{Menu, MenuItem, ContextMenu};

let menu = ContextMenu::new()
    .add_item(&MenuItem::with_id("show", "显示窗口", true, None)?)?
    .add_separator()
    .add_item(&MenuItem::with_id("settings", "设置...", true, None)?)?
    .add_item(&MenuItem::with_id("about", "关于", true, None)?)?
    .add_separator()
    .add_item(&MenuItem::with_id("quit", "退出", true, None)?)?;

let _tray_icon = TrayIconBuilder::new()
    .with_icon(icon)
    .with_tooltip("qiming-agent")
    .with_menu(Box::new(menu))
    .with_menu_on_left_click(true)
    .build()?;

3.2.4 auto-launch

use auto_launch::{AutoLaunch, AutoLaunchBuilder, MacOSLaunchMode};

let auto = AutoLaunchBuilder::new()
    .set_app_name("qiming-agent")
    .set_app_path("/Applications/qiming-agent.app")
    .set_macos_launch_mode(MacOSLaunchMode::SMAppService)
    .build()?;

// 检查状态
let is_enabled = auto.is_enabled().await?;

// 启用
auto.enable().await?;

// 禁用
auto.disable().await?;

3.2.5 enigo

use enigo::{Enigo, Key, Button, Direction, Coordinate};

let mut enigo = Enigo::new();

// 键盘输入
enigo.key(Key::Layout('a'), Direction::Click)?;

// 组合键 Ctrl+C
enigo.key(Key::Control, Direction::Down)?;
enigo.key(Key::Layout('c'), Direction::Click)?;
enigo.key(Key::Control, Direction::Up)?);

// 鼠标移动到绝对位置
enigo.move_mouse(100, 200, Coordinate::Absolute)?;

// 鼠标点击
enigo.button(Button::Left, Direction::Click)?;

// 滚轮滚动
enigo.scroll(100, enigo::Axis::Vertical)?;

3.2.6 rcoder/agent_runner直接调用不使用 gRPC

使用方式:客户端不运行 gRPC 服务,直接通过 path 依赖调用 agent_runner 的函数接口。

use agent_runner::{
    start_agent, cancel_agent, get_agent_status,
    AgentRequest, AgentOutput, AgentStatus,
};

// 1. 启动 Agent 任务(返回异步迭代器,流式输出)
let mut output_stream = start_agent(AgentRequest {
    prompt: "帮助我写代码".to_string(),
    work_dir: PathBuf::from("./workspace"),
    env: vec![("KEY".to_string(), "value".to_string())],
    timeout: Some(Duration::from_secs(300)),
}).await?;

// 2. 处理输出流
while let Some(output) = output_stream.next().await {
    match output.output_type {
        // 标准输出
        AgentOutputType::Stdout => {
            println!("{}", output.content);
        }
        // 标准错误
        AgentOutputType::Stderr => {
            eprintln!("[错误] {}", output.content);
        }
        // 状态更新
        AgentOutputType::Status => {
            println!("[状态] {}", output.content);
        }
        // 执行完成
        AgentOutputType::Complete => {
            println!("[完成] {}", output.content);
        }
        // 执行错误
        AgentOutputType::Error => {
            eprintln!("[执行错误] {}", output.content);
        }
    }
}

// 3. 取消任务
cancel_agent(session_id).await?;

// 4. 查询状态
let status = get_agent_status(session_id).await?;
match status {
    AgentStatus::Idle => println!("空闲"),
    AgentStatus::Active => println!("执行中"),
    AgentStatus::Pending => println!("等待中"),
    AgentStatus::Terminating => println!("停止中"),
    AgentStatus::Error => println!("出错"),
}

核心函数接口

函数 返回类型 说明
start_agent(request) AsyncIter<AgentOutput> 启动任务,流式返回输出
cancel_agent(session_id) Result<()> 取消正在执行的任务
get_agent_status(session_id) Result<AgentStatus> 查询任务状态
list_active_agents() Vec<SessionInfo> 列出所有活跃任务

4. 核心模块设计

4.1 应用状态管理

结构体/枚举 说明
AppState 全局应用状态Tab 导航、连接状态、配置)
ActiveTab 当前激活的 Tab
ConnectionState 连接状态Disconnected/Connecting/Connected/Error
ConnectionMode 连接模式P2P/Relay
SettingsSubTab 设置 Tab 子页面
PasswordChangeState 密码修改状态

4.2 配置管理

结构体/枚举 说明
AppConfig 应用配置(服务器/安全/常规/外观/日志)
ServerConfig 服务器地址配置ID服务器/中继服务器/API服务器
SecurityConfig 安全设置(密码策略、自动登出)
GeneralConfig 常规设置(自启动、语言、启动行为)
AppearanceConfig 外观设置(主题、工作目录)
LoggingConfig 日志设置(级别、文件路径)

4.3 依赖管理

结构体/枚举 说明
DependencyManager 依赖管理器
DependencyInfo 依赖信息(名称、版本、来源)
DependencySource 来源类型System/Client/NotInstalled
DependencyStatus 安装状态Installed/UpdateAvailable/NotInstalled/Installing/Failed

4.4 权限管理

结构体/枚举 说明
PermissionManager 权限管理器
PermissionCheck 权限检查项
PermissionType 权限类型ScreenCapture/Accessibility/FullDiskAccess/Microphone
PermissionStatus 权限状态Granted/Denied/NotDetermined/Restricted

4.5 跨平台目录管理

使用 dirs 库获取各平台标准目录:

函数 macOS Windows Linux
data_dir() ~/Library/Application Support/ %LOCALAPPDATA% ~/.local/share/
config_dir() ~/Library/Application Support/ %LOCALAPPDATA% ~/.config/
cache_dir() ~/Library/Caches/ %LOCALAPPDATA% ~/.cache/

关键路径

路径 macOS Windows Linux
应用数据 ~/Library/Application Support/qiming-agent/ %LOCALAPPDATA%\qiming-agent\ ~/.local/share/qiming-agent/
工具目录 tools/ tools\ tools/
Node.js tools/node/ tools\node\ tools/node/
npm 全局 tools/npm-global/ tools\npm-global\ tools/npm-global/

5. 通信协议设计

5.1 Protobuf 消息类型

消息 ID 类型名称 用途
100 HandshakeRequest 握手请求
101 HandshakeResponse 握手响应
102 HeartbeatPing 心跳
103 HeartbeatPong 心跳响应
200 AgentTaskRequest Agent 任务请求
201 AgentTaskResponse Agent 任务响应
202 AgentTaskOutput Agent 任务输出
203 AgentTaskCancel 取消任务
300 FileTransferRequest 文件传输请求
301 FileTransferChunk 文件分片
302 FileTransferComplete 传输完成
400 ScreenCaptureRequest 屏幕捕获请求
401 ScreenCaptureFrame 屏幕帧数据
402 InputEvent 输入事件
500 UpdatePasswordRequest 更新密码请求
501 UpdatePasswordResponse 更新密码响应

5.2 连接流程

1. 客户端 → data-server: HandshakeRequest
   - 包含客户端信息OS 版本、应用版本等)
   - 包含协议版本

2. data-server → 客户端: HandshakeResponse
   - 成功: 包含分配的 Client ID、连接模式
   - 失败: 包含错误信息

3. 双方保持心跳 (间隔 5 秒)
   - HeartbeatPing → HeartbeatPong
   - 包含延迟信息

4. 传输数据
   - 根据消息类型路由到对应处理器

5.3 完整消息流转流程Browser → Agent 执行)

┌─────────────────────────────────────────────────────────────────────────────────────┐
│                              完整消息流转流程                                         │
└─────────────────────────────────────────────────────────────────────────────────────┘

  浏览器                    agent-server-admin                  data-server          agent-client
    │                            │                                 │                    │
    │  1. 用户输入 Prompt         │                                 │                    │
    │ ──────────────────────────>│                                 │                    │
    │                            │                                 │                    │
    │                            │  2. 通过 WebSocket 发送到 data-server │
    │                            │────────────────────────────────>│                    │
    │                            │                                 │                    │
    │                            │                                 │  3. 路由到目标客户端
    │                            │                                 │───────────────────>│
    │                            │                                 │                    │
    │                            │                                 │                    │  4. Agent 执行
    │                            │                                 │                    │───> Agent Runner
    │                            │                                 │                    │
    │                            │                                 │  5. 输出回传       │
    │                            │                                 │<───────────────────│
    │                            │                                 │                    │
    │  6. SSE 推送结果            │  6. SSE 推送结果                 │                    │
    │<───────────────────────────│────────────────────────────────│                    │
    │                            │                                 │                    │
    │  7. 浏览器实时显示          │                                 │                    │
    │    (流式输出)              │                                 │                    │
    │                            │                                 │                    │

详细步骤说明

步骤 发送方 接收方 消息类型 说明
1 浏览器 agent-server-admin HTTP POST 用户提交 prompt
2 agent-server-admin data-server WebSocket + Protobuf 转发任务请求
3 data-server agent-client 路由转发 + Protobuf 根据 Client ID 定位目标
4 agent-client agent_runner 直接调用 解析 protobuf调用内部逻辑执行
5 agent-client data-server WebSocket + Protobuf 流式输出回传
6 data-server agent-server-admin WebSocket + Protobuf 转发输出
7 agent-server-admin 浏览器 SSE 流式推送结果
8 浏览器 用户 渲染 实时显示执行过程

5.4 客户端 Agent 执行机制

设计原则:客户端不运行 gRPC 服务端,直接解析 protobuf 消息后调用 agent_runner 内部逻辑。

agent-client 内部架构
┌─────────────────────────────────────────────────────────────────┐
│                         agent-client                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────────┐    ┌─────────────────────┐            │
│  │  Protobuf 消息解析   │───▶│  消息路由器          │            │
│  │  (消息反序列化)      │    │  (根据 message_id)  │            │
│  └─────────────────────┘    └──────────┬──────────┘            │
│                                        │                        │
│                                        ▼                        │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │                     任务处理器                           │   │
│  │  ┌─────────────────────────────────────────────────┐   │   │
│  │  │  ChatHandler (对应 gRPC Chat)                   │   │   │
│  │  │  - 解析 AgentTaskRequest                        │   │   │
│  │  │  - 调用 agent_runner::start_agent()            │   │   │
│  │  │  - 等待响应返回 ChatResponse                    │   │   │
│  │  └─────────────────────────────────────────────────┘   │   │
│  │  ┌─────────────────────────────────────────────────┐   │   │
│  │  │  SubscribeHandler (对应 gRPC SubscribeProgress)│   │   │
│  │  │  - 订阅 agent_runner 输出流                     │   │   │
│  │  │  - 转换为 ProgressEvent 推送                    │   │   │
│  │  │  - 支持心跳保活30秒间隔                      │   │   │
│  │  └─────────────────────────────────────────────────┘   │   │
│  │  ┌─────────────────────────────────────────────────┐   │   │
│  │  │  CancelHandler (对应 gRPC CancelSession)        │   │   │
│  │  │  - 解析 AgentTaskCancel                         │   │   │
│  │  │  - 调用 agent_runner::cancel()                  │   │   │
│  │  │  - 返回 CancelResponse                          │   │   │
│  │  └─────────────────────────────────────────────────┘   │   │
│  │  ┌─────────────────────────────────────────────────┐   │   │
│  │  │  StatusHandler (对应 gRPC GetStatus)            │   │   │
│  │  │  - 解析 GetStatusRequest                        │   │   │
│  │  │  - 调用 agent_runner::get_status()             │   │   │
│  │  │  - 返回 GetStatusResponse                       │   │   │
│  │  └─────────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────────┘   │
│                            │                                   │
│                            ▼                                   │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │               agent_runner (rcoder)                      │   │
│  │                                                         │   │
│  │  使用方式path 依赖,直接调用函数接口                    │   │
│  │  核心依赖模块:                                          │   │
│  │  ├── AgentWorkerManager  ─ 任务调度与执行               │   │
│  │  ├── AGENT_REGISTRY       ─ 任务状态注册表              │   │
│  │  ├── SESSION_CACHE        ─ 会话缓存与进度推送           │   │
│  │  └── LocalSetAgentRequest ─ 任务请求封装                │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.4.1 gRPC 方法与客户端调用的映射关系

gRPC 方法 客户端调用函数 说明
Chat start_agent(request) 启动 Agent 任务,返回响应
SubscribeProgress 订阅 SESSION_CACHE 流式推送进度事件
CancelSession cancel_agent(session_id) 取消正在执行的任务
GetStatus get_agent_status(project_id) 查询任务状态
StopAgent stop_agent(project_id) 停止 Agent 进程
GetContainerStatus get_container_status() 查询容器状态
GetVncStatus get_vnc_status() 查询 VNC 服务状态

5.4.2 内部状态管理

agent_runner 使用 DashMap 管理全局状态,客户端直接调用:

// AGENT_REGISTRY - 任务注册表
// key: project_id, value: AgentInfo { status, session_id, cancel_tx, ... }
AGENT_REGISTRY.set_pending(&project_id);           // 设置等待中
AGENT_REGISTRY.get_agent_info(&project_id);        // 查询状态
AGENT_REGISTRY.remove_by_project(&project_id);     // 移除任务

// SESSION_CACHE - 会话缓存(用于进度推送)
// key: session_id, value: SessionData { message_tx, ... }
SESSION_CACHE.get(&session_id);                    // 获取会话
SESSION_CACHE.insert(session_id, session_data);    // 创建会话

5.4.3 任务状态流转

Idle ──────► Pending ──────► Active ──────► Terminating ──────► Idle
  ▲              │              │              │                   │
  │              │              │              │                   │
  │              ▼              │              │                   │
  │        任务已接收          │              │                   │
  │                           ▼              │                   │
  │                      正在执行            │                   │
  │                                           │                   │
  │                                           ▼                   │
  │                                     正在停止                   │
  │                                                           │
  └───────────────────────────────────────────────────────────┘
                              │
                              ▼
                        Error (执行出错)

5.4.4 完整的 Chat 处理流程(客户端直接调用)

use agent_runner::{
    start_agent, AgentRequest, AgentStatus,
    AGENT_REGISTRY, SESSION_CACHE,
};
use crate::message_converter::{to_agent_request, to_chat_response};

pub async fn handle_chat(grpc_request: ChatRequest) -> Result<ChatResponse, Status> {
    let project_id = grpc_request.project_id;
    let session_id = grpc_request.session_id;

    // 1. 检查 Agent 状态(防止并发)
    if let Some(info) = AGENT_REGISTRY.get_agent_info(&project_id) {
        if info.status == AgentStatus::Active || info.status == AgentStatus::Pending {
            return Ok(ChatResponse {
                success: false,
                error_code: Some("ERR_AGENT_BUSY".to_string()),
                error: Some("Agent 正在执行任务,请等待当前任务完成".to_string()),
                ..Default::default()
            });
        }
    }

    // 2. 预注册 Pending 状态
    AGENT_REGISTRY.set_pending(&project_id);

    // 3. 转换 gRPC 请求为 AgentRequest
    let agent_request = to_agent_request(grpc_request);

    // 4. 启动任务(返回异步迭代器)
    let mut output_stream = start_agent(agent_request).await?;

    // 5. 处理输出流
    let mut session_id = None;
    while let Some(output) = output_stream.next().await {
        // 发送到 SESSION_CACHE供进度订阅使用
        if let Some(sid) = &output.session_id {
            session_id = Some(sid.clone());
            SESSION_CACHE.push_message(sid, output.clone());
        }
    }

    // 6. 构建响应
    Ok(to_chat_response(&project_id, session_id))
}

5.4.5 进度推送实现(对应 gRPC SubscribeProgress

pub async fn subscribe_progress(session_id: String) -> impl Stream<Item = ProgressEvent> {
    let (tx, rx) = mpsc::channel(100);

    tokio::spawn(async move {
        // 从 SESSION_CACHE 订阅消息
        if let Some(session_data) = SESSION_CACHE.get(&session_id) {
            let (mut msg_rx, _) = session_data.create_new_connection(100).await;

            loop {
                tokio::select! {
                    // 接收进度消息
                    Some(msg) = msg_rx.recv() => {
                        let event = ProgressEvent {
                            message_type: format!("{:?}", msg.message_type),
                            sub_type: msg.sub_type,
                            payload: serde_json::to_string(&msg.data).unwrap_or_default(),
                            timestamp: msg.timestamp.timestamp_millis(),
                        };
                        if tx.send(Ok(event)).await.is_err() {
                            break; // 客户端断开
                        }
                    }
                    // 心跳保活30秒
                    _ = tokio::time::sleep(Duration::from_secs(30)) => {
                        let heartbeat = ProgressEvent {
                            message_type: "Heartbeat".to_string(),
                            sub_type: "ping".to_string(),
                            payload: r#"{"type":"heartbeat"}"#.to_string(),
                            timestamp: Utc::now().timestamp_millis(),
                        };
                        if tx.send(Ok(heartbeat)).await.is_err() {
                            break;
                        }
                    }
                }
            }
        }
    });

    ReceiverStream::new(rx)
}

5.4.6 取消任务实现(对应 gRPC CancelSession

pub async fn cancel_session(
    project_id: String,
    session_id: String,
    reason: String,
) -> Result<CancelResponse> {
    // 1. 查询 Agent 状态
    let agent_info = match AGENT_REGISTRY.get_agent_info(&project_id) {
        Some(info) => info,
        None => return Ok(CancelResponse {
            success: true,
            message: Some("任务不存在或已完成".to_string()),
        }),
    };

    // 2. 发送取消通知
    let cancel_tx = agent_info.cancel_tx.clone();
    let cancel_notification = CancelNotification::new(SessionId::new(session_id.as_str()));
    let (result_tx, result_rx) = oneshot::channel();

    cancel_tx.send(CancelNotificationRequestWrapper {
        cancel_notification,
        result_tx,
    })?;

    // 3. 等待取消结果30秒超时
    match tokio::time::timeout(Duration::from_secs(30), result_rx).await {
        Ok(Ok(result)) => {
            if result.is_success() {
                // 清理资源
                SESSION_CACHE.remove(&session_id);
                AGENT_REGISTRY.clear_pending_if_exists(&project_id);
            }
            Ok(CancelResponse {
                success: result.is_success(),
                message: result.message(),
            })
        }
        _ => Ok(CancelResponse {
            success: false,
            message: Some("取消超时".to_string()),
        }),
    }
}

5.4.7 状态查询实现(对应 gRPC GetStatus

pub async fn get_status(project_id: String, session_id: String) -> GetStatusResponse {
    // 通过 session_id 反查 project_id
    let actual_project_id = if project_id.is_empty() {
        AGENT_REGISTRY.get_project_by_session(&session_id)
            .unwrap_or_else(|| project_id)
    } else {
        project_id
    };

    // 查询状态
    if let Some(info) = AGENT_REGISTRY.get_agent_info(&actual_project_id) {
        let status_str = match info.status {
            AgentStatus::Pending | AgentStatus::Active | AgentStatus::Terminating => "busy",
            AgentStatus::Idle => "idle",
        };
        GetStatusResponse {
            status: status_str.to_string(),
            is_found: true,
        }
    } else {
        GetStatusResponse {
            status: "idle".to_string(),
            is_found: false,
        }
    }
}

5.4.8 错误码定义

错误码 说明 处理方式
ERR_AGENT_BUSY (9010) Agent 正忙 等待当前任务完成
ERR_TIMEOUT 执行超时 检查超时设置
ERR_PERMISSION_DENIED 权限不足 检查目录/文件权限
ERR_WORKER_STOPPED Worker 已停止 重启 Agent 服务

5.5 业务数据通道设计

设计目标:复用 qiming-rustdesk 的底层 P2P/Relay 数据通道,在上层创建独立的业务数据通道,用于传输 Agent 任务请求/响应等业务数据。

架构概览

qiming-rustdesk 数据通道
┌─────────────────────────────────────────────────────────────────────────────┐
│                      物理连接 (TCP/WebSocket/P2P)                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │              Message Router (消息路由器)                              │   │
│  │  ─────────────────────────────────────────────────────────────────  │   │
│  │                                                                       │   │
│  │  ┌─────────────┬─────────────┬─────────────┬─────────────┐          │   │
│  │  │ 视频通道    │ 音频通道    │ 文件传输    │ 业务数据    │          │   │
│  │  │ Video(0x01) │ Audio(0x02) │ File(0x04)  │ Biz(0x08)   │          │   │
│  │  │ 远程桌面    │ 远程桌面    │ 文件传输    │ Agent/自定义│          │   │
│  │  └─────────────┴─────────────┴─────────────┴─────────────┘          │   │
│  │                                                                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

5.5.1 业务通道 API 设计

在 qiming-rustdesk 中新增 business_channel 模块:

// vendors/qiming-rustdesk/src/business_channel.rs

use std::sync::Arc;
use tokio::sync::{mpsc, RwLock};
use hbb_common::{ResultType, Stream};

/// 业务通道 ID固定值
pub const BUSINESS_CHANNEL_ID: u32 = 0xBIZ;

/// 业务消息类型
#[derive(Debug, Clone)]
pub enum BusinessMessageType {
    /// Agent 任务请求
    AgentTask = 0x01,
    /// Agent 任务响应
    AgentResponse = 0x02,
    /// Agent 进度推送
    AgentProgress = 0x03,
    /// Agent 取消请求
    AgentCancel = 0x04,
    /// 自定义业务消息
    Custom(u32),
}

/// 业务通道配置
#[derive(Clone)]
pub struct BusinessChannelConfig {
    /// 通道 ID固定为 BUSINESS_CHANNEL_ID
    pub channel_id: u32,
    /// 消息发送超时(毫秒)
    pub send_timeout_ms: u64,
    /// 消息接收缓冲区大小
    pub buffer_size: usize,
}

impl Default for BusinessChannelConfig {
    fn default() -> Self {
        Self {
            channel_id: BUSINESS_CHANNEL_ID,
            send_timeout_ms: 5000,
            buffer_size: 100,
        }
    }
}

/// 业务数据通道
#[derive(Clone)]
pub struct BusinessChannel {
    /// 底层 Stream
    stream: Arc<dyn Stream + Send + Sync>,
    /// 配置
    config: BusinessChannelConfig,
    /// 消息发送器
    sender: Arc<RwLock<Option<mpsc::UnboundedSender<Vec<u8>>>>>,
    /// 消息接收器(用于内部路由)
    receiver: Arc<RwLock<Option<mpsc::UnboundedReceiver<Vec<u8>>>>>,
}

impl BusinessChannel {
    /// 从底层 Stream 创建业务通道
    pub async fn from_stream(
        stream: Arc<dyn Stream + Send + Sync>,
        config: Option<BusinessChannelConfig>,
    ) -> ResultType<Self> {
        let config = config.unwrap_or_default();

        let (tx, rx) = mpsc::unbounded_channel(config.buffer_size);

        Ok(Self {
            stream,
            config,
            sender: Arc::new(RwLock::new(Some(tx))),
            receiver: Arc::new(RwLock::new(Some(rx))),
        })
    }

    /// 发送业务消息
    pub async fn send(&self, message_type: BusinessMessageType, payload: &[u8]) -> ResultType<()> {
        let mut sender = self.sender.write().await;
        if let Some(tx) = sender.as_ref() {
            // 封装消息
            let mut data = Vec::with_capacity(1 + 4 + payload.len());
            data.push(message_type as u8);
            data.extend_from_slice(&(payload.len() as u32).to_le_bytes());
            data.extend_from_slice(payload);

            tx.send(data)?;
            Ok(())
        } else {
            Err(anyhow::anyhow!("Business channel sender not initialized"))
        }
    }

    /// 接收业务消息
    pub async fn recv(&self) -> ResultType<Option<(BusinessMessageType, Vec<u8>)>> {
        let mut receiver = self.receiver.write().await;
        if let Some(rx) = receiver.as_ref() {
            match rx.recv().await {
                Some(data) => {
                    if data.len() < 5 {
                        return Err(anyhow::anyhow!("Invalid message format"));
                    }
                    let msg_type = BusinessMessageType::Custom(data[0] as u32);
                    let len = u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as usize;
                    let payload = if data.len() > 5 {
                        data[5..5 + len].to_vec()
                    } else {
                        Vec::new()
                    };
                    Ok(Some((msg_type, payload)))
                }
                None => Ok(None),
            }
        } else {
            Err(anyhow::anyhow!("Business channel receiver not initialized"))
        }
    }

    /// 订阅业务消息流
    pub fn subscribe(&self) -> mpsc::UnboundedReceiver<(BusinessMessageType, Vec<u8>)> {
        let (tx, rx) = mpsc::unbounded_channel(self.config.buffer_size);
        let mut sender = self.sender.write().await;

        // 创建一个内部任务来转发消息
        let mut inner_rx = {
            let mut receiver = self.receiver.write().await;
            receiver.take()
        };

        if let Some(inner_rx_val) = inner_rx {
            tokio::spawn(async move {
                let mut tx = tx;
                let mut inner_rx = inner_rx_val;
                while let Some(data) = inner_rx.recv().await {
                    if data.len() < 5 {
                        continue;
                    }
                    let msg_type = BusinessMessageType::Custom(data[0] as u32);
                    let len = u32::from_le_bytes([data[1], data[2], data[3], data[4]]) as usize;
                    let payload = if data.len() > 5 {
                        data[5..5 + len].to_vec()
                    } else {
                        Vec::new()
                    };
                    if tx.send((msg_type, payload)).is_err() {
                        break;
                    }
                }
            });
        }

        rx
    }

    /// 检查通道是否有效
    pub fn is_valid(&self) -> bool {
        self.sender.read().await.is_some()
    }
}

5.5.2 业务通道消息格式

// vendors/qiming-rustdesk/proto/business.proto

syntax = "proto3";

package qiming_business;

// 业务消息封装(外层)
message BusinessEnvelope {
    uint32 channel_id = 1;     // 固定为 0xBIZ (十进制 11)
    uint32 message_type = 2;   // 消息类型
    bytes payload = 3;         // 业务数据(可以是任何 protobuf 或 JSON
    int64 timestamp = 4;       // 时间戳(毫秒)
}

// 业务消息类型枚举
enum BusinessMessageType {
    BUSINESS_MESSAGE_TYPE_UNKNOWN = 0;
    BUSINESS_MESSAGE_TYPE_AGENT_TASK = 1;      // Agent 任务请求
    BUSINESS_MESSAGE_TYPE_AGENT_RESPONSE = 2;  // Agent 任务响应
    BUSINESS_MESSAGE_TYPE_AGENT_PROGRESS = 3;  // Agent 进度推送
    BUSINESS_MESSAGE_TYPE_AGENT_CANCEL = 4;    // 取消任务
    BUSINESS_MESSAGE_TYPE_CUSTOM = 100;        // 自定义消息起始
}

// Agent 任务请求payload
message AgentTaskPayload {
    string task_id = 1;                    // 任务唯一 ID
    string prompt = 2;                     // 执行指令
    repeated string attachments = 3;       // 附件列表
    int32 timeout_seconds = 4;             // 超时时间
    map<string, string> env = 5;           // 环境变量
    string work_dir = 6;                   // 工作目录
}

// Agent 任务响应payload
message AgentResponsePayload {
    string task_id = 1;
    bool success = 2;
    string error = 3;                      // 错误信息
    string result = 4;                     // 执行结果
    string session_id = 5;                 // 会话 ID
}

// Agent 进度推送payload
message AgentProgressPayload {
    string task_id = 1;
    ProgressType type = 2;
    string content = 3;                    // 输出内容
    int64 timestamp = 4;

    enum ProgressType {
        PROGRESS_TYPE_UNKNOWN = 0;
        PROGRESS_TYPE_STDOUT = 1;
        PROGRESS_TYPE_STDERR = 2;
        PROGRESS_TYPE_STATUS = 3;
        PROGRESS_TYPE_COMPLETE = 4;
        PROGRESS_TYPE_ERROR = 5;
    }
}

// 取消任务请求payload
message AgentCancelPayload {
    string task_id = 1;
    string reason = 2;
}

5.5.3 在 agent-client 中使用业务通道

// agent-client/src/core/business_channel.rs

use librustdesk::{BusinessChannel, BusinessChannelConfig, BUSINESS_CHANNEL_ID};
use crate::message_converter::{to_agent_request, to_agent_response};

/// 业务通道管理器
pub struct BusinessChannelManager {
    /// 业务通道实例
    channel: Option<BusinessChannel>,
    /// 配置
    config: BusinessChannelConfig,
}

impl BusinessChannelManager {
    pub fn new() -> Self {
        Self {
            channel: None,
            config: BusinessChannelConfig::default(),
        }
    }

    /// 从 qiming-rustdesk 的 Stream 创建业务通道
    pub async fn create_from_stream(&mut self, stream: Arc<dyn Stream + Send + Sync>) -> Result<()> {
        self.channel = Some(BusinessChannel::from_stream(stream, Some(self.config.clone())).await?);
        Ok(())
    }

    /// 发送 Agent 任务请求
    pub async fn send_agent_task(&self, task: AgentTaskRequest) -> Result<()> {
        if let Some(channel) = &self.channel {
            let payload = to_agent_request_payload(task);
            channel.send(BusinessMessageType::AgentTask, &payload).await?;
        }
        Ok(())
    }

    /// 接收业务消息并处理
    pub async fn handle_messages(&self) -> Result<()> {
        if let Some(channel) = &self.channel {
            let mut subscriber = channel.subscribe();
            while let Some((msg_type, payload)) = subscriber.recv().await {
                match msg_type {
                    BusinessMessageType::AgentTask => {
                        self.handle_agent_task(payload).await?;
                    }
                    BusinessMessageType::AgentCancel => {
                        self.handle_cancel(payload).await?;
                    }
                    _ => {
                        warn!("Unknown business message type: {:?}", msg_type);
                    }
                }
            }
        }
        Ok(())
    }

    /// 处理 Agent 任务
    async fn handle_agent_task(&self, payload: Vec<u8>) -> Result<()> {
        let task: AgentTaskPayload = protobuf::decode(&payload)?;
        let request = to_agent_request(task);

        // 调用 agent_runner 执行任务
        let mut output_stream = start_agent(request).await?;

        // 订阅输出并发送进度
        while let Some(output) = output_stream.next().await {
            let progress = to_progress_payload(&output);
            if let Some(channel) = &self.channel {
                channel.send(BusinessMessageType::AgentProgress, &progress).await?;
            }
        }

        Ok(())
    }
}

5.5.4 与 qiming-rustdesk 的集成方式

修改点:在 qiming-rustdesk 中新增 business_channel.rs 模块,导出 BusinessChannel 和相关类型。

// vendors/qiming-rustdesk/src/lib.rs 新增
mod business_channel;
pub use self::business_channel::*;

使用方式

// agent-client 使用示例
use librustdesk::{BusinessChannel, BUSINESS_CHANNEL_ID};

// 1. 从 qiming-rustdesk 获取已连接的 Stream
let stream = rustdesk_connection.get_stream();

// 2. 创建业务通道
let biz_channel = BusinessChannel::from_stream(
    Arc::new(stream),
    None,  // 使用默认配置
).await?;

// 3. 发送业务消息
biz_channel.send(
    BusinessMessageType::AgentTask,
    &agent_task_payload,
).await?;

// 4. 接收业务消息
let (msg_type, payload) = biz_channel.recv().await?;

5.5.5 消息路由流程

agent-server-admin                         data-server                    agent-client
      │                                         │                              │
      │  1. 发送业务消息 (AgentTask)             │                              │
      │ ───────────────────────────────────────>│                              │
      │                                         │  2. 路由到目标客户端          │
      │                                        │──────────────────────────────>│
      │                                         │                              │
      │                                         │  3. 业务通道处理              │
      │                                         │    (BusinessChannel)         │
      │                                         │    ────────────────────────>│
      │                                         │                              │ 4. 调用
      │                                         │                              │    agent_runner
      │                                         │                              │
      │                                         │  5. 进度回传 (Progress)      │
      │                                         │<──────────────────────────────│
      │                                         │                              │
      │  6. SSE 推送结果                        │  6. 转发进度                 │
      │<───────────────────────────────────────│<──────────────────────────────│
      │                                         │                              │

通道复用

通道 ID 用途 数据类型
0x01 视频通道 ScreenCaptureFrame
0x02 音频通道 AudioFrame
0x04 文件传输 FileChunk
0x08 业务通道 BusinessEnvelope (Agent/Custom)

5.5.6 qiming-rustdesk 最小修改清单

文件 修改内容
src/lib.rs 新增 mod business_channel; pub use self::business_channel::*;
src/business_channel.rs 新增业务通道实现(约 200 行)
proto/business.proto 新增业务消息定义(约 80 行)

不需要修改的部分

  • 不修改 RendezvousMediator
  • 不修改 Client::start()
  • 不修改现有的消息路由器逻辑

5.6 远程桌面消息流程

  agent-server-admin                  data-server          agent-client
        │                                │                    │
        │  1. 请求远程桌面连接            │                    │
        │────────────────────────────────>│                    │
        │                                │  2. 路由请求        │
        │                                │───────────────────>│
        │                                │                    │
        │                                │  3. 同意连接       │
        │                                │<───────────────────│
        │                                │                    │
        │  4. 建立 P2P 通道               │                    │
        │<───────────────────────────────│                    │
        │                                │                    │
        │  5. 屏幕数据流 (P2P)            │                    │
        │<───────────────────────────────────────────────────>│
        │                                │                    │
        │  6. 输入事件 (P2P)              │                    │
        │───────────────────────────────────────────────────>│
        │                                │                    │

5.7 文件传输消息流程

  agent-server-admin                  data-server          agent-client
        │                                │                    │
        │  1. 发送文件请求                │                    │
        │  (文件信息、目标 Client ID)     │                    │
        │────────────────────────────────>│                    │
        │                                │  2. 通知接收方      │
        │                                │───────────────────>│
        │                                │                    │
        │                                │  3. 用户确认接收   │
        │                                │<───────────────────│
        │                                │                    │
        │  4. 建立传输通道               │                    │
        │<───────────────────────────────│                    │
        │                                │                    │
        │  5. 分片传输数据               │                    │
        │<───────────────────────────────────────────────────>│
        │                                │                    │
        │  6. 传输完成通知               │                    │
        │<───────────────────────────────────────────────────>│
        │                                │                    │

5.8 Protobuf 消息定义示例

// message.proto

// 协议版本
syntax = "proto3";

package qiming_agent;

// 通用消息头
message MessageHeader {
    uint32 version = 1;      // 协议版本号
    uint32 message_id = 2;   // 消息唯一 ID
    string client_id = 3;    // 客户端 ID
    int64 timestamp = 4;     // 时间戳
}

// 握手请求
message HandshakeRequest {
    MessageHeader header = 1;
    string os_version = 2;       // 操作系统版本
    string app_version = 3;      // 应用版本
    string device_name = 4;      // 设备名称
    map<string, string> extras = 5; // 扩展信息
}

// 握手响应
message HandshakeResponse {
    MessageHeader header = 1;
    bool success = 2;
    string client_id = 3;        // 分配的客户端 ID
    ConnectionType conn_type = 4; // 连接类型
    string error_message = 5;    // 错误信息(失败时)
}

enum ConnectionType {
    CONNECTION_TYPE_UNKNOWN = 0;
    CONNECTION_TYPE_P2P = 1;
    CONNECTION_TYPE_RELAY = 2;
}

// Agent 任务请求
message AgentTaskRequest {
    MessageHeader header = 1;
    string task_id = 2;          // 任务唯一 ID
    string prompt = 3;           // 执行指令
    repeated string attachments = 4; // 附件列表
    int32 timeout_seconds = 5;   // 超时时间0 表示不超时)
    map<string, string> env = 6; // 环境变量
    string work_dir = 7;         // 工作目录
}

// Agent 任务输出(流式)
message AgentTaskOutput {
    MessageHeader header = 1;
    string task_id = 2;
    OutputType type = 3;
    string content = 4;          // 输出内容
    int64 timestamp = 5;         // 时间戳

    enum OutputType {
        OUTPUT_TYPE_UNKNOWN = 0;
        OUTPUT_TYPE_STDOUT = 1;  // 标准输出
        OUTPUT_TYPE_STDERR = 2;  // 标准错误
        OUTPUT_TYPE_STATUS = 3;  // 状态信息
        OUTPUT_TYPE_COMPLETE = 4; // 执行完成
        OUTPUT_TYPE_ERROR = 5;   // 执行错误
    }
}

// 屏幕捕获帧
message ScreenCaptureFrame {
    MessageHeader header = 1;
    uint32 width = 2;            // 画面宽度
    uint32 height = 3;           // 画面高度
    bytes image_data = 4;        // 压缩后的图像数据
    ImageFormat format = 5;
    uint32 sequence = 6;         // 帧序列号

    enum ImageFormat {
        IMAGE_FORMAT_UNKNOWN = 0;
        IMAGE_FORMAT_JPEG = 1;
        IMAGE_FORMAT_PNG = 2;
    }
}

// 输入事件
message InputEvent {
    MessageHeader header = 1;
    InputType type = 2;
    int32 x = 3;                 // X 坐标
    int32 y = 4;                 // Y 坐标
    int32 delta = 5;             // 滚轮增量
    repeated KeyCode keys = 6;   // 按键列表

    enum InputType {
        INPUT_TYPE_UNKNOWN = 0;
        INPUT_TYPE_MOUSE_MOVE = 1;
        INPUT_TYPE_MOUSE_DOWN = 2;
        INPUT_TYPE_MOUSE_UP = 3;
        INPUT_TYPE_MOUSE_WHEEL = 4;
        INPUT_TYPE_KEY_DOWN = 5;
        INPUT_TYPE_KEY_UP = 6;
    }

    enum KeyCode {
        KEY_CODE_UNKNOWN = 0;
        KEY_CODE_ENTER = 1;
        KEY_CODE_ESCAPE = 2;
        // ... 其他按键
    }
}

// 文件传输请求
message FileTransferRequest {
    MessageHeader header = 1;
    string transfer_id = 2;      // 传输 ID
    string file_name = 3;        // 文件名
    int64 file_size = 4;         // 文件大小(字节)
    string checksum = 5;         // 文件校验和
    string target_client_id = 6; // 目标客户端 ID
}

// 文件分片
message FileChunk {
    MessageHeader header = 1;
    string transfer_id = 2;
    uint32 chunk_index = 3;      // 分片索引
    bytes chunk_data = 4;        // 分片数据
    bool is_last = 5;            // 是否最后一块
}

5.9 版本兼容性策略

场景 处理方式
老客户端 + 新协议 忽略未知字段,基础功能可用
新客户端 + 老协议 使用默认值填充缺失字段
协议重大升级 版本号递增,拒绝不兼容的旧版本
字段删除 保留字段编号,标记为 deprecated
字段类型变更 新增字段,逐步迁移

5.10 安全设计

5.10.1 认证机制

认证方式 说明 适用场景
密码认证 客户端连接密码8位以上字母+数字) 基础认证方式
Token 认证 临时 Token24小时有效期 API 调用认证
Session 认证 Session ID + 过期时间 管理端会话保持

密码存储

/// 密码存储策略
trait PasswordStorage {
    /// 使用 Argon2 加密存储密码
    fn hash_password(password: &str) -> Result<String>;
    /// 验证密码
    fn verify_password(password: &str, hash: &str) -> bool;
    /// 密码强度检查
    fn check_strength(password: &str) -> PasswordStrength;
}

Token 管理

/// Token 管理器
struct TokenManager {
    /// Token 有效期24小时
    const TOKEN_TTL_HOURS: u64 = 24;
    /// 刷新提前量1小时
    const REFRESH_AHEAD_HOURS: u64 = 1;

    /// 生成新 Token
    fn generate_token(&self, client_id: &str) -> Token;
    /// 验证 Token
    fn validate_token(&self, token: &Token) -> bool;
    /// 刷新 Token
    fn refresh_token(&self, old_token: &Token) -> Result<Token>;
}

5.10.2 传输安全

TLS 配置要求

配置项 要求
最低 TLS 版本 TLS 1.2
推荐 TLS 版本 TLS 1.3
密码套件 仅支持前向安全的套件
证书验证 必须验证服务器证书

数据加密

/// 敏感数据加密
trait SensitiveDataEncryption {
    /// 加密敏感配置密码、Token 等)
    fn encrypt_sensitive(&self, data: &[u8]) -> Vec<u8>;
    /// 解密敏感配置
    fn decrypt_sensitive(&self, encrypted: &[u8]) -> Vec<u8>;
    /// 敏感信息脱敏(日志输出时)
    fn redact_sensitive(&self, data: &str) -> String;
}

5.10.3 审计日志

审计事件类型

事件类型 说明 记录内容
连接事件 客户端连接/断开 时间、客户端ID、连接类型、IP地址
认证事件 登录成功/失败 时间、客户端ID、认证方式、结果
任务事件 Agent 任务开始/完成/失败 时间、任务ID、任务内容摘要
文件事件 文件传输开始/完成 时间、文件ID、文件名、大小
配置变更 密码修改等配置变更 时间、变更类型、变更结果
/// 审计日志结构
struct AuditLog {
    timestamp: DateTime<Utc>,
    event_type: AuditEventType,
    client_id: String,
    user_id: String,
    action: String,
    details: serde_json::Value,
    ip_address: Option<String>,
    result: AuditResult,
}

enum AuditEventType {
    Connection,
    Authentication,
    TaskExecution,
    FileTransfer,
    ConfigChange,
    SecurityEvent,
}

5.10.4 敏感信息脱敏规则

数据类型 脱敏规则 示例
密码 替换为 ****** ******
Token 保留首尾字符 sk-abc...xyz
API Key 保留前4后4位 abcd...wxyz
文件路径 提取文件名 C:\...\file.txtfile.txt
IP 地址 部分隐藏 192.168.**.**

5.11 网络策略

5.11.1 重连机制

重连策略

参数 默认值 可配置范围
初始重连延迟 1秒 1-10秒
最大重连延迟 30秒 10-300秒
重连指数基数 2 1.5-3.0
最大重试次数 0无限 0-100
/// 重连策略配置
struct ReconnectConfig {
    /// 初始延迟(毫秒)
    initial_delay_ms: u64,
    /// 最大延迟(毫秒)
    max_delay_ms: u64,
    /// 指数退避基数
    backoff_base: f64,
    /// 最大重试次数0 表示无限)
    max_retries: u32,
    /// 是否随机抖动
    jitter: bool,
}

impl Default for ReconnectConfig {
    fn default() -> Self {
        Self {
            initial_delay_ms: 1000,
            max_delay_ms: 30000,
            backoff_base: 2.0,
            max_retries: 0,  // 无限重试
            jitter: true,
        }
    }
}

重连流程

连接断开
    │
    ▼
检查是否达到最大重试次数?
    │
    ├── 是 ───► 进入离线模式,缓存消息
    │
    └── 否
        │
        ▼
    计算延迟时间(指数退避 + 随机抖动)
        │
        ▼
    等待延迟时间
        │
        ▼
    尝试重连
        │
        ├── 成功 ───► 重置重试计数,恢复消息队列
        │
        └── 失败 ───► 返回步骤 2

5.11.2 离线消息队列

队列配置

配置项 默认值 说明
最大队列大小 1000 条 离线消息最大缓存数量
消息保留时间 24 小时 消息过期时间
单消息最大大小 1 MB 超过则拒绝入队
/// 离线消息队列
struct OfflineMessageQueue {
    /// 队列容量
    capacity: usize,
    /// 消息保留时间
    ttl: Duration,
    /// 待发送消息
    pending: Arc<RwLock<Vec<QueuedMessage>>>,
}

struct QueuedMessage {
    /// 消息内容
    payload: Vec<u8>,
    /// 消息类型
    message_type: u32,
    /// 入队时间
    enqueued_at: DateTime<Utc>,
    /// 重试次数
    retry_count: u32,
}

5.11.3 带宽控制

带宽配置

配置项 默认值 说明
最大上传速度 10 MB/s 仅远程桌面/文件传输
最大下载速度 10 MB/s 仅远程桌面/文件传输
心跳间隔 30 秒 心跳包发送间隔
连接超时 10 秒 TCP 连接超时
/// 带宽管理器
struct BandwidthManager {
    /// 最大上传速度(字节/秒)
    max_upload: u64,
    /// 最大下载速度(字节/秒)
    max_download: u64,
    /// 令牌桶
    upload_bucket: TokenBucket,
    download_bucket: TokenBucket,
}

struct TokenBucket {
    capacity: u64,
    tokens: f64,
    refill_rate: f64,  // 令牌填充速度
}

动态带宽调整

impl BandwidthManager {
    /// 根据网络状况动态调整带宽
    async fn adapt_to_network(&self, latency_ms: u64, loss_rate: f64) {
        match (latency_ms, loss_rate) {
            (latency, loss) if latency > 500 || loss > 0.05 => {
                // 高延迟或高丢包,降低带宽
                self.set_limit(1024 * 1024, 1024 * 1024).await;  // 1 MB/s
            }
            (latency, loss) if latency > 200 || loss > 0.02 => {
                // 中等延迟或丢包,中等带宽
                self.set_limit(5 * 1024 * 1024, 5 * 1024 * 1024).await;  // 5 MB/s
            }
            _ => {
                // 正常网络,使用全带宽
                self.set_limit(self.max_upload, self.max_download).await;
            }
        }
    }
}

5.11.4 网络状态监听

/// 网络状态监听器
trait NetworkStatusListener {
    /// 网络状态变化回调
    fn on_status_change(&self, status: NetworkStatus);

    /// 延迟变化回调
    fn on_latency_change(&self, latency_ms: u64);

    /// 连接质量评估
    fn evaluate_quality(&self) -> ConnectionQuality {
        // 基于延迟和丢包率评估
    }
}

enum NetworkStatus {
    Connected(ConnectionType),     // P2P 或 Relay
    Connecting,
    Disconnected,
    Offline,
}

enum ConnectionQuality {
    Excellent,  // < 50ms, < 1% loss
    Good,       // < 150ms, < 2% loss
    Fair,       // < 300ms, < 5% loss
    Poor,       // > 300ms 或 > 5% loss
}

5.12 国际化方案

5.12.1 字符串资源结构

i18n/
├── en-US/
│   └── messages.toml
├── zh-CN/
│   └── messages.toml
├── ja-JP/
│   └── messages.toml
└── fallback.toml

资源文件格式

# en-US/messages.toml

[common]
ok = "OK"
cancel = "Cancel"
save = "Save"
close = "Close"
settings = "Settings"
about = "About"

[status]
connected = "Connected"
disconnected = "Disconnected"
connecting = "Connecting..."

[client_info]
title = "Client Information"
client_id = "Client ID"
password = "Password"
copy_id = "Copy ID"
copy_password = "Copy Password"
change_password = "Change Password"
# zh-CN/messages.toml

[common]
ok = "确定"
cancel = "取消"
save = "保存"
close = "关闭"
settings = "设置"
about = "关于"

[status]
connected = "已连接"
disconnected = "已断开"
connecting = "连接中..."

[client_info]
title = "客户端信息"
client_id = "客户端 ID"
password = "连接密码"
copy_id = "复制 ID"
copy_password = "复制密码"
change_password = "修改密码"

5.12.2 i18n 基础设施

/// 国际化管理器
struct I18nManager {
    /// 当前语言
    current_lang: RwLock<Language>,
    /// 语言资源
    resources: DashMap<Language, ResourceBundle>,
    /// 回退资源
    fallback: ResourceBundle,
}

impl I18nManager {
    /// 加载语言资源
    async fn load_language(&self, lang: Language) -> Result<()> {
        let resource = ResourceBundle::from_file(format!("i18n/{}/messages.toml", lang.code())).await?;
        self.resources.insert(lang, resource);
        Ok(())
    }

    /// 获取翻译字符串
    fn t(&self, key: &str) -> String {
        let lang = *self.current_lang.read();
        self.resources.get(&lang)
            .or(Some(&self.fallback))
            .and_then(|r| r.get(key))
            .unwrap_or_else(|| key.to_string())
    }

    /// 格式化带参数的字符串
    fn t_with_args(&self, key: &str, args: &[(&str, &str)]) -> String {
        let mut result = self.t(key);
        for (name, value) in args {
            result = result.replace(&format!("{{{name}}}"), value);
        }
        result
    }
}

5.12.3 日期/时间/数字格式化

/// 区域格式化器
struct LocaleFormatter {
    locale: Language,
}

impl LocaleFormatter {
    /// 格式化日期
    fn format_date(&self, date: &DateTime<Utc>) -> String {
        match self.locale {
            Language::ZhCN => date.format("%Y年%m月%d日").to_string(),
            Language::JaJP => date.format("%Y年%m月%d日").to_string(),
            _ => date.format("%B %d, %Y").to_string(),
        }
    }

    /// 格式化时间
    fn format_time(&self, time: &DateTime<Utc>) -> String {
        match self.locale {
            Language::ZhCN => time.format("%H:%M:%S").to_string(),
            Language::JaJP => time.format("%H:%M:%S").to_string(),
            _ => time.format("%H:%M:%S").to_string(),
        }
    }

    /// 格式化文件大小
    fn format_file_size(&self, bytes: u64) -> String {
        const UNITS: [&str; 5] = ["B", "KB", "MB", "GB", "TB"];
        let mut size = bytes as f64;
        let mut unit_index = 0;

        while size >= 1024.0 && unit_index < UNITS.len() - 1 {
            size /= 1024.0;
            unit_index += 1;
        }

        match self.locale {
            Language::ZhCN => format!("{:.1} {}", size, UNITS[unit_index]),
            _ => format!("{} {:.1} {}", size, UNITS[unit_index]),
        }
    }
}

5.12.4 UI 文本动态切换

/// 支持动态切换语言的 UI 组件
trait LocalizedComponent {
    /// 更新所有文本
    fn update_localization(&mut self, cx: &mut Cx, lang: Language);

    /// 获取当前语言
    fn current_language(&self) -> Language;
}

/// 状态栏语言显示示例
struct StatusBar {
    language: Language,
}

impl Render for StatusBar {
    fn render(&mut self, cx: &mut Cx) {
        // 根据语言显示对应文本
        let lang_text = match self.language {
            Language::ZhCN => "中文",
            Language::EnUS => "English",
            Language::JaJP => "日本語",
        };
        Label::new(cx, lang_text);
    }
}

5.13 会话管理

5.13.1 任务会话状态

/// Agent 任务会话状态
enum AgentSessionState {
    /// 空闲(无任务)
    Idle,
    /// 等待中(任务已接收,等待执行)
    Pending {
        received_at: DateTime<Utc>,
        task_id: String,
    },
    /// 执行中
    Active {
        started_at: DateTime<Utc>,
        session_id: String,
        task_id: String,
    },
    /// 正在停止
    Terminating {
        started_at: DateTime<Utc>,
        reason: String,
    },
    /// 执行出错
    Error {
        error_type: ErrorType,
        message: String,
    },
}

enum ErrorType {
    Timeout,
    PermissionDenied,
    WorkerStopped,
    Unknown,
}

5.13.2 会话超时策略

会话类型 超时时间 可配置范围 处理方式
任务执行超时 30 分钟 5-120 分钟 自动取消
空闲超时(无操作) 2 小时 30 分钟 - 24 小时 断开连接
Token 过期 24 小时 1-72 小时 刷新 Token
消息确认超时 30 秒 10-120 秒 重试 3 次
/// 超时管理器
struct TimeoutManager {
    /// 任务执行超时
    task_timeout: Duration,
    /// 空闲超时
    idle_timeout: Duration,
    /// Token 过期时间
    token_ttl: Duration,
    /// 消息确认超时
    ack_timeout: Duration,
}

impl TimeoutManager {
    /// 检查任务是否超时
    fn is_task_timed_out(&self, session: &AgentSessionState) -> bool {
        if let AgentSessionState::Active { started_at, .. } = session {
            let elapsed = Utc::now().signed_duration_since(*started_at);
            elapsed > self.task_timeout
        } else {
            false
        }
    }

    /// 检查空闲超时
    fn is_idle_timed_out(&self, last_activity: &DateTime<Utc>) -> bool {
        let elapsed = Utc::now().signed_duration_since(*last_activity);
        elapsed > self.idle_timeout
    }
}

5.13.3 并发控制

/// 并发任务管理器
struct ConcurrencyManager {
    /// 最大并发任务数
    max_concurrent: usize,
    /// 当前运行的任务
    running_tasks: Arc<RwLock<HashMap<String, AgentSessionState>>>,
    /// 等待队列
    waiting_queue: Arc<RwLock<Vec<TaskRequest>>>,
}

impl ConcurrencyManager {
    /// 提交任务
    async fn submit(&self, request: TaskRequest) -> Result<SubmissionResult> {
        let running_count = self.running_tasks.read().len();

        if running_count >= self.max_concurrent {
            // 加入等待队列
            self.waiting_queue.write().push(request);
            Ok(SubmissionResult::Queued)
        } else {
            // 直接执行
            self.start_task(request).await?;
            Ok(SubmissionResult::Accepted)
        }
    }

    /// 任务完成回调
    fn on_task_completed(&self, task_id: &str) {
        // 从运行中移除
        self.running_tasks.write().remove(task_id);

        // 检查等待队列,启动下一个任务
        if let Some(next_task) = self.waiting_queue.write().pop() {
            tokio::spawn(async move {
                self.start_task(next_task).await;
            });
        }
    }
}

5.13.4 僵尸会话检测与清理

/// 会话清理策略
struct SessionCleanupPolicy {
    /// 清理间隔
    cleanup_interval: Duration,
    /// 僵尸判定时间(超过此时间无活动视为僵尸)
    zombie_threshold: Duration,
    /// 保留历史记录时间
    history_retention: Duration,
}

impl SessionCleanupPolicy {
    /// 执行清理
    async fn cleanup(&self, sessions: &DashMap<String, AgentSessionState>) -> CleanupResult {
        let mut zombie_sessions = Vec::new();
        let now = Utc::now();

        for (session_id, session) in sessions.iter() {
            if let Some(last_activity) = self.get_last_activity(session).await {
                let elapsed = now.signed_duration_since(last_activity);
                if elapsed > self.zombie_threshold {
                    zombie_sessions.push(session_id.clone());
                }
            }
        }

        // 清理僵尸会话
        for session_id in &zombie_sessions {
            sessions.remove(session_id);
        }

        CleanupResult {
            cleaned_count: zombie_sessions.len(),
            timestamp: now,
        }
    }
}

5.14 系统集成

5.14.1 全局快捷键

/// 全局快捷键管理器
struct GlobalShortcutManager {
    /// 快捷键注册表
    shortcuts: Arc<RwLock<HashMap<Shortcut, ShortcutHandler>>>,
}

impl GlobalShortcutManager {
    /// 注册全局快捷键
    fn register(&self, shortcut: Shortcut, handler: ShortcutHandler) -> Result<()> {
        match current_platform() {
            Platform::MacOS => {
                // 使用 CGEventTap 或 MASShortcutBundle
            }
            Platform::Windows => {
                // 使用 RegisterHotKey
            }
            Platform::Linux => {
                // 使用 X11 GrabKey 或 GTK AccelGroup
            }
        }
        self.shortcuts.write().insert(shortcut, handler);
        Ok(())
    }
}

/// 默认快捷键
const DEFAULT_SHORTCUTS: &[Shortcut] = &[
    Shortcut::new(vec![ModifierKey::Ctrl, ModifierKey::Alt], KeyCode::A),
    // 显示窗口
    Shortcut::new(vec![ModifierKey::Ctrl, ModifierKey::Alt], KeyCode::S),
    // 隐藏窗口
    Shortcut::new(vec![ModifierKey::Ctrl, ModifierKey::Alt], KeyCode::H),
    // 快速连接
    Shortcut::new(vec![ModifierKey::Ctrl, ModifierKey::Alt], KeyCode::C),
];

5.14.2 系统通知集成

/// 系统通知管理器
trait SystemNotification {
    /// 发送通知
    async fn send(&self, notification: NotificationRequest) -> Result<NotificationId>;

    /// 取消通知
    async fn cancel(&self, id: NotificationId);

    /// 设置通知回调
    fn set_callback(&self, callback: NotificationCallback);
}

struct NotificationRequest {
    title: String,
    body: String,
    icon: Option<PathBuf>,
    urgency: NotificationUrgency,
    timeout: Duration,
    actions: Vec<NotificationAction>,
    /// 点击通知时的回调
    on_click: Option<CallbackTarget>,
}

enum NotificationUrgency {
    Low,
    Normal,
    Critical,
}

struct NotificationAction {
    id: String,
    label: String,
    icon: Option<PathBuf>,
}

5.14.3 系统事件处理

/// 系统事件监听器
trait SystemEventListener {
    /// 关机事件
    fn on_shutdown(&self);

    /// 睡眠事件
    fn on_sleep(&self);

    /// 唤醒事件
    fn on_wake(&self);

    /// 屏幕锁定事件
    fn on_screen_lock(&self);

    /// 屏幕解锁事件
    fn on_screen_unlock(&self);

    /// 网络状态变化事件
    fn on_network_change(&self, status: NetworkStatus);
}

impl SystemEventListener for App {
    fn on_shutdown(&self) {
        // 保存状态
        // 清理资源
        // 断开连接
        info!("System shutdown, saving state and disconnecting...");
    }

    fn on_sleep(&self) {
        // 暂停远程桌面流
        // 保存临时数据
        info!("System going to sleep, pausing operations...");
    }

    fn on_wake(&self) {
        // 恢复远程桌面流
        // 检查连接状态
        info!("System woke up, resuming operations...");
    }
}

5.14.4 系统集成配置

/// 系统集成配置
struct SystemIntegrationConfig {
    /// 是否启用全局快捷键
    enable_global_shortcuts: bool,
    /// 是否启用系统通知
    enable_system_notifications: bool,
    /// 通知优先级
    notification_priority: NotificationPriority,
    /// 系统事件处理
    handle_shutdown: bool,
    handle_sleep: bool,
    handle_network_change: bool,
}

impl Default for SystemIntegrationConfig {
    fn default() -> Self {
        Self {
            enable_global_shortcuts: true,
            enable_system_notifications: true,
            notification_priority: NotificationPriority::Normal,
            handle_shutdown: true,
            handle_sleep: true,
            handle_network_change: true,
        }
    }
}

5.14.5 开机自启动与系统托盘集成

/// 启动行为配置
struct StartupBehavior {
    /// 启动时最小化到托盘
    minimize_to_tray_on_startup: bool,
    /// 关闭窗口时最小化到托盘(不退出)
    minimize_to_tray_on_close: bool,
    /// 托盘双击显示窗口
    show_window_on_tray_double_click: bool,
    /// 退出时确认
    confirm_on_quit: bool,
}

impl Default for StartupBehavior {
    fn default() -> Self {
        Self {
            minimize_to_tray_on_startup: false,
            minimize_to_tray_on_close: true,
            show_window_on_tray_double_click: true,
            confirm_on_quit: false,
        }
    }
}

6. 系统架构

6.1 整体架构

┌─────────────────────────────────────────────────────────────────────────────┐
│                              agent-client                                    │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                         UI Layer (gpui-component)                   │   │
│  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐      │   │
│  │  │客户端信息│ │ 设置    │ │依赖管理 │ │ 权限    │ │ 关于    │      │   │
│  │  │ Tab    │ │ Tab    │ │ Tab    │ │ Tab    │ │ Tab    │      │   │
│  │  │(ID/密码)│ │(子页面) │ │         │ │         │ │         │      │   │
│  │  └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘      │   │
│  └───────┼───────────┼───────────┼───────────┼───────────┼───────────┘   │
│          │           │           │           │           │               │
│  ┌───────┴───────────┴───────────┴───────────┴───────────┴───────────┐   │
│  │                         AppStateManager                             │   │
│  │  - Tab 导航                                                          │   │
│  │  - 连接状态                                                          │   │
│  │  - 配置状态                                                          │   │
│  │  - 错误状态                                                          │   │
│  └───────────────────────────────┬─────────────────────────────────────┘   │
│                                  │                                          │
│  ┌───────────────────────────────┴─────────────────────────────────────┐   │
│  │                         Core Layer                                   │   │
│  │                                                                        │   │
│  │  ┌────────────────┐ ┌────────────────┐ ┌────────────────┐           │   │
│  │  │ ConfigManager  │ │ConnectionManager│ │ AgentManager   │           │   │
│  │  │ - TOML 读写    │ │ - qiming-rustdesk│ │ - agent_runner │           │   │
│  │  │ - 配置验证     │ │ - P2P/Relay    │ │ - 任务管理     │           │   │
│  │  └────────────────┘ └────────────────┘ └────────────────┘           │   │
│  │                                                                        │   │
│  │  ┌────────────────┐ ┌────────────────┐ ┌────────────────┐           │   │
│  │  │DependencyManager│ │FileTransfer   │ │ PlatformAdapter│           │   │
│  │  │ - Node.js 安装 │ │ - 分片传输     │ │ - 权限检测     │           │   │
│  │  │ - npm 包管理   │ │ - 断点续传     │ │ - 自启动       │           │   │
│  │  └────────────────┘ └────────────────┘ └────────────────┘           │   │
│  └──────────────────────────────────────────────────────────────────────┘   │
│                                  │                                          │
│  ┌──────────────────────────────┴──────────────────────────────────────┐   │
│  │                         Vendor Libraries                             │   │
│  │                                                                        │   │
│  │  ┌────────────────────────────────────────────────────────────────┐  │   │
│  │  │                    qiming-rustdesk                              │  │   │
│  │  │  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐             │  │   │
│  │  │  │Rendezvous│ │  P2P    │ │ 文件    │ │ 屏幕    │             │  │   │
│  │  │  │ 连接器   │ │ 通道    │ │ 传输    │ │ 捕获    │             │  │   │
│  │  │  └─────────┘ └─────────┘ └─────────┘ └─────────┘             │  │   │
│  │  └────────────────────────────────────────────────────────────────┘  │   │
│  │                                                                        │   │
│  │  ┌─────────────┐ ┌─────────────┐ ┌──────── ┌─────────────┐   ─────┐ │   │
│  │  │ tray-icon   │ │ auto-launch │ │ enigo       │ │ agent_runner│    │   │
│  │  │ 托盘图标    │ │ 开机自启动  │ │ 输入模拟    │ │ Agent 管理  │    │   │
│  │  └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘    │   │
│  │                                                                        │   │
│  └────────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

7. 目录结构

qiming-agent/
├── Cargo.toml
├── crates/
│   ├── agent-client/
│   │   ├── src/
│   │   │   ├── main.rs                      # 程序入口
│   │   │   ├── app.rs                       # 应用状态管理
│   │   │   ├── state.rs                     # 全局状态定义
│   │   │   ├── ui/                          # UI 组件
│   │   │   │   ├── app_window.rs            # 主窗口
│   │   │   │   ├── tabs/                    # Tab 面板
│   │   │   │   │   ├── client_info.rs       # 客户端信息 Tab (ID/密码)
│   │   │   │   │   ├── settings/            # 设置 Tab
│   │   │   │   │   │   ├── mod.rs           # 设置 Tab 入口
│   │   │   │   │   │   ├── server.rs        # 服务器配置子页面
│   │   │   │   │   │   ├── security.rs      # 安全设置子页面 (密码修改)
│   │   │   │   │   │   ├── general.rs       # 常规设置子页面
│   │   │   │   │   │   ├── appearance.rs    # 外观设置子页面
│   │   │   │   │   │   └── logging.rs       # 日志设置子页面
│   │   │   │   │   ├── dependencies.rs      # 依赖管理 Tab
│   │   │   │   │   ├── permissions.rs       # 权限设置 Tab
│   │   │   │   │   └── about.rs             # 关于 Tab
│   │   │   │   ├── status_bar.rs            # 状态栏
│   │   │   │   ├── tray_menu.rs             # 托盘菜单
│   │   │   │   └── components/              # 公共 UI 组件
│   │   │   │       ├── input.rs             # 输入组件封装
│   │   │   │       ├── button.rs            # 按钮组件封装
│   │   │   │       ├── status_indicator.rs  # 状态指示器
│   │   │   │       └── notification.rs      # 通知组件
│   │   │   ├── core/                        # 核心逻辑
│   │   │   │   ├── config.rs                # 配置管理
│   │   │   │   ├── connection.rs            # 连接管理 (qiming-rustdesk)
│   │   │   │   ├── agent.rs                 # Agent 管理 (rcoder)
│   │   │   │   ├── dependency.rs            # 依赖管理
│   │   │   │   ├── file_transfer.rs         # 文件传输
│   │   │   │   └── platform/                # 平台适配
│   │   │   │       ├── mod.rs               # 平台抽象
│   │   │   │       ├── permissions.rs       # 权限检测
│   │   │   │       ├── auto_launch.rs       # 自启动
│   │   │   │       ├── windows.rs
│   │   │   │       ├── macos.rs
│   │   │   │       └── linux.rs
│   │   │   └── utils/                       # 工具函数
│   │   │       ├── clipboard.rs             # 剪贴板操作
│   │   │       ├── version.rs               # 版本信息
│   │   │       └── path.rs                  # 路径处理
│   │   └── Cargo.toml
│   │
│   ├── agent-server-admin/
│   │   ├── src/
│   │   │   ├── main.rs
│   │   │   ├── api/                         # HTTP API
│   │   │   ├── sse/                         # SSE 推送
│   │   │   ├── client_manager/              # 客户端管理
│   │   │   └── ws/                          # WebSocket
│   │   └── Cargo.toml
│   │
│   ├── data-server/
│   │   ├── src/
│   │   │   └── main.rs                      # 基于 rustdesk-server 封装
│   │   └── Cargo.toml
│   │
│   └── agent-protocol/
│       ├── proto/                           # protobuf 定义
│       │   ├── message.proto
│       │   ├── common.proto
│       │   └── file.proto
│       ├── src/
│       │   ├── lib.rs
│       │   └── generated/                   # 生成的代码
│       └── Cargo.toml
│
├── vendors/                                  # 第三方库
│   ├── qiming-rustdesk/
│   ├── rustdesk-server/
│   ├── gpui-component/
│   ├── rcoder/
│   ├── auto-launch/
│   ├── tray-icon/
│   ├── enigo/
│   └── cargo-packager/
│
├── assets/                                   # 资源文件
│   ├── icons/
│   │   ├── icon.png
│   │   ├── icon.ico
│   │   ├── icon.icns
│   │   └── tray_icon.png
│   ├── locales/                             # 国际化文件
│   │   ├── en.json
│   │   └── zh.json
│   └── tray/                                # 托盘图标资源
│
└── specs/
    ├── agent-client.md                      # 需求文档
    ├── agent-client-spec.md                 # 本文档
    └── vendorsdoc/                          # 第三方库文档

8. 阶段一验收测试用例

8.1 测试类型说明

类型 说明 执行方式
单元测试 模块级别的逻辑验证 cargo test 自动执行
集成测试 跨模块的功能验证 cargo test --test integration
端到端测试 完整流程验证 需启动 data-server 和 agent-server-admin
手动验收测试 人工执行的 UI 和功能验收 按测试清单逐项验证

8.2 单元测试用例

Phase 1 单元测试

测试 ID 测试模块 测试用例 预期结果
UT-1.1 config 配置文件加载 正确解析 TOML 配置
UT-1.2 config 配置文件加密存储 敏感信息加密后可解密还原
UT-1.3 protocol Protobuf 序列化/反序列化 消息正确编解码
UT-1.4 protocol 协议版本协商 版本不兼容时返回错误
UT-1.5 tray 托盘菜单构建 菜单项正确创建
UT-1.6 auto_launch 自启动配置读写 各平台正确配置

Phase 2 单元测试

测试 ID 测试模块 测试用例 预期结果
UT-2.1 connection 连接状态机转换 状态正确切换
UT-2.2 connection 重连策略(指数退避) 延迟符合预期
UT-2.3 connection 心跳超时检测 超时后触发重连
UT-2.4 business_channel 消息路由 按类型正确分发
UT-2.5 security 密码 bcrypt 哈希 哈希可验证
UT-2.6 message_queue 离线消息队列 消息正确入队出队
UT-2.7 chunked_transfer 大消息分片 分片后可重组

Phase 3 单元测试

测试 ID 测试模块 测试用例 预期结果
UT-3.1 dependency Node.js 版本检测 正确解析版本号
UT-3.2 dependency 路径查找逻辑 各平台路径正确
UT-3.3 dependency 版本比较 大于/小于/等于正确判断
UT-3.4 installer 下载进度计算 百分比正确

8.3 集成测试用例

测试 ID 测试场景 前置条件 测试步骤 预期结果
IT-1 配置持久化 1. 修改配置 2. 重启应用 3. 读取配置 配置保持修改后的值
IT-2 托盘与窗口交互 应用运行中 1. 点击托盘图标 2. 选择"显示窗口" 主窗口显示
IT-3 依赖检测流程 Node.js 已安装 1. 打开依赖管理 2. 点击刷新 显示 Node.js 版本
IT-4 协议兼容性 1. 发送旧版本消息 2. 检查处理 向后兼容正确处理

8.4 端到端测试用例

测试 ID 测试场景 前置条件 测试步骤 预期结果
E2E-1 客户端注册 data-server 运行 1. 启动 agent-client 2. 观察状态栏 显示"已连接"和分配的 ID
E2E-2 P2P 直连 同一局域网 1. 启动客户端和管理端 2. 管理端连接客户端 连接模式显示 "P2P"
E2E-3 Relay 中继 不同网络 1. 配置不同网络 2. 管理端连接客户端 连接模式显示 "Relay"
E2E-4 双向消息 已连接 1. 管理端发送消息 2. 客户端接收并回复 双向消息正确传递
E2E-5 心跳保活 已连接 1. 保持连接 3 分钟 2. 观察状态 连接保持正常
E2E-6 断线重连 已连接 1. 断开网络 2. 恢复网络 3. 等待重连 自动重连成功
E2E-7 密码验证 已配置密码 1. 管理端使用错误密码连接 连接被拒绝

8.5 手动验收测试清单

UI 验收

  • 主窗口正常显示,无布局错乱
  • Tab 切换流畅,内容正确
  • 状态栏实时更新连接状态
  • 托盘图标正常显示
  • 托盘菜单功能正常
  • 深色/浅色主题正确应用

功能验收

  • 客户端 ID 显示正确且只读
  • 密码可切换显示/隐藏
  • 复制到剪贴板功能正常
  • 密码修改流程完整
  • 服务器配置可保存并生效
  • 开机自启动可配置
  • 依赖状态检测正确
  • 依赖安装流程完整

跨平台验收

平台 安装 启动 托盘 自启动 连接
macOS (Intel)
macOS (Apple Silicon)
Windows 10
Windows 11
Ubuntu 22.04
Fedora 39

8.6 测试覆盖率要求

模块 最低覆盖率
core/ 80%
message/ 90%
utils/ 70%
整体 75%

8.7 性能验收指标

指标 要求
应用启动时间 < 3 秒
内存占用(空闲) < 100 MB
CPU 占用(空闲) < 1%
连接建立时间 < 5 秒
心跳延迟 < 100ms (局域网)

参考文档

文档 路径
需求文档 @agent-client.md
qiming-rustdesk @vendorsdoc/qiming-rustdesk.md
rustdesk-server @vendorsdoc/rustdesk-server.md
rcoder @vendorsdoc/rcoder.md
gpui-component @vendorsdoc/gpui-component.md
auto-launch @vendorsdoc/auto-launch.md
tray-icon @vendorsdoc/tray-icon.md
enigo @vendorsdoc/enigo.md
cargo-packager @vendorsdoc/cargo-packager.md