昨天把本项目里 juejin 和 csdn 两个平台的发布自动化从 AppleScript / JXA 全量迁到 CDP——封面上传那块原来 250 行、7 层 fallback、调几次成几次的代码,重写完只有 30 行,一次跑通。这种"改完即过"的体验在 AppleScript 时代我们从来没拿到过。
真正让我们换路的不是"快"。是发现之前那一长串看似工程问题的失败排查——user-activation 拒绝、Vue rect 漂移、panel 闪关、osascript 卡顿、a11y 权限 transient broken、enterprise profile 静默禁端口——在 CDP 下根本不存在。它们不是"难解决的 bug",是「模拟用户」这个抽象层凭空制造的副作用。换 CDP 不是绕过这些坑,是这些坑在新抽象层下不可能发生。
这篇就说清三件事:CDP 为什么是浏览器自动化的正路;AppleScript / JXA 在哪些场景仍不可替代;浏览器以外的世界,正路的新版本叫什么。
反方先讲清楚:JXA 不是历史包袱,是兜底通路
把 AppleScript / JXA 一棒子打死成"历史包袱"是不公平的。它的正确生态位是:所有没有官方调试协议的程序,目前只有这一条通路。
我们用 JXA 操作过 Chrome——那是它的副业;它的本职是操作 Finder 整理下载、操作邮件批量归档、把当前 iMessage 会话内容贴进 Obsidian、跨多个 GUI App 编排"截图 → OCR → 翻译 → 写日记"这种工作流。这些目标程序里没有一个提供 CDP 等价物。
JXA 在浏览器场景下显得笨重,是因为浏览器有一条更好的路(CDP)。如果不给 JXA 选项 B,单看 JXA 本身:它能让我们用一个脚本驱动十几个互相不知道彼此存在的原生 App 完成自动化——这件事 Playwright 永远做不到。所以"放弃 JXA"这话只能加限定词:在浏览器自动化场景下放弃,而不是全场放弃。
论据 1:抽象层从根上就不同
先看两条路径的栈:
CDP 路径从脚本到 DOM 中间是 WebSocket + DevTools Protocol——是 Chrome官方设计来给程序调用的接口。每一跳都是结构化数据,每个命令都同步返回{method, code, message},错了就抛带堆栈的 RPC 错误。
AppleScript / JXA 路径从脚本到 DOM 中间塞了 osascript 进程跳板、Apple Events、a11y 权限、Chrome UI 层(焦点 / 键盘 / 弹窗)——所有这些层都不是为程序调用设计的,而是为模拟人类。我们把"自动化"伪装成"用户操作",浏览器把它当成"用户操作"来处理:要求 user-activation、检查焦点、给弹窗一个动画过渡时间、走系统的键盘事件路由。
每一层都是为"人"准备的。脚本要骗过每一层,就要复现人类操作的所有时序与状态约束。骗一次容易,骗一万次稳定就需要 250 行 + 7 层防御。
论据 2:「假装人类」凭空制造一整类失败模式
把过去半年我们在 AppleScript 路径上踩的坑分类,每一类都只在"假装人类"这条路径上存在:
逐条对照 CDP:
- 用户激活拒绝:CDP 用
Input.dispatchMouseEvent注入的点击带 isTrusted 标记,文件选择器、剪贴板等需要 user-activation 的 API 全部认账;JXA 注入的合成事件常被 Chromium 标 untrusted,文件输入框直接拒绝。 - Vue rect 漂移:CDP 走 DOM API 直接拿元素引用,框架重渲染不影响;JXA 拿到的是 a11y 截图里的坐标,DOM 一变坐标失效。
- panel 闪关:CDP 用
Runtime.evaluate同步控制弹窗状态机;JXA 发完点击就完事,弹窗刚 open 又被某个失焦事件 close,靠重试和 sleep 凑合。 - 权限静默失效:CDP 不依赖 macOS 权限;JXA 的 a11y / Apple Events 权限会在系统更新、应用版本升级时静默 transient broken,重启或重新授权才好。
- osascript 反馈:CDP 每个命令同步返回;osascript 是"发完不管",错了只有字符串
Can't get object或错误码-25211,没堆栈、没结构。
这五类合计 15 个具体症状,在 CDP 路径上是零。所以"换 CDP 让代码量从 250 行到 30 行"——省掉的那 220 行全部是用来应付这 15 个症状的防御代码。它不是"代码精简",是"防御对象消失"。
论据 3:七个维度全输——这不是个别项强弱
把所有维度摊开:
不存在"AppleScript 在 X 维度反超 CDP"的局面。从反馈方式(开环 vs 闭环)、依赖(macOS + a11y + osascript + 焦点 vs 一个 TCP 端口)、跨平台(仅 mac vs 三平台)、错误结构(字符串 vs JSON)、生态成熟度(自研 vs Playwright/Puppeteer 五年)、代码体量(250 vs 30)、未来友好(Chrome 持续收紧 a11y vs 官方接口)——七个维度全输。
这种全维度落后通常意味着不是工具差距,是所在抽象层差距。
重申与边界:什么场景用什么
主张落到一句操作指南:自动化任意程序之前,先问它有没有官方调试协议。
按这张图走:
- 浏览器 / 网页 →CDP(Playwright / Puppeteer)。Chrome、Edge、Firefox(曾经支持 Marionette,现也实现了部分 CDP)都行。
- 编辑器 / IDE(VS Code / Cursor / Neovim) →LSP / DAP——LSP(Language Server Protocol)是编辑器世界的 CDP,DAP(Debug Adapter Protocol)是调试器世界的 CDP,两者都是结构化、官方、跨厂商。
- 系统原生 App + 有官方 API → 走该 API。比如 Logic Pro 有 Logic Scripter,Final Cut 有 fcpxml。
- 系统原生 App + 没 API + 跨 App 协作 →JXA / AppleScript,仍然是目前唯一通路。可以加 Swift + Apple Scripting Bridge 让代码看着不那么像 1990 年代,但抽象层还是 a11y 模拟。
这条规则的边界也很清晰:只有当目标程序提供了"程序对程序"的官方协议,才能跳出"模拟人类"层。没有协议的程序,无论我们用 JXA 还是 Swift 还是 Python + pyautogui,都还在那一层里。
未来值得关注的几个方向:
- Apple Accessibility API / AXObserver——理论上是 a11y 层的"程序化接口",比 JXA 稳,但权限模型仍然走 macOS Security & Privacy,遇到的环境性问题和 JXA 同源。
- macOS Shortcuts / Intents Framework——苹果自己想替代 AppleScript,目前覆盖度还远远不够,但是趋势。
- 多模态 AI Computer Use(Claude Computer Use、OpenAI 的 Operator)——它选择了另一条相反的路:不是"找到更好的程序协议",而是"让 AI 真的像人那样看屏幕 + 用鼠标键盘"。这是把"模拟人类"做到极致——AppleScript / JXA 是 1995 年的初级模拟人类,Computer Use 是 2025 年的高级模拟人类。它解决的是"找不到调试协议时怎么办"的兜底问题,不是 CDP 的替代品,是 JXA 的替代品。
所以两条路并行:有协议走协议(CDP / LSP / DAP / 官方 API),没协议就用 AI 模拟人类(取代 JXA / AppleScript)。中间那个层——“我们手写 a11y 模拟”——会被两头挤压到只剩"过渡期内还没被任一方覆盖"的细分场景。
回到一开始那个 250 行 → 30 行的故事:CDP 不是赢在"快"。它赢在让我们不再需要每周问一遍"为什么这次又翻车了"。