Skip to content

diagnostics

github-actions[bot] edited this page Oct 14, 2026 · 1 revision

Диагностика: что команда делала

Эта страница нужна, когда команда упала, зависла или идёт слишком долго и хочется понять почему. Прочитав её, вы сможете видеть запросы команды по ходу работы, сохранить запись о запуске, проверить установку и отправить отчёт о проблеме, в котором нет текста сообщений.

Слова, которые здесь встречаются:

  • Запуск — один вызов max, от старта до выхода.
  • Запись запуска — каталог, где хранится, что делал один запуск: команда, итог и по строке на каждый запрос к MAX. Текстов сообщений, имён и названий в нём нет никогда.
  • max personal doctor проверяет установку: версию, вход, настройки и локальную копию.
  • Отчёт о проблеме — один файл JSON из ответа max personal doctor и неудачного запуска. Его прикладывают к задаче на GitHub.

Что можно сделать

Задача Команда Что сохраняется
Видеть каждый запрос по ходу работы max … --trace ничего, только вывод в stderr
Сохранить запись одного запуска max … --record запись запуска
Найти неудачный запуск позже max personal runs list неудачный запуск сохраняется сам
Сохранять каждый запуск max personal config set record true запись каждого запуска
Проверить установку max personal doctor, max personal doctor --online ничего
Составить отчёт о проблеме max personal doctor report create один файл, который вы отправляете

Показать: --trace

max personal chats list --trace
→ session.init      op 6   seq 1  432 B
← session.init      op 6   seq 1  89ms  335 B  38 reg-country-code
→ session.login     op 19  seq 2  871 B
← session.login     op 19  seq 2  202ms  48.0 kB  25 chats  6 contacts
→ contacts.info     op 32  seq 3  154 B  3 contacts
← contacts.info     op 32  seq 3  91ms  6.5 kB  10 contacts

→ — что спросили, ← — что пришло, • — ответ, который не уходил в сеть. Дальше по строке: операция, опкод протокола, номер запроса в соединении, названные идентификаторы, длительность, размер кадра и сколько чего вернулось.

Всё это идёт в stderr, поэтому рядом с --json ничего не ломается:

max personal chats list --json --trace > chats.json    # данные в файл, диагностика на экран

В терминале — строки выше. В трубе — по одному объекту JSON на строку, по тому же правилу, что и для данных:

max personal chats list --json --trace 2>&1 >/dev/null | jq -c 'select(.event == "response")'

--trace сильнее --quiet: флаг, который вы дописали сами, срабатывает всегда.

Команды бота

У max <имя> bot … одна строка на HTTP-запрос к Bot API: операция, идентификаторы из адреса (чат, сообщение, человек, комментарий), HTTP-код, время, размер ответа.

max shop bot messages list -100 --trace
→ getMyInfo        
← getMyInfo        200  143ms  211 B
→ getMessages      chat -100
← getMessages      200  chat -100  187ms  6.2 kB

Отказ показывает код ошибки и ключ MAX, например 404 not_found not.found, но не текст ответа. Загрузка файла (--file, uploads put) — отдельная пара строк upload.image, upload.video и так далее: размер, HTTP-код, время. Адреса загрузки и имени файла в ней нет: адрес сам работает как пропуск.

Сохранить: --record

max personal chats list --record
max personal runs list                 # что делалось, новое сверху
max personal runs show <id>            # один запуск: чем кончился и куда ходил
max personal runs path <id>            # каталог, для jq и grep

Каталог запуска:

~/.local/share/max-cli/runs/2026-09-19/20260919T234428Z-chats-list-9df39e/
  run.json       что это было, когда, какой профиль, сколько длилось, чем кончилось
  events.jsonl   по объекту JSON на запрос, без единого управляющего символа

Каталог — 0700, оба файла — 0600. run.json пишется дважды: как только запуск начался, со статусом running, и в конце — с итогом. Поэтому каталога с событиями, но без описания запуска, не бывает.

Итог записывается на любом пути — в том числе когда команда упала, не успев подключиться:

{ "runId": "…", "command": "chats list", "profile": "default", "status": "failed",
  "requests": 0, "errorCode": "authentication_error", "durationMs": 12 }

Неудачный запуск сохраняется всегда

Если команда кончилась ошибкой, её запуск сохраняется и без --record — с пометкой "keptBecauseFailed": true в run.json. Это касается любой команды и любой ошибки: неверного флага, неизвестной команды, проверки до начала работы, команд, которые в MAX не ходят (models, server, watch, upgrade). В записи — только слова команды, например messages list, без того, что было написано после них.

Полностью удачный запуск без --record записи не оставляет; частичный результат сохраняется по умолчанию. Поэтому у отчёта о проблеме всегда есть что приложить. --no-record или "record": false в настройках отключают и это.

Удачные запросы поиска и статистики хранят свою историю, отдельно от записей запусков. В ней параметры запросов, но не найденные сообщения. Как ею управлять — в разделе сохранённые поиски и история.

Когда записывать постоянно

max personal config set record true          # этот профиль
max personal chats list --no-record          # но не этот запуск

То же в файле настроек:

{ "profiles": { "default": { "record": true } } }

Тогда пишется каждый запуск. По умолчанию полностью удачный запуск не записывается, пока не попросили: каталог с тем, кого и когда вы читали, был бы дневником вашей жизни, о котором никто не просил. --no-record или "record": false отключают и запись запусков, и историю поиска.

Сколько это живёт

30 дней, или keepRunsForDays в настройках. Старое удаляется только тогда, когда что-то записывается: инструменту, который ничего не пишет, незачем ходить по этому каталогу. Удаляется днями целиком, по имени каталога, поэтому ничего не нужно открывать, чтобы решить.

Чего в записи нет

Записывается Не записывается никогда
операция, опкод, номер запроса название чата
идентификатор чата, номер отправки (send), идентификатор сообщения имя человека
сколько байт ушло и пришло текст сообщения
сколько миллисекунд занял ответ номер телефона
сколько чатов, контактов, сообщений вернулось токен
код ошибки и короткий ключ ошибки MAX вида login.token текст ошибки, пришедший от MAX
предупреждение — кодом, например reactions_unread текст предупреждения
при падении — тип ошибки и строки кода, где оно случилось текст ошибки при падении
версия, среда (node или bun), система путь к домашнему каталогу

Ни в обрезанном виде, ни хэшем. Событие собирается из полей, названных по именам, а не фильтрованием копии запроса: поле, которого никто не предвидел, в журнал попасть не может. У session.login есть поле token — и ни одна ветка в коде до него не дотягивается.

Идентификаторы, наоборот, в записи есть. Идентификатор чата — число, бесполезное без сессии, к которой он относится. И только он делает диагностику полезной: любая настоящая жалоба — про один конкретный разговор.

Текст ошибки MAX не записывается по той же причине: отказ сервера может цитировать то, что ему послали, а послано могло быть и сообщение. Записывается только ключ — строчные латинские буквы, цифры, точки и дефисы, как proto.payload. Если MAX ответил чем-то другим, не пишется ничего.

То же верно и для отчёта, собранного из запуска.

Проверить установку: max doctor

max personal doctor              # никуда не подключается
max personal doctor --online     # ещё и входит в MAX один раз; ничего не отправляет

Без --online он печатает, не подключаясь к MAX, то, от чего зависит любая команда:

  • есть ли токен и откуда, какая запись в ключнице и не сдвинули ли её переменные окружения (сам токен не печатается никогда — только «есть» и «откуда»);
  • сколько было входов и когда последний; все профили на этой машине — личный аккаунт, бот или оба;
  • общее хранилище: в ответе --json поле store — путь, схема и число сохранённых чатов и сообщений. Проверка не создаёт и не обновляет хранилище. legacyCache только указывает, есть ли старый файл локальной копии; если он есть, doctor подсказывает путь для ручного удаления;
  • каталог запусков;
  • какой версией веб-клиента MAX max представляется, и дату, когда её прочитали. Если этой дате больше 60 дней, max personal doctor предупредит: MAX может перестать принимать старую версию, и тогда поможет обновление max;
  • чем запущен max (Node или Bun, и где), каким менеджером пакетов поставлен, какую команду max найдёт новый терминал, грузятся ли ключница и SQLite, скачана ли модель речи. Если каталог с командой max не на PATH, max personal doctor печатает точные команды, которые его туда добавят: на Windows — для PowerShell, на Linux и macOS — строку export;
  • пауза входа после отказа MAX за слишком частые входы, если она есть.

У профиля бота max <имя> doctor показывает токен бота (откуда он), сколько чатов бот видел и где лежат файлы профиля: состояние, кеш, записи запусков, файлы ботов, журнал отправок и общее хранилище сообщений.

--online — один вход, один чат из списка, затем сервер MCP запускается так, как его запускает клиент, и отвечает списком инструментов. Ничего не отправляет и ничего не отмечает прочитанным. Вход засчитывается в лимит MAX на входы, поэтому при ошибке он не повторяется. Если какая-то часть не прошла, код возврата — не 0. Если у профиля есть токен бота, --online ещё спрашивает у Bot API, чей это токен, и печатает имя и id бота. Если личного токена нет, вход в MAX не делается.

Отчёт о проблеме

max personal doctor report create              # о последнем неудачном запуске
max personal doctor report create --run <id>   # об этом запуске

Команда пишет файл JSON — то, что показывает max personal doctor, и запуск — и печатает ссылку на новую задачу в github.com/WireCatLabs/max-cli/issues. Прочитайте файл, прежде чем отправлять. Текстов сообщений в нём нет, а номера чатов и сообщений заменены метками. Что ещё в нём есть — в разделе как сообщить о проблеме.

Если неудачного запуска нет, запустите упавшую команду ещё раз: её ошибка сохранится сама.

Что делать с записями

Записи — это JSON, поэтому на вопросы о них отвечает jq. max personal runs list --json отвечает { items, page, limit, hasMore }, а items — это run.json запусков, новые сверху:

# какие запуски закончились плохо
max personal runs list --json | jq '.items[] | select(.status=="failed") | {runId, command, errorCode}'

# сколько времени ушло на вход в последних запусках
for id in $(max personal runs list --json | jq -r '.items[].runId'); do
  jq -r 'select(.operation=="session.login" and .event=="response") | "\(.durationMs)ms"' \
    "$(max personal runs path "$id" --json | jq -r .path)/events.jsonl"
done

max personal runs show печатает те же события таблицей, без служебных полей, которые повторяются в каждой строке: иначе таблица уезжает за правый край экрана раньше, чем доходит до длительностей. Файл целиком, со служебными полями, лежит в каталоге, который печатает max personal runs path.

Дальше

Найти ошибку или частичный запуск

max personal runs search --error-code rate_limited --json
max personal runs search --status partial --since-time 2026-10-01 --json

runs search ищет буквальный текст в метаданных и безопасных диагностических событиях; --operation, --profile, --since-time, --limit и --page уточняют поиск. Одна страница — до 100 запусков; в запуске возвращается до 100 совпавших событий, eventsTruncated сообщает о сокращении. Ошибка отдельного файла или страницы истории создаёт частичную запись status: "partial" с неудачными ID, этапами и кодами. Текст сообщений и текст отказа сервера в неё не копируются. Запись сохраняется по умолчанию, если её явно не выключили. Поиск сам не создаёт новый запуск.

В MCP вызовите max_read с command: "runs search"; поля arguments: query, status, error_code, operation, since_time, limit, page. Он читает только записи активного профиля, не подключается к MAX и не открывает сырые журналы фоновых заданий. Полный результат частичной команды содержит действия для восстановления; диагностическая запись сохраняет только безопасные идентификаторы и коды.

Clone this wiki locally