PC 桌面自动化支持
Midscene 可以驱动原生键盘和鼠标控制,在 Windows、macOS 和 Linux 上支持 PC 桌面自动化。
通过采用视觉模型方案,自动化流程可以适配任何桌面应用程序——无论是用 Electron、Qt、WPF 还是原生技术构建的。开发者在调试 UI 自动化脚本时,只需关注最终的用户体验。
PC 桌面自动化方案具备 Midscene 的所有特性:
- 支持使用 Playground 进行零代码试用
- 支持 JavaScript SDK 进行脚本编写
- 支持 YAML 格式的自动化脚本和命令行工具
- 支持 HTML 报告回放所有操作路径
- 跨 Windows、macOS 和 Linux 平台
- 支持 Linux CI 无头模式(通过 Xvfb,无需物理显示器)
- 多显示器支持复杂设置
案例展示
Prompt (macOS): Help me post a tweet promoting Midscene's support for AutoGLM through safari, with the following requirements:
- Text content: Midscene now supports AutoGLM!
- Media content: Use the AutoGLM video from the download folder!
查看此次任务的完整报告:report.html
Prompt (Windows): 打开 Sauce Demo 电商网站,登录并添加商品到购物车
查看此次任务的完整报告:report.html
Prompt (macOS): 打开 Google 查询圣何塞明天天气温度
查看此次任务的完整报告:report.html
Prompt (Linux): 打开 TodoMVC,添加多个任务并筛选
查看此次任务的完整报告:report.html
查看更多案例:showcases
本指南将引导您完成使用 Midscene 自动化 PC 桌面应用所需的一切:安装依赖、配置模型凭据并运行您的第一个 JavaScript 脚本。
配置 AI 模型服务
将你的模型配置写入环境变量,可参考 模型策略 了解更多细节。
export MIDSCENE_MODEL_BASE_URL="https://替换为你的模型服务地址/v1"
export MIDSCENE_MODEL_API_KEY="替换为你的 API Key"
export MIDSCENE_MODEL_NAME="替换为你的模型名称"
export MIDSCENE_MODEL_FAMILY="替换为你的模型系列"
更多配置信息请参考 模型策略 和 模型配置。
系统要求
Node.js
需要 Node.js 18.19.0 或更高版本。
平台特定依赖
macOS:需要辅助功能权限才能控制键盘和鼠标。首次运行脚本时,macOS 会提示你授予访问权限。前往 系统设置 > 隐私与安全性 > 辅助功能,为运行脚本的应用程序(如 Terminal、iTerm2、VS Code、WebStorm 或其他 IDE)启用权限。更多详情请参阅 nut.js macOS 设置。
Windows:操作普通程序无需额外配置。但 Windows 会按权限级别隔离输入(UIPI):未提权的进程无法向以管理员 身份运行(已提权)的窗口发送鼠标或键盘输入,输入会被静默丢弃——光标仍会移动到正确位置,但点击和按键不生效。优先尝试让目标程序不要以管理员权限运行;如果目标程序必须保持提权状态,再同样以管理员身份运行启动 Midscene 的终端或 Node.js,让两个进程处于相同的权限级别。详见 Windows:点击对某些程序不生效。
Linux:需要安装 ImageMagick 用于截图功能。
无头 Linux(CI 环境):要在无头 Linux 服务器(如 GitHub Actions)上运行桌面自动化,需安装 Xvfb 及其依赖,然后启用 headless 模式:
# 安装依赖
sudo apt-get install -y xvfb x11-xserver-utils imagemagick
// 方式 1:传入 headless 选项
const agent = await agentForComputer({ headless: true });
// 方式 2:设置环境变量
// MIDSCENE_COMPUTER_HEADLESS_LINUX=true npx tsx example.ts
Xvfb 会创建一个虚拟显示器,使鼠标、键盘和截图操作在没 有物理显示器的情况下正常工作。详见 API 参考。
试用 Playground(零代码)
Playground 是验证连接、观察 AI Agent 的最快方式,无需编写代码。它与 @midscene/computer 共享相同的代码实现,因此在 Playground 上验证通过的流程,用脚本运行时也完全一致。
- 启动 Playground CLI:
npx --yes @midscene/computer-playground
- 点击 Playground 窗口中的齿轮按钮,粘贴你的 API Key 配置。如果还没有 API Key,请回到 模型配置 获取。
开始体验
配置完成后,你可以立即开始体验 Midscene。它提供了多个关键操作 Tab:
- Act: 与网页进行交互,这就是自动规划(Auto Planning),对应于
aiAct 方法。比如
在搜索框中输入 Midscene,执行搜索,跳转到第一条结果
- Query: 从界面中提取 JSON 结构的数据,对应于
aiQuery 方法。
类似的方法还有 aiBoolean(), aiNumber(), aiString(),用于直接提取布尔值、数字和字符串。
提取页面中的用户 ID,返回 { id: string } 结构的 JSON 数据
- Assert: 理解页面,进行断言,如果不满足则抛出错误,对应于
aiAssert 方法。
页面上存在一个登录按钮,它的下方有一个用户协议的链接
- Tap: 在某个元素上点击,这就是即时操作(Instant Action),对应于
aiTap 方法。
关于自动规划(Auto Planning)和即时操作(Instant Action)的区别,请参考 API 文档。
与 Midscene Agent 集成
当 Playground 运行正常后,就可以切换到可复用的 JavaScript 脚本。
步骤 1. 安装依赖
npm install @midscene/computer
yarn add @midscene/computer
pnpm add @midscene/computer
bun add @midscene/computer
deno add npm:@midscene/computer
步骤 2. 编写您的第一个脚本
创建 example.ts:
import { agentForComputer } from '@midscene/computer';
(async () => {
// 创建 agent
const agent = await agentForComputer({
aiActionContext: '你正在控制一台桌面计算机。',
});
// 截图并查询信息
const screenInfo = await agent.aiQuery(
'{width: number, height: number}, 获取屏幕分辨率'
);
console.log('屏幕分辨率:', screenInfo);
// 移动鼠标到中心
await agent.aiAct('将鼠标移动到屏幕中心');
// 断言屏幕有内容
await agent.aiAssert('屏幕有可见内容');
console.log('桌面自动化完成!');
})();
步骤 3. 运行脚本
通过 RDP 连接远程 Windows 桌面
@midscene/computer 也可以通过专用的 agentForRDPComputer() 工厂,直接经由 RDP 协议控制远程 Windows 桌面。
前提条件
- 一台已开启 RDP 且网络可达的 Windows 机器。
- 在运行脚本的机器上安装 FreeRDP。
示例
import { agentForRDPComputer } from '@midscene/computer';
const agent = await agentForRDPComputer({
aiActionContext:
'You are controlling a remote Windows desktop over the RDP protocol.',
host: '10.75.166.249',
port: 3389,
username: 'Admin',
password: 'replace-with-your-password',
ignoreCertificate: true,
});
await agent.aiWaitFor('The remote Windows desktop is visible');
await agent.aiAct('Click the Windows Start button');
await agent.aiAct('Open Settings');
await agent.aiAssert('The Windows Settings window is visible');
常用 RDP 选项
host:远程 Windows 主机名或 IP。
port:RDP 端口,默认是 3389。
username / password:远程会话使用的账号凭据。
domain:可选的 Windows 域。
ignoreCertificate:用于跳过自签名证书校验。
desktopWidth / desktopHeight:请求指定远程桌面分辨率。
adminSession:当服务端允许时,请求远程管理员会话。
对于 Midscene 来说,一个 RDP 会话会被视为单个远程显示器。你仍然可以像本机桌面自动化一样,使用 aiAct、aiQuery、aiAssert 和报告能力。
多显示器支持
如果您有多个显示器,可以控制特定的显示器:
import { ComputerDevice, agentForComputer } from '@midscene/computer';
// 列出所有显示器
const displays = await ComputerDevice.listDisplays();
console.log('可用显示器:', displays);
// 连接到特定显示器
const agent = await agentForComputer({
displayId: displays[0].id,
});
使用示例
基本鼠标操作
// 在屏幕中心点击
await agent.aiAct('在屏幕中心点击鼠标');
// 移动鼠标到特定位置
await agent.aiAct('将鼠标移动到左上角');
// 双击
await agent.aiAct('双击桌面图标');
// 右键
await agent.aiAct('右键打开上下文菜单');
键盘操作
// 输入文本
await agent.aiAct('输入 "你好世界"');
// 按快捷键
if (process.platform === 'darwin') {
await agent.aiAct('按 Cmd+Space 打开 Spotlight');
await agent.aiAct('输入 "计算器" 并按回车');
} else {
await agent.aiAct('按 Windows 键');
await agent.aiAct('输入 "计算器" 并按回车');
}
// 按功能键
await agent.aiAct('按 Escape');
await agent.aiAct('按 Enter');
查询信息
// 提取屏幕信息
const info = await agent.aiQuery(
'{hasDesktop: boolean, visibleApps: string[]}, 检查桌面是否可见并列出可见应用'
);
// 定位 元素
const position = await agent.aiLocate('文件菜单');
console.log('文件菜单位置:', position);
复杂工作流
// 打开应用并与之交互
await agent.aiAct('打开访达');
await agent.aiWaitFor('访达窗口可见');
await agent.aiAct('点击文稿文件夹');
await agent.aiAct('按 Cmd+N 创建新文件夹');
await agent.aiAct('输入 "我的项目"');
await agent.aiAct('按回车');
await agent.aiAssert('存在名为 "我的项目" 的文件夹');
环境检查
您可以检查系统是否正确配置:
import { checkComputerEnvironment } from '@midscene/computer';
const env = await checkComputerEnvironment();
console.log('平台:', env.platform);
console.log('可用:', env.available);
console.log('显示器数量:', env.displays);
if (!env.available) {
console.error('环境不可用:', env.error);
}
常见问题
macOS:脚本无法控制鼠标或键盘
macOS 需要辅助功能权限才能控制键盘和鼠标。请前往 系统设置 > 隐私与安全性 > 辅助功能,为运行脚本的应用程序(如 Terminal、iTerm2、VS Code 或 WebStorm)开启权限。
如果已经授权但仍然无法控制,可以尝试将该应用从辅助功能列表中移除后重新添加——macOS 有时会缓存过期的权限。
Windows:点击对某些程序不生效
如果光标能移动到正确位置,但对某个程序点击或按键毫无反应,而其他程序却正常,请检查目标程序是否以管理员身份运行(已提权)。Windows 的 UIPI 机制会拦截由低权限进程注入到高权限窗口的输入,并静默丢弃,且不报任何错误。
优先尝试降低目标程序的权限级别,例如不要使用“以管理员身份运行”启动它,或关闭总是提权启动的相关设置。如果目标程序必须保持提权状态,再以管理员身份运行启动 Midscene 的终端(或 Node.js),使其与目标程序处于相同的权限级别,然后重试。像 Win+Tab 这类系统级快捷键由 shell 处理,即使在这种 情况下也照常生效——这也是为什么有时键盘快捷键看起来能用、但程序内的点击却不行。
在 Windows 上,如果 Midscene 未以管理员身份运行,连接时的健康检查日志会打印这条排查文档链接。
Linux:在无头服务器上截图或交互失败
无头 Linux 环境(如 CI)没有物理显示器,需要安装 Xvfb 和 ImageMagick,并启用无头模式:
sudo apt-get install -y xvfb x11-xserver-utils imagemagick
const agent = await agentForComputer({ headless: true });
或通过环境变量设置:
MIDSCENE_COMPUTER_HEADLESS_LINUX=true npx tsx example.ts
API 参考
本节记录了 @midscene/computer 提供的 PC 桌面特定 API。
有关适用于所有平台的通用 API,请参阅 通用 API 参考。
Agent 创建
agentForComputer(opts?): Promise<ComputerAgent>
创建用于本机桌面自动化的 agent。
向后兼容:agentFromComputer 仍可作为别名使用。
agentForRDPComputer(opts): Promise<ComputerAgent<RDPDevice>>
创建用于通过 RDP 控制远程 Windows 桌面的 agent。
参数:
interface BaseComputerAgentOpt {
// Agent 选项(继承自 AgentOpt)
aiActionContext?: string;
cache?: boolean;
// ... 其他 AgentOpt 属性
customActions?: DeviceAction<any>[];
}
interface LocalComputerAgentOpt extends BaseComputerAgentOpt {
// 本机桌面选项
displayId?: string;
headless?: boolean;
xvfbResolution?: string;
}
interface RDPComputerAgentOpt extends BaseComputerAgentOpt {
host: string;
port?: number;
username?: string;
password?: string;
domain?: string;
adminSession?: boolean;
ignoreCertificate?: boolean;
securityProtocol?: 'auto' | 'tls' | 'nla' | 'rdp';
desktopWidth?: number;
desktopHeight?: number;
}
本机桌面选项
displayId(可选):指定要控制的显示器。使用 ComputerDevice.listDisplays() 获取可用显示器。
customActions(可选):向设备添加自定义操作。
headless(可选,仅 Linux):设为 true 时通过 Xvfb 启动虚拟显示器,使桌面自动化能在无物理显示器的 Linux 服务器和 CI 环境中运行。也可通过环境变量 MIDSCENE_COMPUTER_HEADLESS_LINUX=true 设置。
xvfbResolution(可选):Xvfb 虚拟显示器的分辨率,默认为 '1920x1080x24'。
RDP 选项
host:远程 Windows 主机名或 IP。
port:RDP 端口,默认是 3389。
username / password:远程会话凭据。
domain:可选的 Windows 域。
adminSession:当服务端允许时,请求远程管理员会话。
ignoreCertificate:用于跳过自签名证书校验。
securityProtocol:可选 'auto'、'tls'、'nla' 或 'rdp'。
desktopWidth / desktopHeight:请求指定远程桌面分辨率。
示例:在无头 Linux CI 中测试 Electron 应用
示例:
import { agentForComputer } from '@midscene/computer';
// 连接到主显示器
const agent = await agentForComputer({
aiActionContext: '你正在自动化一个桌面应用。',
});
// 连接到特定显示器
const displays = await ComputerDevice.listDisplays();
const agent2 = await agentForComputer({
displayId: displays[1].id,
});
示例:通过 RDP 连接远程 Windows 桌面
import { agentForRDPComputer } from '@midscene/computer';
const agent = await agentForRDPComputer({
aiActionContext:
'You are controlling a remote Windows desktop over the RDP protocol.',
host: '10.75.166.249',
port: 3389,
username: 'Admin',
password: 'replace-with-your-password',
// 可选:把 TCP 连接绑定到这个本地源 IP。
localAddress: '10.75.166.10',
ignoreCertificate: true,
});
await agent.aiWaitFor('The remote Windows desktop is visible');
await agent.aiAct('Click the Windows Start button');
await agent.aiAct('Open Settings');
示例:通过 RDP 控制远程 Windows 桌面
当运行 Midscene 的机器存在多条出站路由,且 RDP 服务器要求从指定本地源
IP 访问时,可以使用 localAddress。这里传入的是 IP 地址,不是网卡名。
设备管理
ComputerDevice.listDisplays(): Promise<DisplayInfo[]>
列出所有可用显示器。
返回:
interface DisplayInfo {
id: string;
name: string;
primary?: boolean;
}
示例:
import { ComputerDevice } from '@midscene/computer';
const displays = await ComputerDevice.listDisplays();
console.log('可用显示器:', displays);
// [
// { id: '0', name: '内置显示器', primary: true },
// { id: '1', name: '外接显示器', primary: false }
// ]
checkComputerEnvironment(): Promise<EnvironmentCheck>
检查计算机环境是否正确配置。
返回:
interface EnvironmentCheck {
available: boolean;
error?: string;
platform: string;
displays: number;
}
示例:
import { checkComputerEnvironment } from '@midscene/computer';
const env = await checkComputerEnvironment();
console.log('环境检查:', env);
if (!env.available) {
console.error('环境错误:', env.error);
}
ComputerAgent
ComputerAgent 类继承自 PageAgent<ComputerDevice>,并继承所有通用 agent 方法:
aiAct(action: string):使用 AI 执行操作
aiQuery(query: string):使用 AI 提取信息
aiAssert(assertion: string):使用 AI 断言条件
aiWaitFor(condition: string):等待条件
aiLocate(description: string):定位元素
- 更多...
定位元素后,还可以使用即时操作(Instant Action)进行直接、确定性的控制:
aiTap()、aiDoubleClick()、aiRightClick()、aiHover():鼠标操作
aiInput()、aiClearInput()、aiKeyboardPress():键盘操作
aiScroll():滚动操作
详见 通用 API 参考。
可用操作
ComputerDevice 支持以下操作:
鼠标操作
Tap(点击)
在目标位置单击。
await agent.aiAct('点击文件菜单');
await agent.aiAct('点击屏幕中心');
DoubleClick(双击)
在目标位置双击。
await agent.aiAct('双击桌面图标');
RightClick(右键)
右键点击打开上下文菜单。
await agent.aiAct('右键点击桌面');
await agent.aiAct('右键点击文件');
MouseMove(移动鼠标 / 悬停)
将鼠标移动到目标元素上,也就是鼠标悬停(hover),例如用于触发悬停菜单或提示框。
// 自然语言形式(移动鼠标 / 悬停)
await agent.aiAct('移动鼠标到菜单项');
// 即时操作:一次调用完成定位并悬停
await agent.aiHover('菜单项「Products」');
DragAndDrop(拖放)
从一个位置拖动并放到另一个位置。
await agent.aiAct('将文件拖到文件夹');
键盘操作
KeyboardPress(按键)
按键盘按键,可选修饰键。
支持的按键:
- 普通键:
a-z、0-9、Enter、Escape、Space、Tab 等
- 方向键:
ArrowUp、ArrowDown、ArrowLeft、ArrowRight
- 功能键:
F1-F12
- 修饰键:
Command/Cmd(macOS)、Control/Ctrl、Alt、Shift、Win(Windows)
- 媒体键:
VolumeUp、VolumeDown、Mute 等
示例:
// 简单按键
await agent.aiAct('按 Enter');
await agent.aiAct('按 Escape');
// 组合键(平台特定)
if (process.platform === 'darwin') {
// macOS
await agent.aiAct('按 Cmd+Space'); // 打开 Spotlight
await agent.aiAct('按 Cmd+Tab'); // 应用切换器
await agent.aiAct('按 Cmd+C'); // 复制
await agent.aiAct('按 Cmd+V'); // 粘贴
} else {
// Windows/Linux
await agent.aiAct('按 Windows 键'); // 开始菜单
await agent.aiAct('按 Alt+Tab'); // 应用切换器
await agent.aiAct('按 Ctrl+C'); // 复制
await agent.aiAct('按 Ctrl+V'); // 粘贴
}
// 方向键
await agent.aiAct('按 ArrowDown');
await agent.aiAct('按 ArrowUp');
// 功能键
await agent.aiAct('按 F5'); // 刷新
在输入框中输入文本。
await agent.aiAct('在搜索框输入 "你好世界"');
await agent.aiAct('输入 "我的文档.txt"');
清空输入框内容。
await agent.aiAct('清空文本框');
滚动操作
滚动屏幕或特定区域。
// 滚动方向
await agent.aiAct('向下滚动');
await agent.aiAct('向上滚动');
await agent.aiAct('向左滚动');
await agent.aiAct('向右滚动');
// 滚动到位置
await agent.aiAct('滚动到顶部');
await agent.aiAct('滚动到底部');
显示器操作
ListDisplays(列出显示器)
获取所有已连接显示器的信息。
const displays = await ComputerDevice.listDisplays();
使用 RDP 时,ListDisplays 会把当前远程会话视为单个显示器返回。
示例
打开应用并导航
import { agentForComputer } from '@midscene/computer';
const agent = await agentForComputer();
// 打开应用
if (process.platform === 'darwin') {
await agent.aiAct('按 Cmd+Space');
await agent.aiAct('输入 "文本编辑" 并按回车');
} else {
await agent.aiAct('按 Windows 键');
await agent.aiAct('输入 "记事本" 并按回车');
}
await agent.aiWaitFor('文本编辑器窗口可见');
// 输入内容
await agent.aiAct('输入 "你好,Midscene!"');
// 保存文件
if (process.platform === 'darwin') {
await agent.aiAct('按 Cmd+S');
} else {
await agent.aiAct('按 Ctrl+S');
}
多显示器工作流
import { ComputerDevice, agentForComputer } from '@midscene/computer';
// 列出显示器
const displays = await ComputerDevice.listDisplays();
console.log(`找到 ${displays.length} 个显示器`);
// 控制主显示器
const agent1 = await agentForComputer({
displayId: displays[0].id,
});
await agent1.aiAct('将鼠标移动到屏幕中心');
// 控制副显示器
if (displays.length > 1) {
const agent2 = await agentForComputer({
displayId: displays[1].id,
});
await agent2.aiAct('将鼠标移动到屏幕中心');
}
Web 浏览器自动化
import { agentForComputer } from '@midscene/computer';
const agent = await agentForComputer();
// 打开浏览器
if (process.platform === 'darwin') {
await agent.aiAct('按 Cmd+Space');
await agent.aiAct('输入 "Safari" 并按回车');
} else {
await agent.aiAct('按 Windows 键');
await agent.aiAct('输入 "Chrome" 并按回车');
}
await agent.aiWaitFor('浏览器窗口已打开');
// 导航
await agent.aiAct('点击地址栏');
await agent.aiAct('输入 "example.com" 并按回车');
await agent.aiWaitFor('页面已加载');
// 提取信息
const title = await agent.aiQuery('string, 获取页面标题');
console.log('页面标题:', title);
TypeScript 类型
import type {
ComputerAgent,
ComputerAgentOpt,
ComputerDevice,
ComputerDeviceOpt,
DisplayInfo,
EnvironmentCheck,
} from '@midscene/computer';
另请参阅