chore(agents): задание Antigravity по метрикам узлов в консоли

Windows-клиент показывает CPU, память, диск, задержку и здоровье по
каждому серверу, веб-консоль не показывает ничего из этого. Данные при
этом уже лежат на Hub: таблица metric_samples наполняется из heartbeat и
чистится по MetricRetentionHours, но в группе /api/v1/control нет ни
одного маршрута для их чтения.

Границей задания зафиксировано то, что переносить нельзя: SSH-терминал и
приватные ключи остаются на Windows-машине, это заявленное свойство
продукта, а не объём работы.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Ochenstarik 2026-08-20 17:38:52 +07:00
parent 3960d30ee6
commit 7fac6d5d22

View file

@ -0,0 +1,110 @@
# Метрики и короткая история узлов в веб-консоли
- **Кому:** `antigravity`
- **Дата:** 2026-08-20
- **От кого:** главный агент (`claude`), по задаче владельца «консоль надо
сделать так же, как и Windows-программа»
- **Ветка:** `antigravity/console-node-metrics`
- **Файл отчёта:** `../done/2026-08-20-console-node-metrics.md`
## Зачем
Windows-клиент на странице «Серверы» показывает по каждому серверу CPU,
память, диск, задержку, состояние здоровья и статус
(`Pages/ServersPage.xaml`: `CpuText`, `MemoryText`, `DiskText`,
`LatencyText`, `HealthText`, `Status`). Веб-консоль не показывает ничего из
этого: в ней есть только имя узла, состояние, версия агента, время
heartbeat и остаток срока сертификата.
Это первая из нескольких задач по сближению консоли с Windows-клиентом.
Здесь закрывается ровно метрики и короткая история — остальное перечислено
ниже и в эту задачу не входит.
## Установленный факт, от которого отталкиваемся
Данные **уже есть на Hub**, показывать нечем.
`ControlStore` содержит таблицу (`ControlStore.cs:81`):
```sql
CREATE TABLE IF NOT EXISTS metric_samples (
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
node_id TEXT NOT NULL REFERENCES agents(node_id) ON DELETE CASCADE,
recorded_at TEXT NOT NULL,
payload_json TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS ix_metric_samples_node_time
ON metric_samples(node_id, recorded_at DESC);
```
Наполняется она из heartbeat агента, чистится по `MetricRetentionHours`
(по умолчанию 168 часов). При этом в группе `/api/v1/control` **нет ни
одного маршрута для метрик** — я перечитал все `control.Map*` в
`Program.cs`. То есть оператор физически не может их получить.
## Что нужно сделать
1. **Добавить в Control чтение метрик под ролью Operator.** Как минимум
последний срез по каждому узлу и история по одному узлу за period.
Форму запроса, имена полей и ограничение на объём выбираешь сам и
обосновываешь в отчёте. Обязательные требования: маршрут в группе
`control` (то есть `RequireAuthorization("Operator")`), ограничение
количества возвращаемых точек, и никакого доступа к чужим данным по
роли Agent или Automation.
2. **Показать это в консоли** в разделе узлов: текущие CPU, память, диск и
задержка рядом с узлом, плюс короткая история — так же, как её
показывает Windows-клиент.
3. **`payload_json` — данные из внешнего источника.** Он приходит от
агента. Разбирать его в консоли только через уже имеющийся
`escapeHtml`, как это сделано с payload событий; ни одной вставки в
`innerHTML` без экранирования. Отсутствующие или битые поля не должны
ронять таблицу узлов.
## Границы — что переносить нельзя
**SSH-терминал и приватные ключи в веб-консоль не переносятся.** Это не
вопрос трудоёмкости, а заявленное свойство продукта: README обещает
«opens direct SSH terminals without sending a private terminal key to the
Hub», а ключ мониторинга и сертификат оператора защищены DPAPI и лежат
только на Windows-машине. Страница Sessions остаётся у Windows-клиента.
Также не входит в эту задачу и делается отдельно: страница Settings,
группы и теги серверов, alert rules и журнал уведомлений (последние два
пункта в `docs/roadmap.md` числятся невыполненными и для Windows-клиента).
Не трогать: `deploy/**`, релизные workflow, Desktop, схему существующих
таблиц (добавление индексов допустимо и обосновывается).
## Как проверить результат
- новый маршрут закрыт ролью: запрос без токена → 401, под ролью Agent и
Automation → 403, под Operator → 200. Три теста, вывод в отчёт;
- ограничение объёма истории проверено тестом: запрос заведомо большего
количества точек возвращает не больше предела;
- битый или неполный `payload_json` не роняет ни маршрут, ни отрисовку —
тест на каждую сторону;
- `dotnet test` целиком зелёный, с выводом;
- прогоны CI на PR — ссылками.
## Связь с другими заданиями
Эта задача пересекается с двумя уже выданными и делать её нужно **после**
них, иначе правки лягут друг на друга в одном файле:
- `2026-08-19-console-behaviour-tests.md` — поведенческие тесты консоли.
Новая функциональность обязана прийти с такими тестами сразу, а не
проверками строк в HTML;
- `2026-08-19-console-events-throttle.md` — троттлинг обновления дашборда.
Метрики увеличат объём каждого обновления, поэтому без троттлинга
усиление нагрузки станет заметнее.
Если считаешь, что порядок должен быть другим, — скажи в отчёте и обоснуй,
это решение приму.
## Контекст и ограничения
Проверено чтением кода и запуском Control локально на пустой базе: узлов
ноль, метрик ноль. Как выглядят настоящие `payload_json` от живого агента,
я не видел — на живом Hub не проверял. Прежде чем фиксировать контракт
ответа, посмотри фактическую форму payload в коде агента, а не в моём
описании.