트렌딩 딥다이브 · 2026-07-22 · TrendShift Daily #3 · LIDGE-JUN

lidge-jun/opencodex 딥다이브
— Codex와 Claude Code를 아무 LLM에나 꽂는 로컬 프록시

opencodexocx start 한 번으로 내 컴퓨터 localhost:10100에 작은 프록시 서버를 띄운다. 그러면 OpenAI의 코딩 도구 Codex(CLI·앱·SDK)와 Anthropic의 Claude Code가, 자기는 OpenAI/Anthropic 서버와 대화하는 줄 알면서도 사실은 이 프록시를 거쳐 Claude·Gemini·Grok·DeepSeek·GLM·Ollama 등 아무 LLM에나 연결된다. 핵심은 "번역"이다. Codex가 쓰는 OpenAI Responses API 요청을 상대 제공자(provider)가 알아듣는 포맷으로 바꾸고, 응답 스트림을 다시 Codex가 알아듣는 포맷으로 되돌린다 — 스트리밍·툴 호출·추론(thinking) 토큰·이미지까지 양방향으로. 여기에 ChatGPT 계정 여러 개를 묶어 쿼터를 돌려쓰는 계정 풀까지 얹혀 있다.

(저장소 lidge-jun/opencodex · npm 배포명 @bitkyc08/opencodex · v2.7.31 · Bun + TypeScript 약 7만 줄 · 제공자 레지스트리 56종·어댑터 7종 · MIT · 최신 커밋 2026-07-22, 클론 소스 직접 분석)
목차
  1. 프로젝트 한 줄 요약
  2. 왜 주목받는가 — OmniRoute·claude-code-router와 뭐가 다른가
  3. 기술 스택 전체 지도
  4. 아키텍처 심화 — 번역 파이프라인과 히스토리-세이프 주입
  5. 디렉토리 구조 해부
  6. 학습 포인트 — 여기서 훔쳐올 설계
  7. 하드웨어 / 시스템 요구사항
  8. 직접 해볼 수 있는 실습 과제
  9. 관련 기술 심화 학습 로드맵
  10. 핵심 키워드 사전
  11. 참고 링크

1프로젝트 한 줄 요약

"make codex open" — 벤더에 잠긴 코딩 에이전트를 아무 모델에나 풀어준다
한 문장으로

opencodex = "Codex/Claude Code ↔ 아무 LLM"을 실시간 통역하는 로컬 프록시 + ChatGPT 계정 풀 매니저

npm install -g @bitkyc08/opencodexocx start를 치면 localhost:10100에 데몬이 뜬다. 그다음 ocx가 Codex의 설정 파일에 "OpenAI 주소를 이 프록시로" 한 줄만 심는다. 이제 Codex는 평소처럼 동작하지만, 실제 요청은 전부 프록시를 거쳐 내가 고른 제공자(Claude·Gemini·Grok·Ollama…)로 흘러간다.

같은 데몬이 /v1/messages(Anthropic Messages API)도 서빙하기 때문에, ocx claude 한 번이면 Claude Code도 똑같이 아무 백엔드에나 붙는다. 즉 "Codex는 GPT만, Claude Code는 Claude만"이라는 벤더 잠금을 프록시 하나가 풀어버린다.

이 프로젝트가 푸는 문제는 단순하다. Codex CLI는 OpenAI의 Responses API라는 독특한 형식으로만 말하고, Claude Code는 Anthropic의 Messages API로만 말한다. 두 도구 모두 "다른 회사 모델을 쓰고 싶어도 프로토콜이 안 맞아서" 갈아끼우기 어렵다. opencodex는 이 두 입력 포맷을 받아, 56종 제공자가 각자 쓰는 포맷으로 번역해 던지고, 돌아온 응답을 원래 도구가 알아듣는 포맷으로 되돌린다. README의 표현을 그대로 옮기면 "Codex의 Responses API를 당신의 제공자가 쓰는 언어로 번역하는 가벼운 로컬 프록시. 스트리밍·툴 호출·추론 토큰·이미지 — 전부, 양방향으로 동작한다."

용어
프록시(proxy) 번역기
여기서 프록시는 "요청을 대신 받아 다른 곳으로 넘겨주는 중계 서버"다. opencodex가 특별한 이유는 단순 중계가 아니라 프로토콜을 통역한다는 점이다. Codex가 POST /v1/responses로 OpenAI 형식 요청을 보내면, opencodex는 그걸 뜯어 Anthropic /v1/messages 형식(또는 Gemini·OpenAI-chat 형식)으로 다시 조립해 실제 제공자에게 보낸다. 응답이 스트리밍으로 오면 다시 Codex가 기대하는 response.output_text.delta 같은 이벤트로 되돌린다 — 통역사가 양쪽 말을 실시간으로 옮기는 것과 같다.
먼저 짚고 갈 것
저장소 이름과 npm 패키지 이름이 다르다

TrendShift·GitHub의 저장소 주소는 lidge-jun/opencodex지만, 실제 설치 명령은 npm install -g @bitkyc08/opencodex이고 커밋 작성자도 bitkyc08-arch다. package.jsonhomepagelidge-jun.github.io/opencodex. 즉 lidge-jun과 bitkyc08은 같은 관리자의 다른 핸들로 보인다. 설치할 때는 npm 스코프(@bitkyc08)를, 저장소를 가리킬 때는 lidge-jun/opencodex를 쓰면 된다. 명령어는 둘 다 opencodex 또는 짧은 별칭 ocx로 실행된다.

저장소는 순수 Bun + TypeScript 프로젝트로 약 7만 줄, 테스트 파일만 283개다. Docker도 파이썬도 없다 — npm으로 전역 설치해 OS 서비스(macOS launchd / Linux systemd / Windows 작업 스케줄러)로 등록하는 방식이다. 대시보드(관리 UI)는 React 19 + Vite 8로 짠 별도 패키지가 빌드 시 함께 묶인다.

2왜 주목받는가 — OmniRoute·claude-code-router와 뭐가 다른가

"게이트웨이"는 많다 — opencodex의 승부수는 Responses API 양방향 번역과 계정 풀

"아무 LLM에나 붙는 게이트웨이/프록시"는 이미 붐비는 카테고리다. 같은 날 TrendShift 상위권에도 OmniRoute(diegosouzapw, "231+ 제공자 통합 게이트웨이")·opencodex가 나란히 올라 있고, claude-code-router·LiteLLM·OpenRouter 같은 선배들도 있다. 이 무리에서 opencodex가 눈에 띄는 지점은 "제공자를 많이 지원한다"가 아니라 "Codex라는 다루기 까다로운 클라이언트를, 그 클라이언트를 포크하지 않고, 양방향으로 통역한다"는 접근이다.

기존 도구와의 결정적 차이

일반 LLM 게이트웨이(LiteLLM·OpenRouter 등)opencodex
대상 클라이언트주로 Chat Completions API를 쓰는 앱 전반OpenAI Codex(Responses API)와 Claude Code(Messages API)에 특화
연결 방식내 앱 코드에서 base URL을 게이트웨이로 바꿔 호출클라이언트를 포크하지 않고 Codex 설정 파일에 base URL 한 줄만 주입 → 되돌리기도 깔끔
번역 난이도Chat Completions는 사실상 업계 표준이라 상대적으로 단순Responses API는 reasoning·encrypted_content·custom tool 등 특수 필드가 많아 양방향 번역이 훨씬 까다로움
인증(auth)보통 제공자 API 키 하나ChatGPT 계정 풀(5시간/주간/30일 쿼터 창을 여러 계정으로 돌려쓰고 쿨다운·페일오버) + OAuth(Claude Pro/Max, xAI, Kimi, Copilot, Cursor)
추론/캐시대개 그대로 전달모델별 reasoning-effort→budget 변환, Anthropic prompt cache 브레이크포인트 자동 배치 등 제공자별 최적화
부가 기능라우팅·로깅 위주텍스트 전용 모델용 비전 사이드카(이미지 묘사), 웹서치 사이드카, 이미지 생성 프록시, 관리 대시보드까지 내장
비유

일반 게이트웨이가 "여러 나라 콘센트를 꽂게 해주는 멀티 어댑터"라면, opencodex는 "특정 고급 가전(Codex)을 위한 전담 통역·변압 스테이션"이다. 멀티 어댑터는 웬만한 기기를 두루 받지만, Codex처럼 전압(Responses API)·플러그 모양(추론 토큰·암호화된 reasoning)이 유별난 기기는 그냥 꽂으면 400 에러가 난다. opencodex는 그 유별난 규격을 하나하나 맞춰주는 대신, 대상 기기를 Codex·Claude Code로 좁혀 그 둘만큼은 완벽하게 통역한다.

주목 포인트 4가지

① 클라이언트를 포크하지 않는다. Codex를 개조하거나 별도 빌드를 배포하는 대신, Codex가 이미 갖고 있는 내장 openai 제공자의 base URL만 localhost:10100으로 돌린다(src/codex/inject.ts). Codex는 자기가 OpenAI와 대화하는 줄 안다. ocx stop이면 설정이 원상복구된다 — 업스트림 Codex가 업데이트돼도 깨질 여지가 적은, 유지보수 관점에서 영리한 선택이다.

② Responses API 양방향 번역이 진짜 어렵다. Chat Completions만 지원하는 프록시는 많아도, Codex의 Responses API를 제대로 통역하는 도구는 드물다. 스트리밍 이벤트(response.output_text.delta·response.reasoning_summary_text.delta·response.function_call_arguments.delta), 암호화된 추론 봉투(encrypted_content), custom tool 호출까지 왕복시켜야 한다. opencodex는 이걸 어댑터 계약 하나로 추상화해 6개 제공자 계열에 재사용한다.

③ ChatGPT 계정 풀이라는 실전 기능. ChatGPT/Codex 로그인 기반 사용은 시간·주·월 단위 쿼터가 걸린다. opencodex는 계정 여러 개를 풀로 묶어, 요청을 특정 계정에 고정(affinity)하되 쿼터가 차면 다른 계정으로 페일오버하고 쿨다운을 관리한다. "무료/저렴한 경로를 최대한 오래 쓰기"라는 현실적 수요를 정면으로 겨냥했다.

④ 완전한 MIT 오픈소스 + 활발한 개발. 코어·어댑터·대시보드 전부 MIT이고, 최신 커밋이 클론 당일(2026-07-22)일 만큼 개발이 빠르다. 40개 이상을 표방하지만 실제 레지스트리에는 56개 제공자가 등록돼 있고, 한국어를 포함해 4개 언어 README를 갖췄다.

용어
Responses API vs Chat Completions
둘 다 OpenAI의 API지만 구조가 다르다. Chat Completions(/v1/chat/completions)는 업계 표준처럼 널리 복제된 단순한 "메시지 배열 → 답변" 형식이다. Responses API(/v1/responses)는 더 새롭고 에이전트 지향적이어서, 추론 과정(reasoning)·툴 호출·이미지 생성·암호화된 사고 흔적 같은 요소를 1급 시민으로 다룬다. Codex는 이 Responses API를 쓴다. 그래서 "아무 모델이나 Codex에 붙이려면" 이 복잡한 형식을 상대 모델 언어로 번역할 수 있어야 하고, 그게 opencodex의 핵심 난이도이자 차별점이다.

3기술 스택 전체 지도

Bun 런타임 하나 + 손으로 짠 어댑터들 + 별도 React 대시보드

이 프로젝트의 기술 선택은 "의존성을 최대한 줄이고, 제공자별 통신은 SDK 대신 직접 HTTP로 짠다"로 요약된다. 런타임 의존성이 단 네 개뿐이라는 사실이 그 철학을 잘 보여준다.

① 런타임 — Bun 위의 TypeScript

레이어선택비고
런타임Bun 1.3.14(의존성으로 번들됨)HTTP 서버는 Bun.serve. Express·Fastify 같은 웹 프레임워크를 안 쓴다. 빠른 시작·네이티브 TS 실행
Node 역할bin/ocx.mjs 런처 하나사용자는 Node ≥18만 있으면 됨. Node는 번들된 Bun을 src/cli/index.ts로 실행하는 얇은 껍데기
요청 검증zod 4.4.3들어오는 Responses API 요청을 스키마로 엄격 검증 — 이게 신뢰 경계(trust boundary)
MCP 지원@modelcontextprotocol/sdk ^1MCP 툴이 프록시를 왕복. 툴 이름은 namespace__name으로 평탄화 후 복원
바이너리 전송@bufbuild/protobuf ^2.12.0Cursor 어댑터가 protobuf/HTTP-2 바이너리 프로토콜로 말하기 때문
타입/빌드TypeScript 5.9.3, @types/buntsconfig: ESNext, moduleResolution: bundler, strict: true
테스트Bun test, 283개 *.test.tsCI에서 ubuntu·macos·windows 3면 + typecheck + privacy 스캔 게이트
용어
Bun을 "의존성으로" 번들한다
보통 Bun 프로젝트는 사용자가 Bun을 따로 설치해야 한다. opencodex는 package.jsondependenciesbun을 넣어, npm 설치 시 Bun 런타임까지 함께 내려받게 했다. 덕분에 사용자는 흔한 Node만 있으면 되고(bin/ocx.mjs가 번들된 Bun을 찾아 실행), Bun의 빠른 시작·네이티브 TypeScript 실행 이점은 그대로 누린다. "런타임을 앱에 끼워 배포한다"는 요즘 방식의 사례다.

② 어댑터 레이어 — 제공자마다 손으로 짠 통역기

opencodex는 LLM 벤더 SDK를 하나도 쓰지 않는다. Anthropic SDK, OpenAI SDK, Google SDK … 전부 안 쓰고, 각 제공자를 직접 HTTP로 말하는 어댑터를 손으로 구현했다. 이유는 명확하다 — SDK에 묶이면 버전 업데이트마다 끌려다니고, Responses API ↔ 각 포맷의 미묘한 변환을 SDK가 대신 해주지 못하기 때문이다. 어댑터는 7종이다.

어댑터담당특이점
openai-chatChat Completions 계열 (제일 많음, 42개 제공자)OpenAI 호환 엔드포인트라면 대부분 이걸로 커버. /v1/models로 모델 자동 발견
openai-responsesResponses API 계열OpenAI 자기 자신 등
anthropicClaude(/v1/messages)reasoning budget 변환·prompt cache·oauth 지문까지 처리 (4장 참고)
googleGemini(AI Studio·Vertex·Antigravity)3개 제공자
azure-openaiAzure OpenAIAzure 배포 규격
kiroAWS Kiro(CodeWhisperer)AWS 계열 인증
cursorCursorprotobuf/HTTP-2 바이너리 프로토콜(실험적). 15k줄 생성 코드

③ 관리 대시보드 — 별도 React 패키지

항목선택
UI 프레임워크React 19.2.7 + react-dom 19.2.7
가상 스크롤@tanstack/react-virtual(대량 로그/요청 목록용)
번들러Vite 8.1.0, TypeScript 6, ESLint 10
빌드gui/는 완전히 분리된 패키지 → 배포 시 gui/dist로 번들되어 서버가 /api/*와 함께 서빙

④ 내장 사이드카 · 부가 서버

코어 프록시 외에 사이드카(sidecar) 두 개가 딸려 있다. 비전 사이드카는 텍스트 전용 모델에 이미지를 보낼 때, 이미지를 대신 "묘사"해 텍스트로 끼워 넣는다(src/vision/). 웹서치 사이드카는 합성 툴을 만들어 에이전트 루프로 검색을 수행한다(src/web-search/). 여기에 이미지 생성 프록시(/v1/images/generations·/edits), 히스토리 압축(/v1/responses/compact), 관리 API(/api/*)까지 한 데몬 안에 들어 있다.

용어
사이드카(sidecar)
본체 옆에 붙어 보조 기능을 담당하는 별도 컴포넌트를 뜻하는 오토바이 사이드카 비유. 여기서는 "메인 번역 흐름"에 없는 기능(이미지→텍스트 묘사, 웹 검색)을 본체를 건드리지 않고 옆에서 처리한다. 예컨대 Gemini 텍스트 모델은 이미지를 못 받으니, 비전 사이드카가 먼저 이미지를 "이 사진에는 …가 있다"로 바꿔 프롬프트에 합쳐준다 — 덕분에 텍스트 전용 모델도 이미지가 포함된 Codex 요청을 처리할 수 있다.

4아키텍처 심화 — 번역 파이프라인과 히스토리-세이프 주입

"요청이 들어와 답이 나가기까지"와 "어떻게 Codex를 안 고치고 가로채는가"

이 저장소를 이해하는 핵심은 두 가지다. 하나는 번역 파이프라인(하나의 요청이 파서→라우터→어댑터→제공자→브릿지를 지나는 흐름), 다른 하나는 가로채기 방식(Codex를 포크하지 않고 어떻게 요청을 낚아채는가)이다.

(A) 데이터 흐름 — 파서 · 라우터 · 어댑터 · 브릿지

Codex CLI/App/SDK ──POST /v1/responses──┐ Claude Code ───────POST /v1/messages────┤ ▼ opencodex (Bun.serve @ localhost:10100) │ ┌─────────────────────────────────────────────────────────┐ │ parser ─▶ router ─▶ [vision] ─▶ adapter ─▶ provider(HTTP)│ │ (Zod) routeModel 이미지묘사 buildRequest │ │ │ │ │ Ocx→제공자포맷 │ │ │ OcxParsed provider ▼ │ │ Request +adapter parseStream ◀── 제공자 SSE/스트림 │ │ │ │ │ AdapterEvent[] │ │ ▼ │ │ bridge (역번역) │ │ AdapterEvent ─▶ Responses SSE │ └─────────────────────────────────────────────────────────┘ │ Codex/Claude Code ◀── (SSE 스트림으로 응답)

단계별로 뜯어보면 이렇다.

용어
어댑터 계약(ProviderAdapter interface)
src/adapters/base.ts가 모든 제공자가 지켜야 할 "통역 계약"을 정의한다. 핵심 메서드는 buildRequest(내부표현) → 제공자 요청(보내는 쪽 번역)과 parseStream(제공자응답) → AsyncGenerator<AdapterEvent>(받는 쪽 번역) 둘이다. 즉 새 제공자를 추가하려면 이 두 방향의 번역만 구현하면 되고, 나머지 파이프라인(파서·라우터·브릿지)은 그대로 재사용된다. 인터페이스 하나로 56개 제공자를 규격화한 셈이다.

라우터의 우선순위 사다리

routeModel은 아래 순서로 "어디로 보낼지"를 정한다. 위에서 걸리면 거기서 확정된다.

순서규칙예시
1명시적 제공자/모델 네임스페이스anthropic/claude-opus-4-8 → anthropic 제공자로
2OpenAI 계열 맨모델gpt-* → Codex 로그인 openai 제공자
3제공자의 defaultModel제공자만 지정하면 그 기본 모델
4내장 접두사 패턴claude-*→anthropic, llama-/mixtral-/gemma-*→groq
5제공자의 models[] 목록설정에 나열된 모델이면 그 제공자
6defaultProvider 폴백다 안 걸리면 기본 제공자로

(B) 어떻게 Codex를 "안 고치고" 가로채는가 — 히스토리-세이프 주입

opencodex는 Codex를 포크하거나 개조하지 않는다. Codex가 이미 갖고 있는 내장 openai 제공자의 openai_base_url 한 줄만 Codex의 config.toml에 심어(src/codex/inject.ts) localhost:10100을 가리키게 한다. Codex는 자기가 OpenAI Responses API와 대화하는 줄 알지만, 모든 요청은 프록시로 떨어진다. ocx stop/restore면 설정이 원래대로 돌아오고, 실행 중 Codex 히스토리도 복원된다.

영리한 디테일
Codex 자체 업데이트에 안 깨지는 설계

클라이언트를 포크하는 프록시들은 업스트림(Codex)이 새 버전을 내면 따라서 갈아엎어야 한다. opencodex는 "설정 파일에 한 줄 주입 + 되돌리기"만 하기 때문에 Codex가 업데이트돼도 대체로 그대로 동작한다. 게다가 codex 명령이 호출될 때마다 ocx ensure를 돌리는 codex-shim(src/codex/shim.ts)을 둬서, 프록시가 꺼져 있으면 자동으로 되살린다. "가로채되 흔적을 남기지 않는다"는 원칙이 곳곳에 배어 있다.

(C) 가장 까다로운 번역 — OpenAI Responses ↔ Anthropic Messages

어댑터 번역이 왜 어려운지는 anthropic 어댑터(src/adapters/anthropic.ts)의 buildRequest가 잘 보여준다. Codex는 추론 예산과 최대 출력 토큰에 같은 값을 넣어 보내는데, Anthropic은 max_tokensthinking budget보다 커야 하므로 그대로 넘기면 400 에러가 난다. 어댑터는 이 지점을 이렇게 푼다.

# Codex가 보낸 reasoning effort → Anthropic thinking budget 계산 budget = reasoningBudget(effort) # 하한 1024, 상한 32000 maxOut = 요청의 max_output_tokens max_tokens = min(32000, max(maxOut, budget + 8192)) # 항상 max_tokens > budget 보장 # 확장 사고(extended thinking)에서 Anthropic이 금지하는 필드 제거 delete temperature ; delete top_p # 최신 adaptive-thinking 모델은 다른 경로 thinking = { type: "adaptive" } ; output_config = { effort } # 인증 이중 경로 oauth(Claude Pro/Max) → Authorization: Bearer + anthropic-beta + Claude Code 지문 헤더 key → x-api-key # prompt cache 브레이크포인트 최대 4개 배치 (캐시 적중 극대화) cache_control @ [tools, system, 직전 user, 마지막 user]

즉 단순히 필드 이름만 바꾸는 게 아니라, 제공자별 제약과 과금 최적화까지 어댑터가 떠안는다. 반대 방향(parseStream)에서는 Anthropic SSE의 content_block_delta를 읽어 내부 AdapterEvent로 바꾸고, 브릿지가 그걸 다시 Codex의 Responses SSE로 되돌린다.

브릿지의 역번역 매핑

내부 AdapterEventCodex Responses SSE로 변환
text_deltaresponse.output_text.delta → done
thinking_deltaresponse.reasoning_summary_text.delta
tool_call_startresponse.output_item.added(function_call/custom_tool_call)
tool_call_deltaresponse.function_call_arguments.delta
thinking_signature/redacted_thinkingencrypted_content reasoning 봉투로 보존
주의
"아무 모델이나" 붙지만 품질은 모델·번역에 좌우된다

opencodex가 프로토콜을 통역해준다고 해서 모든 조합이 Codex 순정만큼 잘 동작한다는 보장은 아니다. Codex는 원래 OpenAI 모델에 맞춰 튜닝된 프롬프트·툴 사용 패턴을 갖고 있어서, 다른 제공자로 돌리면 툴 호출 형식·추론 토큰 처리에서 미묘한 차이가 생길 수 있다. 어댑터가 그 간극을 메우려 애쓰지만(위의 reasoning budget·cache 처리가 그 예다), 실사용 전에는 자신이 쓸 제공자 조합에서 실제로 툴 호출·스트리밍이 잘 되는지 직접 확인하는 게 좋다. 무료·저가 경로일수록 편차가 크다.

5디렉토리 구조 해부

약 7만 줄 TypeScript — src/의 18개 서브디렉토리 중 어디부터 열까
opencodex/ (npm: @bitkyc08/opencodex) │ package.json ★ name/version(2.7.31)/bin(ocx)/deps 4개 │ bunfig.toml Bun test 루트를 ./tests로 고정 │ tsconfig.json ESNext · bundler · strict │ ├─ bin/ │ └─ ocx.mjs ★ npm이 심는 실행 파일. 번들 Bun으로 src/cli/index.ts 실행 │ ├─ src/ ★★★ 본체 (18개 서브디렉토리) │ ├─ cli/index.ts ★ ocx 명령 디스패처(start/init/stop/gui/claude/login/service…) │ ├─ index.ts 공개 프로그래밍 API — startServer·parseRequest·bridge·어댑터 팩토리 재노출 │ ├─ server/ ★★ Bun.serve HTTP 경계. /v1/* 프록시 + /api/* 관리 API │ │ (responses.ts 90KB 데이터플레인, management-api.ts 86KB) │ ├─ adapters/ ★★ 제공자 어댑터(anthropic·openai-chat·openai-responses· │ │ google·azure·kiro·cursor·mimo-free) + 이미지/툴 가드 │ ├─ codex/ ★ config.toml 주입·모델 카탈로그 동기화(catalog.ts 92KB)· │ │ ChatGPT 계정 풀 인증/쿼터·히스토리 remap·shim │ ├─ claude/ Claude Code 인바운드/아웃바운드 번역·모델 별칭 │ ├─ responses/ ★ Responses API 파서·zod 스키마·압축(compaction) │ ├─ providers/ 제공자 레지스트리(56개)·API키 풀·쿼터·slug 코덱 │ ├─ router.ts ★ routeModel — 라우팅 우선순위 사다리 │ ├─ bridge.ts ★ bridgeToResponsesSSE — 역번역 │ ├─ oauth/ OAuth 흐름(xAI·Anthropic·Kimi·GitHub Copilot·Cursor) + 토큰 저장/갱신 │ ├─ web-search/ 웹서치 사이드카(합성 툴·에이전트 루프·실행기) │ ├─ vision/ 비전 사이드카(텍스트 모델용 이미지 묘사) │ ├─ usage/ 요청 사용량 추출·JSONL 로그·요약 │ ├─ combos/ 모델 콤보 라우팅(가상 멀티타깃 모델) │ ├─ service.ts launchd/systemd/작업스케줄러 서비스 설치(960줄) │ ├─ reasoning-effort.ts 추론 강도 → budget 변환 │ ├─ config.ts / types.ts(37KB) / storage/ / update/ / generated/ / lib/ │ ├─ gui/ React 19 + Vite 8 관리 대시보드(별도 패키지) ├─ docs-site/ Astro Starlight 공식 문서 사이트 ├─ structure/ 관리자용 불변조건 노트(00_overview … 08_openai-provider-tiers) ├─ devlog/ 날짜별 엔지니어링 일지(_plan / _fin / _chase) ├─ scripts/ 빌드·릴리스·privacy-scan 도구 └─ tests/ 283개 *.test.ts (Bun test)
읽는 순서 추천

src/cli/index.ts(ocx가 어떤 명령을 갖는지 전체 지도) → src/server/index.ts(Bun.serve가 노출하는 엔드포인트 목록 = 이 프로젝트의 표면) → src/responses/parser.ts(요청이 내부 표현으로 바뀌는 곳, 신뢰 경계) → src/router.ts + src/adapters/base.ts(라우팅 규칙과 어댑터 계약) → src/adapters/anthropic.ts(가장 복잡한 실제 번역) → src/bridge.ts(역번역) → src/codex/inject.ts + shim.ts(가로채기 메커니즘). 이 순서면 "요청이 들어와 나가기까지"를 코드로 완주할 수 있다. structure/*.mddocs-site는 관리자가 직접 쓴 고신뢰 배경 자료다.

용어
데이터 플레인(data plane) vs 컨트롤 플레인(control plane)
네트워크 장비에서 온 용어. 데이터 플레인은 실제 트래픽(여기선 LLM 요청/응답)이 흐르는 통로 — server/responses.ts(90KB)가 담당한다. 컨트롤 플레인은 그 통로를 설정·감시하는 관리 계층 — server/management-api.ts(86KB)와 React 대시보드가 담당한다. 파일 크기가 둘 다 비슷하게 큰 걸 보면, opencodex가 "번역"만큼이나 "운영 관리(계정·쿼터·로그 관찰)"에도 무게를 뒀음을 알 수 있다.

6학습 포인트 — 여기서 훔쳐올 설계

LLM 프록시에 관심 없어도 배울 값어치가 있는 것들
패턴 1 · 어댑터 인터페이스로 이질적인 것들을 규격화

buildRequest / parseStream 두 방향만 구현하면 새 제공자 완성

56개 제공자가 각자 다른 API를 쓰지만, opencodex는 ProviderAdapter 인터페이스(buildRequest=보내는 번역, parseStream=받는 번역) 하나로 전부 감쌌다. 새 제공자 추가 = 이 두 메서드 구현. 파서·라우터·브릿지는 손대지 않는다. "이질적인 외부 시스템 N개를 하나의 좁은 인터페이스로 어댑팅한다"는 어댑터 패턴의 교과서적 실전 사례로, 결제 게이트웨이·알림 채널·스토리지 백엔드 등 어디에나 옮겨 쓸 수 있다.

패턴 2 · 클라이언트를 포크하지 말고 "설정만 주입"

가로채되 되돌릴 수 있게

Codex를 개조하는 대신 config.toml에 base URL 한 줄을 주입하고, ocx stop이면 원복한다. 업스트림이 업데이트돼도 안 깨진다. 남의 도구 동작을 바꾸고 싶을 때 "포크해서 유지보수 지옥에 빠지는" 대신 "설정·환경변수·프록시로 얇게 개입하고 흔적을 남기지 않는" 접근은 거의 항상 더 오래간다. codex-shim으로 자동 복구까지 얹은 건 덤이다.

패턴 3 · 신뢰 경계에 스키마 검증을 세운다

들어오는 요청은 zod로 먼저 검증

외부(Codex/Claude Code)에서 들어오는 Responses 요청을 zod 스키마로 검증한 뒤에야 내부 표현으로 낮춘다. "신뢰할 수 없는 입력이 시스템에 들어오는 첫 지점에서 형태를 강제한다"는 원칙. 이렇게 하면 이후 파이프라인(라우터·어댑터)은 항상 잘 정형화된 데이터만 다루므로 방어 코드가 줄고, 잘못된 요청은 경계에서 명확한 에러로 튕겨난다.

패턴 4 · 런타임을 앱에 끼워 배포한다

사용자는 Node만, 성능은 Bun으로

bun을 dependency로 번들해, 흔한 Node 환경만 있으면 설치되지만 실행은 빠른 Bun 위에서 이뤄지도록 했다. "우리 앱이 원하는 런타임을 사용자에게 강요하지 않고, 앱과 함께 배포한다"는 전략은 배포 마찰을 크게 줄인다. Node 런처(ocx.mjs)를 얇게 두고 실제 로직은 전부 Bun/TS로 둔 계층 분리도 참고할 만하다.

패턴 5 · 쿼터를 "풀"로 관리한다

계정 여러 개를 affinity + 페일오버 + 쿨다운으로

ChatGPT 계정 풀은 요청을 특정 계정에 고정(캐시 적중·일관성)하되, 쿼터가 차면 다른 계정으로 넘기고 소진된 계정엔 쿨다운을 건다. 이 "고정 우선 + 한계 시 페일오버 + 회복 대기"는 DB 커넥션 풀, API 키 로테이션, 다중 리전 트래픽 분산 등 제한된 자원을 여러 주체가 나눠 쓰는 모든 문제의 정석 패턴이다.

패턴 6 · 부족한 능력은 사이드카로 채운다

텍스트 모델에 눈·검색을 달아준다

이미지를 못 받는 텍스트 모델에는 비전 사이드카가 이미지를 묘사해 끼워 넣고, 검색이 필요하면 웹서치 사이드카가 합성 툴로 대신 검색한다. "본체 능력이 부족한 대상에게, 본체를 고치지 않고 옆에서 능력을 보강한다"는 사이드카 사고는 레거시 시스템 확장·기능 폴리필에 두루 통한다. 능력 차이를 어댑터 바깥의 별도 컴포넌트로 흡수한 관심사 분리가 깔끔하다.

7하드웨어 / 시스템 요구사항

로컬 프록시라 가볍다 — 진짜 부담은 내가 부르는 원격 모델 쪽

opencodex 자체는 요청을 받아 번역해 넘기는 가벼운 로컬 데몬이라, 평범한 노트북이면 충분하다. 무거운 연산(추론)은 전부 원격 제공자(또는 로컬 Ollama)가 하고, 프록시는 그 사이에서 통역만 한다.

항목요구/권장
Node.js≥ 18 (engines.node). npm 전역 설치용 런처만 Node로 돎
런타임(내장)Bun 1.3.14 — 패키지에 번들되므로 별도 설치 불필요
설치npm install -g @bitkyc08/opencodexocx start
포트기본 localhost:10100. 0.0.0.0로 열려면 OPENCODEX_API_AUTH_TOKEN 필수(외부 노출 방어)
서비스 등록macOS launchd · Linux systemd 유저 유닛 · Windows 작업 스케줄러/WinSW
설정 파일~/.opencodex/config.json (ocx init 또는 대시보드가 생성)
GPU불필요(로컬 추론을 Ollama/vLLM로 돌릴 때만 해당 백엔드 사양이 필요)
제공자 인증제공자별로 API 키 또는 OAuth. ChatGPT 경로는 계정 풀(codexAccounts[])
대시보드브라우저 하나 — ocx gui로 관리 UI 접속
보안 주의
이 프록시는 내 모든 코딩 요청과 자격증명을 거쳐 간다

opencodex는 Codex/Claude Code의 모든 요청·응답과 여러 제공자의 API 키·OAuth 토큰을 로컬에서 다룬다. 그래서 기본 바인딩이 localhost이고, 외부(0.0.0.0) 노출 시엔 인증 토큰을 강제한다. 신뢰할 수 없는 네트워크에 포트를 열지 말고, 설정 파일(~/.opencodex/config.json)에 담기는 키 관리에 주의할 것. 코드가 MIT 오픈소스라 직접 감사할 수 있다는 점은 이런 "모든 걸 거쳐 가는" 도구에서 큰 장점이다.

8직접 해볼 수 있는 실습 과제

난이도별로, 손을 움직이며 배우는 순서

과제 1. Codex를 Claude에 붙여보기 난이도 ★☆☆

npm i -g @bitkyc08/opencodexocx start로 데몬을 띄우고, ocx init(또는 ocx gui 대시보드)로 anthropic 제공자를 defaultProvider로 설정한다. 그다음 평소처럼 codex를 실행해, 실제로 Claude가 응답하는지 확인한다. ocx stop 후 Codex 설정이 원복되는지도 함께 관찰하라 — "포크 없는 주입"의 실체를 눈으로 확인하는 과제다.

과제 2. 로컬 모델(Ollama)로 완전 오프라인 코딩 난이도 ★★☆

Ollama를 설치해 모델 하나를 받고(ollama pull ...), config에 ollama 제공자(adapter: openai-chat, baseUrl: http://localhost:11434/v1)를 추가한다. Codex 요청이 인터넷 없이 로컬 모델로만 처리되는지, 툴 호출·스트리밍이 어디까지 동작하는지 한계를 직접 체감해본다.

과제 3. Claude Code도 다른 백엔드로 난이도 ★★☆

ocx claude로 Claude Code를 프록시에 연결하고, /v1/messages 인바운드를 Gemini나 GLM 같은 비-Anthropic 모델로 라우팅해본다. Messages API → 다른 제공자 포맷 번역이 실제로 어떻게 동작하는지, 그리고 어디서 어색해지는지 로그(src/usage/ JSONL)로 추적한다.

과제 4. 번역 파이프라인을 로그로 뜯어보기 난이도 ★★★

src/usage/의 요청 로그를 켜고, 한 번의 Codex 요청이 parser → router → adapter → provider → parseStream → bridge를 지나는 동안 각 단계의 입출력을 추적한다. 특히 anthropic 어댑터에서 reasoning effort → thinking budget → max_tokens가 어떻게 재계산되는지, cache_control 브레이크포인트가 어디에 찍히는지 src/adapters/anthropic.ts와 대조하며 확인한다.

과제 5. 나만의 제공자 어댑터 추가 난이도 ★★★★

src/adapters/base.tsProviderAdapter 인터페이스를 구현해, 아직 레지스트리에 없는 OpenAI 호환 엔드포인트(또는 사내 게이트웨이)를 붙여본다. buildRequest/parseStream 두 방향만 채우고 src/providers/registry.ts에 등록하면 되는지, 정말 파이프라인 나머지를 안 건드리고 새 제공자가 붙는지 직접 검증하라 — 어댑터 패턴의 확장성을 몸으로 확인하는 과제다.

9관련 기술 심화 학습 로드맵

이 저장소를 발판 삼아 넓혀갈 4주 코스

1주차 — LLM API 3형식 완전 정복

OpenAI Chat Completions, OpenAI Responses API, Anthropic Messages API 세 가지 요청/응답 스키마를 공식 문서로 나란히 놓고 비교한다. 특히 스트리밍 이벤트(SSE) 형식, 툴 호출 표현, 추론(reasoning/thinking) 필드가 서로 어떻게 다른지 정리하면, opencodex 어댑터가 무엇을 번역하는지가 한눈에 들어온다. src/responses/schema.ts(zod)와 대조하면 좋다.

2주차 — 어댑터/전략 패턴과 프록시 아키텍처

src/adapters/base.ts의 인터페이스를 출발점으로, 어댑터 패턴과 전략 패턴을 코드로 익힌다. 리버스 프록시가 요청을 가로채 변형·전달하는 일반 원리(예: Envoy·nginx의 요청 변환)와 opencodex의 애플리케이션 레벨 프로토콜 번역이 어떻게 닮고 다른지 비교해본다.

3주차 — Bun 런타임과 배포 전략

Bun의 Bun.serve·테스트 러너·번들링을 직접 써보고, "런타임을 dependency로 번들해 배포"하는 방식의 장단점을 정리한다. 이어 src/service.ts를 교재로 launchd·systemd·Windows 작업 스케줄러에 백그라운드 서비스를 등록하는 크로스플랫폼 방법을 실습한다.

4주차 — 에이전트 인증·쿼터·MCP

OAuth(src/oauth/)와 계정 풀·쿼터 관리(src/codex/)를 읽으며 "제한된 자원을 풀로 돌려쓰는" 설계를 정리한다. 마지막으로 @modelcontextprotocol/sdk로 MCP 툴이 프록시를 어떻게 왕복하는지 확인하고, Model Context Protocol 스펙과 대조하며 에이전트 도구화의 큰 그림을 완성한다.

10핵심 키워드 사전

이 문서에 나온 용어를 한 줄로
opencodex / ocx
Codex와 Claude Code를 아무 LLM에나 붙이는 로컬 프록시. ocx는 짧은 실행 별칭. 저장소는 lidge-jun/opencodex, npm 배포명은 @bitkyc08/opencodex.
Responses API
OpenAI의 에이전트 지향 API(/v1/responses). 추론·툴 호출·이미지·암호화된 사고 흔적을 1급으로 다룬다. Codex가 쓰는 형식이며 opencodex 번역의 핵심 대상.
Messages API
Anthropic의 API(/v1/messages). Claude Code가 쓴다. opencodex는 같은 데몬에서 이 엔드포인트도 서빙해 Claude Code를 다른 백엔드에 붙인다.
어댑터(ProviderAdapter)
제공자 하나를 통역하는 계약. buildRequest(내부표현→제공자요청)와 parseStream(제공자응답→내부이벤트) 두 방향으로 구성. 새 제공자는 이 둘만 구현.
AdapterEvent
어댑터가 제공자 스트림을 파싱해 내놓는 제공자 중립 내부 이벤트(text_delta·thinking_delta·tool_call_start 등). 브릿지가 이걸 Codex Responses SSE로 되돌린다.
브릿지(bridge)
bridgeToResponsesSSE. 내부 AdapterEvent 스트림을 Codex가 알아듣는 Responses SSE 이벤트로 역번역하는 마지막 단계.
라우터(routeModel)
요청을 어느 제공자/모델로 보낼지 6단계 우선순위로 결정. 명시적 네임스페이스 → OpenAI 맨모델 → defaultModel → 접두사 패턴 → models[] → defaultProvider 순.
히스토리-세이프 주입
Codex를 포크하지 않고 config.toml에 base URL 한 줄만 심어 가로채는 방식(src/codex/inject.ts). ocx stop이면 원복, codex-shim이 자동 복구.
ChatGPT 계정 풀
여러 ChatGPT 계정을 묶어 쿼터(5시간/주/30일 창)를 돌려쓰는 기능. affinity 고정 + 소진 시 페일오버 + 쿨다운으로 무료·저가 경로를 최대한 오래 유지.
reasoning budget 변환
Codex의 추론 강도(effort)를 Anthropic thinking budget(하한 1024·상한 32000)으로 바꾸고, max_tokens > budget이 항상 성립하도록 재계산하는 anthropic 어댑터의 핵심 로직.
prompt cache 브레이크포인트
Anthropic의 프롬프트 캐시 적중을 극대화하려고 cache_control 마커를 최대 4곳(tools·system·직전/마지막 user)에 배치하는 최적화.
사이드카(vision/web-search)
본체 번역 흐름 밖에서 능력을 보강하는 보조 컴포넌트. 비전 사이드카는 텍스트 모델용으로 이미지를 묘사하고, 웹서치 사이드카는 합성 툴로 검색을 대행.
Bun
빠른 JS/TS 런타임. opencodex는 이걸 dependency로 번들해 사용자는 Node만 있으면 되게 했다. HTTP 서버는 Bun.serve로 프레임워크 없이 구현.
MCP(Model Context Protocol)
AI 에이전트에 툴을 노출하는 표준 프로토콜. opencodex는 @modelcontextprotocol/sdk로 MCP 툴이 프록시를 왕복하게 하고, 이름을 namespace__name으로 평탄화·복원한다.

11참고 링크

원본부터 배경 지식까지

프로젝트

핵심 소스(먼저 읽을 파일)

기반 기술