ENGitHubУстановить

🇬🇧 English version — каноническая версия, она может быть новее.

Плагины

Плагин - это то, как Ива растёт: папка, которую вы приносите откуда угодно, ставите одной командой и снимаете другой. Внутри скиллы, код и MCP-серверы, поэтому новая способность не стоит ни правки поставляемого дерева, ни форка (ADR-0008). Формат - Agent Plugins 1.0.0, та же папка, которую читают Codex, Cursor и Kiro. Своего формата Ива не придумывала.

Плагин ставит только владелец и только из терминала. Ни команды в Telegram, ни инструмента у модели нет намеренно: код плагина работает внутри процесса агента, а в окружении процесса лежат ключи этой инсталляции, поэтому подсунутое сообщение не должно уметь поставить плагин (ADR-0009). Что это значит для вас - в конце, в разделе Чем вы рискуете.

Поставить плагин

iva plugin add trace                         # имя из Marketplace
iva plugin add smixs/iva-plugins/trace       # owner/repo[/подпапка][@ref]
iva plugin add smixs/iva-plugins/[email protected]  # запинить тег, ветку или полный sha
iva plugin add https://github.com/you/my-plugin.git
iva plugin add ./my-plugin                   # папка на этой машине

add печатает состав папки до того, как что-то встанет на место:

  trace 0.1.0 — skills: trace; code: sh.iva; mcp: files

Перед самой первой установкой один раз печатается принятый риск. Если плагин несёт stdio MCP-серверы или свои сервисы, команда покажет их команды запуска и спросит Start these processes on this machine? [y/N]. Флаг --trust отвечает «да» без вопроса, а неинтерактивная оболочка без --trust отвечает «нет».

Что будет дальше, зависит от состава:

Команды

КомандаЧто делает
iva plugin add <имя|источник> [--trust]Поставить по имени из Marketplace или из папки, owner/repo[/подпапка][@ref], https://…, git@…
iva plugin listЧто стоит: версия, sha, enabled/disabled, trusted/untrusted, состав, источник
iva plugin list --availableЧто предлагают ваши маркетплейсы и что из этого уже стоит
iva plugin update [имя] [--force]Подтянуть отслеживаемый ref, печатая old → new; без имени - все плагины
iva plugin enable <имя>Включить плагин обратно, скиллы вернутся со следующего хода
iva plugin disable <имя>Выключить, не удаляя
iva plugin trust <имя>Разрешить его MCP-серверам и сервисам работать процессами на этой машине
iva plugin untrust <имя>Погасить эти процессы и снять их юниты; скиллы продолжают работать
iva plugin remove <имя>Удалить плагин; его данные в data/plugin-data/<имя>/ остаются
iva plugin syncПочинка: пересобрать plugins.json по папкам и доставить недостающее
iva plugin marketplace add|remove|listВаши списки плагинов, которые ставятся по имени

iva plugin без аргументов печатает тот же список. Все команды, меняющие состояние, берут тот же лок, что iva update: работает одна за раз, вторая посреди обновления скажет об этом, а не полезет в те же каталоги.

Где живёт плагин

Всё лежит в Custom layer или рядом с ним, поэтому iva update этого не касается:

ПутьЧто это
data/custom/plugins/<имя>/сам плагин: git-checkout, запиненный на один sha, или копия вашей папки
data/custom/plugins/<имя>.config.jsonваши настройки для кода плагина, читаются на старте и в сборку не зашиваются
data/custom/plugins/<имя>.envокружение MCP-серверов плагина, по одной строке KEY=value
data/plugin-data/<имя>/данные самого плагина (PLUGIN_DATA), переживают его обновление и даже удаление
data/custom/plugins.jsonодин файл состояния: name, source, ref, sha, digest, enabled, trusted, installedAt, плюс ваши маркетплейсы и выданные порты
data/marketplace-cache/списки маркетплейсов в том виде, в каком их прочитали последний раз

plugins.json - источник истины. Снесли папки, переехали на другой сервер, развернули старый бэкап - iva plugin sync вернёт каждый плагин на записанный sha. Папку без записи он примет обратно в файл, запись без папки доставит заново. Если восстановленный плагин несёт код, sync об этом скажет: код попадёт в работающую версию следующим iva update.

Файлы конфига и env лежат рядом с папкой плагина, а не внутри: папку обновление перезаписывает, а ваши значения обязаны это пережить.

enabled и trusted

Два независимых тумблера, и list печатает оба:

Недоверенный плагин работает, просто тише: скиллы грузятся, stdio MCP-серверы и сервисы не поднимаются. iva plugin trust <имя> их запускает, а untrust, disable и remove гасят и снимают юниты. Доверие плагину с MCP-серверами ещё и пересобирает версию: его connections генерируются именно туда. Плагину с одними сервисами хватает юнитов.

Имена скиллов общие на всю Иву, поэтому add отказывает двум плагинам с одноимённым скиллом: один из них молча погас бы. Ваш собственный скилл в data/custom/agent/skills/ побеждает одноимённый скилл плагина, а скилл плагина побеждает ядровой.

Что делает с плагинами iva update

Каждый включённый плагин с кодом собирается заново тем eve, который запустит новая версия. Поэтому плагин не коммитит dist/ и не отстаёт от бампа eve: папка, конфиг, данные и флаг доверия остаются как были, переделывается только сборка.

Плагин, чей код не собрался с новым eve, обновление не роняет. Версия собирается без него, плагин выключается, а один Alert называет виновника:

iva plugin update <имя>
iva plugin enable <имя>

iva plugin update идёт по ref, записанному при установке: ветка, тег или sha, - и печатает old → new. Папку, которую вы правили руками, он не трогает (digest в plugins.json показывает, что содержимое разошлось), пока не передадите --force. Так локальные правки не затираются молча. Записи, которую Marketplace запинил на коммит, идти некуда: чтобы её сдвинуть, сделайте remove и add заново.

Если обновление сменило содержимое плагина, его юниты - сервисы и MCP proxy - тут же перезапускаются на новый код. Поэтому плагину с одними сервисами iva update для новой версии его кода не нужен.

Marketplace

Marketplace - это список плагинов в git-репозитории: имя и источник, больше ничего. Ни центра, ни модерации, ни чьего-либо разрешения на публикацию. Ива читает .agents/plugins/marketplace.json по конвенции Codex, поэтому тот же список работает и для его пользователей.

iva plugin list --available          # что предлагают
iva plugin marketplace add you/your-plugins
iva plugin marketplace list
iva plugin marketplace remove your-plugins

Список по умолчанию - smixs/iva-plugins, и свой добавляется рядом, не вместо него. Списки кэшируются в data/marketplace-cache/; без сети берётся кэш с пометкой, что он может быть устаревшим. Имя, которое предлагают два списка, отклоняется с подсказкой add <имя>@<список>. А запись, у которой манифест называет плагин иначе, не ставится вообще: список - это файл, который никто не проверял.

Написать свой

Минимальный плагин - манифест и один скилл:

my-plugin/
  plugin.json
  skills/
    weather/
      SKILL.md
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "my-plugin",
  "version": "0.1.0",
  "description": "Что он делает, одной строкой."
}

Обязательных полей два: $schema и name. Имя - от 1 до 64 знаков, строчные буквы, цифры, дефисы и точки, первый и последний знак буква или цифра, без -- и без ... Манифест проверяется локально, схема из сети никогда не тянется.

Два ограничения стоит знать до упаковки: симлинки отвергаются целиком, а на плагин действуют потолки 2000 файлов, 50 МБ и 16 уровней вложенности. Битая компонента не роняет остальные: плагин с непригодным mcp.json всё равно отдаст свои скиллы, а iva plugin list напечатает диагностику.

Пока пишете, ставьте саму папку: iva plugin add ./my-plugin. Она копируется в стор, а не линкуется, и iva plugin update my-plugin копирует её снова из того же пути.

Скиллы

skills/<имя>/SKILL.md, ровно один уровень вложенности, формат Agent Skills с name и description во frontmatter. Описание пишется как условие срабатывания («Use when…»): до загрузки скилла модель видит только эту строку. Всё остальное про скиллы - в extending.md: скилл плагина это обычный скилл, который приехал в папке.

Код: eve Extension в sh.iva/

sh.iva - namespace Ивы, и внутри него живут две разные вещи: eve Extension, то есть sh.iva/package.json, и сервисы, то есть sh.iva/services/<svc>/service.json. Любая из них годится по отдельности. Namespace обязан быть объявлен в манифесте в обоих случаях: папку sh.iva/, на которую манифест не заявился, Ива игнорирует и пишет об этом диагностику в iva plugin list.

Для кода объявите ключ и положите в папку настоящий пакет eve Extension:

{ "extensions": { "sh.iva": {} } }
my-plugin/
  sh.iva/
    package.json          # eve.extension.{source,dist}, пишется командой: eve extension init
    tsconfig.json         # обязателен: eve эмитит декларации через tsc и без него отказывается
    extension/extension.ts

dist/ не коммитьте. Ива собирает его внутри версии: копия плагина, npm ci без dev- и peer-зависимостей, eve extension build тем самым eve, который его запустит, сгенерированный mount agent/extensions/<ns>.ts, дальше обычная сборка, проверка старта, переключение и рестарт. <ns> - имя плагина, где . и - свёрнуты в _; оно же становится префиксом каждого тула вашего расширения: my_plugin__forecast.

Настройки владельца приходят из data/custom/plugins/<имя>.config.json. Объявите их форму и читайте значения через handle: сгенерированный mount передаёт ему содержимое файла на старте, поэтому изменённая настройка стоит рестарта, а не пересборки.

// sh.iva/extension/extension.ts
import { defineExtension } from "eve/extension";
import { z } from "zod";

export default defineExtension({ config: z.object({ city: z.string() }) });

Расширение не может объявить agent.ts, sandbox, schedules и вложенные extensions: это принадлежит агенту, который его монтирует. Своих расписаний у плагина нет по той же причине - eve их запрещает.

MCP-серверы: mcp.json

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
  "mcpServers": {
    "files": {
      "type": "stdio",
      "command": "node",
      "args": ["server.mjs", "--root", "${PLUGIN_DATA}"],
      "env": { "FILES_LOG": "${PLUGIN_DATA}/log" }
    },
    "search": {
      "type": "streamable-http",
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer ${SEARCH_TOKEN}" }
    }
  }
}

Работают оба транспорта, и обоим нужен trusted:

command - один токен: голое имя исполняемого файла или ./-путь внутри плагина, но никогда шелл-строка. ${PLUGIN_ROOT} и ${PLUGIN_DATA} раскрываются один проход в args, env и cwd; рабочий каталог по умолчанию - PLUGIN_ROOT.

Что stdio-сервер видит в своём окружении - ради этого прокси и стоит: PATH, HOME, PLUGIN_ROOT, PLUGIN_DATA, объявленное в mcp.json и то, что вы написали в <имя>.env. Больше из окружения агента ему не достаётся ничего: ни токена Telegram, ни ключа провайдера.

Порты выдаются при trust, от 8730 вверх, записываются в plugins.json и держатся до удаления плагина.

Долгоживущий сервис

Плагин, которому нужен свой процесс - вьюер, воркер, что угодно, что должно пережить рестарт агента, - объявляет его в sh.iva/ рядом с тем же ключом extensions и без всякого package.json:

my-plugin/
  sh.iva/
    services/
      viewer/
        service.json
        server.mjs
{ "command": "node", "args": ["server.mjs"], "port": 8726 }

Ива пишет юнит iva-plugin-<имя>-<svc>.service и поднимает его только пока плагин включён и доверен, оба условия сразу, с Restart=on-failure. Рабочий каталог - папка самого сервиса, а в окружении ровно четыре переменные:

ПеременнаяЧто в ней
IVA_SERVICE_PORTпорт, который надо слушать; слушать только на 127.0.0.1
IVA_DATA_DIRабсолютный путь каталога данных Ивы, чтобы читать оттуда, например, trace/
PLUGIN_ROOTпапка плагина в сторе
PLUGIN_DATAdata/plugin-data/<имя>/, ваш каталог для записи

Плагины, которые Ива привозит из smixs/iva-plugins, написаны на TypeScript и запускаются прямо через Node - "command": "node", "args": ["server.ts"], без всякой сборки, - а ваш собственный сервис может быть любой командой: контракт - это только service.json и те же четыре переменные.

port в service.json - это пожелание: Ива берёт его, если свободен, иначе выдаёт следующий свободный. Поэтому номер приезжает через IVA_SERVICE_PORT, а не константой в вашем коде. Плагину с сервисами и без расширения сборка версии не нужна, ему нужны только юниты, а iva plugin update перезапускает их на код, который только что привёз.

Юниты работают на Linux. На машине без systemd команды печатают no systemd и идут дальше: состояние всё равно записано, поэтому та же инсталляция на сервере поднимет процессы.

Проверить

iva plugin list      # по каждому плагину: тумблеры, состав, источник, диагностика
iva doctor           # раздел Plugins
iva plugin sync      # то, что doctor велит запустить, когда они расходятся

iva doctor читает манифесты, сверяет plugins.json с data/custom/plugins/, говорит, правда ли код каждого плагина собран в работающую версию, и проверяет ожидаемые юниты: is-active для каждого плюс GET /health для каждого MCP proxy.

Чем вы рискуете

Прочитайте один раз, дальше решайте по каждому плагину отдельно.

Код плагина работает внутри процесса агента, а bash внутри его скилла - с окружением агента. Значит оба видят все ключи этой инсталляции: токен Telegram, провайдера модели, всё остальное из .env. Поставить чужой плагин - отдать ему эти ключи. Проверка манифеста от этого не спасает: plugin.json описывает состав, а не поведение.

Это принятый риск, а не недосмотр (ADR-0008): изоляция секретов от шелла - отдельная отложенная работа, и плагины её не приближают. Что рельсы делают вместо этого - оставляют решение вам и показывают его прямо:

Свой плагин вы видите насквозь, чужой - нет. Читайте код до установки или пиньте плагин на тот sha, который прочитали.

Остальная картина по безопасности - в security.md.

Исходный Markdown ↗