vault backup: 2026-06-09 23:15:17

This commit is contained in:
2026-06-09 23:15:17 +08:00
parent 31fd89aafe
commit e92c0327d9
111 changed files with 7276 additions and 8846 deletions
@@ -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]]