Repository navigation
diagnostics
Эта страница нужна, когда команда упала, зависла или идёт слишком долго и хочется понять почему. Прочитав её, вы сможете видеть запросы команды по ходу работы, сохранить запись о запуске, проверить установку и отправить отчёт о проблеме, в котором нет текста сообщений.
Слова, которые здесь встречаются:
-
Запуск — один вызов
max, от старта до выхода. - Запись запуска — каталог, где хранится, что делал один запуск: команда, итог и по строке на каждый запрос к MAX. В строках о запросах текстов сообщений, имён и названий нет никогда. Командную строку запись может хранить так, как вы её набрали, но без секретов (подробнее).
-
max personal doctorпроверяет установку: версию, вход, настройки и локальную копию. -
Отчёт о проблеме — один файл JSON из ответа
max personal doctorи неудачного запуска. Его прикладывают к задаче на GitHub.
| Задача | Команда | Что сохраняется |
|---|---|---|
| Видеть каждый запрос по ходу работы | max … --trace |
ничего, только вывод в stderr |
| Сохранить запись одного запуска | max … --record |
запись запуска |
| Найти запуск позже | max personal runs list |
каждый запуск сохраняется сам |
| Сохранять только неудачные запуски | max personal config set record false |
запись неудачного запуска |
| Проверить установку |
max personal doctor, max personal doctor --online
|
ничего |
| Составить отчёт о проблеме | max personal doctor reports create |
один файл, который вы отправляете |
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 GET /me
← getMyInfo GET /me 200 143ms 211 B
→ getMessages GET /messages chat -100
← getMessages GET /messages 200 chat -100 187ms 6.2 kB
Отказ показывает код ошибки и ключ MAX, например 404 not_found not.found, но не текст
ответа. Загрузка файла (--file, uploads put) — отдельная пара строк upload.image,
upload.video и так далее: размер, HTTP-код, время. Адреса загрузки и имени файла в ней нет: адрес
сам работает как пропуск.
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 на запрос, без единого управляющего символа
Каждый запрос в events.jsonl помечен "level": "wire" и транспортом: "transport": "max-ws" для кадра
личного протокола (с opcode и seq), "transport": "http" для Bot API (метод и путь с :id вместо
id, без строки запроса). У запроса Bot API есть attempt: 1 для первой попытки, 2 и дальше для повтора.
После отказа 429 или сбоя чтения в журнале сначала ответ с "outcome": "error", затем повтор с
"attempt": 2 и "wait_ms" — сколько миллисекунд клиент ждал перед ним (Retry-After от MAX или своя пауза).
Так же помечены повторы messages send, пока MAX отвечает attachment.not.ready: кадр каждого повтора несёт
attempt и "wait_ms": 1000.
Каталог — 0700, оба файла — 0600. run.json пишется дважды: как только запуск начался, со
статусом running, и в конце — с итогом. Поэтому каталога с событиями, но без описания запуска, не
бывает.
Итог записывается на любом пути — в том числе когда команда упала, не успев подключиться:
{ "run_id": "…", "command": "chats list", "profile": "personal", "status": "failed",
"requests": 0, "error_code": "authentication_error", "duration_ms": 12 }Запуск сохраняется и без --record, удачный тоже. Неудачный сохраняется при любой ошибке: неверного
флага, неизвестной команды, проверки до начала работы, команд, которые в MAX не ходят (models,
server, watch, upgrade). В run.json такой записи есть argv — командная строка, как вы её набрали:
название чата, поисковый запрос, текст сообщения. Секреты в ней скрыты всегда, а набранное можно скрыть
настройкой record_input (подробнее).
Удачный запуск, который не записал ни одного события, — это одна строка в списке дня, без каталога; runs show
находит его там. Команды, которые работают, пока их не остановят (watch, serve, mcp serve), и команды,
которые WireCat Desktop запускает для себя, сохраняют удачный запуск только с --record. --no-record не
сохраняет ничего, даже неудачный запуск.
Удачные запросы поиска и статистики хранят свою историю, отдельно от записей запусков. В ней параметры запросов, но не найденные сообщения. Как ею управлять — в разделе сохранённые поиски и история.
max personal config set record false # этот профиль: только неудачные запуски
max personal chats list --no-record # этот запуск: ничегоТо же в файле настроек:
{ "profiles": { "personal": { "record": false } } }С "record": false удачные запуски не пишутся, неудачные пишутся по-прежнему. Удачный запуск хранит
набранную командную строку — например, текст отправленного сообщения; record_input решает, сохранять ли её
(подробнее). --no-record или "record": false отключают и историю поиска.
Неудачный запуск — 30 дней (keep_runs_for_days), удачный — 7 дней (keep_successful_runs_for_days), в
настройках. Каталог запусков не растёт больше runs_size_limit_mb (200 МБ): сначала
удаляются самые старые удачные. Старое удаляется только тогда,
когда что-то записывается: инструменту, который ничего не пишет, незачем ходить по этому каталогу.
Удаляется днями целиком, по имени каталога, поэтому ничего не нужно открывать, чтобы решить.
В run.json записи запуска может быть argv — слова после max, как вы их набрали. По ним видно,
что именно запускали:
{ "command": "messages list", "argv": ["personal", "messages", "list", "Семья", "--token=[redacted]"] }Вместо секрета всегда стоит [redacted], что бы ни было в настройках. Секрет — это значение флага,
в имени которого есть token, password, secret, credential, cookie, authorization или API key; пароль
внутри адреса; длинная строка без пробелов, похожая на ключ; тело bot api … --body. Номер телефона
max в командной строке не принимает: если его всё же написать после contacts lookup, команда
откажется, а в записи будет [redacted].
Чтобы набранное не попадало в записи запусков:
max personal config set record_input false # этот профиль
max config set record_input false --defaults # все профилиТогда в argv остаются слова команды, имена флагов, имя профиля и значения, которые не могут быть
чьими-то словами: короткие числа, даты, длительности, true и false. Всё остальное заменяется на
[redacted]. Если файл настроек не читается, набранное тоже скрывается: возможно, именно в нём
record_input и был выключен.
| Записывается | Не записывается никогда |
|---|---|
| операция, опкод, номер запроса | название чата |
идентификатор чата, номер отправки (send), идентификатор сообщения |
имя человека |
| сколько байт ушло и пришло | текст сообщения |
| сколько миллисекунд занял ответ | номер телефона |
| сколько чатов, контактов, сообщений вернулось | токен |
код ошибки и короткий ключ ошибки MAX вида login.token
|
текст ошибки, пришедший от MAX |
предупреждение — кодом, например reactions_unread
|
текст предупреждения |
| при падении — тип ошибки и строки кода, где оно случилось | текст ошибки при падении |
версия, среда (node или bun), система |
путь к домашнему каталогу |
Таблица — о строках событий и итоге запуска. Командная строка в argv — отдельно, выше.
Ни в обрезанном виде, ни хэшем. Событие собирается из полей, названных по именам, а не
фильтрованием копии запроса: поле, которого никто не предвидел, в журнал попасть не может. У
session.login есть поле token — и ни одна ветка в коде до него не дотягивается.
Идентификаторы, наоборот, в записи есть. Идентификатор чата — число, бесполезное без сессии, к которой он относится. И только он делает диагностику полезной: любая настоящая жалоба — про один конкретный разговор.
Текст ошибки MAX не записывается по той же причине: отказ сервера может цитировать то, что ему
послали, а послано могло быть и сообщение. Записывается только ключ — строчные латинские буквы,
цифры, точки и дефисы, как proto.payload. Если MAX ответил чем-то другим, не пишется ничего.
То же верно и для отчёта, собранного из запуска.
max personal doctor # никуда не подключается
max personal doctor --online # ещё и входит в MAX один раз; ничего не отправляетБез --online он печатает, не подключаясь к MAX, то, от чего зависит любая команда:
- есть ли токен и откуда, какая запись в ключнице и не сдвинули ли её переменные окружения (сам токен не печатается никогда — только «есть» и «откуда»);
- сколько было входов и когда последний; все профили на этой машине — личный аккаунт, бот или оба;
- общее хранилище: в ответе
--jsonполеitem.store— путь, схема и число сохранённых чатов и сообщений. Проверка не создаёт и не обновляет хранилище; - каталог запусков;
- какой версией веб-клиента 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 reports create # о последнем неудачном запуске
max personal doctor reports create --run <id> # об этом запускеЕсли конфигурация не читается, сначала вызовите max doctor --json. В config.issues он перечисляет каждую
ошибку файла: вид, ключ, строку и столбец для неверного JSON и команду config unset, которая убирает ключ.
Значение настройки в ответ не попадает. С ошибками в файле doctor всё равно отвечает и завершается с кодом 0.
В files.problems он называет файл настроек и его каталог, если их могут читать другие, а на stderr — chmod, который это исправляет.
Создание файла отчёта требует проверки прав на запись, поэтому неверную настройку нужно исправить до него.
Команда пишет файл JSON — то, что показывает max personal doctor, и запуск — и печатает ссылку на новую
задачу в github.com/WireCatLabs/max-cli/issues. Прочитайте
файл, прежде чем отправлять. Текстов сообщений в нём нет, а номера чатов и сообщений заменены
метками. Что ещё в нём есть — в разделе как сообщить о проблеме.
Если неудачного запуска нет, запустите упавшую команду ещё раз: её ошибка сохранится сама.
Записи — это JSON, поэтому на вопросы о них отвечает jq. max personal runs list --json отвечает
{ items, page: { limit, has_more } }, а items — это run.json запусков, новые сверху:
# какие запуски закончились плохо
max personal runs list --json | jq '.items[] | select(.status=="failed") | {run_id, command, error_code}'
# сколько времени ушло на вход в последних запусках
for id in $(max personal runs list --json | jq -r '.items[].run_id'); do
jq -r 'select(.operation=="session.login" and .event=="response") | "\(.duration_ms)ms"' \
"$(max personal runs path "$id" --json | jq -r .item.path)/events.jsonl"
donemax personal runs show печатает те же события таблицей, без служебных полей, которые повторяются в каждой
строке: иначе таблица уезжает за правый край экрана раньше, чем доходит до длительностей. Файл
целиком, со служебными полями, лежит в каталоге, который печатает max personal runs path.
max personal search runs error_code:rate_limited --json
max personal search runs status:partial 'started_at>=2026-10-01' --jsonsearch runs ищет буквальный текст в метаданных и безопасных диагностических событиях;
условия status:, error_code:, operation:, profile: и сравнения или диапазоны started_at: пишутся в запросе. --limit и --page
задают страницу. Одна страница — до 100
запусков; в запуске возвращается до 100 совпавших событий, events_truncated сообщает о сокращении.
Ошибка отдельного файла или страницы истории создаёт частичную запись status: "partial" с
неудачными ID, этапами и кодами. Текст сообщений и текст отказа сервера в неё не копируются.
Запись сохраняется по умолчанию, если её явно не выключили. Поиск сам не создаёт новый запуск.
В MCP вызовите max_read с command: "search runs"; поля arguments: query (включая условия и границы времени), limit и page. Он читает только записи активного профиля, не подключается к MAX и не открывает сырые
журналы фоновых заданий. Полный результат частичной команды называет неудачные элементы и коды их ошибок;
диагностическая запись сохраняет только безопасные идентификаторы и коды.