Я хотел запускать 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и потоковые ответы. Буду рад, если мой прокси кому-то еще будет полезен :)