Что показывает вкладка «Токены», как устроен прокси внутри OpenCode, установка без обёртки, переменные окружения, запуск server.mjs отдельно под Node.
10 KiB
context-panel
Панель «Токены» для веб-интерфейса OpenCode 2: что сейчас лежит в контексте сессии, как он рос и сколько токенов ушло за сессию, за день и за всё время. Работает как плагин OpenCode и поднимается в его же процессе.
Что на вкладке
В шапке сессии появляется кнопка с кружком и % заполнения контекста. Она открывает боковую панель на вкладке
«Токены» рядом с 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 не получил бы запрос, который загрузит плагин снова. Живёт, пока жив процесс.
Установка
-
Положить ссылку на плагин в
plugins/конфига OpenCode (в"plugin"вopencode.jsoncне добавлять: имя там считается npm-пакетом).server.mjsиpanel.jsплагин ищет рядом с настоящим файлом:ln -sfn "$PWD/context-panel.v2.ts" "${XDG_CONFIG_HOME:-$HOME/.config}/opencode/plugins/" -
Запустить OpenCode на соседнем порту и сказать плагину, где слушать:
CTX_PANEL_LISTEN=4196 CTX_PANEL_TARGET=4197 opencode serve --hostname 127.0.0.1 --port 4197 -
Загрузить плагин первым запросом к API и открыть
http://127.0.0.1:4196/(на 4197 панели нет):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:
node --disable-warning=ExperimentalWarning server.mjs --listen 4196 --target 4197 \
--db "${XDG_DATA_HOME:-$HOME/.local/share}/opencode/opencode.db"
Проверка
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.