!3 feat: 新增桌面GUI自动化插件 (gui_auto) - Windows UIA 元素识别与操控

Merge pull request !3 from shuiyu1123/feature/gui-auto-plugin
This commit is contained in:
shuiyu1123 authored and Gitee committed 2026-04-29 07:42:34 +00:00
commit 530a255091
5 files changed
+1229 -6

No files matched your search

+24
View File
@@ -4,6 +4,30 @@
---
## v5.1.4 (2026-04-29)
### 新增功能
- **桌面GUI自动化插件** (`gui_auto`) — 基于 Windows UI Automation (UIA) API,实现屏幕 UI 元素识别与操控,20 个操作
- **元素树获取** (`snapshot`) — 获取整个屏幕或指定窗口的 UI 元素树,支持自定义遍历深度
- **元素交互** — 点击/右键/双击元素(支持名称模糊匹配和坐标点击)
- **文本输入** — 在指定元素中输入文本,支持中文 Unicode 输入,自动尝试 ValuePattern
- **按键发送** — 发送键盘快捷键和单个按键,支持组合键(如 Ctrl+S)
- **窗口管理** — 列出所有窗口、聚焦指定窗口
- **元素查询** — 查找/查找所有/获取详情/获取值/子树遍历/检查存在/获取状态
- **高级功能** — 等待元素出现(带超时)、高亮闪烁元素(GDI 绘制红色边框)
- **MCP/FC 完整支持** — 实现 `get_mcp_definition()` + `convert_mcp_args()`,可被 AI 通过 Function Calling 直接调用
- **跨平台安全** — 非 Windows 环境下优雅降级,给出清晰错误提示
### 文档
- 更新 README.md 插件列表:17 个 → 18 个
- 更新操作数:141+ → 160+
- 更新 badge: plugins 17 → 18
### 测试
- 新增 gui_auto 插件 30 个单元测试(工具信息、MCP 定义、参数转换、错误处理、操作路由)
---
## v5.1.3 (2026-04-28)
### 新增功能
+20
View File
@@ -154,6 +154,26 @@ run make build --timeout 60 # 60秒超时
| `pdf page.pdf` | 保存 PDF |
| `stop` | 关闭浏览器 |
### 桌面GUI自动化 (`gui_auto`) ⭐ NEW
> 仅支持 Windows,基于 UI Automation API
| 操作 | 说明 |
|------|------|
| `snapshot [深度]` | 获取屏幕 UI 元素树 |
| `snapshot --window 记事本` | 获取指定窗口元素树 |
| `screenshot [路径]` | 全屏截图 |
| `windows` | 列出所有窗口 |
| `focus 记事本` | 聚焦窗口 |
| `click 确定` | 点击元素(模糊匹配) |
| `click --xy 500 300` | 点击坐标 |
| `type 搜索框 Hello` | 在元素中输入文本 |
| `keys ctrl+s` | 发送按键 |
| `find 保存` | 查找元素 |
| `exists 确定` | 检查元素是否存在 |
| `wait 加载完成 10` | 等待元素出现 |
| `highlight 确定` | 高亮闪烁元素 |
### 子 Agent (`sub_agent`)
| 操作 | 说明 |
+8 -6
View File
@@ -9,7 +9,7 @@
<img src="https://img.shields.io/badge/python-3.10+-green" alt="python">
<img src="https://img.shields.io/badge/license-MIT-orange" alt="license">
<img src="https://img.shields.io/badge/tests-113%20passed-brightgreen" alt="tests">
<img src="https://img.shields.io/badge/plugins-17-blueviolet" alt="plugins">
<img src="https://img.shields.io/badge/plugins-18-blueviolet" alt="plugins">
<img src="https://img.shields.io/badge/MCP-supported-brightgreen" alt="mcp">
</p>
@@ -28,8 +28,8 @@
- **Clawli 远程模式** — PC-手机 WebSocket 双向通信,支持内网穿透代理
- **定时任务** — 支持相对时间、绝对时间、重复任务,到期自动激活 AI
- **系统通知** — 跨平台任务完成通知(Windows / macOS / Linux)
- **MCP 协议** — 17 个插件全部实现 MCP 定义,支持 Function Calling 原生调用
- **17 个插件 141+ 个操作** — 代码编辑、Git、浏览器、工程化、文件管理、图像生成、记忆等
- **MCP 协议** — 18 个插件全部实现 MCP 定义,支持 Function Calling 原生调用
- **18 个插件 160+ 个操作** — 代码编辑、Git、浏览器、工程化、文件管理、图像生成、记忆、桌面GUI自动化等
- **三模式界面** — TUI 图形化 + 传统命令行 + **Web UI 浏览器界面**
- **113 个自动化测试** — pytest 覆盖全部插件和核心模块
@@ -87,7 +87,7 @@ launcher.exe
}
```
## 插件列表 (17 个)
## 插件列表 (18 个)
### 核心生产力
@@ -106,6 +106,7 @@ launcher.exe
|------|--------|------|
| `sub_agent` | 8 | 子 Agent 系统。spawn/list/status/result/send/kill,多 Agent 并行协作 |
| `browser_auto` | 28 | 浏览器自动化。导航/点击/填写/截图/JS执行/PDF/cookies(基于 Playwright) |
| `gui_auto` | 20 | **桌面GUI自动化**。基于 Windows UIA,屏幕元素识别/点击/输入/截图/元素树遍历(仅Windows) |
| `image_generator` | 7 | **图像生成**。基于 ComfyUI 在线服务,自然语言/Tag 双模式,28 个角色工作流 |
| `memory_plugin` | 11 | **记忆系统**。AI 写日记/搜索记忆/管理聊天记录/长期记忆 MEMORY.md |
| `scheduler` | 4 | **定时任务**。支持相对时间/绝对时间/重复任务,到期自动激活 AI |
@@ -186,7 +187,7 @@ memory_plugin chat_search 图像生成
## MCP 协议支持
所有 17 个插件均实现 MCP (Model Context Protocol) 定义,支持三种工具调用协议:
所有 18 个插件均实现 MCP (Model Context Protocol) 定义,支持三种工具调用协议:
| 协议 | 说明 |
|------|------|
@@ -256,7 +257,7 @@ xiaoli-cli/
│ ├── ollama_engine.py # Ollama 本地模型
│ └── openai_engine.py # OpenAI 兼容格式
│
├── plugins/ # 插件 (17 个)
├── plugins/ # 插件 (18 个)
│ ├── code_editor.py # 代码编辑+搜索 (24 操作)
│ ├── git_tools.py # Git 版本控制 (17 操作)
│ ├── cmd_executor.py # Shell 命令执行
@@ -265,6 +266,7 @@ xiaoli-cli/
│ ├── task_manager.py # 任务管理
│ ├── sub_agent.py # 子 Agent 系统 (8 操作)
│ ├── browser_auto.py # 浏览器自动化 (28 操作)
│ ├── gui_auto.py # 桌面GUI自动化 (20 操作) ⭐ NEW
│ ├── image_generator.py # 图像生成 (7 操作) ⭐ NEW
│ ├── memory_plugin.py # 记忆系统 (11 操作) ⭐ NEW
│ ├── scheduler.py # 定时任务
+1036
View File
File diff suppressed because it is too large. Load diff
+141
View File
@@ -357,6 +357,147 @@ class TestBrowserAuto:
# 引擎加载测试
# ══════════════════════════════════════
class TestGuiAuto:
"""gui_auto 功能测试"""
@pytest.fixture
def gui(self):
mod = load_module("gui_auto", os.path.join(PLUGINS_DIR, "gui_auto.py"))
return mod.Plugin()
def test_tool_info(self, gui):
info = gui.get_tool_info()
assert info["name"] == "gui_auto"
assert "GUI" in info["description"]
assert len(info["keywords"]) >= 5
def test_mcp_definition(self, gui):
mcp = gui.get_mcp_definition()
assert mcp["name"] == "gui_auto"
assert "inputSchema" in mcp
assert "operation" in mcp["inputSchema"]["properties"]
def test_convert_mcp_args(self, gui):
args = {"operation": "click", "target": "确定"}
result = gui.convert_mcp_args(args)
assert "click" in result
assert "确定" in result
def test_convert_mcp_args_xy(self, gui):
args = {"operation": "click", "x": 100, "y": 200}
result = gui.convert_mcp_args(args)
assert "--xy" in result
assert "100" in result
def test_convert_mcp_args_snapshot(self, gui):
args = {"operation": "snapshot", "depth": 3}
result = gui.convert_mcp_args(args)
assert "snapshot" in result
assert "3" in result
def test_empty_args(self, gui):
result = gui.handle("")
assert "错误" in result
def test_invalid_operation(self, gui):
result = gui.handle("nonexistent_op")
assert "不支持" in result
def test_click_no_args(self, gui):
result = gui.handle("click")
assert "错误" in result
def test_type_no_args(self, gui):
result = gui.handle("type")
assert "错误" in result
def test_type_missing_text(self, gui):
result = gui.handle("type 按钮")
assert "错误" in result
def test_find_no_args(self, gui):
result = gui.handle("find")
assert "错误" in result
def test_findall_no_args(self, gui):
result = gui.handle("findall")
assert "错误" in result
def test_info_no_args(self, gui):
result = gui.handle("info")
assert "错误" in result
def test_value_no_args(self, gui):
result = gui.handle("value")
assert "错误" in result
def test_tree_no_args(self, gui):
result = gui.handle("tree")
assert "错误" in result
def test_wait_no_args(self, gui):
result = gui.handle("wait")
assert "错误" in result
def test_exists_no_args(self, gui):
result = gui.handle("exists")
assert "错误" in result
def test_highlight_no_args(self, gui):
result = gui.handle("highlight")
assert "错误" in result
def test_state_no_args(self, gui):
result = gui.handle("state")
assert "错误" in result
def test_keys_no_args(self, gui):
result = gui.handle("keys")
assert "错误" in result
def test_focus_no_args(self, gui):
result = gui.handle("focus")
assert "错误" in result
def test_click_xy_format_error(self, gui):
result = gui.handle("click --xy abc")
assert "错误" in result
def test_format_element_info(self, gui):
el = {
"name": "测试按钮", "type": "Button", "class": "Button",
"automation_id": "btn_ok",
"bounds": {"left": 100, "top": 200, "width": 80, "height": 30,
"right": 180, "bottom": 230},
"enabled": True, "focused": False
}
result = gui._format_element_info(el)
assert "测试按钮" in result
assert "Button" in result
assert "btn_ok" in result
def test_format_element_info_detailed(self, gui):
el = {
"name": "输入框", "type": "Edit", "class": "Edit",
"automation_id": "", "enabled": True, "focused": True,
"value": "hello",
"bounds": {"left": 0, "top": 0, "width": 200, "height": 25,
"right": 200, "bottom": 25}
}
result = gui._format_element_info(el, detailed=True)
assert "输入框" in result
assert "hello" in result
def test_all_operations_route(self, gui):
"""所有操作都能正确路由"""
for op in ["snapshot", "screenshot", "windows", "focus", "click",
"rightclick", "doubleclick", "type", "clear", "keys",
"scroll", "find", "findall", "info", "value", "tree",
"wait", "exists", "highlight", "state"]:
result = gui.handle(f"{op} test")
assert isinstance(result, str)
class TestSubAgent:
"""sub_agent 功能测试"""