本页译自英文文档。命令、标识符和示例保持原样。运行时 7.24.4 · SDK 2.6.7。 英文原文
gRPC 与原生 SDK 传输
状态:生效 范围:桌面原生传输契约 最近审阅:2026-09-02 负责人:ax-code sdk
AX Code 现在为桌面和原生 GUI 应用提供可选的、形如 gRPC 的传输契约。该契约有意窄于完整的 HTTP/OpenAPI 路由树:它聚焦 GUI 要显得原生所需的无头运行时能力,同时在内部保留 HTTP/OpenAPI,以用于兼容、诊断和生成的客户端。
建议
把 gRPC/原生传输作为第一方桌面应用的首选边界。保留 HTTP/SSE 作为回退和调试表面。
| 需求 | 推荐路径 | 原因 |
|---|---|---|
| 第一方桌面 GUI | @defai-digital/ax-code-sdk/grpc |
稳定的命令/事件契约、支持流式、对原生友好的元数据与截止时间 |
| 同一进程中的 TypeScript 自动化 | @defai-digital/ax-code-sdk,配合 createAgent() |
开销最低,并支持自定义工具 |
| 浏览器、WebView 回退或简易诊断 | 带 @defai-digital/ax-code-sdk/headless 的 HTTP/SSE |
可用于 fetch、curl、浏览器开发者工具以及当前的服务器认证控制 |
| 外部非 JS 集成 | gRPC proto 或 OpenAPI 生成的客户端 | 工具支持面广,无需维护第一方 HTTP SDK |
| Rust 宿主嵌入 | gRPC/原生服务或子进程桥接 | 避免向应用外壳暴露完整的 HTTP 路由树 |
为何不移除 HTTP
从运行时移除 HTTP/OpenAPI 会去掉最可检查、最可移植的兼容路径。JavaScript SDK 不应把 HTTP 客户端/服务器子路径当作第一方支持表面来暴露,但内部 HTTP 桥接对诊断、现有无头后端启动和生成客户端工作流仍然有用。当前的 HTTP 服务器控制包括强制仅环回绑定、SDK 管理的后端辅助程序中生成的 Basic Auth 凭据、对变更类浏览器请求的来源检查、目录校验、请求速率限制,以及仅环回的实时 OpenAPI 文档。
对普通智能体回合而言,传输不是主要延迟来源。LLM 调用、shell 命令、文件 IO、索引、LSP 启动和工具执行通常比 localhost JSON 更昂贵。gRPC 对桌面 GUI 仍然有用,因为它提供更干净的原生 API 契约、截止时间、元数据、服务器流,以及通往 Unix 套接字或命名管道传输的路径,而不必把面向浏览器的 API 拖进应用外壳。
契约形态
语言中立的契约位于 packages/sdk/proto/ax_code/v1/headless.proto。JSR 包也把该 proto 作为资源包含在内。TypeScript 宿主用 resolveAxCodeGrpcProtoUrl() 定位它;非 JavaScript 生成器应使用仓库中的规范契约。
TypeScript 外观位于 @defai-digital/ax-code-sdk/grpc,覆盖:
- 健康检查与生命周期就绪
- 应用日志采集,以及供原生宿主生命周期管理使用的实例释放/重启控制
- 会话创建
- 提示、命令、shell、中止、权限回复和问题回复
- 面向提供方、会话、权限、问题、路径、VCS、LSP、MCP、格式化器和命令状态的 GUI 引导快照
- 会话列表、详情、消息历史、消息详情、子项、目标、待办、差异、分叉、分享和摘要操作
- 面向智能体、技能、项目、路径、VCS、命令、文件树/内容/状态、文本/文件/符号搜索和工具模式的 GUI 发现与工作区导航
- 项目上下文、上下文模板、缓存记忆的刷新/清除,以及调试引擎的待处理计划诊断
- 供受监督 GUI 流程使用的待处理权限与问题的列出/回复/拒绝操作
- 供 GUI 设置画面使用的提供方、配置、API 密钥认证和提供方 OAuth 设置
- 自主模式、隔离模式和智能 LLM 路由的运行时设置控制
- MCP 状态、资源发现、动态服务器管理、OAuth、连接和断开控制
- 供诊断和设置画面使用的 LSP 与格式化器状态
- PTY 终端管理与双向终端流
- 供审阅/调试界面使用的会话证据
- 任务队列操作
- 计划任务操作
- 工作流模板、工作流运行、仪表板摘要、评估用例、工作流例程和运行产物
- 服务器流式的运行时事件
proto 对命令体以及工作流/任务载荷使用结构化 JSON 载荷。这样在 AX Code 运行时模式快速演进时,传输仍保持稳定。
@defai-digital/ax-code-sdk/grpc 还导出 AX_CODE_GRPC_METHOD_DESCRIPTORS、listAxCodeGrpcMethods()、getAxCodeGrpcMethodDescriptor()、assertAxCodeGrpcMethodSupported()、listMissingAxCodeGrpcNativeHandlers() 和 assertAxCodeGrpcNativeHandlers()。原生宿主在构建处理程序映射、gRPC 服务绑定器、预加载允许列表或启动门控时,应把这些描述符和覆盖检查当作规范的方法目录。每个描述符包含方法名、完全限定的方法路径、流种类、proto 请求与响应消息名、GUI 域、HTTP 桥接可用性以及当前稳定性。这样可以明确原生传输边界,而不暴露或镜像完整的 HTTP 路由树。
TypeScript 用法
当宿主内部仍需要现有 HTTP 运行时时,使用 SDK 管理的 gRPC 无头后端。它把 HTTP 桥接留在宿主进程内,只返回 gRPC 客户端和生命周期句柄:
import {
createAxCodeGrpcClientFromNativeBridge,
resolveAxCodeGrpcProtoUrl,
startAxCodeGrpcHeadlessBackend,
} from "@defai-digital/ax-code-sdk/grpc"
const backend = await startAxCodeGrpcHeadlessBackend({ directory: "/workspace/app" })
try {
const client = backend.client
const session = await client.createSession({ title: "GUI session" })
const messages = await client.session.messages((session as { id: string }).id, { limit: 50 })
const skills = await client.app.skills()
const readme = await client.file.read("README.md")
const authMethods = await client.provider.auth()
const bootstrap = await client.bootstrap.load({
include: { sessions: true, providers: true, providerList: true, path: true, vcs: true },
})
const terminal = (await client.pty.create({ title: "GUI shell" })) as { id: string }
const protoUrl = resolveAxCodeGrpcProtoUrl()
await client.sendPrompt((session as { id: string }).id, {
parts: [{ type: "text", text: "Review this workspace" }],
})
for await (const event of client.subscribeEvents({ sessionID: (session as { id: string }).id })) {
if (event.type === "server.heartbeat") continue
// Project event into GUI state.
}
} finally {
await backend.close()
}
当桌面宿主通过 Electron preload、Tauri 命令或其他结构化克隆边界拥有特权运行时边界时,使用原生 IPC 桥接。IPC 调用有意省略 AbortSignal,并把双向输入流留在调用载荷之外,以便调用对象能干净地跨越渲染器/宿主边界:
const client = createAxCodeGrpcClientFromNativeIpc({
unary(call) {
return window.axCodeNative.unary(call)
},
serverStream(call) {
return window.axCodeNative.serverStream(call)
},
bidiStream(call, input) {
return window.axCodeNative.bidiStream(call, input)
},
})
仅当双方处于同一个 JavaScript 领域时才使用 createAxCodeGrpcClientFromNativeBridge(),此时可以在调用对象中安全地直接传递 AbortSignal 和异步可迭代对象。
若宿主暴露推送式订阅,使用 createAxCodeGrpcNativeIpcBridgeFromChannels() 或 createAxCodeGrpcNativeIpcStream() 把宿主回调适配成 gRPC SDK 期望的 AsyncIterable 流。这些辅助函数适用于 Tauri 事件监听器、Electron preload 回调,以及其他返回取消订阅函数而不是 JavaScript 异步生成器的 IPC 系统。
原生宿主也可以暴露处理程序映射,而不必手写方法分支。这对 Rust/Tauri 命令、Electron preload API,或希望逐方法绑定 AX Code 运行时操作的真实本地 gRPC 服务器很有用。在把桥接交给渲染器代码之前,用方法描述符校验每个预期域都已覆盖:
import {
AX_CODE_GRPC_METHOD,
assertAxCodeGrpcNativeHandlers,
createAxCodeGrpcNativeBridgeFromHandlers,
listAxCodeGrpcMethods,
} from "@defai-digital/ax-code-sdk/grpc"
const handlers = {
unary: {
[AX_CODE_GRPC_METHOD.GetSession](request, options) {
return runtime.getSession(request.sessionID, options)
},
},
serverStream: {
[AX_CODE_GRPC_METHOD.SubscribeEvents](_request, options) {
return runtime.events(options)
},
},
bidiStream: {
[AX_CODE_GRPC_METHOD.ConnectPty](request, input, options) {
return runtime.connectPty(request.id, input, options)
},
},
}
const mcpMethods = listAxCodeGrpcMethods({ domain: "mcp" })
const streamingMethods = listAxCodeGrpcMethods({ kind: "serverStream" })
const ptyDescriptor = listAxCodeGrpcMethods({ kind: "bidiStream" })[0]
// ptyDescriptor.requestType === "PtyClientEvent"
// ptyDescriptor.responseType === "PtyServerEvent"
assertAxCodeGrpcNativeHandlers(handlers, {
methods: [AX_CODE_GRPC_METHOD.GetSession, AX_CODE_GRPC_METHOD.SubscribeEvents, AX_CODE_GRPC_METHOD.ConnectPty],
})
const bridge = createAxCodeGrpcNativeBridgeFromHandlers(handlers, {
requireHandlers: {
methods: [AX_CODE_GRPC_METHOD.GetSession, AX_CODE_GRPC_METHOD.SubscribeEvents, AX_CODE_GRPC_METHOD.ConnectPty],
},
})
bootstrap.load() 有意做成面向 GUI 的快照,而不是每条 HTTP 路由的一一复制。使用 include 只请求当前视图所需的状态。失败的子请求报告在 errors 中,成功字段仍会返回,因此缺少某个可选子系统不会阻止桌面外壳打开。
事件流接受可选的 types 和 sessionID 过滤器。原生传输应在服务端应用这些过滤器。HTTP 兼容桥接在现有 SSE 路由上于客户端应用相同过滤器,以便在原生服务器实现期间,GUI 代码可以保持同一种订阅形态。
startAxCodeGrpcHeadlessBackend() 是首选的临时回退,用于宿主内部仍启动 ax-code serve 的情况。它不返回 HTTP URL 或授权头,因此渲染器代码可以针对 gRPC 外观来写,日后迁到真实的原生 gRPC 传输时无需改写公共 API。
基于 Node 的桌面宿主可以用 @defai-digital/ax-code-sdk/grpc/node 从同一原生桥接暴露真实的 HTTP/2 gRPC 端点。这面向特权宿主进程,而不是渲染器代码:
import { createAxCodeGrpcNativeBridgeFromHandlers, AX_CODE_GRPC_METHOD } from "@defai-digital/ax-code-sdk/grpc"
import { startAxCodeGrpcNodeHttp2Server } from "@defai-digital/ax-code-sdk/grpc/node"
const bridge = createAxCodeGrpcNativeBridgeFromHandlers({
unary: {
[AX_CODE_GRPC_METHOD.Health]() {
return { status: "SERVING" }
},
},
serverStream: {
[AX_CODE_GRPC_METHOD.SubscribeEvents](request) {
return runtime.subscribeEvents(request)
},
},
})
const server = await startAxCodeGrpcNodeHttp2Server({ bridge, host: "127.0.0.1" })
try {
// Native clients can generate from ax_code/v1/headless.proto and connect to server.url.
} finally {
await server.close()
}
PTY 流被建模为 gRPC 双向流。HTTP 桥接为兼容而把该流适配到现有 WebSocket 路由;原生 GUI 宿主应在其本地 gRPC、Unix 套接字或命名管道传输上实现它,而不是向渲染器代码暴露 WebSocket 路由。
当真实的 gRPC 传输可用时,把它提供给 createAxCodeGrpcClient({ transport })。高层客户端保持不变。
安全态势
对桌面应用,优先采用以下顺序:
- 当 GUI 是 TypeScript 且可以安全加载运行时时,使用进程内 SDK。
- 经环回、Unix 套接字或命名管道的本地 gRPC/原生传输。
- 带生成的一次性 Basic Auth 凭据的 HTTP/SSE 无头桥接。
- 不要通过网络 HTTP 暴露 AX Code。
gRPC HTTP 兼容桥接和 SDK 管理的 HTTP 后端辅助程序只接受字面环回端点。旧的 allowRemoteHttpBridge 和 allowNetworkBind 选项为源码兼容而保留,但不会绕过仅本地策略。把 /doc 限制在环回服务器上。
HTTP 兼容桥接默认拒绝跨源 WebSocket 升级。仅当该浏览器来源属于受信任的应用外壳时,才把来源加入显式的服务器 CORS 允许列表。
不要向任意 WebView 暴露完整的 HTTP API、PTY WebSocket 或 OpenAPI 文档。若使用 WebView,把它保持为渲染器,并通过原生宿主使用 gRPC/原生外观来路由特权操作。