Files
claude-code/src/utils/computerUse/common.ts
unraid c17edcb12e feat: Computer Use — Windows 跨平台支持 + GUI 无障碍增强 + Python Bridge
三平台 Computer Use (macOS + Windows + Linux),Windows 专项增强。

- MCP server: toolCalls/tools/executor/mcpServer 等 12 文件完整实现
- 平台抽象层: platforms/{win32,darwin,linux}.ts
- 跨平台 executor: executorCrossPlatform.ts
- CHICAGO_MCP + VOICE_MODE feature flags 启用

- windowMessage.ts: SendMessageW (WM_CHAR Unicode + 剪贴板粘贴)
- windowBorder.ts: 4 叠加窗口边框 (30fps 跟踪)
- uiAutomation.ts: UI Automation 元素树/点击/写值
- accessibilitySnapshot.ts: 无障碍快照 → 模型感知 GUI
- bridge.py + bridgeClient.ts: Python 长驻进程 (替代 per-call PS)

- window_management: min/max/restore/close/focus (Win32 API)
- click_element / type_into_element: 按名称操作 (无需坐标)
- 截图自动附带 Accessibility Snapshot

- 17 种方法, stdin/stdout JSON 通信
- 窗口枚举 1.5ms vs PS 500ms, 截图 360ms vs PS 800ms
- 依赖: mss + Pillow + pywinauto
2026-04-05 15:47:20 +08:00

68 lines
2.7 KiB
TypeScript

import { normalizeNameForMCP } from '../../services/mcp/normalization.js'
import { env } from '../env.js'
export const COMPUTER_USE_MCP_SERVER_NAME = 'computer-use'
/**
* Sentinel bundle ID for the frontmost gate. Claude Code is a terminal — it has
* no window. This never matches a real `NSWorkspace.frontmostApplication`, so
* the package's "host is frontmost" branch (mouse click-through exemption,
* keyboard safety-net) is dead code for us. `prepareForAction`'s "exempt our
* own window" is likewise a no-op — there is no window to exempt.
*/
export const CLI_HOST_BUNDLE_ID = 'com.anthropic.claude-code.cli-no-window'
/**
* Fallback `env.terminal` → bundleId map for when `__CFBundleIdentifier` is
* unset. Covers the macOS terminals we can distinguish — Linux entries
* (konsole, gnome-terminal, xterm) are deliberately absent since
* `createCliExecutor` is darwin-guarded.
*/
const TERMINAL_BUNDLE_ID_FALLBACK: Readonly<Record<string, string>> = {
'iTerm.app': 'com.googlecode.iterm2',
Apple_Terminal: 'com.apple.Terminal',
ghostty: 'com.mitchellh.ghostty',
kitty: 'net.kovidgoyal.kitty',
WarpTerminal: 'dev.warp.Warp-Stable',
vscode: 'com.microsoft.VSCode',
}
/**
* Bundle ID of the terminal emulator we're running inside, so `prepareDisplay`
* can exempt it from hiding and `captureExcluding` can keep it out of
* screenshots. Returns null when undetectable (ssh, cleared env, unknown
* terminal) — caller must handle the null case.
*
* `__CFBundleIdentifier` is set by LaunchServices when a .app bundle spawns a
* process and is inherited by children. It's the exact bundleId, no lookup
* needed — handles terminals the fallback table doesn't know about. Under
* tmux/screen it reflects the terminal that started the SERVER, which may
* differ from the attached client. That's harmless here: we exempt A
* terminal window, and the screenshots exclude it regardless.
*/
export function getTerminalBundleId(): string | null {
const cfBundleId = process.env.__CFBundleIdentifier
if (cfBundleId) return cfBundleId
return TERMINAL_BUNDLE_ID_FALLBACK[env.terminal ?? ''] ?? null
}
/**
* Static capabilities for macOS CLI. `hostBundleId` is not here — it's added
* by `executor.ts` per `ComputerExecutor.capabilities`. `buildComputerUseTools`
* takes this shape (no `hostBundleId`, no `teachMode`).
*/
export const CLI_CU_CAPABILITIES = {
screenshotFiltering: (process.platform === 'darwin'
? 'native'
: 'none') as any,
platform: (process.platform === 'win32'
? 'win32'
: process.platform === 'linux'
? 'linux'
: 'darwin') as any,
}
export function isComputerUseMCPServer(name: string): boolean {
return normalizeNameForMCP(name) === COMPUTER_USE_MCP_SERVER_NAME
}