vault backup: 2026-06-09 23:15:17
This commit is contained in:
@@ -1,18 +1,30 @@
|
||||
---
|
||||
tags: [computer-use, 架构, windows, 跨平台, SendMessage]
|
||||
create time: 2026-06-09 22:30
|
||||
---
|
||||
|
||||
# Computer Use 架构修正方案 v2
|
||||
|
||||
更新时间:2026-04-04
|
||||
## 概述
|
||||
|
||||
## 1. 当前架构的问题
|
||||
针对 Computer Use v1 架构中的三个核心问题——平台代码混放、全局输入干扰用户、截图能力归属不清——提出的修正方案。核心思路是将跨平台抽象层 `platforms/` 从 `@ant/` 包中独立出来,Windows 使用 SendMessage 无焦点输入替代全局 SendInput。
|
||||
|
||||
### 问题 A:平台代码混在错误的包里
|
||||
> [!info]
|
||||
> 更新时间:2026-04-04
|
||||
|
||||
## 正文
|
||||
|
||||
### 1. 当前架构的问题
|
||||
|
||||
#### 问题 A:平台代码混在错误的包里
|
||||
|
||||
`@ant/computer-use-swift` 是 macOS Swift 原生模块的包装器,但我们把 Windows(`backends/win32.ts`)和 Linux(`backends/linux.ts`)的截图/应用管理代码塞进了这个包。"swift" 在名字里就意味着 macOS,后期维护者无法区分。
|
||||
|
||||
`@ant/computer-use-input` 同样——原本是 macOS enigo Rust 模块,我们也往里面塞了 win32/linux 后端。
|
||||
|
||||
### 问题 B:输入方式不对
|
||||
#### 问题 B:输入方式不对
|
||||
|
||||
当前 Windows 后端(`packages/@ant/computer-use-input/src/backends/win32.ts`)使用 `SetCursorPos` + `SendInput` + `keybd_event`——这是**全局输入**:
|
||||
当前 Windows 后端使用 `SetCursorPos` + `SendInput` + `keybd_event`——这是**全局输入**:
|
||||
|
||||
- 鼠标真的会移动到屏幕上
|
||||
- 键盘真的打到当前前台窗口
|
||||
@@ -26,44 +38,49 @@
|
||||
- `PrintWindow` — 截取窗口内容,不需要窗口在前台
|
||||
- **不抢焦点、不影响用户当前操作**
|
||||
|
||||
已验证:向记事本 `SendMessage(WM_CHAR)` 成功写入文字,记事本在后台,终端保持前台。
|
||||
> [!tip]
|
||||
> 已验证:向记事本 `SendMessage(WM_CHAR)` 成功写入文字,记事本在后台,终端保持前台。
|
||||
|
||||
### 问题 C:截图是公共能力,不属于 swift
|
||||
#### 问题 C:截图是公共能力,不属于 swift
|
||||
|
||||
截图(screenshot)、显示器枚举(display)、应用管理(apps)是所有平台都需要的公共能力,不应该放在 `@ant/computer-use-swift`(macOS 专属包名)里。
|
||||
|
||||
## 2. 修正后的架构
|
||||
### 2. 修正后的架构
|
||||
|
||||
### 2.1 分层原则
|
||||
#### 2.1 分层原则
|
||||
|
||||
```
|
||||
packages/@ant/ ← macOS 原生模块包装器(不放其他平台代码)
|
||||
├── computer-use-input/ ← macOS: enigo .node 键鼠(仅 darwin)
|
||||
├── computer-use-swift/ ← macOS: Swift .node 截图/应用(仅 darwin)
|
||||
└── computer-use-mcp/ ← 跨平台: MCP server + 工具定义(不改)
|
||||
|
||||
src/utils/computerUse/
|
||||
├── platforms/ ← 新增: 跨平台抽象层
|
||||
│ ├── types.ts ← 公共接口: InputPlatform, ScreenshotPlatform, AppsPlatform, DisplayPlatform
|
||||
│ ├── index.ts ← 平台分发器: 按 process.platform 加载后端
|
||||
│ ├── darwin.ts ← macOS: 委托给 @ant/computer-use-{input,swift}
|
||||
│ ├── win32.ts ← Windows: SendMessage 输入 + PrintWindow 截图 + EnumWindows + UIA + OCR
|
||||
│ └── linux.ts ← Linux: xdotool + scrot + xrandr + wmctrl
|
||||
│
|
||||
├── win32/ ← Windows 专属增强能力(不在公共接口中)
|
||||
│ ├── windowCapture.ts ← PrintWindow 窗口绑定截图
|
||||
│ ├── windowEnum.ts ← EnumWindows 窗口枚举
|
||||
│ ├── windowMessage.ts ← SendMessage/PostMessage 无焦点输入(新增)
|
||||
│ ├── uiAutomation.ts ← IUIAutomation UI 元素操作
|
||||
│ └── ocr.ts ← Windows.Media.Ocr 文字识别
|
||||
│
|
||||
├── executor.ts ← 改: 通过 platforms/ 获取平台实现,不直接调 @ant 包
|
||||
├── swiftLoader.ts ← 改: 仅 darwin 使用
|
||||
├── inputLoader.ts ← 改: 仅 darwin 使用
|
||||
└── ...其他文件不动
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "packages/@ant/ macOS 原生模块包装器"
|
||||
A["computer-use-input: macOS enigo 键鼠 (仅 darwin)"]
|
||||
B["computer-use-swift: macOS Swift 截图/应用 (仅 darwin)"]
|
||||
C["computer-use-mcp: MCP server + 工具定义 (跨平台)"]
|
||||
end
|
||||
subgraph "src/utils/computerUse/ 跨平台抽象层"
|
||||
D["platforms/types.ts: 公共接口"]
|
||||
E["platforms/index.ts: 平台分发器"]
|
||||
F["platforms/darwin.ts: 委托 @ant 包"]
|
||||
G["platforms/win32.ts: SendMessage + PrintWindow + EnumWindows"]
|
||||
H["platforms/linux.ts: xdotool + scrot + xrandr"]
|
||||
end
|
||||
subgraph "src/utils/computerUse/win32/ Windows专属增强"
|
||||
I["windowCapture.ts: PrintWindow 窗口绑定截图"]
|
||||
J["windowEnum.ts: EnumWindows 窗口枚举"]
|
||||
K["windowMessage.ts: SendMessage 无焦点输入"]
|
||||
L["uiAutomation.ts: UI 元素操作"]
|
||||
M["ocr.ts: Windows.Media.Ocr 文字识别"]
|
||||
end
|
||||
E --> F
|
||||
E --> G
|
||||
E --> H
|
||||
G --> I
|
||||
G --> J
|
||||
G --> K
|
||||
G --> L
|
||||
G --> M
|
||||
```
|
||||
|
||||
### 2.2 公共接口(`platforms/types.ts`)
|
||||
#### 2.2 公共接口(`platforms/types.ts`)
|
||||
|
||||
```typescript
|
||||
/** 窗口标识 — 跨平台 */
|
||||
@@ -84,7 +101,7 @@ export interface InputPlatform {
|
||||
keys(combo: string[]): Promise<void>
|
||||
scroll(amount: number, direction: 'vertical' | 'horizontal'): Promise<void>
|
||||
mouseLocation(): Promise<{ x: number; y: number }>
|
||||
|
||||
|
||||
// 模式 B: 窗口绑定输入(Windows SendMessage,不抢焦点)
|
||||
sendChar?(hwnd: string, char: string): Promise<void>
|
||||
sendKey?(hwnd: string, vk: number, action: 'down' | 'up'): Promise<void>
|
||||
@@ -94,11 +111,8 @@ export interface InputPlatform {
|
||||
|
||||
/** 截图平台接口 */
|
||||
export interface ScreenshotPlatform {
|
||||
// 全屏截图
|
||||
captureScreen(displayId?: number): Promise<ScreenshotResult>
|
||||
// 区域截图
|
||||
captureRegion(x: number, y: number, w: number, h: number): Promise<ScreenshotResult>
|
||||
// 窗口截图(Windows: PrintWindow,macOS: SCContentFilter,Linux: xdotool+import)
|
||||
captureWindow?(hwnd: string): Promise<ScreenshotResult | null>
|
||||
}
|
||||
|
||||
@@ -116,33 +130,9 @@ export interface AppsPlatform {
|
||||
getFrontmostApp(): FrontmostAppInfo | null
|
||||
findWindowByTitle(title: string): WindowHandle | null
|
||||
}
|
||||
|
||||
export interface ScreenshotResult {
|
||||
base64: string
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export interface DisplayInfo {
|
||||
width: number
|
||||
height: number
|
||||
scaleFactor: number
|
||||
displayId: number
|
||||
}
|
||||
|
||||
export interface InstalledApp {
|
||||
id: string // macOS: bundleId, Windows: exe path, Linux: .desktop name
|
||||
displayName: string
|
||||
path: string
|
||||
}
|
||||
|
||||
export interface FrontmostAppInfo {
|
||||
id: string
|
||||
appName: string
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 平台分发器(`platforms/index.ts`)
|
||||
#### 2.3 平台分发器(`platforms/index.ts`)
|
||||
|
||||
```typescript
|
||||
import type { InputPlatform, ScreenshotPlatform, DisplayPlatform, AppsPlatform } from './types.js'
|
||||
@@ -168,12 +158,12 @@ export function loadPlatform(): Platform {
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 各平台实现
|
||||
#### 2.4 各平台实现
|
||||
|
||||
**`platforms/darwin.ts`** — 委托给 @ant 包(保持兼容):
|
||||
|
||||
```typescript
|
||||
// macOS: 通过 @ant/computer-use-input 和 @ant/computer-use-swift
|
||||
// 这两个包的 darwin 后端保留不动
|
||||
import { requireComputerUseInput } from '../inputLoader.js'
|
||||
import { requireComputerUseSwift } from '../swiftLoader.js'
|
||||
|
||||
@@ -186,19 +176,19 @@ export const platform = {
|
||||
```
|
||||
|
||||
**`platforms/win32.ts`** — 使用 `src/utils/computerUse/win32/` 模块:
|
||||
|
||||
```typescript
|
||||
// Windows: SendMessage 输入 + PrintWindow 截图 + EnumWindows 应用
|
||||
import { sendChar, sendKey, sendClick, sendText } from '../win32/windowMessage.js'
|
||||
import { captureWindow } from '../win32/windowCapture.js'
|
||||
import { listWindows } from '../win32/windowEnum.js'
|
||||
// ... PowerShell P/Invoke 全局输入作为 fallback
|
||||
|
||||
export const platform = {
|
||||
input: {
|
||||
// 全局模式: PowerShell SetCursorPos/SendInput(fallback)
|
||||
// 窗口模式: SendMessage(首选)
|
||||
sendChar, sendKey, sendClick, sendText, // 窗口绑定
|
||||
moveMouse, click, typeText, ... // 全局 fallback
|
||||
sendChar, sendKey, sendClick, sendText,
|
||||
moveMouse, click, typeText, /* ... */
|
||||
},
|
||||
screenshot: {
|
||||
captureScreen, // CopyFromScreen
|
||||
@@ -211,6 +201,7 @@ export const platform = {
|
||||
```
|
||||
|
||||
**`platforms/linux.ts`** — 使用 xdotool/scrot:
|
||||
|
||||
```typescript
|
||||
// Linux: xdotool + scrot + xrandr + wmctrl
|
||||
export const platform = {
|
||||
@@ -221,7 +212,7 @@ export const platform = {
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 executor.ts 改造
|
||||
#### 2.5 executor.ts 改造
|
||||
|
||||
```typescript
|
||||
// 之前: 直接调 requireComputerUseSwift() 和 requireComputerUseInput()
|
||||
@@ -244,7 +235,7 @@ platform.input.moveMouse(500, 500)
|
||||
platform.input.click(500, 500, 'left')
|
||||
```
|
||||
|
||||
## 3. Windows 输入模式对比
|
||||
### 3. Windows 输入模式对比
|
||||
|
||||
| 方式 | API | 抢焦点 | 移鼠标 | 窗口可最小化 | 适用场景 |
|
||||
|------|-----|--------|--------|-------------|---------|
|
||||
@@ -254,9 +245,10 @@ platform.input.click(500, 500, 'left')
|
||||
| **窗口截图** | `PrintWindow(hwnd, PW_RENDERFULLCONTENT)` | ❌ 不抢 | ❌ 不动 | ✅ 可以 | 窗口截图 |
|
||||
| **UI 操作** | `UIAutomation InvokePattern` | ❌ 不抢 | ❌ 不动 | ✅ 可以 | 按钮点击、文本写入 |
|
||||
|
||||
**策略**:优先用窗口消息 + UIAutomation(不干扰用户),全局输入作为 fallback。
|
||||
> [!tip]
|
||||
> **策略**:优先用窗口消息 + UIAutomation(不干扰用户),全局输入作为 fallback。
|
||||
|
||||
## 4. 需要新增的文件
|
||||
### 4. 需要新增的文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
@@ -267,7 +259,7 @@ platform.input.click(500, 500, 'left')
|
||||
| `src/utils/computerUse/platforms/linux.ts` | Linux: xdotool/scrot |
|
||||
| `src/utils/computerUse/win32/windowMessage.ts` | **新增**: SendMessage 无焦点输入 |
|
||||
|
||||
## 5. 需要移除/清理的文件
|
||||
### 5. 需要移除/清理的文件
|
||||
|
||||
| 文件 | 操作 | 原因 |
|
||||
|------|------|------|
|
||||
@@ -278,7 +270,7 @@ platform.input.click(500, 500, 'left')
|
||||
| `packages/@ant/computer-use-input/src/types.ts` | 删除 | 移到 platforms/types.ts |
|
||||
| `packages/@ant/computer-use-swift/src/types.ts` | 删除 | 移到 platforms/types.ts |
|
||||
|
||||
## 6. 需要修改的文件
|
||||
### 6. 需要修改的文件
|
||||
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
@@ -288,7 +280,7 @@ platform.input.click(500, 500, 'left')
|
||||
| `src/utils/computerUse/swiftLoader.ts` | 仅 darwin 加载 |
|
||||
| `src/utils/computerUse/inputLoader.ts` | 仅 darwin 加载 |
|
||||
|
||||
## 7. @ant 包的定位(修正后)
|
||||
### 7. @ant 包的定位(修正后)
|
||||
|
||||
| 包 | 职责 | 平台 |
|
||||
|---|------|------|
|
||||
@@ -298,28 +290,19 @@ platform.input.click(500, 500, 'left')
|
||||
|
||||
Windows/Linux 的平台实现全部在 `src/utils/computerUse/platforms/` 和 `src/utils/computerUse/win32/` 中。
|
||||
|
||||
## 8. 执行顺序
|
||||
### 8. 执行顺序
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A["Phase 1: 创建 platforms/ 抽象层"] --> B["Phase 2: 创建 Windows 平台实现"]
|
||||
B --> C["Phase 3: 创建 Linux 平台实现"]
|
||||
C --> D["Phase 4: 改造 executor.ts"]
|
||||
D --> E["Phase 5: 清理 @ant 包"]
|
||||
E --> F["Phase 6: 验证 + PR"]
|
||||
```
|
||||
Phase 1: 创建 platforms/ 抽象层
|
||||
├── platforms/types.ts(公共接口)
|
||||
├── platforms/index.ts(分发器)
|
||||
└── platforms/darwin.ts(委托 @ant 包)
|
||||
|
||||
Phase 2: 创建 Windows 平台实现
|
||||
├── win32/windowMessage.ts(SendMessage 无焦点输入)
|
||||
└── platforms/win32.ts(组合 win32/ 各模块)
|
||||
## 关联笔记
|
||||
|
||||
Phase 3: 创建 Linux 平台实现
|
||||
└── platforms/linux.ts(xdotool/scrot)
|
||||
|
||||
Phase 4: 改造 executor.ts
|
||||
└── 通过 platforms/ 获取实现,不直接调 @ant
|
||||
|
||||
Phase 5: 清理 @ant 包
|
||||
├── 删除 @ant/computer-use-input/src/backends/{win32,linux}.ts
|
||||
├── 删除 @ant/computer-use-swift/src/backends/{win32,linux}.ts
|
||||
└── 恢复 index.ts 为 darwin-only
|
||||
|
||||
Phase 6: 验证 + PR
|
||||
```
|
||||
- [[computer-use]]
|
||||
- [[computer-use-windows-enhancement]]
|
||||
- [[computer-use-tools-reference]]
|
||||
|
||||
Reference in New Issue
Block a user