У любой удобной штуки есть момент, когда она начинает раздражать.

С 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.

Логика такая:

  1. если есть account_id, используем его

  2. если нет, пробуем sub из id_token

  3. если и этого нет, пробуем email

  4. если уже совсем ничего нет, берём 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

  • при необходимости жить через прокси

Иногда этого более чем достаточно.

Если захотите повторить

По зависимостям всё скромно. Нужны обычные утилиты:

  • bash

  • jq

  • curl

  • awk

  • sort

  • base64

  • stat

То есть ничего экзотического.

А дальше уже можно развивать как угодно. Например, добавить:

  • удаление профиля

  • переименование

  • экспорт в JSON

  • более красивую таблицу вывода

  • отдельную интеграцию с shell prompt

Но это уже следующая итерация.

Заключение

Иногда самые полезные инструменты не те, что решают огромную архитектурную задачу, а те, что просто перестают бесить каждый день.

Для меня codex-auth оказался именно таким.

Если у вас один профиль, он, скорее всего, вообще не нужен.

Если профилей несколько, вы часто переключаетесь и не хотите каждый раз вспоминать, что именно лежит в ~/.codex/auth.json, то такой маленький CLI оказывается surprisingly полезным.

Код скрипта: https://gist.github.com/megamen32/523e2d6325641df69f2e0aa47ea054df