Plugin
Plugin 系统允许扩展 OpenCode 的功能。
概述
Plugin 是动态加载的模块,可以提供钩子(hooks)来自定义 OpenCode 的行为。插件可以是内置的、从 npm 安装的,或本地文件。
定义位置
packages/opencode/src/plugin/index.ts:1-136
Plugin 接口
插件必须导出一个函数,接收 PluginInput 并返回 Hooks。
type Plugin = (input: PluginInput) => Promise<Hooks> | Hooks
interface PluginInput {
client: OpencodeClient
project: Project
worktree: string
directory: string
serverUrl: string
$: typeof Bun.$
}
interface Hooks {
config?: (config: Config) => void
auth?: (input: AuthInput, output: AuthOutput) => AuthOutput
tool?: (input: ToolInput, output: ToolOutput) => ToolOutput
event?: (input: EventInput) => void
}
内置插件
OpenCode 内置以下插件:
1. CodexAuthPlugin
OpenAI Codex 认证插件。
2. CopilotAuthPlugin
GitHub Copilot 认证插件。
插件配置
在 opencode.jsonc 中配置插件:
{
"plugin": [
// 从 npm 安装
"my-plugin@1.0.0",
// 使用 latest 版本
"another-plugin",
// 本地文件
"file:///home/user/my-plugin.js",
// 相对路径
"./local-plugin.js",
// 内置插件(自动安装)
"opencode-anthropic-auth@0.0.9",
"@gitlab/opencode-gitlab-auth@1.3.0",
],
}
Hooks
config
在配置加载后调用,允许插件修改配置。
hooks.config = (config) => {
// 修改配置
config.agent = {
...config.agent,
"my-agent": {
description: "Plugin-provided agent",
mode: "primary",
},
}
}
auth
在认证请求时调用。
hooks.auth = (input, output) => {
// 处理认证
return output
}
tool
在工具调用前/后调用。
hooks.tool = (input, output) => {
// 修改工具调用
return output
}
event
监听所有事件。
hooks.event = (input) => {
// 处理事件
console.log(`Event: ${input.event.type}`)
}
典型使用场景
1. 创建简单插件
// my-plugin.js
export default async function plugin(input) {
console.log(`Plugin loaded for project: ${input.project.id}`)
return {
config: (config) => {
console.log("Config hook called")
// 修改配置
},
event: (eventInput) => {
console.log(`Event: ${eventInput.event.type}`)
// 处理事件
},
}
}
2. 安装插件
# 全局安装
bun add -g my-plugin
# 在 OpenCode 项目中
bun add my-plugin
3. 配置插件
{
"plugin": ["my-plugin@1.0.0"],
}
4. 插件访问 OpenCode Client
export default async function plugin(input) {
const { client, project, worktree } = input
// 使用 SDK 访问会话
const sessions = await client.session.list()
console.log(`Found ${sessions.length} sessions`)
// 创建新会话
const session = await client.session.create({
title: "Plugin-created session",
directory: worktree,
})
return {}
}
5. 插件执行命令
export default async function plugin(input) {
const { $ } = input
// 使用 Bun.$ 执行命令
const result = await $`npm test`.quiet()
if (result.exitCode !== 0) {
console.error("Tests failed")
}
return {}
}
6. 列出加载的插件
const hooks = await Plugin.list()
for (const hook of hooks) {
console.log(`Plugin has hooks:`)
if (hook.config) console.log(" - config")
if (hook.auth) console.log(" - auth")
if (hook.tool) console.log(" - tool")
if (hook.event) console.log(" - event")
}
插件目录
插件在以下位置查找:
- 全局配置目录:
~/.opencode/ - 项目配置目录:
.opencode/(向上搜索) - 全局 Claude 目录:
~/.claude/ - 项目 Claude 目录:
.claude/(向上搜索)
每个目录还扫描 node_modules/ 中的插件。
插件安装
当插件目录检测到需要安装时:
// 检查 node_modules 是否存在
const exists = await fs.stat(path.join(dir, "node_modules"))
// 如不存在,自动安装
if (!exists) {
// 安装 @opencode-ai/plugin
await BunProc.run(["add", "@opencode-ai/plugin@" + version, "--exact"], { cwd: dir })
// 安装 package.json 中的依赖
await BunProc.run(["install"], { cwd: dir })
}
插件触发
同步钩子
config 和 auth 钩子在特定操作时同步触发。
异步事件钩子
event 钩子订阅所有事件总线消息:
Bus.subscribeAll(async (event) => {
const hooks = await Plugin.list()
for (const hook of hooks) {
hook.event?.({ event })
}
})
插件错误处理
插件初始化失败时的处理:
try {
const mod = await import(pluginPath)
const init = await plugin(input)
hooks.push(init)
} catch (error) {
log.error("failed to load plugin", {
path: pluginPath,
error: error.message,
})
//继续加载其他插件
}
插件依赖
插件可以声明依赖:
{
"name": "my-plugin",
"dependencies": {
"@opencode-ai/sdk": "^2.0.0"
}
}
依赖会自动安装到插件的 node_modules/ 目录。
内置插件列表
const BUILTIN = ["opencode-anthropic-auth@0.0.9", "@gitlab/opencode-gitlab-auth@1.3.0"]
加载流程
安全考虑
插件隔离
- 插件运行在 Bun 沙箱中
- 使用
Bun.$执行命令受权限系统控制 - 文件访问受权限系统控制
插件验证
- 仅允许导出函数的模块
- 防止恶意代码执行
- 记录所有插件加载失败
插件开发最佳实践
- 错误处理: 始终捕获并记录错误
- 性能: 避免阻塞主线程
- 兼容性: 检查 API 版本
- 文档: 提供清晰的 README
示例插件
日志插件
export default async function plugin(input) {
return {
event: (eventInput) => {
const { event } = eventInput
console.log(`[${event.type}]`, event.properties)
// 写入日志文件
await Bun.appendFile('plugin-events.log', JSON.stringify(event) + '\n')
}
}
Agent 注入插件
export default async function plugin(input) {
return {
config: (config) => {
// 注入自定义 agent
config.agent = {
...config.agent,
"plugin-agent": {
description: "Agent provided by plugin",
mode: "primary",
},
}
},
}
}
对象关系
变更历史
| 版本 | 变更内容 | 日期 |
|---|---|---|
| v1 | 初始 Plugin 架构 | - |
| v1.1 | 添加内置插件支持 | - |
| v1.2 | 改进错误处理 | - |
相关文档
- Config - 配置系统