@opencode/client 是 OpenCode HTTP API 的 TypeScript 客户端。当你的应用需要通过网络连接到 OpenCode 服务器时,可以使用它。它的原生类型和方法与 API 参考 基于同一份契约生成。插件 RPC 类型来自导入的 RPC 定义。

安装

bun add @opencode/client

创建客户端

使用服务器 URL 创建客户端,然后按 API 资源分组调用方法:
import { OpenCode } from "@opencode/client"

const client = OpenCode.make({
  baseUrl: "http://localhost:4096",
})

const session = await client.session.create({
  location: { directory: "/workspace" },
})

await client.session.prompt({
  sessionID: session.id,
  text: "Review the current changes",
})

请求头与请求

通过 headers 将默认的身份验证或应用请求头传给 OpenCode.make。你也可以提供自定义的 fetch 实现。每个操作都接受请求选项作为最后一个参数,用于设置 AbortSignal 或单次请求的请求头。事件订阅是例外:它们只接受订阅者本地的取消,并使用基础客户端的请求头。
const client = OpenCode.make({
  baseUrl: "https://opencode.example.com",
  headers: {
    authorization: `Bearer ${process.env.OPENCODE_TOKEN}`,
  },
})

await client.session.list(undefined, {
  signal: AbortSignal.timeout(10_000),
})

流式事件

流式端点返回异步可迭代对象:
for await (const event of client.event.subscribe()) {
  console.log(event.type)
}
原生和 RPC 事件订阅者在同一客户端上共享一个惰性连接。创建客户端、句柄和可迭代对象都不会打开事件连接;开始消费时才会建立连接。中断迭代或中止某个订阅者只会结束该迭代器。最后一个订阅者离开时会关闭连接。共享源会等待活跃的订阅者接收每个事件;消费者在执行耗时操作前应先进行缓冲。
订阅仅提供实时数据,没有回放,也不会自动重连。源故障会结束当前订阅;恢复后请重新订阅。较晚加入的原生订阅者会收到当前的 server.connected 标记,而不会收到过去的业务事件。

插件 RPC

导入插件的共享契约并传给 client.rpc:
import { OpenCode } from "@opencode/client"
import { Acme } from "opencode-acme-plugin/rpc"

const acme = client.rpc(Acme)
const result = await acme.search(
  { query: "hello" },
  { location: { directory: "/workspace" }, signal: AbortSignal.timeout(10_000) },
)

const unsubscribe = acme.events.on("updated", (event) => {
  console.log(event.type, event.location.directory, event.data.text)
})
方法的第二个可选参数包含 location、signal 和 headers,与插件定义的输入分开。省略 location 时遵循原生请求默认值:先使用基础 location 请求头,然后使用服务器的工作目录。构造子客户端时不会选择任何 location。
方法从定义中推断参数和结果。Schema 解析属于契约边界;调用方发送被接受的输入表示形式,而处理方接收解析后的值。Promise 客户端接受 Standard Schema 或普通 JSON Schema 定义,不在本地运行 schema 解析器:服务器返回已经解析和转换后的输出。Effect Schema 定义需要使用 Effect 客户端。
已声明的方法错误会像其他 Promise 客户端端点的错误一样拒绝,捕获到的错误保持无类型。通用 HTTP RPC 错误包装器会被类型化子客户端移除。保留的 rpc.* 框架故障仍是普通的 RPC 故障,而无关的身份验证、传输和协议错误保持其正常的客户端表示形式。
RPC 订阅使用本地名称,并接收该 RPC 在所有位置的事件。检查必需的 event.location 来筛选事件。events.subscribe 与原生异步可迭代对象 API 一致:
for await (const event of acme.events.subscribe("updated")) {
  console.log(event.data.text)
}
events.on 是对同一源的便捷包装。它返回取消订阅函数;异步回调会被顺序等待。回调或源故障会被记录并结束该监听器。原生订阅和类型化订阅接收相同的普通 rpc.<rpcID>.<event> 信封,其中事件数据是直接的对象。实时订阅不会回放错过的事件。
服务器插件必须已配置并实现该 RPC;导入定义不会注册它。有关定义和注册 API,请参阅插件 RPC。任何 HTTP 客户端也可以调用通用的 POST /api/rpc/{rpcID}/{method} 路由,传入 { "input": ... } 并接收 { "output": ... }。省略的 input/output 字段表示没有值。

本地后台服务

客户端主入口与浏览器兼容,不包含本地进程管理。在 Node 应用中,从 @opencode/client/service 导入原生 Promise 服务 API。
  • Service.discover() 返回一个健康的已注册端点,且不启动进程。
  • Service.ensure() 返回一个兼容的服务,必要时启动一个。
  • Service.stop() 停止确切的已注册服务实例。
  • Service.headers(endpoint) 为客户端创建身份验证请求头。
import { OpenCode } from "@opencode/client"
import { Service } from "@opencode/client/service"

const endpoint = await Service.ensure()
const client = OpenCode.make({
  baseUrl: endpoint.url,
  headers: Service.headers(endpoint),
})

const info = await client.server.info()
Service.ensure() 接受可选的注册文件、版本、服务命令和 onStart 回调。version 接受精确值或兼容性谓词:
const endpoint = await Service.ensure({
  file: "/var/run/opencode/service.json",
  version: (version) => version.startsWith("2."),
  command: ["opencode", "serve", "--service"],
  onStart(reason, previousVersion) {
    console.log(reason, previousVersion)
  },
})
省略这些选项则使用标准注册路径和 opencode serve --service 命令。