Модуль attachments (fmt_size, ref_names, resolve, state, get); mirror.get_issue отдаёт attachments, description_attachment_ids, attachment_ids комментариев и скрывает fetch_attachment из commands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 KiB
Архитектура
Хаб моста Jira: SQLite-зеркало задач проектов-меток Jira-проекта AURORA, служебные заметки, очередь команд, HTTP API и MCP. Агент (jira-agent, виртуалка) опрашивает хаб, пишет в Jira и возвращает состояние; хаб Jira не вызывает.
Модули
| Модуль | Ответственность | Зависит от |
|---|---|---|
paths |
каталог данных (JIRA_HUB_HOME), пути базы и backups/, права 0700 (secure_dir) |
stdlib |
db |
схема SQLite (v6: + issue_attachment, attachment_file), миграции по schema_version (копия базы перед миграцией), connect, время Jira → UTC (jira_utc) |
backup, paths, projects (ленивый импорт в миграции v3→v4) |
settings |
настройки (setting), токены, темп, перенос из config.example.json |
db |
projects |
проекты-метки: таблица project (метка, название, board_id, board_type), проверка метки, базовая JQL из меток проектов (base_jql), доски (boards), миграция схемы v3→v4 (migrate_v3) |
db, settings |
attachments |
вложения для интерфейсов: ссылки на них в вики-разметке текста (ref_names, resolve), формат размера (fmt_size), состояние ready/fetching/failed/none по attachment_file, хранилищу и последней fetch_attachment (state, get) |
files |
mirror |
зеркало задач, связей, комментариев (время комментария в UTC — created_at) и списка вложений issue_attachment (схема v6; check_attachments, пишет apply_state; attachment_file ему не принадлежит), разрез по проекту (свои, соседи по связям, эпики своих), apply_state (с проверками check_comments, check_attachments), заметки, фильтры, снимок доски board, карточка get_issue (с историей задачи из history) |
db, history, attachments |
history |
журнал изменений задач issue_change (схема v5): проверка и запись пачек агента (check_changes, record_changes, логическое имя поля logical_field), параметр history для выдачи refresh (delivery_param: отметка — последний полный прогон с sync_run.history_at, минус 15 минут; стартовая JQL backfill_jql), признак принятого журнала accepted, лента feed (журнал + новые комментарии по issue_comment.created_at) и история задачи issue_changes |
db, settings, projects |
commands |
контракт типов команд, валидация, временные ключи, оценка, подтверждение по состоянию | mirror |
queue |
пакеты, проверки постановки (_check_setup: bug без настройки, файл вложения; _check_project: проверки пакета проекта — метка, чужие метки, задачи вне проекта; _assign_boards: board_id проекта у ensure_sprint), выдача (claim, size у ensure_attachment и boards у refresh — deliver_params), отчёты, отмена, повтор, автообновление |
commands, mirror, settings, files, projects, db |
backup |
копии файла базы, ротация | paths |
files |
хранилище файлов вложений <данные>/files/<sha256> (каталог — параметр, Hub.files_dir): put (атомарно, одна копия на содержимое, mtime при повторе, предел 10 МБ, пустой — отказ), path/exists/size/read (только sha из 64 hex нижнего регистра), sweep (старше 30 дней, кроме файлов ensure_attachment в queued/claimed) |
paths |
api |
HTTP-сервер, маршруты, авторизация по токенам, фоновый tick (автообновление и уборка files/ раз в сутки); маршруты файлов POST /files, GET /agent/files/{sha256} (каталог Hub.files_dir, 0700 при старте); раздача веб-приложения /app… (каталог web/app пакета, без токена) и перенаправление /, /links; снимок GET /board; маршруты /projects |
queue, mirror, projects, settings, backup, files, relay, release |
relay |
реле общей папки (настройка relay_dir): поток в serve, файлы req/ → свой HTTP (адрес serve, для 0.0.0.0 — 127.0.0.1) → resp/; агент с hub_url = folder:<путь> ходит через него; HTTP-обработчики о нём не знают |
settings |
release |
опубликованная версия агента: release/agent.py + manifest.json в каталоге данных, publish (только вверх; запись с fsync, прежний файл на время записи — agent.py.prev, при сбое возвращается), current (проверка манифеста, предупреждение один раз на ошибку), load (с проверкой sha256 и UTF-8), подсказка hint для agent_status |
paths |
client |
HTTP-клиент хаба | db, paths, settings (только для from_home) |
mcp_server |
инструменты MCP по stdio; привязка сессии к проекту (jira-hub mcp --project <метка>), инструменты projects и project_create (фрагмент .mcp.json), лента changes (GET /changes, по умолчанию — с начала суток); batch_create читает файлы ensure_attachment.path и загружает их POST /files до постановки пакета |
client, commands, files (только MAX_SIZE) |
importer |
разовый импорт CSV-пакета в заметки, сверка с зеркалом | mirror, settings |
cli |
init, serve, import, mcp, tokens, agent publish |
api, mcp_server, importer, release, settings, db, paths |
web/ — веб-приложение хаба (React + TypeScript + Vite): вход по клиентскому токену, страницы «Доска» (/app/board),
«Задачи» (/app/issues) и «Лента» (/app/feed: журнал изменений GET /changes за период; web/src/feed/ — чистые
функции events.ts (период, виды, текст события, группировка) и EventList.tsx, общий с блоком «История» карточки); текущий проект — ?project=<метка> в адресе (web/src/projects/: контекст проекта, выпадающий список «Проект», окно «Новый проект», заглушка «проект не найден»; страницы пересоздаются по key={project}); карточка /app/issues/{key} — окно поверх страницы, с которой её открыли: shell/Layout.tsx рисует
страницу по адресу из location.state.background (useRoutes с явным адресом) и issues/IssueModal.tsx поверх неё. Собирается make web в src/jira_hub/web/app/; api отдаёт сборку
без токена (/app/assets/* — файлы, прочие /app… — index.html, 503 без сборки), / и /links перенаправляет на
/app/board. Данные — только через HTTP API с клиентским токеном; mirror.board отдаёт зеркало без преобразований,
перевод в модель доски — в web/src/board/model. Логика доски — чистые модули web/src/board/{model,analysis,layout}
и storage.ts (ключи localStorage прежней страницы; у проекта AWPCA — прежние, у остальных с суффиксом @<метка>; набор ключей — состояние модуля, выставляется при монтировании страницы), отрисовка — web/src/board/components.
Вид — дизайн-система AURA: токены цвета, шрифта, отступов и теней (светлая и тёмная темы) — web/src/styles/tokens.css,
единственное место с литералами цвета; базовые компоненты (Button, Notice, Dialog, Field, Panel, меню и др.) —
web/src/ui/, остальные стили подключаются к токенам по семантическим именам. Оболочка — web/src/shell/Layout.tsx: grid из боковой
панели Sidebar.tsx (закреплена у левого края, сворачивается в полосу значков — ключ jira-hub-sidebar; на экранах
уже 640 px — строка с «Меню» и выезжающая панель) и страницы; прокрутка — у окна. Обновление данных из Jira (кнопка в
боковой панели, отслеживание пакета) — web/src/shell/refresh.ts (провайдер в App.tsx); доска отдаёт наружу только
источник и события снимка.
Цепочки: api → queue → commands/mirror/settings/projects/history → db; mirror → history → projects/settings; mirror → attachments → files; db → projects (только миграция, ленивый импорт); projects → settings; db → backup → paths, db → paths; files → paths; api → relay → settings; mcp_server → client → HTTP API; importer → mirror/settings.
Потоки данных
- Запись в Jira: клиент (
mcp_server/интерфейс) →POST /batches→queue→ агентclaim→ Jira →POST /agent/commands/{id}/result. - Вложение: файл →
POST /files→files/<sha256>→ensure_attachment {key, file, filename}в пакете (queue._check_setupпроверяет, что файл есть) →claim(хаб дописываетsize, в базеparamsбез него) → агентGET /agent/files/{sha256}→ Jira; уборкаfiles/— изtickраз в сутки. - Создание проекта:
POST /projects(веб илиproject_create) →projects.createи пакет «Новый проект <метка>» (refreshпо общей JQL,batch.project = NULL) в одной транзакции →claim(хаб дописывает вrefreshдоски проектов,boards) → агент определяет тип доски → отчётrefreshпишетboard_type→ensure_sprintпроекта получаетboard_id. - Чтение Jira: команда
refresh→ агент →POST /agent/state→mirror.apply_state. - Журнал изменений: выдача
refresh(queue.deliver_paramsдописываетhistory—sinceи/или стартовуюjql) → агент v13 после состояния читает changelog Jira →POST /agent/history→history.record_changes(повторы отбрасываются по(history_id, seq)) → отчётrefreshсresult.history→sync_run.history_atу полного прогона (новая отметка) →GET /changes,GET /issues/{key}.changes, MCPchanges. - Агент за VPN: агент → файлы в общей папке (
relay_dir) →relay→ HTTP хаба → ответ файлом. - Самообновление агента:
jira-hub agent publish→release/→claim.agent_release→ агент скачиваетGET /agent/release; состояние обновления агент шлёт вclaim, хаб держит в памяти (вместе с признакомagent_reported— был лиclaimс запуска хаба) и отдаёт вGET /agent. - Токены: агентский — только
/agent/*, клиентский — остальное; MCP берёт клиентский токен из базы черезHubClient.from_home.
Границы
- Зеркало (включая
issue_attachment) пишет толькоmirror.apply_stateпо запросу агента (и заметки черезPUT /issues/{key}/note); из очереди и API задач напрямую не правят. - Журнал
issue_changeпишет толькоhistory.record_changesпоPOST /agent/history; API и MCP его только читают. - Хаб не знает пароль Jira и не обращается к Jira;
initне переноситauth,base_url,verify_ssl,ca_bundle. queueне создаёт команд сам: повтор после failed — новый пакет. Выданная без отчёта команда довыдаётся при следующемclaim(до 3 выдач, затем failed).mcp_serverне импортируетqueue/mirror/db/projects/historyнапрямую и ходит только черезclient; изfilesберёт только пределMAX_SIZE(файлы в хранилище кладёт хаб поPOST /files).commands,mirror,queueне знают про HTTP; HTTP живёт только вapi,clientиrelay(реле — клиент своего же HTTP).- Веб-приложение (
web/) ходит в хаб только через HTTP API (web/src/api/client.ts); словариkind_labels,link_typesи набор закрытых статусовclosed_statusesберёт изGET /settingsи своих копий не держит.
Открытые вопросы
- Правки из UI в Jira (спринт, срок, связи, исполнитель) — change
viewer-edit; страницы очереди, агента и настроек — позже. - Автоподстройка темпа по фактам 429 не реализована: хаб только показывает их.