별도의 Codex Desktop 앱에서 커스텀 모델 제공자(Model Providers)를 실행하는 방법
요약
Codex Desktop의 메인 환경을 유지하면서 커스텀 모델 제공자를 독립적으로 실행하기 위한 격리된 앱 구축 방법을 설명합니다. macOS 및 Linux 환경에서 별도의 앱 ID, 설정, 세션을 가진 오픈 소스 스타터 키트와 구현 사례를 소개합니다.
핵심 포인트
- 기존 Codex 설정과 충돌하지 않는 독립적인 커스텀 앱 환경 구축
- 앱 ID, 아이콘, 세션, 포트 등을 격리하여 시스템 충돌 방지
- Codex MiniMax를 통한 로컬 호환성 브리지 구현 사례 제시
- 격리(Isolation)를 핵심 설계 원칙으로 채택
Toolboard를 구축한 후, 저는 다른 종류의 제품 문제에 대해 탐구하기 시작했습니다. 그것은 바로 메인 설치 환경을 실험적인 환경으로 만들지 않으면서, 어떻게 Codex Desktop 내부에서 커스텀 모델 제공자 (custom model provider)를 사용할 것인가 하는 문제였습니다.
가장 명백한 접근 방식은 기존 설정을 편집하여 다른 엔드포인트 (endpoint)를 가리키도록 하는 것이었습니다.
저는 그렇게 하고 싶지 않았습니다.
저의 일반적인 Codex 설정에는 이미 자체적인 세션 (sessions), 설정 (configuration), 자동화 (automations), 아이콘 (icon), 런처 (launcher), 그리고 인증 상태 (authentication state)가 존재했습니다. 커스텀 제공자는 이 중 그 어떤 것도 덮어써서는 안 됩니다. 그것은 원본 옆에 공존할 수 있는 별개의 애플리케이션처럼 동작해야 합니다.
그 요구 사항이 바로 Codex Desktop Custom Models가 되었습니다. 이는 macOS 및 Linux에서 격리된 Codex Desktop 앱을 만들기 위한 오픈 소스 스타터 키트 (open-source starter kit)입니다.
참조 구현체는 Codex MiniMax로, 로컬 호환성 브리지 (local compatibility bridge)를 통해 MiniMax를 사용하는 별도의 브랜드 Codex Desktop 앱입니다.
이 글에서는 그 이면에 있는 아키텍처 (architecture), 브리지가 필요했던 API 불일치 (API mismatch), 그리고 왜 격리 (isolation)가 가장 중요한 제품 결정이 되었는지에 대해 설명합니다.
진짜 문제는 모델을 바꾸는 것이 아니었습니다
모델 이름을 바꾸는 것은 쉽습니다.
첫 번째 애플리케이션을 방해하지 않는 두 번째 데스크톱 애플리케이션을 만드는 것은 더 큰 시스템 문제 (systems problem)입니다.
커스텀 Codex 앱에는 다음과 같은 자체 요소가 필요합니다:
- 애플리케이션 이름 및 아이콘 (application name and icon);
- 번들 또는 데스크톱 애플리케이션 식별자 (bundle or desktop application identity);
- 런처 및 실행 파일 경로 (launcher and executable path);
- 웹뷰 포트 (webview port);
CODEX_HOME디렉토리;- 제공자 설정 (provider configuration);
- API 자격 증명 (API credentials);
- 세션 및 로컬 상태 (sessions and local state);
- 브리지 프로세스 (bridge process);
- 자동화 정의 및 러너 상태 (automation definitions and runner state).
만약 이러한 경계 중 단 하나라도 실수로 공유된다면, 두 앱은 미묘한 방식으로 충돌할 수 있습니다.
잘못된 아이콘으로 열리거나, 세션 기록을 공유하거나, 설정을 덮어쓰거나, 동일한 웹뷰 포트를 재사용하거나, 어떤 제공자가 활성화되어 있는지 불분명해질 수 있습니다.
그래서 저는 격리 (isolation)를 최우선 불변량 (primary invariant)으로 삼았습니다:
커스텀 Codex 앱은 친숙하게 느껴져야 하지만, 기본 설치 버전과 ID, 상태(state), 포트(ports) 또는 제공자 설정(provider configuration)을 공유해서는 안 됩니다.
격리된 설정의 모습
일반적인 설치 버전과 커스텀 프로필은 나란히 공존할 수 있습니다:
| 기존 Codex | 커스텀 Codex 앱 |
|---|---|
| 기본 애플리케이션 ID (identity) | 고유한 앱 이름, 아이콘 및 앱 ID |
| ... |
MiniMax 참조 프로필의 경우, 결과적인 흐름은 다음과 같습니다:
Codex MiniMax Desktop
|
v
...
기존 Codex 설치 버전은 전혀 수정되지 않은 상태로 유지됩니다.
macOS의 경우, 설치 프로그램은 ~/Applications 아래에 별도의 앱 번들(app bundle)을 생성하고, ~/.local/bin 아래에 커맨드 래퍼(command wrappers)를 생성하며, 격리된 홈 디렉토리와 브리지 프로세스(bridge process)를 위한 LaunchAgent를 생성합니다.
Linux의 경우, 사용자의 로컬 애플리케이션 디렉토리 아래에 사이드 바이 사이드(side-by-side) 데스크톱 셸을 생성하며, 실용적인 경우에는 대규모 런타임 자산(runtime assets)을 재사용합니다.
로컬 브리지(local bridge)가 필요했던 이유
모델 제공자(model provider)가 유일한 호환성 계층(compatibility layer)은 아니었습니다.
Codex Desktop은 **Responses API 형태 (Responses API shape)**를 사용하여 제공자와 통신합니다. 반면, 많은 서드파티 제공자들은 대신 OpenAI와 호환되는 Chat Completions API를 노출합니다.
이 API들은 겹치는 부분이 있지만, 서로 교체 가능한 것은 아닙니다.
브리지는 다음 항목들을 번역해야 합니다:
- Responses 입력 항목을 채팅 메시지(chat messages)로 변환
- 개발자 지침(developer instructions)을 올바른 업스트림 역할(upstream role)로 변환
- 어시스턴트 메시지(assistant messages) 및 출력 텍스트
- 함수 호출(function calls) 및 함수 호출 출력(function-call outputs)
- 네임스페이스가 지정된 도구(namespaced tools)
- 도구 선택(tool-choice) 동작
- 스트리밍(streaming) 및 비스트리밍(non-streaming) 출력
- 제공자별 모델 이름
- 에러 및 상태 확인(health checks)
기본 경로는 다음과 같습니다:
POST /v1/responses
|
v
...
텍스트 전용 요청은 쉬운 사례입니다.
도구 호출(Tool calls)은 설계가 더욱 흥미로워지는 지점입니다.
네임스페이스가 지정된 도구 번역하기
Codex는 네임스페이스 내부의 도구를 표현할 수 있습니다. 하지만 Chat Completions 제공자는 보통 평탄한(flat) 함수 이름을 기대합니다.
예를 들어, 개념적인 Codex 도구는 다음과 같을 수 있습니다:
namespace: github
function: fetch_file
브릿지(bridge)는 이를 github__fetch_file과 같이 제공자(provider)가 안전하게 사용할 수 있는 함수 이름으로 평탄화(flatten)합니다.
브릿지는 내부 맵(internal map)을 유지하여, 제공자가 도구 호출(tool call)을 반환할 때 결과가 Codex로 돌아가기 전 원래의 네임스페이스(namespace)와 함수 이름을 복구할 수 있도록 합니다.
이는 사소한 변환처럼 들릴 수 있지만, 매우 필수적입니다. 가역적인 매핑(reversible mapping)이 없다면, 모델은 함수를 성공적으로 호출하더라도 Codex가 그 결과를 올바른 도구로 라우팅(route)하지 못할 수 있습니다.
또한 브릿지는 이름을 정제(sanitize)합니다. 왜냐하면 제공자의 함수 이름 규칙이 소스 도구 시스템에서 사용하는 식별자(identifier)보다 더 엄격할 수 있기 때문입니다.
로컬 HTTP 서비스를 사용한 이유
브릿지는 오직 127.0.0.1에서만 실행됩니다.
이러한 결정은 몇 가지 유용한 속성을 제공합니다:
- Codex가 이를 일반적인 제공자 엔드포인트(endpoint)처럼 취급할 수 있습니다.
- 업스트림(upstream) API 키가 커스텀 프로필 환경(environment)에 유지됩니다.
- 제공자별 번역 로직이 데스크톱 애플리케이션 번들(bundle) 외부에 유지됩니다.
- 상태 확인(health checks) 및 테스트 요청을 실행하기 쉽습니다.
- 브릿지를 독립적으로 재시작할 수 있습니다.
- 다른 OpenAI 호환 제공자도 동일한 아키텍처를 재사용할 수 있습니다.
커스텀 설정은 Codex가 로컬 Responses 엔드포인트를 가리키도록 하며, 브릿지는 실제 제공자의 Chat Completions 엔드포인트를 가리킵니다.
참조 프로필은 MiniMax를 사용하지만, 아키텍처는 의도적으로 범용적(generic)으로 설계되었습니다. 다른 제공자를 사용하려면 주로 다음 항목들을 변경해야 합니다:
- 업스트림 베이스 URL (upstream base URL)
- 기본 모델 이름 (default model name)
- 지원되는 모델 목록 (supported model list)
- API 키 환경 변수 (API-key environment variable)
- 제공자별 응답 정규화 (provider-specific response normalization)
macOS에서의 애플리케이션 격리
macOS 애플리케이션 식별(identity)은 단순히 폴더 이름 그 이상을 의미합니다.
병렬로 실행되는 애플리케이션은 고유한 번들 식별자(bundle identifier), 표시 이름(display name), 아이콘, 헬퍼 식별자(helper identity), 런처 동작(launcher behavior), 그리고 웹뷰 포트(webview port)가 필요합니다. 기본 .app 번들을 복사함으로써 커스텀 프로필은 이러한 값들을 패치(patch)할 수 있는 통제된 공간을 갖게 됩니다.
그 후 설치 프로그램은 다음과 같은 래퍼(wrapper)들을 생성합니다:
codex-minimax
codex-minimax-desktop
codex-minimax-proxy
각 래퍼(wrapper)는 다음과 같은 하나의 좁은 책임만을 갖습니다:
- 격리된 홈(home) 디렉토리를 사용하여 Codex CLI 호출;
- 커스텀 데스크톱 번들(desktop bundle) 실행;
- 브리지(bridge) 프로세스 관리, 테스트 및 검사.
브리지는 LaunchAgent를 통해 등록되므로, 이를 생성한 셸(shell) 세션이 종료된 후에도 계속 유지될 수 있습니다.
이는 데스크톱 앱이 사용자가 터미널 명령어를 계속 실행 중인지 기억해야 하는 것에 의존해서는 안 되기 때문에 중요합니다.
Linux에서의 애플리케이션 격리 (Application isolation)
Linux 경로는 다른 제약 조건 세트를 가집니다.
많은 대규모 런타임(runtime) 파일들을 재사용할 수 있지만, 식별(identity)에 민감한 파일들은 링크(link)하기보다는 복사(copy)해야 합니다.
커스텀 앱은 다음과 같은 항목들의 독립적인 버전을 유지합니다:
- 런처 스크립트 (launcher script);
- 실행 파일 식별 정보 (executable identity);
- 브랜딩 자산 (branding assets);
- 데스크톱 엔트리 메타데이터 (desktop entry metadata);
- 애플리케이션 디렉토리 구조.
공유 가능한 쓰기 상태(shared writable state)를 생성하지 않는 경우, 대규모 내부 런타임 자산은 링크할 수 있습니다.
규칙은 "모든 것을 복사하라" 또는 "모든 것을 심볼릭 링크(symlink)하라"가 아닙니다. 규칙은 다음과 같습니다:
애플리케이션의 식별을 정의하는 파일은 복사하고, 사실상 불변(immutable)인 런타임 의존성 파일만 재사용하십시오.
이렇게 하면 Dock, 작업 표시줄(taskbar), 아이콘 또는 프로필 충돌을 일으키지 않으면서 불필요한 중복을 피할 수 있습니다.
별도의 CODEX_HOME이 진정한 경계입니다
가장 중요한 디렉토리는 앱 번들(app bundle)이 아닙니다. 바로 커스텀 CODEX_HOME입니다.
~/.codex-minimax와 같은 프로필에는 커스텀 앱의 다음 항목들이 포함됩니다:
config.toml;- 제공자 환경 파일 (provider environment files);
- 브리지 스크립트 (bridge scripts);
- 자동화 정의 (automation definitions);
- 세션 및 애플리케이션 상태 (session and application state);
- 로컬 로그 및 SQLite 파일.
런처는 Codex를 호출하기 전에 이 디렉토리를 내보냅니다(export).
이는 커스텀 앱이 ~/.codex를 변경할 필요가 없음을 의미하며, 커스텀 애플리케이션을 제거하더라도 사용자의 메인 Codex 상태를 삭제할 필요가 없음을 의미합니다.
삭제(uninstall) 프로세스는 기본적으로 커스텀 홈을 의도적으로 유지합니다. 앱을 제거하는 것과 그 기록을 삭제하는 것은 별개의 결정입니다.
그러한 구분은 모든 데스크톱 도구에서 가치가 있습니다. 실행 파일(executable files)을 제거하는 것이 사용자 데이터를 조용히 파괴해서는 안 됩니다.
자동화(Automations)를 위한 두 번째 호환성 계층 필요
커스텀 Codex 프로필은 호스트 환경에서 제공하는 자동화 도구를 자동으로 상속받지 않습니다.
따라서 이 프로젝트에는 작은 로컬 MCP 서버와 러너(runner)가 포함되어 있습니다.
MCP 서버는 자동화 업데이트 도구를 노출하고, 격리된 커스텀 홈(custom home) 아래에 정의를 저장합니다. 또한 앱이 해당 자동화들을 표시할 수 있도록 충분한 메타데이터를 로컬 SQLite 상태(state)로 미러링합니다.
러너는 예정된 작업(due jobs)을 폴링(poll)하며 두 가지 패턴을 지원합니다:
- 백그라운드 또는 인박스(inbox) 스타일의 작업;
- 동일 스레드(same-thread) 후속 작업.
동일 스레드 전달이 중요한 세부 사항이었습니다.
예약된 기록을 생성하는 것은 사용자가 요청한 대화창에 결과가 나타나게 하는 것과는 다릅니다.
동일 스레드 리마인더(reminders)를 위해, 자동화는 대상 스레드 ID를 저장하고 나중에 개념적으로 다음과 동일한 명령을 실행합니다:
codex exec resume <thread-id> ...
이를 통해 후속 작업이 다른 곳에 분리된 출력을 생성하는 대신 원래의 스레드로 돌아올 수 있습니다.
스케줄러(scheduler)는 의도적으로 일반적인 분 단위, 시간 단위, 일 단위 패턴으로 제한되어 있습니다. 이는 완전한 캘린더 시스템을 대체하려는 것이 아닙니다.
안전성 및 리포지토리 위생(repository hygiene)
커스텀 모델 제공자(model providers) 및 로컬 Codex 상태와 함께 작동하는 프로젝트는 명백한 배포 위험을 가지고 있습니다. 바로 자격 증명(credentials)이나 개인 세션 데이터를 실수로 커밋(commit)하는 것입니다.
리포지토리에는 구문을 확인하고 다음과 같은 패턴을 스캔하는 검증 명령이 포함되어 있습니다:
- API 키;
- 개인 홈 디렉토리 경로;
- 로컬 프로젝트 이름;
- 개인 상태 파일(private state files);
- 제공자 환경 파일(provider environment files).
공개 스타터 키트(public starter kit)에는 템플릿과 예시가 포함되어 있으며, 실제 Codex 프로필의 복사본은 들어있지 않습니다.
비공개로 유지되어야 하는 파일에는 제공자 키(provider keys), 세션 인덱스(session indexes), 셸 스냅샷(shell snapshots), 로그(logs), 그리고 애플리케이션 상태 데이터베이스(application state databases)가 포함됩니다.
이것이 제가 기본 설치(primary installation)를 수정하는 것보다 격리된 프로필(isolated profiles)을 선호하는 이유이기도 합니다. 격리(Isolation)를 통해 보안 경계(security boundary)를 더 쉽게 이해하고 감사(audit)할 수 있기 때문입니다.
내가 배운 것들
1. 호환성은 API 구문(syntax)보다 더 광범위하다
요청(request) 및 응답(response) 형식은 하나의 레이어에 불과했습니다. 진정한 데스크톱 통합에는 프로세스 관리(process management), 상태 격리(state isolation), 앱 식별자(app identity), 포트(ports), 아이콘(icons), 런처(launchers), 그리고 자동화 동작(automation behavior)이 필요했습니다.
2. 병렬 제품(Side-by-side products)에는 명시적인 식별자가 필요하다
이름을 바꾼 폴더는 별개의 애플리케이션이 아닙니다. 운영 체제는 앱이 어떻게 그룹화되고 실행될지를 결정하기 위해 번들 ID(bundle IDs), 실행 파일 경로(executable paths), 헬퍼 식별자(helper identities), 그리고 데스크톱 메타데이터(desktop metadata)를 사용합니다.
3. 도구 호출(Tool calling)은 제공자 브리지(provider bridges)를 훨씬 더 어렵게 만든다
텍스트는 종종 직접 매핑될 수 있습니다. 하지만 네임스페이스(Namespaces), 함수 호출 ID(function-call IDs), 도구 출력(tool outputs), 그리고 제공자 명명 제한(provider naming restrictions)은 가역적이고 신중하게 검증된 변환(transformations)을 요구합니다.
4. 로컬 브리지(Local bridges)는 유용한 아키텍처적 이음매(architectural seams)이다
제공자 적응(provider adaptation)을 작은 로컬호스트(localhost) 서비스로 유지함으로써, 데스크톱 셸(desktop shell), Codex 설정(configuration), 그리고 상위 제공자 로직(upstream provider logic)이 각각 독립적으로 이해될 수 있는 상태를 유지할 수 있었습니다.
5. 격리는 제품의 기능이다
가장 강력한 이점은 이 프로젝트가 MiniMax에 연결할 수 있다는 점이 아닙니다. 사용자가 이미 의존하고 있는 설정을 불안정하게 만들지 않고도 실험을 할 수 있다는 점입니다.
이 프로젝트의 대상
Codex Desktop Custom Models는 클릭 한 번으로 실행되는 소비자용 애플리케이션이 아닙니다.
이 프로젝트는 다음과 같은 개발자들을 위한 운영자 친화적인 스타터 키트(starter kit)입니다:
- 이미 Codex Desktop이 작동하고 있는 개발자
- 상위 내부 로직(upstream internals)이 변경될 수 있음을 이해하는 개발자
- OpenAI 호환 제공자(OpenAI-compatible provider)를 위한 별도의 앱을 원하는 개발자
- 기본 환경을 수정하는 것보다 검사 가능한 스크립트(inspectable scripts)를 선호하는 개발자
- Codex 업데이트 이후 통합(integrations)을 테스트하는 데 익숙한 개발자
이 프로젝트는 비공식 프로젝트이며, OpenAI, MiniMax, 또는 Ollama 프로젝트가 아닙니다.
리포지토리(The repository)
해당 리포지토리(repository)에는 프로바이더 브릿지(provider bridge), macOS 및 Linux용 설치 프로그램(installers), 설정 템플릿(configuration templates), 자동화 MCP 및 러너(runner), 다이어그램(diagrams), 스크린샷(screenshots), 문제 해결 노트(troubleshooting notes), 그리고 게시 체크리스트(publishing checklist)가 포함되어 있습니다.
GitHub logo ademisler / codex-desktop-custom-models
프로바이더 브릿지(provider bridges), 커스텀 아이콘(custom icons), 로컬 자동화(local automations)를 포함하여, macOS 및 Linux에서 커스텀 모델 제공자(custom model providers)를 위한 격리된 Codex Desktop 앱을 구축할 수 있는 오픈 소스 스타터 키트(Open-source starter kit).
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기