У любой удобной штуки есть момент, когда она начинает раздражать.
С Codex у меня это случилось, когда аккаунтов стало больше одного. Один рабочий, один личный, один запасной. И дальше начинается обычная бытовая возня: то не тот auth.json, то уже кончился лимит, то вообще непонятно, под каким профилем сейчас запущен CLI.
Можно, конечно, хранить несколько файлов и копировать их руками:
cp auth-work.json ~/.codex/auth.json
Но это ровно тот способ, который сначала кажется нормальным, а потом внезапно бесит каждый день.
Я в итоге написал маленький bash-скрипт codex-auth, который делает три простые вещи:
сохраняет текущий
auth.jsonкак профильпереключает профили по имени
показывает usage и лимиты по текущему аккаунту
Плюс там появился кеш, прокси и несколько мелочей, без которых инструмент быстро превратился бы в очередной одноразовый костыль.
В статье покажу, как это устроено, где у Codex лежит авторизация, как вообще смотреть usage через API и почему даже для такого маленького скрипта быстро вылезают кроссплатформенные грабли.
Где у Codex лежит auth.json
Если говорить грубо, вся локальная авторизация крутится вокруг одного файла:
~/.codex/auth.json
На Linux и macOS это обычно именно он.
На Windows, если запускать нативно, путь будет таким:
%USERPROFILE%\.codex\auth.json
Если вы работаете через WSL, Git Bash или что-то похожее, то там уже всё зависит от среды, но логика та же: есть каталог .codex, внутри него лежит auth.json.
Внутри файла обычно находятся токены и связанные поля вроде access_token, id_token, account_id. Конкретная структура может со временем меняться, так что опираться на неё надо аккуратно.
И вот тут появляется первая проблема. Файл один, а сценариев много.
Почему не хватило обычного cp
Поначалу я тоже думал, что всё решается одной строкой:
cp ~/.codex-auth-backups/work.json ~/.codex/auth.json
На практике этого хватает ровно до того момента, пока профилей не становится хотя бы три.
Потом начинаются вопросы:
какой профиль сейчас реально активен
не перетру ли я нужный
auth.jsonне сохранил ли я один и тот же аккаунт под разными именами
сколько осталось лимита на текущем профиле
когда он сбросится
То есть технически cp работает. Но как инструмент на каждый день это неудобно.
Мне хотелось, чтобы было как с нормальным маленьким CLI: сохранил профиль один раз, дальше переключаешься по имени и сразу видишь, что происходит.
Что умеет codex-auth
В итоге набор команд получился таким:
codex-auth # список профилей с usage codex-auth usage # лимиты для текущего auth.json codex-auth save work # сохранить текущий профиль codex-auth use work # переключиться на профиль codex-auth list-fast # список без API-запросов codex-auth backup # резервная копия текущего auth.json
Отдельно есть блок для прокси:
codex-auth proxy set http://127.0.0.1:8888 codex-auth proxy show codex-auth proxy unset
И управление кешем:
codex-auth cache-time codex-auth cache-time 60 codex-auth cache-time 0
По сути это просто аккуратная обвязка вокруг ~/.codex/auth.json, но именно такой обвязки мне и не хватало.
Как это выглядит в работе
Сначала логинимся обычным способом и получаем рабочий auth.json.
Потом сохраняем его как профиль:
codex-auth save work
Потом, например, перелогиниваемся под другим аккаунтом и сохраняем второй:
codex-auth save personal
После этого переключение уже выглядит нормально:
codex-auth use work codex-auth use personal
или вообще коротко:
codex-auth work
Список профилей:
codex-auth
Если не хочется сетевых запросов, можно быстро посмотреть только локальные данные:
codex-auth list-fast
Как смотреть usage через API
Самая полезная часть скрипта для меня оказалась не переключение, а именно просмотр usage.
Под капотом там обычный HTTP-запрос к backend endpoint:
https://chatgpt.com/backend-api/wham/usage
Запрос делается с Authorization: Bearer <access_token>, а в auth.json есть account_id, он тоже пробрасывается заголовком.
Это выглядит так:
curl -s \ -H "Authorization: Bearer $token" \ -H "Accept: application/json" \ -H "User-Agent: CodexCLI" \ "https://chatgpt.com/backend-api/wham/usage"
Дальше ответ просто разбирается через jq.
Мне было важно видеть не сырой JSON, а нормальную человеческую сводку:
какой email у профиля
какой тип плана
сколько осталось в основном окне
когда reset
что с недельным лимитом
что с code review limit, если он есть
То есть вместо того, чтобы каждый раз вручную ковырять JSON, я просто вызываю:
16:12:03 ❯ codex-auth 📊 5h: 100% ⏱5h 7️⃣ 15% ⏱4d 9h work <asdx@mail.com> 📊 5h: 100%v⏱5h 7️⃣ 84% ⏱3d 18h *personal <example2@mail.com>
и вижу уже готовую картину.
Зачем понадобился кеш
Очень быстро выяснилось, что usage без кеша ощущается туповатым.
Если каждый list или usage ходит в API заново, это уже не маленький быстрый CLI, а инструмент, который постоянно чего-то ждёт.
Поэтому я сделал простой файловый кеш с TTL.
По умолчанию сейчас это 15 секунд. Для живой работы хватает.
Ключ кеша считается из токена, account_id и текущего proxy. Это важно, потому что разные профили и разные сетевые пути не должны смешиваться.
Хранится всё в отдельной папке:
~/.codex-auth-profiles/.usage-cache
Если хочется чаще обновлять:
codex-auth cache-time 5
Если хочется совсем без кеша:
codex-auth cache-time 0
Прокси: мелочь, которая оказалась не мелочью
Прокси я сначала вообще не планировал делать.
А потом быстро выяснилось, что в некоторых сетях прямой доступ к API работает так себе. Где-то мешает корпоративный прокси, где-то firewall, где-то просто неприятная маршрутизация.
В результате появился режим с дефолтным прокси для всех curl-запросов:
codex-auth proxy set http://127.0.0.1:8888
После этого usage и остальные сетевые запросы идут через него.
Никакой магии там нет. Скрипт просто хранит URL прокси в отдельном файле и подставляет --proxy в curl.
Простая вещь, но на практике сильно помогает, если работаешь не из идеальной домашней сети.
Как я определяю, что это тот же самый аккаунт
Ещё одна штука, которую хотелось сделать нормально: не плодить дубликаты.
Если один и тот же аккаунт сохранить как work, потом как main, потом ещё как test, очень быстро получается бардак.
Поэтому у каждого профиля есть fingerprint.
Логика такая:
если есть
account_id, используем егоесли нет, пробуем
subизid_tokenесли и этого нет, пробуем email
если уже совсем ничего нет, берём sha256 самого файла
Этого достаточно, чтобы понять, какие профили действительно разные, а какие просто по-разному названы.
Заодно это позволяет понять, совпадает ли текущий ~/.codex/auth.json с каким-то уже сохранённым профилем.
Что оказалось неожиданно неприятным
Сам скрипт маленький, но даже в таком размере быстро вылезают детали, про которые сначала не думаешь.
base64 на Linux и macOS отличается
На Linux обычно работает:
base64 -d
На macOS часто нужен уже другой флаг:
base64 -D
Так что пришлось делать fallback.
stat тоже отличается
На Linux:
stat -c %Y file
На macOS:
stat -f %m file
А это нужно для нормальной проверки возраста кеша.
sha256sum есть не везде
На Linux чаще всего есть sha256sum, а на macOS чаще удобнее использовать:
shasum -a 256
Запись файлов лучше делать атомарно
Когда работаешь с auth.json, очень не хочется случайно оставить битый файл.
Поэтому я везде делаю запись через временный файл и mv:
tmp=$(mktemp) cp "$src" "$tmp" mv "$tmp" "$dst"
Это не делает скрипт бессмертным, но хотя бы убирает часть совсем тупых проблем.
Где профили хранятся локально
Сами профили у меня складываются сюда:
~/.codex-auth-profiles
Там лежат:
сохранённые
*.jsonфайл текущей подсказки профиля
настройки прокси
TTL кеша
сам кеш usage
Мне хотелось держать это отдельно от .codex, чтобы не смешивать рабочую директорию CLI и свою локальную обвязку.
Ограничения у такого подхода есть
Тут важно не притворяться, что это какой-то официальный менеджер профилей.
Это просто локальная утилита, и у неё есть понятные слабые места.
Во-первых, она завязана на текущую структуру auth.json.
Во-вторых, она использует backend endpoint для usage, а такие вещи могут меняться.
В-третьих, это bash. Для маленького инструмента это плюс, но если раздувать логику дальше, код быстро станет менее приятным, чем хотелось бы.
То есть да, это рабочая штука. Но именно как маленький локальный CLI, а не как вечная универсальная система.
Что в итоге получилось
Мне нравится в этой истории то, что решение получилось очень приземлённым.
Без отдельного сервиса. Без базы данных. Без попытки изобрести новый менеджер секретов.
Просто нормальный локальный инструмент, который закрывает конкретную бытовую боль:
быстро сохранить профиль
быстро переключиться
не путаться в аккаунтах
сразу видеть usage
при необходимости жить через прокси
Иногда этого более чем достаточно.
Если захотите повторить
По зависимостям всё скромно. Нужны обычные утилиты:
bashjqcurlawksortbase64stat
То есть ничего экзотического.
А дальше уже можно развивать как угодно. Например, добавить:
удаление профиля
переименование
экспорт в JSON
более красивую таблицу вывода
отдельную интеграцию с shell prompt
Но это уже следующая итерация.
Заключение
Иногда самые полезные инструменты не те, что решают огромную архитектурную задачу, а те, что просто перестают бесить каждый день.
Для меня codex-auth оказался именно таким.
Если у вас один профиль, он, скорее всего, вообще не нужен.
Если профилей несколько, вы часто переключаетесь и не хотите каждый раз вспоминать, что именно лежит в ~/.codex/auth.json, то такой маленький CLI оказывается surprisingly полезным.
Код скрипта: https://gist.github.com/megamen32/523e2d6325641df69f2e0aa47ea054df
