Tyojong
[Codex] 구조 분석 - (Codex 실행 진입) 본문
주요 프로젝트 구조
https://github.com/openai/codex/tree/main/codex-rs
codex-rs/
├── cli/ CLI 명령행 진입점
├── tui/ 터미널 UI와 사용자 입력
├── core/ Agent loop와 핵심 상태 관리
├── protocol/ 내부 이벤트와 API용 데이터 타입
├── exec/ 비대화형 codex exec
├── app-server/ 외부 클라이언트와 연결되는 서버 계층
└── ...
실행 흐름
최상위 구조체 MultitoolCli
https://github.com/openai/codex/blob/main/codex-rs/cli/src/main.rs
이 파일은 사용자가 codex 명령어를 이용해 실행했을 때 운영체제가 처음 실행하는 Rust함수가 존재한다.
https://github.com/openai/codex/blob/main/codex-rs/cli/src/main.rs#L966-L972

let remote_control_disabled = codex_app_server::take_remote_control_disabled_env();
main() 함수에서는 환경변수를 먼저 읽어온다.
환경변수에는 API Key, 사용할 모델, 프록시, 로그 레벨, 원격 제어 등의 설정이 가능하다.
이후 cli_main함수를 호출한다.
https://github.com/openai/codex/blob/main/codex-rs/cli/src/main.rs#L974-L1673

최상위 CLI 구조체(사용자가 입력한 모든 명령줄 인자를 가장 먼저 저장하는 구조체)는 MultitoolCli다.
https://github.com/openai/codex/blob/main/codex-rs/cli/src/main.rs#L105-L120
이 구조체는 codex 프로그램 전체에서 사용할 인자를 분류한다.
- config_overrides: -c key=value 형태 설정
- feature_toggles: 기능 플래그
- remote: App Server 연결 관련 옵션
- interactive: 대화형 TUI 실행 옵션
- subcommand: exec, login, mcp 같은 서브커맨드
사용자가 다음과 같이 평범하게 입력하면
codex \
--model gpt-5 \
--sandbox workspace-write \
--ask-for-approval on-request \
--cd /tmp/project
내부적으로 다음과 같이 중첩된다.
MultitoolCli {
interactive: TuiCli {
shared: TuiSharedCliOptions(
SharedCliOptions {
model: Some("gpt-5".to_string()),
sandbox_mode: Some(...),
cwd: Some(...),
..
}
),
approval_policy: Some(...),
..
},
..
}
interactive: TuiCli

MultitoolCli 안에서 대화형 Codex 옵션을 담당하는 필드이다.
TuiCli는 다음처럼 import 되어 사용되는데

대략적인 구조는 다음과 같다.
https://github.com/openai/codex/blob/main/codex-rs/tui/src/cli.rs
pub struct Cli {
pub prompt: Option<String>,
pub strict_config: bool,
// resume 및 fork 관련 내부 필드
pub shared: TuiSharedCliOptions,
pub approval_policy: Option<ApprovalModeCliArg>,
pub web_search: bool,
pub no_alt_screen: bool,
pub config_overrides: CliConfigOverrides,
}
즉 대화형 실행과 관련된 값은 TuiCli에 모인다.
interactive: TuiCli → SharedCliOptions
모델, Sandbox, 작업 디렉터리 같은 공통 옵션은 TuiCli 에 직접 선언되지 않고 SharedCliOptions 에 들어간다.
https://github.com/openai/codex/blob/main/codex-rs/utils/cli/src/shared_options.rs
pub struct SharedCliOptions {
pub images: Vec<PathBuf>,
pub model: Option<String>,
pub oss: bool,
pub oss_provider: Option<String>,
pub config_profile_v2: Option<ProfileV2Name>,
pub sandbox_mode: Option<SandboxModeCliArg>,
pub dangerously_bypass_approvals_and_sandbox: bool,
pub bypass_hook_trust: bool,
pub cwd: Option<PathBuf>,
pub add_dir: Vec<PathBuf>,
}
이 구조체는 대화형 TUI뿐 아니라 codex exec 같은 실행 모드에서도 공통으로 사용할 옵션을 묶은 것이다.
TUI는 터미널에 대화형 화면을 띄우고 사용자와 계속 대화하는 모드이다.
vs
codex exec는 대화형 화면 없이 한 번의 작업을 실행하는 모드이다.
예시) codex exec "현재 프로젝트의 테스트를 실행하고 실패 원인을 분석해"
interactive: TuiCli → prompt
pub prompt: Option<String>,
명령행에서 직접 전달한 초기 사용자 프롬프트다.
codex "이 프로젝트 구조를 분석해줘"
라는 명령어를 실행하게 되면
prompt: Some("이 프로젝트 구조를 분석해줘".to_string())
이렇게 저장된다.
반대로
codex
그냥 실행하게 되면
prompt: None
가 되고 TUI가 열린 뒤 입력창에서 직접 프롬프트를 작성하게 된다.
interactive: TuiCli → SharedCliOptions → model
pub model: Option<String>,
는 아래 옵션에 대응한다.
codex --model gpt-5
또는
codex -m gpt-5
데이터 흐름은 다음과 같다.
--model
→ SharedCliOptions.model
→ TUI 초기화
→ Config 구성
→ Thread 시작
→ 모델 API 요청
CLI 단계(사용자가 입력한 CLI 명령어를 해석하는 단계)에서는 모델을 실제로 호출하지 않는다. 사용자가 선택한 모델명을 구조체에 저장하는 단계이다.
(CLI가 --model gpt-5 옵션을 읽고 바로 OpenAI API를 호출하는 것이 아닌 구조체에 저장만 진행)
interactive: TuiCli → SharedCliOptions → sandbox_mode
pub sandbox_mode: Option<SandboxModeCliArg>,
다음 옵션에 대응한다.
codex --sandbox workspace-write
sandbox 모드는 모델이 생성한 명령을 실행할 때 OS 자원 접근 범위를 결정한다.
read-only
→ 파일 쓰기 제한
workspace-write
→ 작업공간 범위 쓰기 허용
danger-full-access
→ 강한 Sandbox 제한 없이 실행
CLI 단계에서는 문자열을 SandboxModeCliArg 타입으로 변환해 저장할 뿐이고 실제 적용은 뒤의 Config와 Tool 실행 계층에서 이루어진다.
"workspace-write"
→ SandboxModeCliArg
→ Config
→ SandboxPolicy
→ 실제 프로세스 실행
interactive: TuiCli → approval_policy
pub approval_policy: Option<ApprovalModeCliArg>,
다음 옵션에 대응한다.
codex --ask-for-approval on-request
sandbox와 승인 정책은 다른 개념이다.
approval_policy → 사용자에게 실행 허가를 물어볼지 결정
sandbox_mode → 실행이 허용된 뒤 어디까지 접근할 수 있는지 결정
예를 들어 명령 실행을 사용자가 승인했어도 workspace-write Sandbox가 적용되면 작업공간 외부 파일 쓰기는 제한될 수 있다.
interactive: TuiCli → SharedCliOptions → cwd
pub cwd: Option<PathBuf>,
다음 옵션에 대응한다.
codex --cd /tmp/project
또는
codex -C /tmp/project
cwd는 단순히 Shell의 현재 디렉터리만 뜻하지 않는다. 이후 여러 기능이 어디를 기준으로 동작할지 결정하는 기준값 된다.
/home/tyojong/project
├── AGENTS.md
├── README.md
├── src/
│ └── main.rs
└── skills/
└── review.md
프로젝트 구조가 다음과 같고 사용자가
codex --cd /home/tyojong/project
를 실행하게 되면 CLI 단계에서는
SharedCliOptions {
cwd: Some("/home/tyojong/project")
}
만 저장한다.
다음에 AGENTS.md를 읽을 때 어떤 코드에서
find_agents_md(cwd)
를 호출한다고 하면
cwd → /home/tyojong/project 를 기준으로 /home/tyojong/project/AGENTS.md 를 찾는다.
즉
CLI
↓
cwd 저장
↓
TUI
↓
Config
↓
find_agents_md(cwd)
처럼 cwd가 계속 전달된다.
프로젝트 루트 탐색
AGENTS.md 탐색
상대 경로 파일 접근
Git 저장소 확인
Workspace Sandbox 범위
Skill 및 설정 탐색
따라서 AGENTS.md나 Skill 로딩 흐름을 분석할 때 cwd가 어디까지 전달되는지 추적해야 한다.
interactive: TuiCli → SharedCliOptions → add_dir
pub add_dir: Vec<PathBuf>,
주 작업 디렉터리 외에 추가로 접근하거나 쓸 수 있는 디렉터리를 전달한다.
codex \
--cd /tmp/project \
--add-dir /tmp/shared \
--add-dir /tmp/output
위 명령어를 사용자가 입력하면
add_dir: vec![
PathBuf::from("/tmp/shared"),
PathBuf::from("/tmp/output"),
]
위와 같이 저장되고 이 값은 뒤에서 sandbox의 추가 쓰기 가능 경로를 구성하는 데 사용될 수 있다.
위험한 우회 옵션
interactive: TuiCli → SharedCliOptions → dangerously_bypass_approvals_and_sandbox
pub dangerously_bypass_approvals_and_sandbox: bool,
다음 옵션에 대응한다.
codex --dangerously-bypass-approvals-and-sandbox
해당 옵션은 승인과 sandbox를 모두 우회한다.
sandbox_mode
→ 어떤 Sandbox를 사용할지 선택
dangerously_bypass_approvals_and_sandbox
→ 승인 검사와 Sandbox 자체를 모두 우회
외부 VM이나 컨테이너처럼 별도 격리가 존재하는 환경을 전제로 한 위험한 옵션이다.
interactive: TuiCli → SharedCliOptions → bypass_hook_trust
pub bypass_hook_trust: bool,
codex --dangerously-bypass-hook-trust
다음 옵션은 hook 신뢰 여부 검사를 우회한다. hook 자동 실행 분석에서 중요한 입력값이다.
config_overrides
최상위 MultitoolCli 와 TuiCli 양쪽에 나타난다.
pub config_overrides: CliConfigOverrides,
최상위에서는 실제 CLI 인자를 파싱한다.
codex -c model='"gpt-5"'
interactive: TuiCli → config_overrides
TuiCli 쪽에는 보통 내부 전달을 위한 필드가 있다.
#[clap(skip)]
pub config_overrides: CliConfigOverrides,
#[clap(skip)]이므로 여기서 직접 명령행 값을 파싱하는 것이 아니다. 최상위에서 받은 override를 대화형 TUI 실행 구조체에 전달하기 위한 용도다.
흐름은 다음과 같다.
-c key=value
→ MultitoolCli.config_overrides
→ cli_main()에서 병합
→ TuiCli.config_overrides
→ Config 로더
subcommand
subcommand: Option<Subcommand>,
사용자가 어떤 실행 모드를 선택했는지 나타낸다.
codex
라고 사용자가 입력하게 된다면
subcommand: None
가 되고 대화형 TUI 경로로 들어가게 된다.
codex exec "테스트 실행" → subcommand: Some(Subcommand::Exec(...))
codex login → subcommand = Some(Login)
codex mcp → subcommand = Some(Mcp)
etc...
정리
struct MultitoolCli {
config_overrides: CliConfigOverrides,
feature_toggles: FeatureToggles,
remote: InteractiveRemoteOptions,
interactive: TuiCli,
subcommand: Option<Subcommand>,
}
struct TuiCli {
prompt: Option<String>,
strict_config: bool,
shared: TuiSharedCliOptions,
approval_policy: Option<ApprovalModeCliArg>,
web_search: bool,
no_alt_screen: bool,
config_overrides: CliConfigOverrides,
}
struct SharedCliOptions {
images: Vec<PathBuf>,
model: Option<String>,
oss: bool,
oss_provider: Option<String>,
config_profile_v2: Option<ProfileV2Name>,
sandbox_mode: Option<SandboxModeCliArg>,
dangerously_bypass_approvals_and_sandbox: bool,
bypass_hook_trust: bool,
cwd: Option<PathBuf>,
add_dir: Vec<PathBuf>,
}
다음 코드 구조로 인해
codex \
--model gpt-5 \
--sandbox workspace-write \
--ask-for-approval on-request \
--cd /tmp/project \
-c features.web_search=true
이 명령어를 입력하게 된다면
MultitoolCli {
config_overrides: CliConfigOverrides {
// features.web_search=true
},
interactive: TuiCli {
shared: TuiSharedCliOptions(
SharedCliOptions {
model: Some("gpt-5".to_string()),
sandbox_mode: Some(SandboxModeCliArg::WorkspaceWrite),
cwd: Some(PathBuf::from("/tmp/project")),
..
}
),
approval_policy: Some(ApprovalModeCliArg::OnRequest),
..
},
subcommand: None,
..
}
위와 같이 파싱이 이루어진다.
따라서 흐름은 다음과 같다.
명령행 문자열
→ clap의 MultitoolCli::parse()
→ MultitoolCli 생성
→ interactive: TuiCli
→ shared: SharedCliOptions
→ 모델·Sandbox·작업공간·승인 옵션 저장
→ subcommand가 없으면 TUI 실행