第一次用 chrome-use 连上 Slack 桌面版、snapshot 出来一堆带名字的按钮时,我愣了一下:这跟操作一个网页没任何区别。后来想通了,因为 Slack 桌面版本来就是个网页。
Electron 应用是一个 Chromium 渲染引擎加一层 Node.js 外壳。你以为在用"桌面软件"的 Slack、VS Code、Discord、Figma、Notion,它们的界面全是 Chromium 渲染出来的 HTML,只是外面套了个原生窗口的壳。这个事实一旦接受,一件本来很难的事就塌方式变简单:操作桌面应用,退化成了操作网页。

桌面自动化本来有多别扭
先说没有这条路的时候,让程序操作一个桌面应用有多难。
一条路是截图、找按钮的像素坐标、点过去。分辨率一变、主题一换、窗口一挪,坐标全错;而且程序"看不懂"界面上是什么,它只看到一张图。另一条路是系统辅助功能 API(macOS 的 Accessibility、Windows 的 UIAutomation),能拿到控件树,但每个平台一套 API,而且 Electron 应用暴露给这套 API 的信息还经常残缺,因为它的"控件"其实是 div,不是原生控件。
两条路都累,而且都绕开了一个事实:这个应用内部明明有一棵结构清晰、带语义的 DOM,你却在外面隔着像素、或者隔着一层转译,去够它。
Chromium 自带一扇门
因为渲染引擎是 Chromium,Electron 应用天然会说 Chrome DevTools Protocol。启动时带上 --remote-debugging-port,它就开一个 CDP 端口,把内部那棵 DOM 直接露出来:
open -a "Slack" --args --remote-debugging-port=9222
chrome-use connect 9222
chrome-use snapshot -i
(每个应用分个不冲突的端口就行,全平台的启动命令文档站有,这里不铺开。)
snapshot 拿回来的,是那棵 DOM 的无障碍视图:每个按钮、输入框都是带名字的元素,用 @ref 定位。你不是在猜"发送按钮大概在右下角那块像素",你拿到的是那个 button "Send" 本身。窗口挪了、主题换了都不影响,因为绑的是元素,不是坐标。对 agent 来说这差别是本质的:它现在能读懂这个桌面应用长什么样,而不是对着一张截图瞎点。
一个必须知道的取舍
这里有个和浏览器场景不一样的地方,值得单独说,因为很容易想当然。
操作你日常那个 Chrome 时,chrome-use 走的是浏览器扩展加 native messaging,不碰调试端口,所以没有"是否允许远程调试"的弹窗,也没有 Runtime.enable 那类协议层痕迹。但 Electron 应用装不了 Chrome 应用商店的扩展,所以这条路只能走 --remote-debugging-port。换句话说,浏览器场景下那些"零痕迹"的优势,到 Electron 这儿是拿不到的,前提就是你自己开一个调试端口连进去。
对自动化 Slack、VS Code 这种"你自己的应用"来说,这完全不是问题:你不是要骗过谁,是要自动化你自己已经登录的工具。但如果脑子里装着浏览器那套"不可检测"的预期跑来操作 Electron,得先把预期调过来:这是"连进自己的应用",不是"隐身操作别人的网站"。
一个有意思的细节:应用里还能套应用
Electron 应用内部经常再嵌 <webview>,比如 Slack 里打开的某个第三方应用面板,本质是又一个独立网页。这些在 CDP 里是独立的 target。连上后先 chrome-use tab 列一下,你会看到主窗口、设置窗口、还有那些 webview 各自是一个 target,可以分别切进去操作。
所以一个 Electron 应用不是"一个网页",是"一叠网页套在一个原生壳里"。想清楚这点,多窗口、嵌套面板这些本来挠头的场景,就都变成了"切到对的那个 target"而已。
边界
这套只对 Chromium 内核的应用成立。用 Swift/AppKit 写的纯原生应用没有 CDP 口,连不上,那种还得回到系统辅助功能,或者换别的工具(操作 iPhone 上的原生 app 我用的是另一套)。判断也简单:Slack、VS Code、Discord、Figma、Notion、Spotify 这类"一套代码跑多平台"的桌面应用,基本都是 Electron。
想通"桌面应用其实是网页"之后,我现在看到一个桌面工具,第一反应是先确认它是不是 Electron。是的话,操作它就不再是"桌面自动化"这个我一直觉得很脏的活,而是我已经很熟的那套开网页的办法。

微信
支付宝
评论
评论发布后会立即公开,如触发规则可能被审核下架。