README со скриншотом панели

Что показывает вкладка «Токены», как устроен прокси внутри OpenCode, установка
без обёртки, переменные окружения, запуск server.mjs отдельно под Node.
This commit is contained in:
2026-10-29 21:28:30 -07:00
parent 1af3ff9d10
commit 1eef73ec22
2 changed files with 123 additions and 0 deletions
+123
View File
@@ -0,0 +1,123 @@
# context-panel
Панель «Токены» для веб-интерфейса OpenCode 2: что сейчас лежит в контексте сессии, как он рос и сколько
токенов ушло за сессию, за день и за всё время. Работает как плагин OpenCode и поднимается в его же процессе.
<img src="docs/panel.png" alt="Вкладка «Токены» в боковой панели OpenCode" width="600">
## Что на вкладке
В шапке сессии появляется кнопка с кружком и % заполнения контекста. Она открывает боковую панель на вкладке
**«Токены»** рядом с Review и Context; вкладку можно выбрать и вручную. Кнопка и вкладка повторяют разметку
OpenCode, поэтому стили у них родные. Если OpenCode перерисует интерфейс, `panel.js` вставит их заново.
- **Состав контекста.** Сколько токенов было в последнем запросе к модели (как в самом OpenCode) из лимита
модели. Разбивка по категориям: сообщения, ответы, reasoning, вывод каждого тула (MCP отдельно,
`gitlab: …`, `jira: …`; для Code Mode — какие MCP вызывал скрипт), сводка compaction. Учитывается только
то, что сейчас в контексте, — его отдаёт сам OpenCode (`/api/session/{id}/context`). Категории считаются как
символы/4, так же оценивает сам OpenCode. «Остаток» — это разница между реальным числом токенов и суммой
оценок: системный промпт, AGENTS.md, схемы тулов и MCP, плюс погрешность.
- **Рост контекста по шагам.** Токенов в каждом запросе к модели за всю сессию, линия лимита модели и отметки
compaction. Под графиком — список compaction: когда, авто или вручную, контекст до и после, размер сводки,
сколько заняло. Провал на графике без отметки compaction — это чистка вывода тулов (prune / DCP).
- **Самые тяжёлые вызовы в контексте.** Топ-10 вызовов тулов в текущем контексте: тул, что вызывали (файл,
команда, описание), токены входа и вывода.
- **Расход за сессию.** Input, cache read/write, output, reasoning, число шагов модели. Если были сабагенты,
рядом показан итог вместе с ними.
- **Скорость модели в сессии.** Генерация (output + reasoning за время потока, ток/с), среднее ожидание первого
токена, prefill (input без кэша за это время, ток/с) и среднее время модели на шаг. Начало потока — первая
часть ответа, конец — `time.streamed` сообщения. Выполнение тулов не считается.
- **Расход за сегодня / 7 дней / 30 дней / всё время**, по проекту или по всем проектам, плюс топ моделей
(со скоростью генерации и временем на шаг). Над таблицей — график с выбором периода: сегодня по часам, 7 и
30 дней по дням, всё время по дням или по неделям, если история длиннее 90 дней. Столбики разбиты по типу
токенов (Cache / Input / Output) или по моделям (топ-4 + «другие»).
Каждый раздел сворачивается кликом по заголовку; какие свёрнуты — запоминается в браузере (`localStorage`).
Данные обновляются раз в 3 секунды, пока вкладка выбрана, % на кнопке — раз в 10 секунд.
## Как устроено
У веб-интерфейса OpenCode нет точек расширения: он зашит в бинарник, слоты для плагинов есть только в TUI, а
своих HTTP-маршрутов серверный плагин добавить не может. Поэтому плагин `context-panel.v2.ts` поднимает внутри
процесса OpenCode прокси (`server.mjs`) на отдельном порту. Прокси пропускает HTTP, SSE и WebSocket как есть, а
в HTML-страницы вставляет `<script src="/__ctx/panel.js">`. CSP страницы (`script-src 'self'`) скрипт
пропускает: он с того же origin. Service worker веб-интерфейса прокси подменяет «гасящим», иначе тот отдавал бы
страницу из кэша без панели.
```
браузер → :4196 server.mjs (в процессе OpenCode) ─┬─ /__ctx/panel.js, /__ctx/api/* → opencode.db (только чтение) + API OpenCode
└─ всё остальное → :4197 opencode serve
```
`/__ctx/*` закрыт тем же паролем, что и сам OpenCode: прокси сверяет заголовок `Authorization` с OpenCode и с
ним же берёт из API лимит модели и текущий контекст сессии. Зависимостей нет: в OpenCode база читается через
`bun:sqlite`, под Node ≥ 22.5 — через `node:sqlite`.
Особенности плагинов OpenCode 2 (проверено на 2.1.3):
- **Плагин грузится лениво** — при первом запросе к API проекта, а не при старте `opencode serve`. Пока такого
запроса не было, порт панели пуст. Поэтому после старта нужно один раз дёрнуть API (см. «Установка»).
- **Модуль импортируется заново для каждого проекта.** Прокси один на процесс (флаг в `globalThis`).
- **При выгрузке проекта прокси не закрывается.** Браузер ходит через него, и без прокси OpenCode не получил
бы запрос, который загрузит плагин снова. Живёт, пока жив процесс.
## Установка
1. Положить ссылку на плагин в `plugins/` конфига OpenCode (в `"plugin"` в `opencode.jsonc` не добавлять:
имя там считается npm-пакетом). `server.mjs` и `panel.js` плагин ищет рядом с настоящим файлом:
```bash
ln -sfn "$PWD/context-panel.v2.ts" "${XDG_CONFIG_HOME:-$HOME/.config}/opencode/plugins/"
```
2. Запустить OpenCode на соседнем порту и сказать плагину, где слушать:
```bash
CTX_PANEL_LISTEN=4196 CTX_PANEL_TARGET=4197 opencode serve --hostname 127.0.0.1 --port 4197
```
3. Загрузить плагин первым запросом к API и открыть `http://127.0.0.1:4196/` (на 4197 панели нет):
```bash
curl -s -o /dev/null -u opencode:"$OPENCODE_SERVER_PASSWORD" http://127.0.0.1:4197/api/plugin
```
Переменные окружения плагина:
| Переменная | По умолчанию | Что |
| --- | --- | --- |
| `CTX_PANEL_LISTEN` | `4196` | порт прокси с панелью |
| `CTX_PANEL_TARGET` | `4197` | порт самого `opencode serve` |
| `CTX_PANEL_DB` | `$XDG_DATA_HOME/opencode/opencode.db` | база OpenCode |
| `OPENCODE_CONTEXT_PANEL` | — | `0` — не запускать |
Журнал плагина: `$XDG_DATA_HOME/opencode/log/context-panel.log` (на каком порту поднялся или почему нет).
В mms-challenge всё это делает обёртка `egress-filter/bin/opencode-web` (`make opencode`): кладёт ссылку,
передаёт порты, после старта дёргает API, ждёт порт 4196 и только потом открывает браузер. Выключить панель:
`OPENCODE_CONTEXT_PANEL=0 make opencode`.
### Без плагина
Тот же `server.mjs` можно запустить отдельным процессом под Node перед уже запущенным OpenCode:
```bash
node --disable-warning=ExperimentalWarning server.mjs --listen 4196 --target 4197 \
--db "${XDG_DATA_HOME:-$HOME/.local/share}/opencode/opencode.db"
```
### Проверка
```bash
curl -s -u opencode:"$OPENCODE_SERVER_PASSWORD" http://127.0.0.1:4196/ -H 'accept: text/html' | grep -c __ctx/panel.js
```
Должно вывести `1`. Если панели нет — смотреть `context-panel.log` и консоль браузера. Если открыта старая
вкладка, её нужно перезагрузить.
## Разработка
- Правки в `panel.js` применяются после перезагрузки страницы, правки в `server.mjs` и `context-panel.v2.ts` —
после перезапуска OpenCode.
- Панель читает внутреннюю схему `opencode.db` (таблицы `session_v2`, `session_message`). Если после обновления
OpenCode она перестанет показывать данные, смотреть надо в `sessionStatsV2` / `globalRowsV2` в `server.mjs`.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB