OpenCode SDK 开发实战:用 TypeScript 编程式控制 AI 编程助手

OpenCode SDK 开发实战:用 TypeScript 编程式控制 AI 编程助手

引言

OpenCode 作为终端中的 AI 编程助手,绝大多数用户通过 TUI(终端用户界面)以对话的形式与它交互。但你可能不知道,OpenCode 还提供了功能强大的 JavaScript/TypeScript SDK——@opencode-ai/sdk,让你能够以编程方式创建会话、发送指令、搜索文件、订阅实时事件,甚至构建完整的自动化工作流。

本文将深入介绍 OpenCode SDK 的安装、核心概念、常用 API 和实战场景,帮助你将 AI 编程能力嵌入到自己的工具链中。

什么是 OpenCode SDK

OpenCode SDK 是一个类型安全的 JavaScript 客户端,它通过 HTTP 协议与 OpenCode Server 通信。当你通过 SDK 创建一个 OpenCode 实例时,底层会自动启动一个 Server 进程,所有 TUI 中能做的事情——创建会话、发送 prompt、读写文件、执行 shell 命令——SDK 都能做到,而且是编程式的、可复用的。

相比于在终端中手动交互,SDK 的优势在于:

  • 自动化:批量处理任务,无需人工逐条输入指令
  • 集成能力:可以将 OpenCode 嵌入到 CI/CD 流水线、代码审查工具或自定义 IDE 插件中
  • 结构化输出:支持 JSON Schema 约束模型输出,让 AI 返回机器可读的格式化数据
  • 事件驱动:订阅 Server-Sent Events,实时响应 AI 的操作

安装与快速上手

安装 SDK

npm install @opencode-ai/sdk

SDK 包名是 @opencode-ai/sdk,支持 TypeScript 类型定义,无需额外安装 @types 包。

创建第一个客户端

import { createOpencode } from "@opencode-ai/sdk";

async function main() {
  const opencode = await createOpencode();

  console.log(`Server 运行在: ${opencode.server.url}`);

  // 用完记得关闭
  opencode.server.close();
}

main();

调用 createOpencode() 后,SDK 会在 127.0.0.1:4096 启动一个 Server 进程,并返回包含 clientserver 两个对象的实例。client 是你调用 API 的入口,server 用于管理服务端生命周期。

连接已有 Server

如果你已经有一个运行中的 OpenCode 实例,可以跳过启动 Server 这一步:

import { createOpencodeClient } from "@opencode-ai/sdk";

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
});

这在生产环境中尤其有用——你可以单独部署 OpenCode Server,然后多个客户端共享同一个实例。

核心 API 速览

SDK 的 API 按功能分为以下几大类,所有方法都有完整的 TypeScript 类型:

| 分类 | 说明 | 核心方法 |
|------|------|----------|
| Global | 服务健康检查 | client.global.health() |
| Project | 项目管理 | project.list(), project.current() |
| Session | 会话管理 | session.create(), session.prompt(), session.command() |
| Files | 文件搜索与读取 | find.text(), find.files(), file.read() |
| TUI | TUI 界面控制 | tui.appendPrompt(), tui.showToast() |
| Config | 配置与提供商 | config.get(), config.providers() |
| Auth | 认证管理 | auth.set() |
| Events | 事件流订阅 | event.subscribe() |

下面我们深入几个最常用的场景。

会话管理:从创建到执行

会话(Session)是 OpenCode 中的核心概念,代表一次对话的上下文。SDK 提供了完整的会话生命周期管理。

创建会话

const session = await opencode.client.session.create({
  body: {
    title: "代码重构任务",
  },
});

console.log(`会话 ID: ${session.data.id}`);

发送 Prompt 并获取响应

const result = await opencode.client.session.prompt({
  path: { id: session.data.id },
  body: {
    parts: [
      {
        type: "text",
        text: "请分析 src/utils/helper.ts 中的函数,找出可以优化的地方",
      },
    ],
  },
});

console.log(result.data.info);

parts 数组支持多种类型:text(文本)、file(文件引用)、image(图片拖拽)等,这与 TUI 中的 @ 引用和拖拽图片功能对应。

在不触发 AI 回复的情况下注入上下文

有时候你只想往会话中注入一些背景信息,而不触发 AI 的回复。设置 noReply: true 即可:

await opencode.client.session.prompt({
  path: { id: session.data.id },
  body: {
    noReply: true,
    parts: [
      {
        type: "text",
        text: "项目背景:这是一个电商后端系统,使用 Node.js + Express + PostgreSQL。",
      },
    ],
  },
});

这个功能非常适合插件开发——你可以预先注入项目规范、编码约定等上下文,再让用户自由提问。

执行斜杠命令

TUI 中的斜杠命令(如 /init/share/undo)也可以通过 SDK 调用:

// 初始化项目,生成 AGENTS.md
await opencode.client.session.command({
  path: { id: session.data.id },
  body: { command: "/init" },
});

// 分享会话
const shared = await opencode.client.session.share({
  path: { id: session.data.id },
});
console.log(`分享链接: ${shared.data.share}`);

撤销和恢复

和 TUI 中的 /undo/redo 对应:

// 撤销最近一次操作
await opencode.client.session.revert({
  path: { id: session.data.id },
  body: { messageID: "msg_xxx" },
});

// 恢复已撤销的操作
await opencode.client.session.unrevert({
  path: { id: session.data.id },
});

文件系统操作:搜索与读取

SDK 提供了比 TUI 更精细的文件操作能力,可以批量搜索、过滤文件类型。

全文搜索

const textResults = await opencode.client.find.text({
  query: { pattern: "function\\s+handleLogin" },
});

for (const match of textResults.data) {
  console.log(`文件: ${match.path}, 行号: ${match.line_number}`);
  for (const line of match.lines) {
    console.log(`  ${line}`);
  }
}

文件名搜索

// 搜索所有 TypeScript 文件
const tsFiles = await opencode.client.find.files({
  query: { query: "*.ts", type: "file" },
});

// 搜索所有目录
const dirs = await opencode.client.find.files({
  query: { query: "components", type: "directory", limit: 20 },
});

读取文件内容

const content = await opencode.client.file.read({
  query: { path: "src/index.ts" },
});

// content.data.type 可能是 "raw"(原始内容)或 "patch"(diff 补丁)
console.log(content.data.content);

结构化输出:让 AI 返回格式化数据

这或许是 SDK 相比 TUI 交互最强大的差异之一。你可以通过 JSON Schema 约束模型的输出格式,让 AI 返回可被程序解析的结构化数据。

基本用法

const result = await opencode.client.session.prompt({
  path: { id: session.data.id },
  body: {
    parts: [
      {
        type: "text",
        text: "分析 src/api/routes.ts 中定义的所有 API 路由",
      },
    ],
    format: {
      type: "json_schema",
      schema: {
        type: "object",
        properties: {
          routes: {
            type: "array",
            items: {
              type: "object",
              properties: {
                method: { type: "string", description: "HTTP 方法" },
                path: { type: "string", description: "路由路径" },
                handler: { type: "string", description: "处理函数名" },
                middleware: {
                  type: "array",
                  items: { type: "string" },
                  description: "使用的中间件列表",
                },
              },
              required: ["method", "path", "handler"],
            },
          },
          totalCount: { type: "number", description: "路由总数" },
        },
        required: ["routes", "totalCount"],
      },
    },
  },
});

// 直接使用结构化数据
const { routes, totalCount } = result.data.info.structured_output;
console.log(`发现 ${totalCount} 个路由:`);
for (const route of routes) {
  console.log(`  ${route.method} ${route.path} -> ${route.handler}`);
}

错误处理与重试

当 AI 未能生成符合 Schema 的输出时,SDK 会返回 StructuredOutputError

if (result.data.info.error?.name === "StructuredOutputError") {
  console.error("结构化输出失败:", result.data.info.error.message);
  console.error("重试次数:", result.data.info.error.retries);
}

可以通过 retryCount 参数调整重试次数(默认为 2):

format: {
  type: "json_schema",
  schema: { /* ... */ },
  retryCount: 5, // 复杂 Schema 建议增加重试次数
}

实战场景:代码质量报告生成器

来看一个完整的实战例子——用结构化输出自动生成代码质量报告:

import { createOpencode } from "@opencode-ai/sdk";

interface CodeIssue {
  file: string;
  line: number;
  severity: "error" | "warning" | "info";
  category: string;
  message: string;
  suggestion?: string;
}

interface QualityReport {
  score: number;
  totalIssues: number;
  issues: CodeIssue[];
  summary: string;
}

async function generateCodeReport(
  client: Awaited<ReturnType<typeof createOpencode>>["client"],
  sessionId: string
): Promise<QualityReport> {
  const result = await client.session.prompt({
    path: { id: sessionId },
    body: {
      parts: [
        {
          type: "text",
          text: "请全面审查当前项目的代码质量,关注命名规范、错误处理、性能、安全问题。",
        },
      ],
      format: {
        type: "json_schema",
        retryCount: 3,
        schema: {
          type: "object",
          properties: {
            score: {
              type: "number",
              description: "代码质量评分,0-100",
            },
            totalIssues: {
              type: "number",
              description: "问题总数",
            },
            issues: {
              type: "array",
              items: {
                type: "object",
                properties: {
                  file: { type: "string" },
                  line: { type: "number" },
                  severity: {
                    type: "string",
                    enum: ["error", "warning", "info"],
                  },
                  category: { type: "string" },
                  message: { type: "string" },
                  suggestion: { type: "string" },
                },
                required: ["file", "line", "severity", "category", "message"],
              },
            },
            summary: {
              type: "string",
              description: "总结性评价",
            },
          },
          required: ["score", "totalIssues", "issues", "summary"],
        },
      },
    },
  });

  return result.data.info.structured_output;
}

事件订阅:实时监控 AI 行为

SDK 通过 Server-Sent Events 提供了实时事件流。你可以监听会话进展、权限请求、工具调用等事件:

const events = await opencode.client.event.subscribe();

for await (const event of events.stream) {
  switch (event.type) {
    case "permission.asked":
      console.log(`权限请求: ${event.properties.tool_name}`);
      // 自动批准已知安全的操作
      if (event.properties.tool_name === "file.read") {
        await opencode.client.session.postSessionByIdPermissionsByPermissionId({
          path: {
            id: event.properties.session_id,
            permissionId: event.properties.id,
          },
          body: { action: "allow", remember: false },
        });
      }
      break;

    case "tool.use":
      console.log(
        `工具调用: ${event.properties.tool}`
      );
      break;

    case "message.part":
      console.log(`收到消息片段`);
      break;
  }
}

注意:event.subscribe() 返回的是异步可迭代对象,适合在长时间运行的后台服务中使用。建议配合 AbortController 管理订阅的生命周期:

const controller = new AbortController();

const events = await opencode.client.event.subscribe();

(async () => {
  for await (const event of events.stream) {
    if (controller.signal.aborted) break;
    handleEvent(event);
  }
})();

// 在需要时取消订阅
controller.abort();

配置与多模型管理

通过 SDK 可以动态管理模型配置,无需手动编辑 opencode.json

// 查看当前配置
const config = await opencode.client.config.get();
console.log(`当前模型: ${config.data.model}`);

// 查看可用提供商和默认模型
const { providers, default: defaults } =
  await opencode.client.config.providers();

for (const provider of providers) {
  console.log(`提供商: ${provider.id}`);
}

// 在创建会话时指定模型
const session = await opencode.client.session.create({
  body: {
    title: "使用特定模型",
    model: {
      providerID: "anthropic",
      modelID: "claude-sonnet-5",
    },
  },
});

你也可以在 prompt 时动态切换模型,适用于 cost-sensitive 场景——简单任务用便宜的模型,复杂重构切换到高级模型。

TUI 控制:远程操控终端界面

SDK 还能控制 TUI 的行为,非常适合构建远程协作工具或辅助功能:

// 往 prompt 输入框追加文本
await opencode.client.tui.appendPrompt({
  body: { text: "请修复 src/components/Modal.tsx 中的 bug" },
});

// 提交当前 prompt(相当于按 Enter)
await opencode.client.tui.submitPrompt();

// 弹出 Toast 通知
await opencode.client.tui.showToast({
  body: { message: "代码审查完成", variant: "success" },
});

// 打开模型选择器
await opencode.client.tui.openModels();

Toast 的 variant 支持 successerrorinfowarning 四种类型。

实战:构建定时代码审查流水线

综合以上知识点,我们来构建一个完整的实战项目——定时运行的代码审查流水线:

import { createOpencode } from "@opencode-ai/sdk";
import { schedule } from "node-cron";

async function runCodeReview() {
  const opencode = await createOpencode({
    config: {
      model: "opencode/deepseek-v4-pro",
    },
  });

  try {
    // 1. 创建审查会话
    const session = await opencode.client.session.create({
      body: { title: `自动代码审查 - ${new Date().toISOString()}` },
    });

    const sessionId = session.data.id;

    // 2. 注入项目规范
    await opencode.client.session.prompt({
      path: { id: sessionId },
      body: {
        noReply: true,
        parts: [
          {
            type: "text",
            text: `
项目代码规范:
- 函数命名使用 camelCase
- 组件文件使用 PascalCase
- 错误必须使用 try-catch 处理
- 禁止使用 any 类型
- 所有 API 调用必须有超时设置
            `.trim(),
          },
        ],
      },
    });

    // 3. 获取 Git 变更文件列表
    const gitStatus = await opencode.client.file.status({});
    const changedFiles = gitStatus.data
      .filter((f) => f.status !== "ignored")
      .map((f) => f.path);

    if (changedFiles.length === 0) {
      console.log("没有变更文件,跳过审查");
      return;
    }

    // 4. 发送审查指令
    const result = await opencode.client.session.prompt({
      path: { id: sessionId },
      body: {
        parts: [
          {
            type: "text",
            text: `请审查以下变更文件,检查是否符合项目代码规范:
${changedFiles.join("\n")}

请重点检查:命名规范、错误处理、类型安全、性能问题。`,
          },
        ],
      },
    });

    // 5. 输出审查结果
    console.log("=== 代码审查结果 ===");
    console.log(result.data.info.text);

    // 6. 可选:分享审查报告
    const shared = await opencode.client.session.share({
      path: { id: sessionId },
    });
    console.log(`审查报告链接: https://opencode.ai/s/${shared.data.share}`);
  } finally {
    opencode.server.close();
  }
}

// 每天早上 9 点执行
schedule("0 9 * * *", () => {
  console.log("开始每日代码审查...");
  runCodeReview().catch(console.error);
});

// 立即执行一次
runCodeReview();

最佳实践

在使用 SDK 进行开发时,有几点建议值得留意:

1. 合理管理 Server 生命周期

每次 createOpencode() 都会启动一个 Server 进程。在长时间运行的服务中,建议复用同一个实例;在脚本任务中,务必在 finally 块中调用 server.close(),避免僵尸进程。

2. 模型选型策略

根据任务复杂度选择不同模型可以显著降低成本:

  • 简单任务(代码格式化、命名建议):使用 opencode-go/deepseek-v4-flashgpt-5.4-mini
  • 中型任务(代码审查、重构建议):使用 opencode-go/deepseek-v4-proclaude-haiku-4-5
  • 复杂任务(架构设计、大型重构):使用 claude-sonnet-5gpt-5.6-luna

3. 结构化输出的 Schema 设计

  • Schema 的 description 字段越详细,AI 的输出质量越高
  • 合理使用 required 字段,告诉 AI 哪些信息是必须的
  • 对于复杂 Schema,增大 retryCount 提高成功率
  • 始终处理 StructuredOutputError,提供降级策略

4. 错误处理

SDK 默认 throwOnError: false,错误会作为返回值而不是抛出异常。在关键任务中,建议设置为 true

const client = createOpencodeClient({
  baseUrl: "http://localhost:4096",
  throwOnError: true,
});

5. 权限管理

当通过 SDK 运行自动化任务时,权限弹窗会阻塞执行。建议在 OpenCode 配置中预先设置合适的权限策略:

{
  "permissions": {
    "allow": ["file.read", "file.write", "bash.git:*"]
  }
}

总结

OpenCode SDK 将 AI 编程助手的能力从交互式对话扩展到了编程式控制,为开发自动化工作流、代码审查流水线、CI/CD 集成等场景提供了基石。通过会话管理、文件操作、结构化输出和事件订阅等核心 API,你可以在任何 Node.js 环境中精确控制 AI 的行为。

本文覆盖了 SDK 的核心功能和实战场景,但 SDK 的能力远不止于此——Plugin 系统、MCP Server 集成、ACP 协议支持等高级特性同样可以通过 SDK 进行编排。建议你在掌握基础用法后,结合 OpenCode 官方文档SDK 类型定义 进一步探索,打造专属于你的 AI 编程工作流。