跳到主要内容

数据截至 (上游 commit 544ec87f2ede)

第 1 章 v4 的外科手术 —— 协议与瘦客户端

本章讲什么: v4 为什么敢把引擎搬进浏览器扩展——因为它先把「客户端 ↔ 引擎」的界面冻成了一份带版本号的协议。本章讲这张契约长什么样、版本怎么协商、客户端薄到什么程度。

1. 为什么需要一张正式协议

v3 时代,Stagehand 是一个 TypeScript npm 包:引擎和你的代码同进程。这有一个隐形上限——引擎被锁死在 JS 生态里,而且引擎升级就逼客户端升级。

v4 把引擎做成 Chrome 扩展后,客户端和引擎之间隔着进程边界,必须有一份正式契约:

  • packages/protocol/stagehand.v4.json:全部方法(init/act/extract/observe/close…)、参数 schema、通知类型的机器可读清单;
  • packages/protocol/types.ts + schemas.ts:TS 侧的类型与 zod 校验;
  • packages/protocol/protocol-version.ts:版本协商逻辑。

2. 版本协商:SemVer 主版本不对等就拒连

协议版本直接取 packages/protocol/package.json 的 version 字段,经 SemVer 正则校验后成为 STAGEHAND_PROTOCOL_VERSION(protocol-version.ts:14-16)。

扩展侧在 init 时检查客户端报上来的版本(controllers/stagehandController.tsinit 里调 checkProtocolCompatibility):

  • 非法版本 → protocol-invalid-version;
  • 主版本不同 → protocol-major-mismatch;
  • 服务端比客户端老 → protocol-server-too-old;
  • 预发布号不一致 → protocol-prerelease-mismatch

不兼容就抛 StagehandProtocolCompatibilityError——宁可连不上,不让两个不同代的端在半路鸡同鸭讲。这是多语言客户端时代的必需品:Go 客户端和 TS 客户端可能差着好几个发布周期。

3. 客户端薄到什么程度

以 TS SDK 为例(packages/sdk-ts/src/stagehand.ts):

  1. 参数校验:zod schema(StagehandClientActOptionsSchema 等)在客户端就先拦一遍;
  2. 打包:act 的重载(:206-208)把「字符串指令」和「Action 对象」归一成协议参数;
  3. 发请求:RPCClient(sdk-ts/src/rpcClient.ts)经 CDP/WebSocket 把 JSON-RPC 发给扩展,收结果。

客户端不知道什么 a11y 树、什么 XPath——它面对的只是「指令进去、ActResult 出来」。BrowserContext/claimStagehandBrowser(sdk-ts/src/stagehand.ts imports)负责的是另一半:帮你在本地 Chrome 里装上扩展、认领一个浏览器实例。

Go/Python 端同理(packages/sdk-gopackages/sdk-python),同一张 stagehand.v4.json 是三端共同的事实源。

4. 这个拆法换来了什么

  • 多语言几乎免费:新语言 = 一个 JSON-RPC 客户端 + 协议 schema;
  • 引擎贴着浏览器跑:扩展和被控页面同源同进程,CDP 调用不必绕远端;
  • 版本各自演进:协议主版本是唯一的硬对齐点,次版本可以错开;
  • 代价:多一个「装扩展、连扩展」的启动环节(claimStagehandBrowser),调试链变长——排错时要分清问题在客户端、协议还是扩展。