Tyojong

[Codex] 구조 분석 - (Codex 실행 진입) 본문

AI

[Codex] 구조 분석 - (Codex 실행 진입)

Tyojong 2026. 7. 29. 17:37

주요 프로젝트 구조

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 실행