Перейти к содержанию

Анатомия пакета

Из чего состоит пакет: раскладка каталога, обёртка объекта, манифест (engines, requires, variables, knowledge), ссылки между объектами по ключам, версии и их неизменяемость, переименования и файл установки. Страница для автора пакета и администратора установки; первый пакет по шагам — в Пакете за 10 минут.

Раскладка

<пакет>/
├── package.yaml               # kind: Package — манифест
├── task-types/                # kind: TaskType
├── artifact-types/            # kind: ArtifactType
├── project-templates/         # kind: ProjectTemplate
├── workspace-types/           # kind: WorkspaceType
├── roles/                     # kind: Role
├── capabilities/              # kind: Capability
├── skills/                    # kind: Skill
├── agents/                    # kind: Agent
├── rules/                     # kind: WorkRule
├── processes/                 # kind: Process
├── calendars/                 # kind: Calendar
├── knowledge-packs/           # kind: KnowledgePack
├── notification-rules/        # kind: NotificationRule
├── schemas/                   # JSON Schema данных процессов (data: {$ref: …})
├── tests/                     # *.test.yaml — сценарии
├── .layout/                   # раскладка визуального редактора; логики не несёт
├── integration/               # код наблюдателя и скиллов — у интеграции
└── README.md
  • Каталог пакета называется его ключом. check и test по пути каталога берут ключ пакета из имени каталога и сверяют его с key манифеста: расхождение — ошибка «key … не совпадает с ключом пакета в установке». Копию пакета для пробы кладите в каталог с тем же именем. Пакет из чужого репозитория, подключённый в файле установки по path или git, может лежать где угодно: его ключ — ключ записи установки.
  • Вид объекта определяет поле kind, а не папка. Папки — соглашение для людей; package-sdk add раскладывает файлы по ним.
  • Загрузчик читает все файлы *.yaml пакета во всех подкаталогах, кроме package.yaml, tests/, schemas/ и .layout/. Каждый такой файл обязан быть объектом каталога: файл с неизвестным kind — ошибка. Поэтому вспомогательные YAML-файлы кладите в schemas/ или давайте им другое расширение.
  • В tests/ читаются только файлы *.test.yaml.
  • Язык файлов — YAML 1.2: булевы значения только true и false. Ключ on у старта процесса берите в кавычки ("on":), если файл читает ещё и инструмент на YAML 1.1.
  • Числа и даты package-sdk читает так же, как ядро при публикации: 012 — двенадцать, 1_000 и 2026-09-30 — строки, .inf и .nan — ошибка. Поэтому check, test и plan видят ровно те значения, которые получит стенд.

Обёртка объекта

# yaml-language-server: $schema=<путь к schema/v1/object.schema.json>
apiVersion: taimen.ai/v1
kind: Role
key: access-approver
spec:
  name: Access approver
  description: Decides on access requests
Поле Правило
apiVersion константа формата taimen.ai/v1 — имя схемы, а не адрес сервиса
kind вид объекта: Package, Installation и виды каталога из карты видов
key идентичность объекта в tenant'е; форма ключа зависит от вида (ниже)
spec ровно тело запроса API сервиса, который хранит объект, в camelCase и без поля идентичности
Вид Ключ
Package, TaskType, ArtifactType, ProjectTemplate, WorkspaceType, Process, Calendar ^[a-z0-9][a-z0-9_-]*$, до 63 символов
Role, Agent slug ^[a-z0-9][a-z0-9-]*$, 2–63 символа
WorkRule, NotificationRule ^[a-z0-9][a-z0-9._-]*$, до 128 символов
Skill имя скилла, например access.grant; версия — в spec.version
Capability имя способности
KnowledgePack имя онтологии ^[a-z0-9][a-z0-9._-]{0,63}$; совпадает со spec.name

У ключа пакета, который создаёт package-sdk init, правило строже: строчные буквы, цифры и -, начинается с буквы.

Первая строка-комментарий подключает схему к редактору с YAML Language Server: подсказки полей и ошибки прямо при наборе. package-sdk init и add пишут её сами, со ссылкой на схему установленного SDK.

Окончательный валидатор — ядро

Схема проверяет форму. Грамматику выражений, условий правил, исходов решений и процессов проверяет код ядра: check импортирует его валидаторы (дополнение sandbox), а с --server отправляет пакет ещё и ядру стенда.

Манифест

apiVersion: taimen.ai/v1
kind: Package
key: access-requests
spec:
  version: 0.2.0
  displayName: Access requests
  description: Access requests to internal resources and their review
  license: Apache-2.0
  authors: ["Example Integrations <dev@example.com>"]
  homepage: https://git.example.com/example/access-requests
  engines:
    control-plane: ">=0.10,<0.11"
  requires:
    - {package: access-base, version: ">=0.1"}
  knowledge: ["access@1"]
  variables:
    ACCESS_WORKSPACE_ID:
      kind: workspace
      description: Workspace where access requests are reviewed
    ACCESS_ESCALATION_HOURS:
      kind: integer
      description: Hours before an unanswered request is escalated
      default: "24"
Поле Обязательно Что задаёт
version да версия пакета, SemVer X.Y.Z
displayName да название для людей
description нет описание
engines нет диапазоны версий компонентов, с которыми пакет совместим
requires нет пакеты, на объекты которых этот пакет ссылается
variables нет объявление каждой переменной установки ${NAME}
knowledge нет онтологии (имя@мажор), на которые опираются процессы пакета
license нет лицензия, идентификатор SPDX
authors, homepage нет авторы; адрес пакета (https://)
renames нет явные переименования объектов (см. ниже)

engines — совместимость

engines — диапазоны версий компонентов платформы, против которых пакет проверен: {control-plane: ">=0.10,<0.11"}. Диапазон — условия через запятую, выполняться должны все; операторы >=, >, <=, <, =, ^, ~; версия без оператора — точная версия или префикс (1.2 — любая 1.2.x); * — любая.

  • init записывает диапазон по коду ядра, который стоит рядом с инструментом: тот же minor.
  • check отвергает неразбираемый диапазон (engines_invalid) и предупреждает, если код ядра рядом с инструментом вне диапазона (engines_mismatch): тогда проверка и тесты идут не той версией, на которую рассчитан пакет.
  • plan читает версию ядра стенда из его openapi.json и отказывает до записи, если она вне диапазона.

requires — зависимости

Пакет ссылается только на свои объекты и объекты пакетов из requires. Элемент — ключ пакета (любая версия) или {package, version} с диапазоном в той же грамматике, что у engines.

  • requires подтягиваются в установку сами: в файле установки достаточно назвать пакет, который вы ставите.
  • Зависимость ищется по порядку: spec.packagesDir установки, packages/ рядом с файлом установки, каталог файла установки. Пакет, который проверяют по пути (check --package <каталог>), ищет свои requires рядом с собой — в соседних каталогах.
  • Версия зависимости в установке вне диапазона — ошибка requires_version_mismatch; цикл зависимостей — ошибка цикл requires.
  • Песочница тестов строит каталог из объектов пакета и всех его requires: роль или скилл из пакета-зависимости в тесте видны.

variables — переменные установки

Всё, что зависит от конкретного стенда — UUID пространства работы, адрес внешней системы, порог суммы, — не пишется в пакет, а выносится в переменную: в любой строке spec пишется ${NAME}, а в манифесте переменная объявляется.

Поле переменной Что задаёт
description обязательно: что это и откуда взять значение
kind обязательно: url (абсолютный http(s):// URL), workspace, project, principal, role (UUID объекта стенда), integer, string
required по умолчанию true
default значение, если установка не задала своё (строкой)
example пример значения для describe --env-example

Правила check:

  • использованная, но не объявленная переменная — ошибка variable_undeclared;
  • объявленная, но нигде не использованная — ошибка variable_unused;
  • default или example, не подходящие виду, — ошибка variable_invalid_value;
  • required: true вместе с default — предупреждение variable_required_with_default: default и так делает переменную необязательной.

Значения переменных задаёт установка: файл --env (по умолчанию .env в текущем каталоге) и окружение процесса, причём окружение сильнее файла. plan проверяет, что каждая обязательная переменная задана, значение подходит виду, а UUID пространства работы, проекта, principal'а и роли существует на стенде. Самих значений в файл плана plan не пишет — только их хэш. Заготовку файла переменных печатает package-sdk describe <пакет> --env-example.

Секретов в пакете нет

У переменной нет поля secret, и значения секретов в пакет и в файл переменных не пишутся. Секрет исполнителя — имя в placement.secrets описания агента: сам секрет лежит на узле, который исполняет агента.

workspaceId правила вывода работы задаётся только переменной: это топология установки, а не содержимое пакета.

Подстановка ${NAME} — текстовая и идёт до того, как ядро разбирает значение: внутри выражения процесса переменная становится частью его текста. Числовую переменную при сравнении с number оборачивайте в double(…), строковую берите в кавычки CEL — примеры в Выражениях. Значение-подстановка всегда строка, даже если переменная вида integer.

knowledge — онтологии

knowledge перечисляет онтологии памяти (имя@мажор), на виды и связи которых опираются процессы пакета в memory, recall, remember и контексте шагов.

  • Онтология из списка должна быть объявлена KnowledgePack в пакете или его requires (иначе knowledge_unknown); встроенная онтология памяти default объявления не требует.
  • Вид или связь, которых нет в объявленных онтологиях, — ошибка knowledge_term_unknown (предупреждение, если содержимое части онтологий не видно, например встроенной default: тогда решает память при регистрации).
  • Процессы обращаются к памяти, а knowledge не объявлен — предупреждение knowledge_undeclared.

Включение онтологий для пространства работы — топология, поэтому оно задаётся в установке, а не в пакете.

Ссылки по ключам

Объекты ссылаются друг на друга ключами, а не идентификаторами стенда:

Откуда Куда Пример
действие правила, шаг human процесса, ensureWork исхода тип задачи taskType: access-review
identity процесса и правила, assignee правила агент identity: {agent: access-requests-process}, assignee: agent:<ключ>
owner, assign шагов процесса роль assign: [{role: access-approver}]
интерпретация правила, шаг call, invokeSkill скилл access.grant@1 — имя и версия
artifactSchema типа задачи тип артефакта type: access-grant
knowledge, extends онтологии онтология access@1

check проверяет, что каждая ссылка замкнута внутри пакета и его requires. Системный тип задачи task есть в каждом tenant'е и объявления не требует. Идентификаторы конкретного стенда (UUID workspace, principal'а) в пакет не пишутся — только через переменные.

Версии

Версия пакета

spec.version — SemVer пакета. По ней установка проверяет диапазоны requires, она попадает в packages.lock, а ядро запоминает у каждого поставленного объекта, каким пакетом и какой версии он поставлен (связь объекта с пакетом, см. Пакеты каталога).

Поднимайте версию пакета при каждом выпуске: patch — исправление без изменения поведения, minor — новые объекты и совместимые изменения, major — изменение, которое ломает установки или зависимые пакеты.

Неизменяемые версии объектов

Часть видов в ядре версионируется и неизменяема: опубликованная версия не меняется, правка — новая версия.

Вид Как публикуется правка
TaskType, ProjectTemplate файл отличается от новейшей активной версии — публикуется новая версия, прежние активные переводятся в deprecated; задачи и проекты остаются на своей версии
ArtifactType новая версия, если файл отличается от новейшей
Process spec.version — целое; правка — version: N+1. То же число с другим содержимым — 409 process_version_conflict. Открытые дела дорабатывают по своей версии, если нет карты миграции
Skill spec.version задаёт пакет. Изменение контракта, протокола, побочных эффектов или уровня риска при той же версии — ошибка «поднимите spec.version»
KnowledgePack spec.version — целое. Другое содержимое при той же версии — ошибка knowledge_pack_conflict
Agent новая неизменяемая ревизия появляется, только если изменилось описание

Изменяемые виды правятся на месте: WorkRule, Role, WorkspaceType — частичным обновлением, Capability только создаётся. Удаления нет ни у одного вида. Типы задач, шаблоны проектов, правила вывода работы, агенты, правила уведомлений, процессы и календари выводятся из оборота списком retire установки.

Переименования

Переименовать объект — не удалить старый и создать новый. Для этого в манифесте есть renames, как moved в Terraform:

spec:
  version: 0.3.0
  renames:
    - {kind: Process, from: access-intake, to: access-requests}
  • to — объект этого пакета, from — ключ, которого в пакете больше нет (check: вид должен быть видом каталога, а объект to — существовать).
  • План переносит процесс или календарь вместе с историей версий, старый ключ выводится: новых дел не заводит (409 process_retired), открытые дорабатывают.
  • Переименование других видов план не переносит — это предупреждение rename_not_planned: старый объект остаётся, новый создаётся.
  • package-sdk edit rename --package <каталог> --kind Process --from <ключ> --to <ключ> переименует файл, ключ и допишет renames сама.

Установка

Файл установки говорит, какие пакеты и откуда ставятся на конкретный стенд. Он живёт в git установки, отдельно от пакетов. Здесь — форма файла; процедура целиком (источники git, lock и кэш, план, правки консоли, применение, вывод из оборота, выпуск тегом) — в Установке и выпуске.

apiVersion: taimen.ai/v1
kind: Installation
key: prod
spec:
  packages:
    - notifications                                   # каталог установки: packages/notifications
    - {key: access-requests, path: ../access-requests} # путь относительно файла установки
    - {key: helpdesk, git: https://git.example.com/example/helpdesk.git, ref: v0.2.0}
  knowledge:
    - {workspace: "${ACCESS_WORKSPACE_ID}", packs: ["access@1"]}
  retire:
    TaskType: [legacy-access-review]
Источник Форма Ключ пакета
каталог установки ключ равен имени каталога в packages/ рядом с файлом установки (или spec.packagesDir)
путь {key, path} из манифеста, сверяется с key
git {key, git, ref, path?} из манифеста, сверяется с key
  • git — https://хост/путь без учётных данных в адресе или git@хост:путь; доступ даёт credential helper git. ref — только тег: ветки и коммиты не принимаются. path — подкаталог пакета в репозитории.
  • knowledge — какие онтологии включить для пространства работы; набор заменяет прежний целиком, план показывает итог и разницу.
  • retire — ключи, которые установка выводит из оборота: TaskType, ProjectTemplate, WorkRule, Agent, NotificationRule, Process, Calendar.
  • Пустой packages: [] — законная установка: только системный тип task.

packages.lock — воспроизводимость

package-sdk lock --install installation.yaml

lock пишет рядом с файлом установки packages.lock (package-sdk.lock/v1): для каждого пакета — источник, версию, коммит тега (у git) и contentHash — хэш канонического набора файлов пакета. plan строится только по зафиксированному содержимому:

Отказ Когда
lock_required пакет из git, а записи в lock нет
source_ref_moved тег в источнике переставили после фиксации
content_mismatch содержимое разошлось с contentHash
lock_stale lock не соответствует установке: нет пакета, другой источник

Для пакетов из каталога установки и по пути lock необязателен — они и так в git установки; но если lock есть, он покрывает все пакеты и сверяется. Кэш источников git — $PACKAGE_SDK_CACHE, иначе $XDG_CACHE_HOME/package-sdk или ~/.cache/package-sdk.

План и применение

package-sdk plan --install installation.yaml --server https://platform.example.com --out plan.json
package-sdk apply --plan plan.json --server https://platform.example.com

План package-sdk.plan/v1 — один документ на все виды, построенный без единой записи. Его секции применяются по порядку:

  1. catalog — виды, которые установщик ставит ресурсами ядра;
  2. core — план ядра для пакетов с процессами или календарями: у такого пакета ядро само планирует и ставит типы задач, агентов, календари, процессы и правила вывода работы;
  3. knowledge — регистрация онтологий и включение их для пространств работы;
  4. notification-rules — правила уведомлений, прошедшие проверку сервиса;
  5. retire — вывод из оборота.

В плане записаны версия ядра стенда, хэш фиксации источников (lockHash), хэш значений переменных (variablesHash) и planHash всего документа. apply применяет только неизменённый план, к тому же стенду, после подтверждения человека в терминале. Перед каждой секцией он строит её заново и сверяет с планом: расхождение — plan_stale до первой записи этой секции.

Секции не атомарны между собой

Каждая запись идемпотентна. Если применение оборвалось между секциями, повторный plan покажет остаток, а повторный apply его доставит.

См. также