feat: 新增桌面GUI自动化插件 (gui_auto) - Windows UIA 元素识别与操控
- 新增 gui_auto 插件 (20 操作): 元素树获取、点击/输入/按键、窗口管理、元素查询、高亮闪烁 - 基于 Windows UI Automation API,支持模糊匹配、中文输入、ValuePattern 自动识别 - 完整 MCP/FC 定义,AI 可通过 Function Calling 直接调用 - 非 Windows 环境优雅降级 - 新增 30 个单元测试 - 更新 README/CHANGELOG/HELP - 插件数: 17 → 18, 操作数: 141+ → 160+
This commit is contained in:
1 parent
a00396b4fd
commit
e6fddf14f8
5 files changed
+1229
-6
No files matched your search
@@ -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)
|
||||
|
||||
### 新增功能
|
||||
|
||||
@@ -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`)
|
||||
|
||||
| 操作 | 说明 |
|
||||
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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 功能测试"""
|
||||
|
||||
|
||||
Reference in new issue
Block a user