乐于分享
好东西不私藏

OpenCode 插件开发指南:深入理解插件系统与实战

OpenCode 插件开发指南:深入理解插件系统与实战

从插件架构设计到源码解析,带你掌握 OpenCode 扩展开发的核心原理

为什么需要插件?

如果你用 OpenCode 做 AI 开发,可能会好奇:能不能扩展它的能力?

当然可以。OpenCode 提供了插件系统,今天我们就来深入剖析它。

本文基于真实插件 opencode-plugin-langfuse 的源码,带你理解插件的架构设计与实战开发。

一、插件系统概述

OpenCode 插件是一个导出 Plugin 函数的 ES 模块。插件本质上是一个函数,接收输入,返回一组钩子(Hooks),从而与 OpenCode 深度集成。

核心概念

概念

说明

Plugin

插件入口函数类型

PluginInput

OpenCode 传递给插件的上下文

Hooks

插件返回的钩子集合,定义插件行为

二、Plugin 接口详解

类型签名

type Plugin = (input: PluginInput) => Promise;

PluginInput 上下文 — 当 OpenCode 加载你的插件时,会传递一个丰富的上下文:

type PluginInput = {          client: ReturnType;// 与 OpenCode 交互      project: Project;// 项目信息      directory: string;// 当前目录      worktree: string;// Worktree 路径      serverUrl: URL;// 服务器 URL      $: BunShell;// Shell 执行能力      };

Hooks 返回结构 — 插件通过返回不同的 Hook 来响应 OpenCode 的生命周期事件:

type Hooks = {          config?: (input: Config) => Promise;      event?: (input: { event: Event }) => Promise;       tool?: { [key: string]: ToolDefinition };       auth?: AuthHook;       // 消息钩子       "chat.message"?: (input, output) => Promise;        "chat.params"?: (input, output) => Promise;         // 工具钩子         "tool.execute.before"?: (input, output) => Promise;          "tool.execute.after"?: (input, output) => Promise;           // 实验性钩子           "experimental.chat.messages.transform"?: ...;           "experimental.session.compacting"?: ...;           };

三、Hooks 系统详解

3.1 config 钩子 — 配置验证:在 OpenCode 读取配置文件后调用。

config: async (config) => {          if (!config.experimental?.openTelemetry) {          client.app.log({          body: {          service: "langfuse-otel",          level: "warn",          message: "OpenTelemetry is disabled"          }          });          }          }

3.2 event 钩子 — 事件响应:监听 OpenCode 内部事件。

event: async ({ event }) => {          switch (event.type) {          case "session.idle":          await processor.forceFlush();          break;          case "server.instance.disposed":          await sdk.shutdown();          break;          }          }

3.3 tool 钩子 — 注册自定义工具(需要额外导入 zod):

import { tool } from "@opencode-ai/plugin";          import { z } from "zod";          return {          tool: {          myTool: tool({          description: "执行自定义操作",          args: {          name: z.string().describe("名称"),          count: z.number().optional().describe("数量"),          },          async execute(args, context) {          return `执行了 ${args.name}`;          },          }),          },          };

四、Langfuse 插件实战解析

完整源码解析 — 以下是 opencode-plugin-langfuse 的核心实现:

import { LangfuseSpanProcessor } from "@langfuse/otel";          import type { Plugin } from "@opencode-ai/plugin";          import { NodeSDK } from "@opentelemetry/sdk-node";          export const LangfusePlugin: Plugin = async ({ client }) => {          // 1. 读取环境变量          const publicKey = process.env.LANGFUSE_PUBLIC_KEY;          const secretKey = process.env.LANGFUSE_SECRET_KEY;          const baseUrl = process.env.LANGFUSE_BASEURL ?? "https://cloud.langfuse.com";          const log = (level: "info" | "warn" | "error", message: string) => {          client.app.log({ body: { service: "langfuse-otel", level, message } });          };          // 2. 凭证检查          if (!publicKey || !secretKey) {          log("warn", "Missing Langfuse credentials");          return {};          }          // 3. 初始化 OpenTelemetry          const processor = new LangfuseSpanProcessor({ publicKey, secretKey, baseUrl });          const sdk = new NodeSDK({ spanProcessors: [processor] });          sdk.start();          log("info", "OpenTelemetry tracing initialized");          // 4. 返回钩子          return {          config: async (config) => {          if (!config.experimental?.openTelemetry) {          log("warn", "tracing disabled in opencode config");          }          },          event: async ({ event }) => {          if (event.type === "session.idle") await processor.forceFlush();          if (event.type === "server.instance.disposed") await sdk.shutdown();          },          };          };

工作流程

1

启动阶段

读取环境变量 → 创建 processor → 启动 SDK → 返回 { config, event } 钩子

2

运行时 — config 钩子

检查 experimental.openTelemetry 配置

3

运行时 — event 钩子

响应 session.idle (flush) / server.disposed (shutdown)

五、开发自己的插件

5.1 最小插件模板

import type { Plugin } from "@opencode-ai/plugin";          export const MyPlugin: Plugin = async ({ client }) => {          const log = (msg: string) => {          client.app.log({ body: { service: "my-plugin", level: "info", message: msg } });          };          return {          config: async (config) => { log("Plugin loaded!"); },          };          };

5.2 带工具的插件 — 注册自定义工具供 OpenCode 调用:

import { tool } from "@opencode-ai/plugin";          import { z } from "zod";          export const MyPlugin: Plugin = async ({ client }) => {          return {          tool: {          greet: tool({          description: "打招呼",          args: { name: z.string().describe("姓名") },          async execute(args, context) {          return `你好,${args.name}!`;          },          }),          },          };          };

5.3 拦截消息 — 监听 chat.message 和 chat.params 钩子:

export const MyPlugin: Plugin = async () => {          return {          "chat.message": async (input, output) => {          console.log("用户消息:", input.message.content);          },          "chat.params": async (input, output) => {          // 可以修改 LLM 参数          output.params.temperature = 0.7;          },          };          };

六、最佳实践

6.1 环境变量检查 — 缺少凭证时返回空 Hooks,插件静默失效:

if (!publicKey || !secretKey) {          log("warn", "Missing credentials, plugin disabled");          return {};// 返回空 Hooks,插件静默失效          }

6.2 资源清理 — server.instance.disposed 时必须清理资源,防止数据丢失:

event: async ({ event }) => {          if (event.type === "server.instance.disposed") {          await sdk.shutdown();// 必须!防止数据丢失          }          }

6.3 配置验证 — 提醒用户开启必要的配置项:

config: async (config) => {          if (config.experimental?.featureX !== true) {          log("warn", "Feature X is not enabled");          }          }

七、总结

要点

说明

入口

导出 Plugin 函数的 ES 模块

输入

PluginInput 提供 client、$、目录等上下文

输出

Hooks 定义插件行为(config、event、tool 等)

关键

记得在 server.disposed 时清理资源

掌握这些,你就能为 OpenCode 开发各种扩展——从 LLM 监控到自定义工具,无所不能。

相关资源

资源

地址

备注

opencode-plugin-langfuse 源码

github.com/omercnet/opencode-plugin-langfuse

真实插件参考

OpenCode 官方文档

opencode.ai

官方文档

@opencode-ai/plugin npm

npmjs.com/package/@opencode-ai/plugin

插件开发包

本文基于 opencode-plugin-langfuse 源码整理          如需动手实践,请参考 GitHub 官方仓库