README со скриншотом панели
Что показывает вкладка «Токены», как устроен прокси внутри OpenCode, установка без обёртки, переменные окружения, запуск server.mjs отдельно под Node.
This commit is contained in:
@@ -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`.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 254 KiB |
Reference in New Issue
Block a user