386 lines
13 KiB
Plaintext
386 lines
13 KiB
Plaintext
|
|
---
|
|||
|
|
title: Плагины
|
|||
|
|
description: Напишите свои собственные плагины для расширения OpenCode.
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
Плагины позволяют расширять OpenCode, подключаясь к различным событиям и настраивая поведение. Вы можете создавать плагины для добавления новых функций, интеграции с внешними сервисами или изменения поведения OpenCode по умолчанию.
|
|||
|
|
|
|||
|
|
Для примера ознакомьтесь с [plugins](/docs/ecosystem#plugins), созданными сообществом.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Используйте плагин
|
|||
|
|
|
|||
|
|
Есть два способа загрузки плагинов.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Из локальных файлов
|
|||
|
|
|
|||
|
|
Поместите файлы JavaScript или TypeScript в каталог плагина.
|
|||
|
|
|
|||
|
|
- `.opencode/plugins/` – плагины уровня проекта.
|
|||
|
|
- `~/.config/opencode/plugins/` — глобальные плагины
|
|||
|
|
|
|||
|
|
Файлы в этих каталогах автоматически загружаются при запуске.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Из НПМ
|
|||
|
|
|
|||
|
|
Укажите пакеты npm в файле конфигурации.
|
|||
|
|
|
|||
|
|
```json title="opencode.json"
|
|||
|
|
{
|
|||
|
|
"$schema": "https://opencode.ai/config.json",
|
|||
|
|
"plugin": ["opencode-helicone-session", "opencode-wakatime", "@my-org/custom-plugin"]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Поддерживаются как обычные, так и ограниченные пакеты npm.
|
|||
|
|
|
|||
|
|
Просмотрите доступные плагины в папке [ecosystem](/docs/ecosystem#plugins).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Как устанавливаются плагины
|
|||
|
|
|
|||
|
|
**Плагины npm** устанавливаются автоматически с помощью Bun при запуске. Пакеты и их зависимости кэшируются в `~/.cache/opencode/node_modules/`.
|
|||
|
|
|
|||
|
|
**Локальные плагины** загружаются непосредственно из каталога плагинов. Чтобы использовать внешние пакеты, вы должны создать `package.json` в своем каталоге конфигурации (см. [Зависимости](#dependencies)) или опубликовать плагин в npm и [добавить его в свой config](/docs/config#plugins).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Порядок загрузки
|
|||
|
|
|
|||
|
|
Плагины загружаются из всех источников, и все хуки запускаются последовательно. Порядок загрузки следующий:
|
|||
|
|
|
|||
|
|
1. Глобальная конфигурация (`~/.config/opencode/opencode.json`)
|
|||
|
|
2. Конфигурация проекта (`opencode.json`)
|
|||
|
|
3. Глобальный каталог плагинов (`~/.config/opencode/plugins/`)
|
|||
|
|
4. Каталог плагинов проекта (`.opencode/plugins/`)
|
|||
|
|
|
|||
|
|
Дубликаты пакетов npm с тем же именем и версией загружаются один раз. Однако локальный плагин и плагин npm со схожими именами загружаются отдельно.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Создать плагин
|
|||
|
|
|
|||
|
|
Плагин — это **модуль JavaScript/TypeScript**, который экспортирует один или несколько плагинов.
|
|||
|
|
функции. Каждая функция получает объект контекста и возвращает объект перехватчика.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Зависимости
|
|||
|
|
|
|||
|
|
Локальные плагины и специальные инструменты могут использовать внешние пакеты npm. Добавьте `package.json` в каталог конфигурации с необходимыми вам зависимостями.
|
|||
|
|
|
|||
|
|
```json title=".opencode/package.json"
|
|||
|
|
{
|
|||
|
|
"dependencies": {
|
|||
|
|
"shescape": "^2.1.0"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
OpenCode запускает `bun install` при запуске для их установки. Затем ваши плагины и инструменты смогут импортировать их.
|
|||
|
|
|
|||
|
|
```ts title=".opencode/plugins/my-plugin.ts"
|
|||
|
|
import { escape } from "shescape"
|
|||
|
|
|
|||
|
|
export const MyPlugin = async (ctx) => {
|
|||
|
|
return {
|
|||
|
|
"tool.execute.before": async (input, output) => {
|
|||
|
|
if (input.tool === "bash") {
|
|||
|
|
output.args.command = escape(output.args.command)
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Базовая структура
|
|||
|
|
|
|||
|
|
```js title=".opencode/plugins/example.js"
|
|||
|
|
export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
|
|||
|
|
console.log("Plugin initialized!")
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
// Hook implementations go here
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Функция плагина получает:
|
|||
|
|
|
|||
|
|
- `project`: Текущая информация о проекте.
|
|||
|
|
- `directory`: текущий рабочий каталог.
|
|||
|
|
- `worktree`: путь к рабочему дереву git.
|
|||
|
|
- `client`: клиент SDK с открытым кодом для взаимодействия с ИИ.
|
|||
|
|
- `$`: [оболочка API](https://bun.com/docs/runtime/shell) Бана для выполнения команд.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Поддержка TypeScript
|
|||
|
|
|
|||
|
|
Для плагинов TypeScript вы можете импортировать типы из пакета плагина:
|
|||
|
|
|
|||
|
|
```ts title="my-plugin.ts" {1}
|
|||
|
|
import type { Plugin } from "@opencode-ai/plugin"
|
|||
|
|
|
|||
|
|
export const MyPlugin: Plugin = async ({ project, client, $, directory, worktree }) => {
|
|||
|
|
return {
|
|||
|
|
// Type-safe hook implementations
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### События
|
|||
|
|
|
|||
|
|
Плагины могут подписываться на события, как показано ниже в разделе «Примеры». Вот список различных доступных событий.
|
|||
|
|
|
|||
|
|
#### Командные события
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События файла
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События установки
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События ЛСП
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События сообщений
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События разрешения
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События сервера
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События сессии
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События оболочки
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### События инструмента
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
#### Мероприятия ТУИ
|
|||
|
|
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
- ХХ0ГГ
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Примеры
|
|||
|
|
|
|||
|
|
Вот несколько примеров плагинов, которые вы можете использовать для расширения открытого кода.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Отправлять уведомления
|
|||
|
|
|
|||
|
|
Отправляйте уведомления при возникновении определенных событий:
|
|||
|
|
|
|||
|
|
```js title=".opencode/plugins/notification.js"
|
|||
|
|
export const NotificationPlugin = async ({ project, client, $, directory, worktree }) => {
|
|||
|
|
return {
|
|||
|
|
event: async ({ event }) => {
|
|||
|
|
// Send notification on session completion
|
|||
|
|
if (event.type === "session.idle") {
|
|||
|
|
await $`osascript -e 'display notification "Session completed!" with title "opencode"'`
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Мы используем `osascript` для запуска AppleScript на macOS. Здесь мы используем его для отправки уведомлений.
|
|||
|
|
|
|||
|
|
:::примечание
|
|||
|
|
Если вы используете настольное приложение OpenCode, оно может автоматически отправлять системные уведомления, когда ответ готов или когда возникает ошибка сеанса.
|
|||
|
|
:::
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### защита .env
|
|||
|
|
|
|||
|
|
Запретите открытому коду читать файлы `.env`:
|
|||
|
|
|
|||
|
|
```javascript title=".opencode/plugins/env-protection.js"
|
|||
|
|
export const EnvProtection = async ({ project, client, $, directory, worktree }) => {
|
|||
|
|
return {
|
|||
|
|
"tool.execute.before": async (input, output) => {
|
|||
|
|
if (input.tool === "read" && output.args.filePath.includes(".env")) {
|
|||
|
|
throw new Error("Do not read .env files")
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Внедрение переменных среды
|
|||
|
|
|
|||
|
|
Внедряйте переменные среды во все процессы выполнения оболочки (инструменты искусственного интеллекта и пользовательские терминалы):
|
|||
|
|
|
|||
|
|
```javascript title=".opencode/plugins/inject-env.js"
|
|||
|
|
export const InjectEnvPlugin = async () => {
|
|||
|
|
return {
|
|||
|
|
"shell.env": async (input, output) => {
|
|||
|
|
output.env.MY_API_KEY = "secret"
|
|||
|
|
output.env.PROJECT_ROOT = input.cwd
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Пользовательские инструменты
|
|||
|
|
|
|||
|
|
Плагины также могут добавлять в открытый код собственные инструменты:
|
|||
|
|
|
|||
|
|
```ts title=".opencode/plugins/custom-tools.ts"
|
|||
|
|
import { type Plugin, tool } from "@opencode-ai/plugin"
|
|||
|
|
|
|||
|
|
export const CustomToolsPlugin: Plugin = async (ctx) => {
|
|||
|
|
return {
|
|||
|
|
tool: {
|
|||
|
|
mytool: tool({
|
|||
|
|
description: "This is a custom tool",
|
|||
|
|
args: {
|
|||
|
|
foo: tool.schema.string(),
|
|||
|
|
},
|
|||
|
|
async execute(args, context) {
|
|||
|
|
const { directory, worktree } = context
|
|||
|
|
return `Hello ${args.foo} from ${directory} (worktree: ${worktree})`
|
|||
|
|
},
|
|||
|
|
}),
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Помощник `tool` создает собственный инструмент, который может вызывать открытый код. Он принимает функцию схемы Zod и возвращает определение инструмента:
|
|||
|
|
|
|||
|
|
- `description`: Что делает инструмент
|
|||
|
|
- `args`: схема Zod для аргументов инструмента.
|
|||
|
|
- `execute`: функция, которая запускается при вызове инструмента.
|
|||
|
|
|
|||
|
|
Ваши пользовательские инструменты будут доступны для открытия кода наряду со встроенными инструментами.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Ведение журнала
|
|||
|
|
|
|||
|
|
Используйте `client.app.log()` вместо `console.log` для структурированного ведения журнала:
|
|||
|
|
|
|||
|
|
```ts title=".opencode/plugins/my-plugin.ts"
|
|||
|
|
export const MyPlugin = async ({ client }) => {
|
|||
|
|
await client.app.log({
|
|||
|
|
body: {
|
|||
|
|
service: "my-plugin",
|
|||
|
|
level: "info",
|
|||
|
|
message: "Plugin initialized",
|
|||
|
|
extra: { foo: "bar" },
|
|||
|
|
},
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Уровни: `debug`, `info`, `warn`, `error`. Подробности см. в документации SDK](https://opencode.ai/docs/sdk).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Уплотняющие крюки
|
|||
|
|
|
|||
|
|
Настройте контекст, включаемый при сжатии сеанса:
|
|||
|
|
|
|||
|
|
```ts title=".opencode/plugins/compaction.ts"
|
|||
|
|
import type { Plugin } from "@opencode-ai/plugin"
|
|||
|
|
|
|||
|
|
export const CompactionPlugin: Plugin = async (ctx) => {
|
|||
|
|
return {
|
|||
|
|
"experimental.session.compacting": async (input, output) => {
|
|||
|
|
// Inject additional context into the compaction prompt
|
|||
|
|
output.context.push(`
|
|||
|
|
## Custom Context
|
|||
|
|
|
|||
|
|
Include any state that should persist across compaction:
|
|||
|
|
- Current task status
|
|||
|
|
- Important decisions made
|
|||
|
|
- Files being actively worked on
|
|||
|
|
`)
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Хук `experimental.session.compacting` срабатывает до того, как LLM сгенерирует сводку продолжения. Используйте его для внедрения контекста, специфичного для домена, который будет пропущен при запросе на сжатие по умолчанию.
|
|||
|
|
|
|||
|
|
Вы также можете полностью заменить запрос на уплотнение, установив `output.prompt`:
|
|||
|
|
|
|||
|
|
```ts title=".opencode/plugins/custom-compaction.ts"
|
|||
|
|
import type { Plugin } from "@opencode-ai/plugin"
|
|||
|
|
|
|||
|
|
export const CustomCompactionPlugin: Plugin = async (ctx) => {
|
|||
|
|
return {
|
|||
|
|
"experimental.session.compacting": async (input, output) => {
|
|||
|
|
// Replace the entire compaction prompt
|
|||
|
|
output.prompt = `
|
|||
|
|
You are generating a continuation prompt for a multi-agent swarm session.
|
|||
|
|
|
|||
|
|
Summarize:
|
|||
|
|
1. The current task and its status
|
|||
|
|
2. Which files are being modified and by whom
|
|||
|
|
3. Any blockers or dependencies between agents
|
|||
|
|
4. The next steps to complete the work
|
|||
|
|
|
|||
|
|
Format as a structured prompt that a new agent can use to resume work.
|
|||
|
|
`
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Если установлен `output.prompt`, он полностью заменяет приглашение на сжатие по умолчанию. Массив `output.context` в этом случае игнорируется.
|