跳转到内容

02 Webview 与 Extension Host 通信

学习目标

本章要解决什么问题:Cline 为什么选择 gRPC-like 协议而非 VS Code 原生 postMessage?如何实现流式响应和调试?


项目实践

通信架构

Proto 文件与代码生成

协议定义在 proto/cline/ 目录下,通过 npm run protos 生成 TypeScript 代码:

proto/
└── cline/
├── webview.proto → Webview → Extension Host 请求
└── ... → 其他服务定义

Proto 文件定义服务接口和消息类型,通过 protobuf 编译器生成类型安全的 TypeScript 客户端和服务器代码。

gRPC-like 协议设计

一元请求:Webview 发送单次请求,Extension Host 返回单次响应。

# 伪代码:一元请求
const response = await grpcClient.send({
method: "getSettings",
params: {}
})

流式响应:Webview 发送请求后,Extension Host 持续返回流式数据。

# 伪代码:流式响应
for await (const chunk of grpcClient.stream({
method: "startTask",
params: { prompt: "..." }
})) {
renderChunk(chunk)
}

支持流式的关键:VS Code 的 postMessage API 本身支持单向消息传递,Cline 在此基础上构建了请求-响应-流式的模式:

  1. Webview 发送请求消息,附带唯一 ID
  2. Extension Host 处理请求,通过多个 postMessage 返回流式响应
  3. Webview 根据请求 ID 将流式响应聚合

grpc-recorder 录制/重放

grpc-recorder 是 Cline 的调试工具:

  • 录制模式:记录所有 gRPC 请求和响应到文件
  • 重放模式:从文件读取请求和响应,模拟 Extension Host 行为

用途

  • 复现特定的交互序列
  • 调试流式解析问题
  • 测试 UI 渲染逻辑,无需实际运行 Agent

为什么选 gRPC-like 而非简单 postMessage?

对比项原生 postMessagegRPC-like
类型安全无,手动定义消息格式Proto 生成,类型安全
流式支持需要手动实现内置流式语义
可调试性难以追踪消息流grpc-recorder 录制/重放
跨平台仅 VS CodeProto 定义可在其他平台复用

问题与规避

陷阱场景对策
消息丢失postMessage 在页面刷新后丢失Webview 状态持久化,刷新后恢复
流式消息乱序并发流式响应交错使用请求 ID 区分不同流
Proto 文件不同步修改 proto 后未重新生成CI 检查生成的 TypeScript 文件是否最新

设计取舍

gRPC-like vs WebSocket

方案优势代价
gRPC-like类型安全、Proto 可复用、流式语义清晰需要代码生成,理解成本高
WebSocket实现简单、实时双向无类型保证、消息格式需要手动约定

Cline 选择 gRPC-like,因为类型安全对于工具调用协议至关重要,且 Proto 定义可在 SDK 的其他宿主中复用。


参考来源

  • Cline 源码:apps/vscode/proto/cline/webview.proto
  • Cline 源码:apps/vscode/src/core/controller/grpc-handler.ts