sdlc-hub/ARCHITECTURE.md
Andrey Epifancev e8707d4762 feat(api): POST /files и GET /agent/files/{sha256}, files/ 0700 при старте, уборка в tick раз в сутки (#43)
Не base64 — 400 bad_request, пустой — 422 empty_file, больше 10 МБ — 413 too_large; агенту — {file, size, content} в base64, нет файла — 404 no_file; уборка отдельно от автообновления (решения 14–16).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 01:41:54 +04:00

7.9 KiB
Raw Blame History

Архитектура

Хаб моста Jira: SQLite-зеркало задач AURORA/AWPCA, служебные заметки, очередь команд, HTTP API и MCP. Агент (jira-agent, виртуалка) опрашивает хаб, пишет в Jira и возвращает состояние; хаб Jira не вызывает.

Модули

Модуль Ответственность Зависит от
paths каталог данных (JIRA_HUB_HOME), пути базы и backups/, права 0700 (secure_dir) stdlib
db схема SQLite, миграции по schema_version (копия базы перед миграцией), connect backup, paths
settings настройки (setting), токены, темп, перенос из config.example.json db
mirror зеркало задач, связей и комментариев, apply_state (с проверкой check_comments), заметки, фильтры, снимок доски board db
commands контракт типов команд, валидация, временные ключи, оценка, подтверждение по состоянию mirror
queue пакеты, выдача (claim), отчёты, отмена, повтор, автообновление commands, mirror, settings, 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 queue, mirror, 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 client
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/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 прежней страницы), отрисовка — web/src/board/components.

Цепочки: api → queue → commands/mirror/settings → db; 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.
  • Чтение Jira: команда refresh → агент → POST /agent/state → mirror.apply_state.
  • Агент за 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.

Границы

  • Зеркало пишет только mirror.apply_state по запросу агента (и заметки через PUT /issues/{key}/note); из очереди и API задач напрямую не правят.
  • Хаб не знает пароль Jira и не обращается к Jira; init не переносит auth, base_url, verify_ssl, ca_bundle.
  • queue не создаёт команд сам: повтор после failed — новый пакет. Выданная без отчёта команда довыдаётся при следующем claim (до 3 выдач, затем failed).
  • mcp_server не импортирует queue/mirror/db напрямую и ходит только через client.
  • commands, mirror, queue не знают про HTTP; HTTP живёт только в api, client и relay (реле — клиент своего же HTTP).
  • Веб-приложение (web/) ходит в хаб только через HTTP API (web/src/api/client.ts); словари kind_labels, link_types, status_labels берёт из GET /settings и своих копий не держит.

Открытые вопросы

  • Правки из UI в Jira (спринт, срок, связи, исполнитель) — change viewer-edit; страницы очереди, агента и настроек — позже.
  • Автоподстройка темпа по фактам 429 не реализована: хаб только показывает их.