Claudex: как я подружил Claude Code с ChatGPT/Codex OAuth без OpenAI API key

Claudex: как я подружил Claude Code с ChatGPT/Codex OAuth без OpenAI API key

Я хотел запускать Claude Code через подписку ChatGPT/Codex. Без OpenAI API key и без потери привычных вещей: инструментов, скриншотов,/compact, длинных сессий и нормальных ошибок.

На бумаге это выглядит как простой локальный прокси. На практике пришлось переводить не только JSON, но и поведение Anthropic API: потоковые события, вызовы инструментов, лимиты контекста, файлы, картинки и типы ошибок.

Так появился мой open source fork Claudex.

Коротко: Claudex делает вид, что он Anthropic-compatible endpoint для Claude Code, а внутри переводит запросы в OpenAI Responses API и ходит в ChatGPT/Codex через OAuth.

Содержание

→Статус на сейчас→✅ Готово для обычной работы в Claude Code→⚠️ Зависит от аккаунта и провайдера→ℹ️ Вне scope по дизайну→Быстрый старт→Чего я хотел→Где ломались простые прокси→Что пришлось чинить→1. Разделить запуск и настройку→2. Использовать ChatGPT/Codex OAuth вместо OpenAI API key→3. Перевести Anthropic Messages в OpenAI Responses→4. Картинки: сохранить текущие, не таскать бесконечно старые→5. Сохранить/compactи восстановление после лимита контекста→6. Контекст на 1M без самообмана→7. Потоковые ответы - это отдельная машина состояний→8. Ошибки нельзя сваливать в один 502→9.config doctor→10. Установщик тоже пришлось допиливать→Что получилось→Что я оставил за бортом→Вывод

Статус на сейчас

✅ Готово для обычной работы в Claude Code

  • ChatGPT/Codex OAuth без OpenAI API key;
  • текстовые диалоги, инструменты и потоковые вызовы инструментов;
  • изображения, файлы и документы в тех форматах, которые можно переложить в Responses API;
  • /compact, автосжатие и восстановление после переполнения контекста;
  • большие GPT-контексты вроде 1M там, где это проверено на практике;
  • перевод ошибок OpenAI/Codex в типы ошибок, понятные Claude Code;
  • claudex-config config doctorдля проверки настройки, авторизации и запущенного прокси;
  • локальный установщик с проверкой версии и SHA256.

⚠️ Зависит от аккаунта и провайдера

  • доступные модели и их mini/pro-варианты;
  • web search и похожие возможности;
  • соответствие слотовhaiku,sonnet,opusреальным моделям аккаунта;
  • поведение OpenAI-compatible провайдеров вне основного ChatGPT/Codex сценария.

ℹ️ Вне scope по дизайну

  • серверные инструменты Anthropic не проксируются как инструменты OpenAI;
  • скрытое reasoning у Codex не превращается в thinking blocks Claude Code.

Быстрый старт

Если хочется просто попробовать, установка на macOS/Linux выглядит так:

На Windows используется PowerShell-установщик:

Установщик ставит две команды:claudexдля запуска Claude Code иclaudex-configдля настройки профилей, OAuth и проверки окружения.

Чего я хотел

Я не хотел делать еще один прокси к OpenAI.

Идея была проще: чтобыclaudexзапускался почти какclaude, только запросы шли не в Anthropic API, а в ChatGPT/Codex через OAuth-профиль.

То есть пользователь остается в Claude Code, но модель и способ авторизации меняются под капотом.

Общий путь запроса выглядит так:

Главная сложность не в самом HTTP-прокси. Claude Code ожидает не просто JSON-ответ, а поведение Anthropic API: вызовы инструментов, потоковые события, учет токенов, причины остановки, ошибки и сигналы о переполненном контексте. Если потерять эти детали, интерфейс вроде бы запускается, но привычный рабочий процесс начинает сыпаться.

Снаружи это должно выглядеть как обычный Claude Code. Внутри - совсем другой протокол и другой способ авторизации.

Где ломались простые прокси

Простой прокси обычно держится, пока сценарий похож на обычный чат:

  • пользователь пишет текст;
  • модель отвечает текстом;
  • история короткая;
  • инструменты почти не участвуют;
  • картинок нет;
  • /compactне нужен;
  • ошибки можно показать как "сбой на стороне провайдера".

В реальной работе Claude Code быстро выходит за эти рамки.

У меня постоянно всплывали другие случаи: Claude читает скриншот из результата инструмента, в истории остаются старые изображения в base64, сессия упирается в лимит контекста, OpenAI Responses отдает потоковые события в другой форме, провайдер возвращает ошибку, которую Claude Code должен распознать какinvalid_request_error,rate_limit_errorилиauthentication_error.

Если на все это отвечать "502 Bad Gateway", Claude Code теряет смысл ошибки. Он не запускает восстановление, не предлагает/compactтам, где должен, неверно считает использование токенов и иногда просто роняет сессию.

Что пришлось чинить

1. Разделить запуск и настройку

В fork я разделил две команды:

Это маленькая деталь, но она сильно влияет на ощущение от инструмента. Еслиclaudexдолжен быть заменойclaude, он не может забирать себе флаги Claude Code.

Например, такие команды должны вести себя как в обычном Claude Code:

Поэтому управление профилями, авторизацией и прокси вынесено вclaudex-config, аclaudexостался тонким слоем запуска.

Вsrc/process/launch.rsон выставляет переменные окруженияANTHROPIC_BASE_URL,ANTHROPIC_AUTH_TOKEN,ANTHROPIC_MODEL, настраивает model slots и запускает настоящий бинарникclaude.

2. Использовать ChatGPT/Codex OAuth вместо OpenAI API key

Я специально не хотел завязываться на OpenAI API key. Вся идея была в том, чтобы использовать подписку ChatGPT Plus/Pro или Codex.

Профиль выглядит примерно так:

Токены хранятся локально в системном хранилище учетных данных: Keychain на macOS или аналогах на других системах. Для пользователя процесс короткий:

Для серверов и удаленных машин есть вход через device code.

3. Перевести Anthropic Messages в OpenAI Responses

Самая большая часть работы - слой перевода.

Anthropic Messages API и OpenAI Responses API похожи только издалека. В деталях приходится переводить почти все:

Эта часть живет вsrc/proxy/translate/responses.rs.

Например, Anthropictool_resultнельзя просто отправить как пользовательский текст. Для Responses API это отдельныйfunction_call_output. Если внутри результата инструмента есть изображение, его нужно вынести отдельным пользовательским сообщением сinput_image.

Иначе ломается обычный сценарий: Claude прочитал файл или скриншот через инструмент и продолжает работать по результату.

4. Картинки: сохранить текущие, не таскать бесконечно старые

С изображениями был неприятный баг.

Claude Code может хранить в истории картинки в base64. Если прокси каждый раз честно пересылает всю историю, запрос быстро разрастается. Потом сервер отказывает из-за лимита размера тела запроса, и пользователь видит странную ошибку вместо ответа.

В fork я сделал более прагматичную логику:

Вresponses.rsза это отвечаютextract_tool_result_images,convert_image_blockи дедупликация больших повторяющихсяfunction_call_output.

Это не идеальная с теоретической точки зрения история сообщений. Зато текущая задача не ломается, а прокси не гоняет один и тот же огромный кусок base64 по кругу.

5. Сохранить /compact и восстановление после лимита контекста

Для Claude Code/compact- не украшение интерфейса. В длинных сессиях это нормальный способ продолжить работу, когда контекст закончился.

Проблема в том, что OpenAI/Codex сообщает о переполненном контексте не так, как Anthropic. Если вернуть ошибку сервера как есть, Claude Code не всегда понимает: это именно "prompt is too long", пора предложить/compactили/clear.

Поэтому переполнение контекста переводится в ошибку Anthropic-формата:

После этого Claude Code идет по своему обычному пути восстановления.

Я намеренно не стал изобретать собственное автоматическое сжатие истории между разными протоколами. Если Codex говорит, что окно заполнено, claudex возвращает Claude Code ожидаемый сигнал. Дальше решает Claude Code и пользователь.

6. Контекст на 1M без самообмана

Еще одна практическая деталь - размер контекста.

Для проверенных больших GPT-моделей claudex показывает Claude Code модель с суффиксом[1m]:

Claude Code видит большое окно. Перед отправкой в OpenAI/Codex суффикс убирается:

А для обычногоgpt-5.5fork оставляет более осторожное окно автосжатия около272k. Причина простая: среда выполнения может отклонять большие запросы, даже если в описании модели фигурирует 1M context.

Формально поддерживает и стабильно работает - не одно и то же.

7. Потоковые ответы - это отдельная машина состояний

Потоковый ответ нельзя просто прокинуть байтами.

Claude Code ожидает события Anthropic SSE:

OpenAI Responses отдает другие события:

Пришлось держать состояние:

  • началось ли сообщение;
  • открыт ли блок содержимого;
  • идет ли вызов инструмента;
  • приходили ли части аргументов;
  • сколько пришло входных и выходных токенов;
  • были ли закэшированные токены;
  • что делать, если сервер оборвал соединение посреди ответа.

В упрощенном виде это выглядит так:

Эта логика вsrc/proxy/translate/responses_stream.rs.

Отдельно я добавил более понятные ошибки для случаев, когда сервер вернул 200 OK, начал потоковый ответ, а потом замолчал или оборвал соединение. Без этого такие баги выглядят как мистика: HTTP вроде успешный, но Claude Code не получил нормальный конец сообщения.

8. Ошибки нельзя сваливать в один 502

В агентном сценарии ошибка - это не просто текст для пользователя. По ней Claude Code выбирает поведение.

Разные случаи требуют разных реакций:

  • ошибка авторизации;
  • лимит запросов;
  • проблема с оплатой или квотой;
  • неверный запрос;
  • слишком длинный prompt;
  • перегрузка провайдера;
  • таймаут;
  • слишком большой запрос.

Если все завернуть в общийapi_error, диагностика становится хуже, а резервные маршруты и circuit breaker начинают принимать неверные решения.

Вsrc/proxy/error_translation.rsошибки переводятся в типы Anthropic:

Плюс есть разделение между ошибками аккаунта/запроса и реальными сбоями провайдера. Если модель недоступна для аккаунта, это надо показать напрямую. Это не повод ломать circuit breaker.

9. config doctor

После нескольких итераций стало ясно: многие проблемы будут не в слое перевода, а в настройке.

Типичные случаи:

  • не тот бинарник вPATH;
  • старый прокси все еще запущен;
  • OAuth-токен истек;
  • профиль выключен;
  • модель не поддерживается аккаунтом;
  • конфиг не найден;
  • symlink указывает не туда.

Поэтому появился:

Он проверяет конфиг, авторизацию, состояние прокси, профили, версию и печатает конкретные следующие действия.

Это скучная часть проекта. Но без нее open source инструмент быстро превращается в "у меня не работает", где непонятно, сломан прокси или локальное окружение.

10. Установщик тоже пришлось допиливать

Для macOS/Linux естьinstall.sh, для Windows -install.ps1.

Установщик скачивает release assets, проверяет SHA256, ставитclaudexиclaudex-config, проверяет версию вPATH, может предложить OAuth-настройку, умеет--dry-runи при необходимости ставит проект из исходников черезcargo install.

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

Что получилось

Сейчас fork закрывает те сценарии, ради которых я его делал:

  • Claude Code запускается через ChatGPT/Codex OAuth без OpenAI API key;
  • текстовые диалоги и инструменты работают через Responses API;
  • потоковые вызовы инструментов переводятся в Anthropic SSE;
  • обычные изображения и изображения из результатов инструментов не теряются;
  • файлы и документы маппятся там, где Responses API может их представить;
  • /compact, автосжатие и восстановление после лимита контекста сохраняются;
  • использование закэшированных токенов возвращается в формате Claude Code;
  • ошибки OpenAI/Codex становятся понятными ошибками для Claude Code;
  • есть установщик иconfig doctor;
  • CI гоняетcargo fmt --check,cargo clippy -- -D warnings,cargo build,cargo test.

Стек обычный: Rust 2021, Axum для локального прокси, Tokio, reqwest, keyring, tracing, serde/toml. Версия вCargo.tomlна момент написания -0.9.32.

В кодовой базе больше 400 тестовых функций/кейсов. Основная масса вокруг OAuth, перевода протоколов, потоковых ответов, ошибок, конфига и проверок установщика. Для такого прокси тесты важны: баг часто выглядит как маленькое отличие JSON, а на выходе ломает целый сценарий в Claude Code.

Что я оставил за бортом

Я сознательно не пытаюсь покрыть все.

  • серверные инструменты Anthropic не проксируются как инструменты OpenAI. Claudex сохраняет локальный путь инструментов Claude Code;
  • скрытое reasoning у Codex не превращается в thinking blocks Claude Code;
  • web search и похожие возможности зависят от провайдера и аккаунта;
  • не все OpenAI-compatible провайдеры ведут себя одинаково;
  • соответствие model slots (haiku,sonnet,opus) приходится настраивать под реальные модели аккаунта.

Мне кажется, это нормальная граница. Иначе прокси быстро становится слоем магии, который якобы знает поведение всех backend-ов сразу. Обычно такие слои потом очень больно чинить.

Я недооценивал, насколько Claude Code зависит не только от модели, но и от протокола вокруг нее.

Для текстового прокси достаточно переложить запрос и ответ. Для реальной агентной работы нужно сохранить гораздо больше:

  • жизненный цикл вызова инструментов;
  • состояние потокового ответа;
  • семантику изображений и файлов;
  • сигнал о переполненном контексте;
  • восстановление через/compact;
  • учет токенов;
  • типы ошибок;
  • диагностику настройки.

Вот эти скучные детали и решают, будет инструмент "иногда отвечать" или им можно пользоваться каждый день.

Claudex fork получился не универсальным шлюзом ко всем моделям, а практическим слоем совместимости между Claude Code и подпиской ChatGPT/Codex. Я хотел сохранить привычный рабочий процесс Claude Code и при этом использовать доступ к Codex через OAuth. В итоге большая часть работы оказалась не в подключении API-адреса, а в том, чтобы не потерять поведение, на которое Claude Code молча рассчитывает.

Код открыт под MIT:https://github.com/pilc80/claudex

Если у вас похожая связка Claude Code и Codex, попробуйте и напишите issue, где сломается. Мне особенно интересны длинные сессии, картинки,/compactи потоковые ответы. Буду рад, если мой прокси кому-то еще будет полезен :)

Читать оригинал