Showing preview only (7,457K chars total). Download the full file or copy to clipboard to get everything.
Repository: Stanford-Trinity/ARTEMIS
Branch: main
Commit: f309242d2a74
Files: 421
Total size: 7.0 MB
Directory structure:
gitextract_2yw_apz6/
├── .env.example
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── NOTICE
├── README.md
├── codex-rs/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── README.md
│ ├── ansi-escape/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ └── lib.rs
│ ├── apply-patch/
│ │ ├── Cargo.toml
│ │ ├── apply_patch_tool_instructions.md
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ ├── parser.rs
│ │ │ ├── seek_sequence.rs
│ │ │ └── standalone_executable.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── cli.rs
│ │ └── mod.rs
│ ├── arg0/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── chatgpt/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── src/
│ │ │ ├── apply_command.rs
│ │ │ ├── chatgpt_client.rs
│ │ │ ├── chatgpt_token.rs
│ │ │ ├── get_task.rs
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ ├── suite/
│ │ │ ├── apply_command_e2e.rs
│ │ │ └── mod.rs
│ │ └── task_turn_fixture.json
│ ├── cli/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── debug_sandbox.rs
│ │ ├── exit_status.rs
│ │ ├── lib.rs
│ │ ├── login.rs
│ │ ├── main.rs
│ │ └── proto.rs
│ ├── clippy.toml
│ ├── common/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── approval_mode_cli_arg.rs
│ │ ├── approval_presets.rs
│ │ ├── config_override.rs
│ │ ├── config_summary.rs
│ │ ├── elapsed.rs
│ │ ├── fuzzy_match.rs
│ │ ├── lib.rs
│ │ ├── model_presets.rs
│ │ ├── sandbox_mode_cli_arg.rs
│ │ └── sandbox_summary.rs
│ ├── config.md
│ ├── core/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── active_directory.md
│ │ ├── approval_prompt.txt
│ │ ├── bugcrowd_approval_prompt.txt
│ │ ├── client_side_web.md
│ │ ├── continuation_prompt.txt
│ │ ├── enumeration.md
│ │ ├── initial_prompt.txt
│ │ ├── linux_privesc.md
│ │ ├── prompt.md
│ │ ├── shelling.md
│ │ ├── src/
│ │ │ ├── apply_patch.rs
│ │ │ ├── bash.rs
│ │ │ ├── chat_completions.rs
│ │ │ ├── client.rs
│ │ │ ├── client_common.rs
│ │ │ ├── codex.rs
│ │ │ ├── codex_conversation.rs
│ │ │ ├── config.rs
│ │ │ ├── config_profile.rs
│ │ │ ├── config_types.rs
│ │ │ ├── conversation_history.rs
│ │ │ ├── conversation_manager.rs
│ │ │ ├── environment_context.rs
│ │ │ ├── error.rs
│ │ │ ├── exec.rs
│ │ │ ├── exec_command/
│ │ │ │ ├── exec_command_params.rs
│ │ │ │ ├── exec_command_session.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── responses_api.rs
│ │ │ │ ├── session_id.rs
│ │ │ │ └── session_manager.rs
│ │ │ ├── exec_env.rs
│ │ │ ├── flags.rs
│ │ │ ├── git_info.rs
│ │ │ ├── is_safe_command.rs
│ │ │ ├── landlock.rs
│ │ │ ├── lib.rs
│ │ │ ├── mcp_connection_manager.rs
│ │ │ ├── mcp_tool_call.rs
│ │ │ ├── message_history.rs
│ │ │ ├── model_family.rs
│ │ │ ├── model_provider_info.rs
│ │ │ ├── openai_model_info.rs
│ │ │ ├── openai_tools.rs
│ │ │ ├── parse_command.rs
│ │ │ ├── plan_tool.rs
│ │ │ ├── project_doc.rs
│ │ │ ├── prompt_for_compact_command.md
│ │ │ ├── rollout.rs
│ │ │ ├── safety.rs
│ │ │ ├── seatbelt.rs
│ │ │ ├── seatbelt_base_policy.sbpl
│ │ │ ├── shell.rs
│ │ │ ├── spawn.rs
│ │ │ ├── terminal.rs
│ │ │ ├── tool_apply_patch.rs
│ │ │ ├── turn_diff_tracker.rs
│ │ │ ├── user_agent.rs
│ │ │ ├── user_notification.rs
│ │ │ └── util.rs
│ │ ├── summarization_prompt.txt
│ │ ├── tests/
│ │ │ ├── all.rs
│ │ │ ├── cli_responses_fixture.sse
│ │ │ ├── common/
│ │ │ │ ├── Cargo.toml
│ │ │ │ └── lib.rs
│ │ │ ├── fixtures/
│ │ │ │ ├── completed_template.json
│ │ │ │ └── incomplete_sse.json
│ │ │ └── suite/
│ │ │ ├── cli_stream.rs
│ │ │ ├── client.rs
│ │ │ ├── compact.rs
│ │ │ ├── exec.rs
│ │ │ ├── exec_stream_events.rs
│ │ │ ├── live_cli.rs
│ │ │ ├── mod.rs
│ │ │ ├── prompt_caching.rs
│ │ │ ├── seatbelt.rs
│ │ │ ├── stream_error_allows_next_turn.rs
│ │ │ └── stream_no_completed.rs
│ │ ├── web.md
│ │ ├── web_enumeration.md
│ │ └── windows_privesc.md
│ ├── cve-prompt.md
│ ├── default.nix
│ ├── disclosure-email-prompt.md
│ ├── docs/
│ │ └── protocol_v1.md
│ ├── exec/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── cli.rs
│ │ │ ├── event_processor.rs
│ │ │ ├── event_processor_with_human_output.rs
│ │ │ ├── event_processor_with_json_output.rs
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ └── realtime_logger.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ ├── fixtures/
│ │ │ ├── apply_patch_freeform_final.txt
│ │ │ ├── sse_apply_patch_add.json
│ │ │ ├── sse_apply_patch_freeform_add.json
│ │ │ ├── sse_apply_patch_freeform_update.json
│ │ │ ├── sse_apply_patch_update.json
│ │ │ └── sse_response_completed.json
│ │ └── suite/
│ │ ├── apply_patch.rs
│ │ ├── common.rs
│ │ ├── mod.rs
│ │ └── sandbox.rs
│ ├── execpolicy/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── build.rs
│ │ ├── src/
│ │ │ ├── arg_matcher.rs
│ │ │ ├── arg_resolver.rs
│ │ │ ├── arg_type.rs
│ │ │ ├── default.policy
│ │ │ ├── error.rs
│ │ │ ├── exec_call.rs
│ │ │ ├── execv_checker.rs
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ ├── opt.rs
│ │ │ ├── policy.rs
│ │ │ ├── policy_parser.rs
│ │ │ ├── program.rs
│ │ │ ├── sed_command.rs
│ │ │ └── valid_exec.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── bad.rs
│ │ ├── cp.rs
│ │ ├── good.rs
│ │ ├── head.rs
│ │ ├── literal.rs
│ │ ├── ls.rs
│ │ ├── mod.rs
│ │ ├── parse_sed_command.rs
│ │ ├── pwd.rs
│ │ └── sed.rs
│ ├── file-search/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── cli.rs
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── justfile
│ ├── linux-sandbox/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── src/
│ │ │ ├── landlock.rs
│ │ │ ├── lib.rs
│ │ │ ├── linux_run_main.rs
│ │ │ └── main.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── landlock.rs
│ │ └── mod.rs
│ ├── login/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── assets/
│ │ │ │ └── success.html
│ │ │ ├── auth_manager.rs
│ │ │ ├── lib.rs
│ │ │ ├── pkce.rs
│ │ │ ├── server.rs
│ │ │ └── token_data.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── login_server_e2e.rs
│ │ └── mod.rs
│ ├── mcp-client/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── main.rs
│ │ └── mcp_client.rs
│ ├── mcp-server/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── codex_message_processor.rs
│ │ │ ├── codex_tool_config.rs
│ │ │ ├── codex_tool_runner.rs
│ │ │ ├── error_code.rs
│ │ │ ├── exec_approval.rs
│ │ │ ├── json_to_toml.rs
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ ├── message_processor.rs
│ │ │ ├── outgoing_message.rs
│ │ │ ├── patch_approval.rs
│ │ │ └── tool_handlers/
│ │ │ └── mod.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ ├── common/
│ │ │ ├── Cargo.toml
│ │ │ ├── lib.rs
│ │ │ ├── mcp_process.rs
│ │ │ ├── mock_model_server.rs
│ │ │ └── responses.rs
│ │ └── suite/
│ │ ├── auth.rs
│ │ ├── codex_message_processor_flow.rs
│ │ ├── codex_tool.rs
│ │ ├── config.rs
│ │ ├── create_conversation.rs
│ │ ├── interrupt.rs
│ │ ├── login.rs
│ │ ├── mod.rs
│ │ └── send_message.rs
│ ├── mcp-types/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── generate_mcp_types.py
│ │ ├── schema/
│ │ │ ├── 2025-03-26/
│ │ │ │ └── schema.json
│ │ │ └── 2025-06-18/
│ │ │ └── schema.json
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── initialize.rs
│ │ ├── mod.rs
│ │ └── progress_notification.rs
│ ├── ollama/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── client.rs
│ │ ├── lib.rs
│ │ ├── parser.rs
│ │ ├── pull.rs
│ │ └── url.rs
│ ├── protocol/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── config_types.rs
│ │ ├── lib.rs
│ │ ├── mcp_protocol.rs
│ │ ├── message_history.rs
│ │ ├── models.rs
│ │ ├── parse_command.rs
│ │ ├── plan_tool.rs
│ │ └── protocol.rs
│ ├── protocol-ts/
│ │ ├── Cargo.toml
│ │ ├── generate-ts
│ │ └── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── rust-toolchain.toml
│ ├── rustfmt.toml
│ ├── scripts/
│ │ └── create_github_release.sh
│ └── tui/
│ ├── Cargo.toml
│ ├── prompt_for_init_command.md
│ ├── src/
│ │ ├── app.rs
│ │ ├── app_backtrack.rs
│ │ ├── app_event.rs
│ │ ├── app_event_sender.rs
│ │ ├── backtrack_helpers.rs
│ │ ├── bottom_pane/
│ │ │ ├── approval_modal_view.rs
│ │ │ ├── bottom_pane_view.rs
│ │ │ ├── chat_composer.rs
│ │ │ ├── chat_composer_history.rs
│ │ │ ├── command_popup.rs
│ │ │ ├── file_search_popup.rs
│ │ │ ├── list_selection_view.rs
│ │ │ ├── mod.rs
│ │ │ ├── popup_consts.rs
│ │ │ ├── scroll_state.rs
│ │ │ ├── selection_popup_common.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__backspace_after_pastes.snap
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__empty.snap
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__large.snap
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__multiple_pastes.snap
│ │ │ │ └── codex_tui__bottom_pane__chat_composer__tests__small.snap
│ │ │ └── textarea.rs
│ │ ├── chatwidget/
│ │ │ ├── agent.rs
│ │ │ ├── interrupts.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── codex_tui__chatwidget__tests__approval_modal_exec.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__approval_modal_patch.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_idle_h1.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_idle_h2.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_idle_h3.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_running_h1.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_running_h2.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_running_h3.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__deltas_then_same_final_message_are_rendered_snapshot.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__final_reasoning_then_message_without_deltas_are_rendered.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__interrupt_exec_marks_failed.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__status_widget_active.snap
│ │ │ │ └── codex_tui__chatwidget__tests__status_widget_and_approval_modal.snap
│ │ │ └── tests.rs
│ │ ├── chatwidget.rs
│ │ ├── chatwidget_stream_tests.rs
│ │ ├── citation_regex.rs
│ │ ├── cli.rs
│ │ ├── clipboard_paste.rs
│ │ ├── common.rs
│ │ ├── custom_terminal.rs
│ │ ├── diff_render.rs
│ │ ├── exec_command.rs
│ │ ├── file_search.rs
│ │ ├── get_git_diff.rs
│ │ ├── history_cell.rs
│ │ ├── insert_history.rs
│ │ ├── lib.rs
│ │ ├── live_wrap.rs
│ │ ├── main.rs
│ │ ├── markdown.rs
│ │ ├── markdown_stream.rs
│ │ ├── onboarding/
│ │ │ ├── auth.rs
│ │ │ ├── mod.rs
│ │ │ ├── onboarding_screen.rs
│ │ │ ├── trust_directory.rs
│ │ │ └── welcome.rs
│ │ ├── pager_overlay.rs
│ │ ├── render/
│ │ │ ├── line_utils.rs
│ │ │ ├── markdown_utils.rs
│ │ │ └── mod.rs
│ │ ├── session_log.rs
│ │ ├── shimmer.rs
│ │ ├── slash_command.rs
│ │ ├── snapshots/
│ │ │ ├── codex_tui__diff_render__tests__add_details.snap
│ │ │ ├── codex_tui__diff_render__tests__blank_context_line.snap
│ │ │ ├── codex_tui__diff_render__tests__single_line_replacement_counts.snap
│ │ │ ├── codex_tui__diff_render__tests__update_details_with_rename.snap
│ │ │ ├── codex_tui__diff_render__tests__vertical_ellipsis_between_hunks.snap
│ │ │ ├── codex_tui__diff_render__tests__wrap_behavior_insert.snap
│ │ │ ├── codex_tui__pager_overlay__tests__static_overlay_snapshot_basic.snap
│ │ │ ├── codex_tui__pager_overlay__tests__transcript_overlay_snapshot_basic.snap
│ │ │ ├── codex_tui__status_indicator_widget__tests__renders_truncated.snap
│ │ │ ├── codex_tui__status_indicator_widget__tests__renders_with_queued_messages.snap
│ │ │ └── codex_tui__status_indicator_widget__tests__renders_with_working_header.snap
│ │ ├── status_indicator_widget.rs
│ │ ├── streaming/
│ │ │ ├── controller.rs
│ │ │ └── mod.rs
│ │ ├── text_formatting.rs
│ │ ├── tui.rs
│ │ ├── updates.rs
│ │ └── user_approval_widget.rs
│ ├── styles.md
│ └── tests/
│ ├── all.rs
│ ├── fixtures/
│ │ ├── binary-size-log.jsonl
│ │ ├── ideal-binary-response.txt
│ │ └── oss-story.jsonl
│ └── suite/
│ ├── mod.rs
│ ├── status_indicator.rs
│ ├── vt100_history.rs
│ ├── vt100_live_commit.rs
│ └── vt100_streaming_no_dup.rs
├── configs/
│ ├── stanford/
│ │ └── example.yaml
│ └── tests/
│ ├── ctf_easy.yaml
│ └── simple.yaml
├── docs/
│ ├── SYSTEM_ARCHITECTURES.md
│ ├── TRIAGE_ARCHITECTURE.md
│ ├── license.md
│ └── supervisor-usage.md
├── pyproject.toml
├── run_docker.sh
├── supervisor/
│ ├── __init__.py
│ ├── config.py
│ ├── context_manager.py
│ ├── orchestration/
│ │ ├── __init__.py
│ │ ├── instance_manager.py
│ │ ├── log_reader.py
│ │ ├── orchestrator.py
│ │ ├── prompt_generator.py
│ │ └── router.py
│ ├── prompts/
│ │ ├── __init__.py
│ │ ├── continuation_context_prompt.py
│ │ ├── router_prompt.py
│ │ ├── summarization_prompt.py
│ │ └── supervisor_prompt.py
│ ├── submissions/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── ctf.py
│ │ ├── registry.py
│ │ └── vulnerability.py
│ ├── supervisor.py
│ ├── todo_generator.py
│ ├── tools.py
│ ├── triage/
│ │ ├── prompts/
│ │ │ ├── __init__.py
│ │ │ ├── initial_review_prompt.py
│ │ │ ├── severity_prompt.py
│ │ │ ├── system_prompt.py
│ │ │ └── validation_prompt.py
│ │ ├── triage_manager.py
│ │ └── triage_tools.py
│ ├── vulnerability_storage.py
│ └── working_hours.py
└── test_files/
└── it_has_begun/
└── script.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .env.example
================================================
# OpenRouter API Configuration
OPENROUTER_API_KEY=your-openrouter-api-key-here
# Optional: Override default models
# SUPERVISOR_MODEL=openai/o1-preview
# SUMMARIZATION_MODEL=openai/o4-mini
# ROUTER_MODEL=openai/o4-mini
# Optional: Override TODO generator models
# TODO_GENERATOR_OPENROUTER_MODEL=anthropic/claude-opus-4.1
# TODO_GENERATOR_OPENAI_MODEL=gpt-5
# Optional: Override prompt generator model for custom system prompts
# PROMPT_GENERATOR_MODEL=anthropic/claude-opus-4.1
# Optional: Override available models for model switching (comma-separated)
# OPENROUTER_AVAILABLE_MODELS=anthropic/claude-sonnet-4,openai/o3,anthropic/claude-opus-4,google/gemini-2.5-pro,openai/o3-pro
# OPENAI_AVAILABLE_MODELS=o3,gpt-5
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
strategy:
matrix:
include:
- os: ubuntu-latest
arch: amd64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install uv
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libssl-dev
- name: Build codex-rs binary (${{ matrix.arch }})
run: |
cargo build --release --manifest-path codex-rs/Cargo.toml
ls -lh codex-rs/target/release/codex
- name: Verify binary is executable
run: |
file codex-rs/target/release/codex
codex-rs/target/release/codex --version || echo "Binary built successfully"
- name: Setup Python environment
run: |
uv sync
- name: Check Python package formatting
run: |
source .venv/bin/activate
python -m py_compile supervisor/*.py || true
- name: Test supervisor imports
run: |
source .venv/bin/activate
python -c "import supervisor.supervisor; print('✓ supervisor.supervisor imports successfully')"
python -c "from supervisor import supervisor; print('✓ supervisor module imports successfully')"
- name: Summary
run: |
echo "====== Build Summary ======"
echo "Architecture: ${{ matrix.arch }}"
echo "Codex binary: ✓ Built"
echo "Python environment: ✓ Set up"
echo "Supervisor imports: ✓ Working"
echo "=========================="
================================================
FILE: .gitignore
================================================
# deps
# Node.js dependencies
node_modules
.pnpm-store
.pnpm-debug.log
# Keep pnpm-lock.yaml
!pnpm-lock.yaml
# build
dist/
build/
out/
storybook-static/
# ignore README for publishing
codex-cli/README.md
# ignore Nix derivation results
result
# editor
.vscode/
.idea/
.history/
.zed/
*.swp
*~
# cli tools
CLAUDE.md
.claude/
# caches
.cache/
.turbo/
.parcel-cache/
.eslintcache
.nyc_output/
.jest/
*.tsbuildinfo
# logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# env
.env*
!.env.example
# package
*.tgz
# ci
.vercel/
.netlify/
# patches
apply_patch/
# coverage
coverage/
# os
.DS_Store
Thumbs.db
Icon?
.Spotlight-V100/
# Unwanted package managers
.yarn/
yarn.lock
# release
package.json-e
session.ts-e
CHANGELOG.ignore.md
# nix related
.direnv
.envrc
# Python stuff
*.pyc
*.pyc
__pycache__/
*.pyo
*.pyd
================================================
FILE: Dockerfile
================================================
FROM ghcr.io/astral-sh/uv:python3.11-bookworm
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
curl \
libssl-dev \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \
~/.cargo/bin/rustup install stable && \
~/.cargo/bin/rustup default stable
ENV PATH="/root/.cargo/bin:${PATH}"
WORKDIR /app
COPY . /app/trinity/ARTEMIS
WORKDIR /app/trinity/ARTEMIS
RUN cargo build --release --manifest-path codex-rs/Cargo.toml
RUN uv sync
ENV VIRTUAL_ENV=/app/trinity/ARTEMIS/.venv
ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2025 OpenAI
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: NOTICE
================================================
OpenAI Codex
Copyright 2025 OpenAI
This project includes code derived from [Ratatui](https://github.com/ratatui/ratatui), licensed under the MIT license.
Copyright (c) 2016-2022 Florian Dehau
Copyright (c) 2023-2025 The Ratatui Developers
================================================
FILE: README.md
================================================
<h1 align="center">🏹 ARTEMIS</h1>
<p align="center"><strong>A</strong>utomated <strong>R</strong>ed <strong>T</strong>eaming <strong>E</strong>ngine with <strong>M</strong>ulti-agent <strong>I</strong>ntelligent <strong>S</strong>upervision</p>
<p align="center">ARTEMIS is an autonomous agent created by the <a href="https://trinity.cs.stanford.edu/">Stanford Trinity project</a> to automate vulnerability discovery.</p>
#### Quickstart
Install `uv` if you haven't already:
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
```
Install the latest version of Rust (required for building):
```bash
# Remove old Rust if installed via apt
sudo apt remove rustc cargo
sudo apt install libssl-dev
# Install rustup (the official Rust toolchain installer)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Restart shell or source the environment
source ~/.cargo/env
# Install latest stable Rust
rustup install stable
rustup default stable
```
First, we have to build the codex binary:
```bash
cargo build --release --manifest-path codex-rs/Cargo.toml
```
Now we can setup the Python environment:
```bash
uv sync
source .venv/bin/activate
```
### Environment Configuration
Copy the example configuration and add your API keys:
```bash
cp .env.example .env
# Edit .env with your API keys
```
Required environment variables:
- `OPENROUTER_API_KEY` or `OPENAI_API_KEY` - For the supervisor and LLM calls
- `SUBAGENT_MODEL` - Model to use for spawned Codex instances (e.g., `anthropic/claude-sonnet-4`)
### Quick Test Run
Try a simple CTF challenge to verify everything works:
```bash
python -m supervisor.supervisor \
--config-file configs/tests/ctf_easy.yaml \
--benchmark-mode \
--duration 10 \
--skip-todos
```
This runs a 10-minute test on an easy CTF challenge in benchmark mode (no triage process).
For detailed configuration options and usage, see [supervisor-usage.md](docs/supervisor-usage.md).
---
## Docker
### Docker Quickstart
Build the Docker image:
```bash
docker build -t artemis .
```
### Environment Configuration
Same as above - copy the example configuration and add your API keys:
```bash
cp .env.example .env
# Edit .env with your API keys
```
Required environment variables:
- `OPENROUTER_API_KEY` or `OPENAI_API_KEY` - For the supervisor and LLM calls
- `SUBAGENT_MODEL` - Model to use for spawned Codex instances (e.g., `anthropic/claude-sonnet-4`)
### Codex Configuration for OpenRouter
If using OpenRouter, you'll need to configure the codex binary. Create `~/.codex/config.toml`:
```bash
mkdir -p ~/.codex
cat > ~/.codex/config.toml <<'EOF'
model_provider = "openrouter"
[model_providers.openrouter]
name = "OpenRouter"
base_url = "https://openrouter.ai/api/v1"
env_key = "OPENROUTER_API_KEY"
[sandbox]
mode = "workspace-write"
network_access = true
EOF
```
### Running with Docker
Use the provided `run_docker.sh` script:
```bash
# Run with OpenRouter (mounts ~/.codex/config.toml)
./run_docker.sh openrouter
# Run with OpenAI only (no config mount needed)
./run_docker.sh openai
```
The script will:
- Mount your `~/.codex/config.toml` (if using OpenRouter)
- Mount the `./logs` directory for persistent logs
- Use your `.env` file for API keys
- Run a 10-minute test on an easy CTF challenge
**Manual Docker Run:**
If you prefer to run docker manually:
```bash
# With OpenRouter
docker run -it \
--env-file .env \
-v $HOME/.codex/config.toml:/root/.codex/config.toml:ro \
-v $(pwd)/logs:/app/trinity/ARTEMIS/logs \
artemis \
python -m supervisor.supervisor \
--config-file configs/tests/ctf_easy.yaml \
--benchmark-mode \
--duration 10 \
--skip-todos
# With OpenAI only
docker run -it \
--env-file .env \
-v $(pwd)/logs:/app/trinity/ARTEMIS/logs \
artemis \
python -m supervisor.supervisor \
--config-file configs/tests/ctf_easy.yaml \
--benchmark-mode \
--duration 10 \
--skip-todos
```
---
## Acknowledgments
This project uses [OpenAI Codex](https://github.com/openai/codex) as a base, forked from [commit c221eab](https://github.com/openai/codex/commit/c221eab0b5cad59ce3dafebf7ca630f217263cc6).
---
## License
This repository is licensed under the [Apache-2.0 License](LICENSE).
================================================
FILE: codex-rs/.gitignore
================================================
/target/
# Recommended value of CARGO_TARGET_DIR when using Docker as explained in .devcontainer/README.md.
/target-amd64/
# Value of CARGO_TARGET_DIR when using .devcontainer/devcontainer.json.
/target-arm64/
================================================
FILE: codex-rs/Cargo.toml
================================================
[workspace]
members = [
"ansi-escape",
"apply-patch",
"arg0",
"cli",
"common",
"core",
"exec",
"execpolicy",
"file-search",
"linux-sandbox",
"login",
"mcp-client",
"mcp-server",
"mcp-types",
"ollama",
"protocol",
"protocol-ts",
"tui",
]
resolver = "2"
[workspace.package]
version = "0.0.0"
# Track the edition for all workspace crates in one place. Individual
# crates can still override this value, but keeping it here means new
# crates created with `cargo new -w ...` automatically inherit the 2024
# edition.
edition = "2024"
[workspace.lints]
rust = {}
[workspace.lints.clippy]
expect_used = "deny"
unwrap_used = "deny"
[profile.release]
lto = "fat"
# Because we bundle some of these executables with the TypeScript CLI, we
# remove everything to make the binary as small as possible.
strip = "symbols"
# See https://github.com/openai/codex/issues/1411 for details.
codegen-units = 1
[patch.crates-io]
# ratatui = { path = "../../ratatui" }
ratatui = { git = "https://github.com/nornagon/ratatui", branch = "nornagon-v0.29.0-patch" }
================================================
FILE: codex-rs/README.md
================================================
# Codex CLI (Rust Implementation)
We provide Codex CLI as a standalone, native executable to ensure a zero-dependency install.
## Installing Codex
Today, the easiest way to install Codex is via `npm`, though we plan to publish Codex to other package managers soon.
```shell
npm i -g @openai/codex@native
codex
```
You can also download a platform-specific release directly from our [GitHub Releases](https://github.com/openai/codex/releases).
## What's new in the Rust CLI
While we are [working to close the gap between the TypeScript and Rust implementations of Codex CLI](https://github.com/openai/codex/issues/1262), note that the Rust CLI has a number of features that the TypeScript CLI does not!
### Config
Codex supports a rich set of configuration options. Note that the Rust CLI uses `config.toml` instead of `config.json`. See [`docs/config.md`](../docs/config.md) for details.
### Model Context Protocol Support
Codex CLI functions as an MCP client that can connect to MCP servers on startup. See the [`mcp_servers`](../docs/config.md#mcp_servers) section in the configuration documentation for details.
It is still experimental, but you can also launch Codex as an MCP _server_ by running `codex mcp`. Use the [`@modelcontextprotocol/inspector`](https://github.com/modelcontextprotocol/inspector) to try it out:
```shell
npx @modelcontextprotocol/inspector codex mcp
```
### Notifications
You can enable notifications by configuring a script that is run whenever the agent finishes a turn. The [notify documentation](../docs/config.md#notify) includes a detailed example that explains how to get desktop notifications via [terminal-notifier](https://github.com/julienXX/terminal-notifier) on macOS.
### `codex exec` to run Codex programmatially/non-interactively
To run Codex non-interactively, run `codex exec PROMPT` (you can also pass the prompt via `stdin`) and Codex will work on your task until it decides that it is done and exits. Output is printed to the terminal directly. You can set the `RUST_LOG` environment variable to see more about what's going on.
### Use `@` for file search
Typing `@` triggers a fuzzy-filename search over the workspace root. Use up/down to select among the results and Tab or Enter to replace the `@` with the selected path. You can use Esc to cancel the search.
### Esc–Esc to edit a previous message
When the chat composer is empty, press Esc to prime “backtrack” mode. Press Esc again to open a transcript preview highlighting the last user message; press Esc repeatedly to step to older user messages. Press Enter to confirm and Codex will fork the conversation from that point, trim the visible transcript accordingly, and pre‑fill the composer with the selected user message so you can edit and resubmit it.
In the transcript preview, the footer shows an `Esc edit prev` hint while editing is active.
### `--cd`/`-C` flag
Sometimes it is not convenient to `cd` to the directory you want Codex to use as the "working root" before running Codex. Fortunately, `codex` supports a `--cd` option so you can specify whatever folder you want. You can confirm that Codex is honoring `--cd` by double-checking the **workdir** it reports in the TUI at the start of a new session.
### Shell completions
Generate shell completion scripts via:
```shell
codex completion bash
codex completion zsh
codex completion fish
```
### Experimenting with the Codex Sandbox
To test to see what happens when a command is run under the sandbox provided by Codex, we provide the following subcommands in Codex CLI:
```
# macOS
codex debug seatbelt [--full-auto] [COMMAND]...
# Linux
codex debug landlock [--full-auto] [COMMAND]...
```
### Selecting a sandbox policy via `--sandbox`
The Rust CLI exposes a dedicated `--sandbox` (`-s`) flag that lets you pick the sandbox policy **without** having to reach for the generic `-c/--config` option:
```shell
# Run Codex with the default, read-only sandbox
codex --sandbox read-only
# Allow the agent to write within the current workspace while still blocking network access
codex --sandbox workspace-write
# Danger! Disable sandboxing entirely (only do this if you are already running in a container or other isolated env)
codex --sandbox danger-full-access
```
The same setting can be persisted in `~/.codex/config.toml` via the top-level `sandbox_mode = "MODE"` key, e.g. `sandbox_mode = "workspace-write"`.
## Code Organization
This folder is the root of a Cargo workspace. It contains quite a bit of experimental code, but here are the key crates:
- [`core/`](./core) contains the business logic for Codex. Ultimately, we hope this to be a library crate that is generally useful for building other Rust/native applications that use Codex.
- [`exec/`](./exec) "headless" CLI for use in automation.
- [`tui/`](./tui) CLI that launches a fullscreen TUI built with [Ratatui](https://ratatui.rs/).
- [`cli/`](./cli) CLI multitool that provides the aforementioned CLIs via subcommands.
# Open Router Support
Add the following to your `~/.codex/config.toml` to enable Open Router support:
```toml
[model_providers.openrouter]
name = "OpenRouter"
base_url = "[https://api.openrouter.ai/v1](https://openrouter.ai/api/v1)"
api_key = "OPENROUTER_API_KEY"
[profiles.openrouter]
name = "OpenRouter"
[profiles.openrouter.sandbox]
network_access = true
```
Call `cargo run --bin codex --release -- --profile OpenRouter --model <model name>` to run Codex with Open Router support and model of your choice. See available models at [Open Router](https://openrouter.ai/models).
================================================
FILE: codex-rs/ansi-escape/Cargo.toml
================================================
[package]
edition = "2024"
name = "codex-ansi-escape"
version = { workspace = true }
[lib]
name = "codex_ansi_escape"
path = "src/lib.rs"
[dependencies]
ansi-to-tui = "7.0.0"
ratatui = { version = "0.29.0", features = [
"unstable-rendered-line-info",
"unstable-widget-ref",
] }
tracing = { version = "0.1.41", features = ["log"] }
================================================
FILE: codex-rs/ansi-escape/README.md
================================================
# oai-codex-ansi-escape
Small helper functions that wrap functionality from
<https://crates.io/crates/ansi-to-tui>:
```rust
pub fn ansi_escape_line(s: &str) -> Line<'static>
pub fn ansi_escape<'a>(s: &'a str) -> Text<'a>
```
Advantages:
- `ansi_to_tui::IntoText` is not in scope for the entire TUI crate
- we `panic!()` and log if `IntoText` returns an `Err` and log it so that
the caller does not have to deal with it
================================================
FILE: codex-rs/ansi-escape/src/lib.rs
================================================
use ansi_to_tui::Error;
use ansi_to_tui::IntoText;
use ratatui::text::Line;
use ratatui::text::Text;
/// This function should be used when the contents of `s` are expected to match
/// a single line. If multiple lines are found, a warning is logged and only the
/// first line is returned.
pub fn ansi_escape_line(s: &str) -> Line<'static> {
let text = ansi_escape(s);
match text.lines.as_slice() {
[] => Line::from(""),
[only] => only.clone(),
[first, rest @ ..] => {
tracing::warn!("ansi_escape_line: expected a single line, got {first:?} and {rest:?}");
first.clone()
}
}
}
pub fn ansi_escape(s: &str) -> Text<'static> {
// to_text() claims to be faster, but introduces complex lifetime issues
// such that it's not worth it.
match s.into_text() {
Ok(text) => text,
Err(err) => match err {
Error::NomError(message) => {
tracing::error!(
"ansi_to_tui NomError docs claim should never happen when parsing `{s}`: {message}"
);
panic!();
}
Error::Utf8Error(utf8error) => {
tracing::error!("Utf8Error: {utf8error}");
panic!();
}
},
}
}
================================================
FILE: codex-rs/apply-patch/Cargo.toml
================================================
[package]
edition = "2024"
name = "codex-apply-patch"
version = { workspace = true }
[lib]
name = "codex_apply_patch"
path = "src/lib.rs"
[[bin]]
name = "apply_patch"
path = "src/main.rs"
[lints]
workspace = true
[dependencies]
anyhow = "1"
similar = "2.7.0"
thiserror = "2.0.12"
tree-sitter = "0.25.8"
tree-sitter-bash = "0.25.0"
[dev-dependencies]
assert_cmd = "2"
pretty_assertions = "1.4.1"
tempfile = "3.13.0"
================================================
FILE: codex-rs/apply-patch/apply_patch_tool_instructions.md
================================================
## `apply_patch`
Use the `apply_patch` shell command to edit files.
Your patch language is a stripped‑down, file‑oriented diff format designed to be easy to parse and safe to apply. You can think of it as a high‑level envelope:
*** Begin Patch
[ one or more file sections ]
*** End Patch
Within that envelope, you get a sequence of file operations.
You MUST include a header to specify the action you are taking.
Each operation starts with one of three headers:
*** Add File: <path> - create a new file. Every following line is a + line (the initial contents).
*** Delete File: <path> - remove an existing file. Nothing follows.
*** Update File: <path> - patch an existing file in place (optionally with a rename).
May be immediately followed by *** Move to: <new path> if you want to rename the file.
Then one or more “hunks”, each introduced by @@ (optionally followed by a hunk header).
Within a hunk each line starts with:
For instructions on [context_before] and [context_after]:
- By default, show 3 lines of code immediately above and 3 lines immediately below each change. If a change is within 3 lines of a previous change, do NOT duplicate the first change’s [context_after] lines in the second change’s [context_before] lines.
- If 3 lines of context is insufficient to uniquely identify the snippet of code within the file, use the @@ operator to indicate the class or function to which the snippet belongs. For instance, we might have:
@@ class BaseClass
[3 lines of pre-context]
- [old_code]
+ [new_code]
[3 lines of post-context]
- If a code block is repeated so many times in a class or function such that even a single `@@` statement and 3 lines of context cannot uniquely identify the snippet of code, you can use multiple `@@` statements to jump to the right context. For instance:
@@ class BaseClass
@@ def method():
[3 lines of pre-context]
- [old_code]
+ [new_code]
[3 lines of post-context]
The full grammar definition is below:
Patch := Begin { FileOp } End
Begin := "*** Begin Patch" NEWLINE
End := "*** End Patch" NEWLINE
FileOp := AddFile | DeleteFile | UpdateFile
AddFile := "*** Add File: " path NEWLINE { "+" line NEWLINE }
DeleteFile := "*** Delete File: " path NEWLINE
UpdateFile := "*** Update File: " path NEWLINE [ MoveTo ] { Hunk }
MoveTo := "*** Move to: " newPath NEWLINE
Hunk := "@@" [ header ] NEWLINE { HunkLine } [ "*** End of File" NEWLINE ]
HunkLine := (" " | "-" | "+") text NEWLINE
A full patch can combine several operations:
*** Begin Patch
*** Add File: hello.txt
+Hello world
*** Update File: src/app.py
*** Move to: src/main.py
@@ def greet():
-print("Hi")
+print("Hello, world!")
*** Delete File: obsolete.txt
*** End Patch
It is important to remember:
- You must include a header with your intended action (Add/Delete/Update)
- You must prefix new lines with `+` even when creating a new file
- File references can only be relative, NEVER ABSOLUTE.
You can invoke apply_patch like:
```
shell {"command":["apply_patch","*** Begin Patch\n*** Add File: hello.txt\n+Hello, world!\n*** End Patch\n"]}
```
================================================
FILE: codex-rs/apply-patch/src/lib.rs
================================================
mod parser;
mod seek_sequence;
mod standalone_executable;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use std::str::Utf8Error;
use anyhow::Context;
use anyhow::Result;
pub use parser::Hunk;
pub use parser::ParseError;
use parser::ParseError::*;
use parser::UpdateFileChunk;
pub use parser::parse_patch;
use similar::TextDiff;
use thiserror::Error;
use tree_sitter::LanguageError;
use tree_sitter::Parser;
use tree_sitter_bash::LANGUAGE as BASH;
pub use standalone_executable::main;
/// Detailed instructions for gpt-4.1 on how to use the `apply_patch` tool.
pub const APPLY_PATCH_TOOL_INSTRUCTIONS: &str = include_str!("../apply_patch_tool_instructions.md");
const APPLY_PATCH_COMMANDS: [&str; 2] = ["apply_patch", "applypatch"];
#[derive(Debug, Error, PartialEq)]
pub enum ApplyPatchError {
#[error(transparent)]
ParseError(#[from] ParseError),
#[error(transparent)]
IoError(#[from] IoError),
/// Error that occurs while computing replacements when applying patch chunks
#[error("{0}")]
ComputeReplacements(String),
}
impl From<std::io::Error> for ApplyPatchError {
fn from(err: std::io::Error) -> Self {
ApplyPatchError::IoError(IoError {
context: "I/O error".to_string(),
source: err,
})
}
}
impl From<&std::io::Error> for ApplyPatchError {
fn from(err: &std::io::Error) -> Self {
ApplyPatchError::IoError(IoError {
context: "I/O error".to_string(),
source: std::io::Error::new(err.kind(), err.to_string()),
})
}
}
#[derive(Debug, Error)]
#[error("{context}: {source}")]
pub struct IoError {
context: String,
#[source]
source: std::io::Error,
}
impl PartialEq for IoError {
fn eq(&self, other: &Self) -> bool {
self.context == other.context && self.source.to_string() == other.source.to_string()
}
}
#[derive(Debug, PartialEq)]
pub enum MaybeApplyPatch {
Body(ApplyPatchArgs),
ShellParseError(ExtractHeredocError),
PatchParseError(ParseError),
NotApplyPatch,
}
/// Both the raw PATCH argument to `apply_patch` as well as the PATCH argument
/// parsed into hunks.
#[derive(Debug, PartialEq)]
pub struct ApplyPatchArgs {
pub patch: String,
pub hunks: Vec<Hunk>,
}
pub fn maybe_parse_apply_patch(argv: &[String]) -> MaybeApplyPatch {
match argv {
[cmd, body] if APPLY_PATCH_COMMANDS.contains(&cmd.as_str()) => match parse_patch(body) {
Ok(source) => MaybeApplyPatch::Body(source),
Err(e) => MaybeApplyPatch::PatchParseError(e),
},
[bash, flag, script]
if bash == "bash"
&& flag == "-lc"
&& APPLY_PATCH_COMMANDS
.iter()
.any(|cmd| script.trim_start().starts_with(cmd)) =>
{
match extract_heredoc_body_from_apply_patch_command(script) {
Ok(body) => match parse_patch(&body) {
Ok(source) => MaybeApplyPatch::Body(source),
Err(e) => MaybeApplyPatch::PatchParseError(e),
},
Err(e) => MaybeApplyPatch::ShellParseError(e),
}
}
_ => MaybeApplyPatch::NotApplyPatch,
}
}
#[derive(Debug, PartialEq)]
pub enum ApplyPatchFileChange {
Add {
content: String,
},
Delete,
Update {
unified_diff: String,
move_path: Option<PathBuf>,
/// new_content that will result after the unified_diff is applied.
new_content: String,
},
}
#[derive(Debug, PartialEq)]
pub enum MaybeApplyPatchVerified {
/// `argv` corresponded to an `apply_patch` invocation, and these are the
/// resulting proposed file changes.
Body(ApplyPatchAction),
/// `argv` could not be parsed to determine whether it corresponds to an
/// `apply_patch` invocation.
ShellParseError(ExtractHeredocError),
/// `argv` corresponded to an `apply_patch` invocation, but it could not
/// be fulfilled due to the specified error.
CorrectnessError(ApplyPatchError),
/// `argv` decidedly did not correspond to an `apply_patch` invocation.
NotApplyPatch,
}
/// ApplyPatchAction is the result of parsing an `apply_patch` command. By
/// construction, all paths should be absolute paths.
#[derive(Debug, PartialEq)]
pub struct ApplyPatchAction {
changes: HashMap<PathBuf, ApplyPatchFileChange>,
/// The raw patch argument that can be used with `apply_patch` as an exec
/// call. i.e., if the original arg was parsed in "lenient" mode with a
/// heredoc, this should be the value without the heredoc wrapper.
pub patch: String,
/// The working directory that was used to resolve relative paths in the patch.
pub cwd: PathBuf,
}
impl ApplyPatchAction {
pub fn is_empty(&self) -> bool {
self.changes.is_empty()
}
/// Returns the changes that would be made by applying the patch.
pub fn changes(&self) -> &HashMap<PathBuf, ApplyPatchFileChange> {
&self.changes
}
/// Should be used exclusively for testing. (Not worth the overhead of
/// creating a feature flag for this.)
pub fn new_add_for_test(path: &Path, content: String) -> Self {
if !path.is_absolute() {
panic!("path must be absolute");
}
#[expect(clippy::expect_used)]
let filename = path
.file_name()
.expect("path should not be empty")
.to_string_lossy();
let patch = format!(
r#"*** Begin Patch
*** Update File: {filename}
@@
+ {content}
*** End Patch"#,
);
let changes = HashMap::from([(path.to_path_buf(), ApplyPatchFileChange::Add { content })]);
#[expect(clippy::expect_used)]
Self {
changes,
cwd: path
.parent()
.expect("path should have parent")
.to_path_buf(),
patch,
}
}
}
/// cwd must be an absolute path so that we can resolve relative paths in the
/// patch.
pub fn maybe_parse_apply_patch_verified(argv: &[String], cwd: &Path) -> MaybeApplyPatchVerified {
match maybe_parse_apply_patch(argv) {
MaybeApplyPatch::Body(ApplyPatchArgs { patch, hunks }) => {
let mut changes = HashMap::new();
for hunk in hunks {
let path = hunk.resolve_path(cwd);
match hunk {
Hunk::AddFile { contents, .. } => {
changes.insert(path, ApplyPatchFileChange::Add { content: contents });
}
Hunk::DeleteFile { .. } => {
changes.insert(path, ApplyPatchFileChange::Delete);
}
Hunk::UpdateFile {
move_path, chunks, ..
} => {
let ApplyPatchFileUpdate {
unified_diff,
content: contents,
} = match unified_diff_from_chunks(&path, &chunks) {
Ok(diff) => diff,
Err(e) => {
return MaybeApplyPatchVerified::CorrectnessError(e);
}
};
changes.insert(
path,
ApplyPatchFileChange::Update {
unified_diff,
move_path: move_path.map(|p| cwd.join(p)),
new_content: contents,
},
);
}
}
}
MaybeApplyPatchVerified::Body(ApplyPatchAction {
changes,
patch,
cwd: cwd.to_path_buf(),
})
}
MaybeApplyPatch::ShellParseError(e) => MaybeApplyPatchVerified::ShellParseError(e),
MaybeApplyPatch::PatchParseError(e) => MaybeApplyPatchVerified::CorrectnessError(e.into()),
MaybeApplyPatch::NotApplyPatch => MaybeApplyPatchVerified::NotApplyPatch,
}
}
/// Attempts to extract a heredoc_body object from a string bash command like:
/// Optimistically
///
/// ```bash
/// bash -lc 'apply_patch <<EOF\n***Begin Patch\n...EOF'
/// ```
///
/// # Arguments
///
/// * `src` - A string slice that holds the full command
///
/// # Returns
///
/// This function returns a `Result` which is:
///
/// * `Ok(String)` - The heredoc body if the extraction is successful.
/// * `Err(anyhow::Error)` - An error if the extraction fails.
///
fn extract_heredoc_body_from_apply_patch_command(
src: &str,
) -> std::result::Result<String, ExtractHeredocError> {
if !APPLY_PATCH_COMMANDS
.iter()
.any(|cmd| src.trim_start().starts_with(cmd))
{
return Err(ExtractHeredocError::CommandDidNotStartWithApplyPatch);
}
let lang = BASH.into();
let mut parser = Parser::new();
parser
.set_language(&lang)
.map_err(ExtractHeredocError::FailedToLoadBashGrammar)?;
let tree = parser
.parse(src, None)
.ok_or(ExtractHeredocError::FailedToParsePatchIntoAst)?;
let bytes = src.as_bytes();
let mut c = tree.root_node().walk();
loop {
let node = c.node();
if node.kind() == "heredoc_body" {
let text = node
.utf8_text(bytes)
.map_err(ExtractHeredocError::HeredocNotUtf8)?;
return Ok(text.trim_end_matches('\n').to_owned());
}
if c.goto_first_child() {
continue;
}
while !c.goto_next_sibling() {
if !c.goto_parent() {
return Err(ExtractHeredocError::FailedToFindHeredocBody);
}
}
}
}
#[derive(Debug, PartialEq)]
pub enum ExtractHeredocError {
CommandDidNotStartWithApplyPatch,
FailedToLoadBashGrammar(LanguageError),
HeredocNotUtf8(Utf8Error),
FailedToParsePatchIntoAst,
FailedToFindHeredocBody,
}
/// Applies the patch and prints the result to stdout/stderr.
pub fn apply_patch(
patch: &str,
stdout: &mut impl std::io::Write,
stderr: &mut impl std::io::Write,
) -> Result<(), ApplyPatchError> {
let hunks = match parse_patch(patch) {
Ok(source) => source.hunks,
Err(e) => {
match &e {
InvalidPatchError(message) => {
writeln!(stderr, "Invalid patch: {message}").map_err(ApplyPatchError::from)?;
}
InvalidHunkError {
message,
line_number,
} => {
writeln!(
stderr,
"Invalid patch hunk on line {line_number}: {message}"
)
.map_err(ApplyPatchError::from)?;
}
}
return Err(ApplyPatchError::ParseError(e));
}
};
apply_hunks(&hunks, stdout, stderr)?;
Ok(())
}
/// Applies hunks and continues to update stdout/stderr
pub fn apply_hunks(
hunks: &[Hunk],
stdout: &mut impl std::io::Write,
stderr: &mut impl std::io::Write,
) -> Result<(), ApplyPatchError> {
let _existing_paths: Vec<&Path> = hunks
.iter()
.filter_map(|hunk| match hunk {
Hunk::AddFile { .. } => {
// The file is being added, so it doesn't exist yet.
None
}
Hunk::DeleteFile { path } => Some(path.as_path()),
Hunk::UpdateFile {
path, move_path, ..
} => match move_path {
Some(move_path) => {
if std::fs::metadata(move_path)
.map(|m| m.is_file())
.unwrap_or(false)
{
Some(move_path.as_path())
} else {
None
}
}
None => Some(path.as_path()),
},
})
.collect::<Vec<&Path>>();
// Delegate to a helper that applies each hunk to the filesystem.
match apply_hunks_to_files(hunks) {
Ok(affected) => {
print_summary(&affected, stdout).map_err(ApplyPatchError::from)?;
Ok(())
}
Err(err) => {
let msg = err.to_string();
writeln!(stderr, "{msg}").map_err(ApplyPatchError::from)?;
if let Some(io) = err.downcast_ref::<std::io::Error>() {
Err(ApplyPatchError::from(io))
} else {
Err(ApplyPatchError::IoError(IoError {
context: msg,
source: std::io::Error::other(err),
}))
}
}
}
}
/// Applies each parsed patch hunk to the filesystem.
/// Returns an error if any of the changes could not be applied.
/// Tracks file paths affected by applying a patch.
pub struct AffectedPaths {
pub added: Vec<PathBuf>,
pub modified: Vec<PathBuf>,
pub deleted: Vec<PathBuf>,
}
/// Apply the hunks to the filesystem, returning which files were added, modified, or deleted.
/// Returns an error if the patch could not be applied.
fn apply_hunks_to_files(hunks: &[Hunk]) -> anyhow::Result<AffectedPaths> {
if hunks.is_empty() {
anyhow::bail!("No files were modified.");
}
let mut added: Vec<PathBuf> = Vec::new();
let mut modified: Vec<PathBuf> = Vec::new();
let mut deleted: Vec<PathBuf> = Vec::new();
for hunk in hunks {
match hunk {
Hunk::AddFile { path, contents } => {
if let Some(parent) = path.parent()
&& !parent.as_os_str().is_empty()
{
std::fs::create_dir_all(parent).with_context(|| {
format!("Failed to create parent directories for {}", path.display())
})?;
}
std::fs::write(path, contents)
.with_context(|| format!("Failed to write file {}", path.display()))?;
added.push(path.clone());
}
Hunk::DeleteFile { path } => {
std::fs::remove_file(path)
.with_context(|| format!("Failed to delete file {}", path.display()))?;
deleted.push(path.clone());
}
Hunk::UpdateFile {
path,
move_path,
chunks,
} => {
let AppliedPatch { new_contents, .. } =
derive_new_contents_from_chunks(path, chunks)?;
if let Some(dest) = move_path {
if let Some(parent) = dest.parent()
&& !parent.as_os_str().is_empty()
{
std::fs::create_dir_all(parent).with_context(|| {
format!("Failed to create parent directories for {}", dest.display())
})?;
}
std::fs::write(dest, new_contents)
.with_context(|| format!("Failed to write file {}", dest.display()))?;
std::fs::remove_file(path)
.with_context(|| format!("Failed to remove original {}", path.display()))?;
modified.push(dest.clone());
} else {
std::fs::write(path, new_contents)
.with_context(|| format!("Failed to write file {}", path.display()))?;
modified.push(path.clone());
}
}
}
}
Ok(AffectedPaths {
added,
modified,
deleted,
})
}
struct AppliedPatch {
original_contents: String,
new_contents: String,
}
/// Return *only* the new file contents (joined into a single `String`) after
/// applying the chunks to the file at `path`.
fn derive_new_contents_from_chunks(
path: &Path,
chunks: &[UpdateFileChunk],
) -> std::result::Result<AppliedPatch, ApplyPatchError> {
let original_contents = match std::fs::read_to_string(path) {
Ok(contents) => contents,
Err(err) => {
return Err(ApplyPatchError::IoError(IoError {
context: format!("Failed to read file to update {}", path.display()),
source: err,
}));
}
};
let mut original_lines: Vec<String> = original_contents
.split('\n')
.map(|s| s.to_string())
.collect();
// Drop the trailing empty element that results from the final newline so
// that line counts match the behaviour of standard `diff`.
if original_lines.last().is_some_and(|s| s.is_empty()) {
original_lines.pop();
}
let replacements = compute_replacements(&original_lines, path, chunks)?;
let new_lines = apply_replacements(original_lines, &replacements);
let mut new_lines = new_lines;
if !new_lines.last().is_some_and(|s| s.is_empty()) {
new_lines.push(String::new());
}
let new_contents = new_lines.join("\n");
Ok(AppliedPatch {
original_contents,
new_contents,
})
}
/// Compute a list of replacements needed to transform `original_lines` into the
/// new lines, given the patch `chunks`. Each replacement is returned as
/// `(start_index, old_len, new_lines)`.
fn compute_replacements(
original_lines: &[String],
path: &Path,
chunks: &[UpdateFileChunk],
) -> std::result::Result<Vec<(usize, usize, Vec<String>)>, ApplyPatchError> {
let mut replacements: Vec<(usize, usize, Vec<String>)> = Vec::new();
let mut line_index: usize = 0;
for chunk in chunks {
// If a chunk has a `change_context`, we use seek_sequence to find it, then
// adjust our `line_index` to continue from there.
if let Some(ctx_line) = &chunk.change_context {
if let Some(idx) = seek_sequence::seek_sequence(
original_lines,
std::slice::from_ref(ctx_line),
line_index,
false,
) {
line_index = idx + 1;
} else {
return Err(ApplyPatchError::ComputeReplacements(format!(
"Failed to find context '{}' in {}",
ctx_line,
path.display()
)));
}
}
if chunk.old_lines.is_empty() {
// Pure addition (no old lines). We'll add them at the end or just
// before the final empty line if one exists.
let insertion_idx = if original_lines.last().is_some_and(|s| s.is_empty()) {
original_lines.len() - 1
} else {
original_lines.len()
};
replacements.push((insertion_idx, 0, chunk.new_lines.clone()));
continue;
}
// Otherwise, try to match the existing lines in the file with the old lines
// from the chunk. If found, schedule that region for replacement.
// Attempt to locate the `old_lines` verbatim within the file. In many
// real‑world diffs the last element of `old_lines` is an *empty* string
// representing the terminating newline of the region being replaced.
// This sentinel is not present in `original_lines` because we strip the
// trailing empty slice emitted by `split('\n')`. If a direct search
// fails and the pattern ends with an empty string, retry without that
// final element so that modifications touching the end‑of‑file can be
// located reliably.
let mut pattern: &[String] = &chunk.old_lines;
let mut found =
seek_sequence::seek_sequence(original_lines, pattern, line_index, chunk.is_end_of_file);
let mut new_slice: &[String] = &chunk.new_lines;
if found.is_none() && pattern.last().is_some_and(|s| s.is_empty()) {
// Retry without the trailing empty line which represents the final
// newline in the file.
pattern = &pattern[..pattern.len() - 1];
if new_slice.last().is_some_and(|s| s.is_empty()) {
new_slice = &new_slice[..new_slice.len() - 1];
}
found = seek_sequence::seek_sequence(
original_lines,
pattern,
line_index,
chunk.is_end_of_file,
);
}
if let Some(start_idx) = found {
replacements.push((start_idx, pattern.len(), new_slice.to_vec()));
line_index = start_idx + pattern.len();
} else {
return Err(ApplyPatchError::ComputeReplacements(format!(
"Failed to find expected lines {:?} in {}",
chunk.old_lines,
path.display()
)));
}
}
Ok(replacements)
}
/// Apply the `(start_index, old_len, new_lines)` replacements to `original_lines`,
/// returning the modified file contents as a vector of lines.
fn apply_replacements(
mut lines: Vec<String>,
replacements: &[(usize, usize, Vec<String>)],
) -> Vec<String> {
// We must apply replacements in descending order so that earlier replacements
// don't shift the positions of later ones.
for (start_idx, old_len, new_segment) in replacements.iter().rev() {
let start_idx = *start_idx;
let old_len = *old_len;
// Remove old lines.
for _ in 0..old_len {
if start_idx < lines.len() {
lines.remove(start_idx);
}
}
// Insert new lines.
for (offset, new_line) in new_segment.iter().enumerate() {
lines.insert(start_idx + offset, new_line.clone());
}
}
lines
}
/// Intended result of a file update for apply_patch.
#[derive(Debug, Eq, PartialEq)]
pub struct ApplyPatchFileUpdate {
unified_diff: String,
content: String,
}
pub fn unified_diff_from_chunks(
path: &Path,
chunks: &[UpdateFileChunk],
) -> std::result::Result<ApplyPatchFileUpdate, ApplyPatchError> {
unified_diff_from_chunks_with_context(path, chunks, 1)
}
pub fn unified_diff_from_chunks_with_context(
path: &Path,
chunks: &[UpdateFileChunk],
context: usize,
) -> std::result::Result<ApplyPatchFileUpdate, ApplyPatchError> {
let AppliedPatch {
original_contents,
new_contents,
} = derive_new_contents_from_chunks(path, chunks)?;
let text_diff = TextDiff::from_lines(&original_contents, &new_contents);
let unified_diff = text_diff.unified_diff().context_radius(context).to_string();
Ok(ApplyPatchFileUpdate {
unified_diff,
content: new_contents,
})
}
/// Print the summary of changes in git-style format.
/// Write a summary of changes to the given writer.
pub fn print_summary(
affected: &AffectedPaths,
out: &mut impl std::io::Write,
) -> std::io::Result<()> {
writeln!(out, "Success. Updated the following files:")?;
for path in &affected.added {
writeln!(out, "A {}", path.display())?;
}
for path in &affected.modified {
writeln!(out, "M {}", path.display())?;
}
for path in &affected.deleted {
writeln!(out, "D {}", path.display())?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use std::fs;
use tempfile::tempdir;
/// Helper to construct a patch with the given body.
fn wrap_patch(body: &str) -> String {
format!("*** Begin Patch\n{body}\n*** End Patch")
}
fn strs_to_strings(strs: &[&str]) -> Vec<String> {
strs.iter().map(|s| s.to_string()).collect()
}
#[test]
fn test_literal() {
let args = strs_to_strings(&[
"apply_patch",
r#"*** Begin Patch
*** Add File: foo
+hi
*** End Patch
"#,
]);
match maybe_parse_apply_patch(&args) {
MaybeApplyPatch::Body(ApplyPatchArgs { hunks, patch: _ }) => {
assert_eq!(
hunks,
vec![Hunk::AddFile {
path: PathBuf::from("foo"),
contents: "hi\n".to_string()
}]
);
}
result => panic!("expected MaybeApplyPatch::Body got {result:?}"),
}
}
#[test]
fn test_literal_applypatch() {
let args = strs_to_strings(&[
"applypatch",
r#"*** Begin Patch
*** Add File: foo
+hi
*** End Patch
"#,
]);
match maybe_parse_apply_patch(&args) {
MaybeApplyPatch::Body(ApplyPatchArgs { hunks, patch: _ }) => {
assert_eq!(
hunks,
vec![Hunk::AddFile {
path: PathBuf::from("foo"),
contents: "hi\n".to_string()
}]
);
}
result => panic!("expected MaybeApplyPatch::Body got {result:?}"),
}
}
#[test]
fn test_heredoc() {
let args = strs_to_strings(&[
"bash",
"-lc",
r#"apply_patch <<'PATCH'
*** Begin Patch
*** Add File: foo
+hi
*** End Patch
PATCH"#,
]);
match maybe_parse_apply_patch(&args) {
MaybeApplyPatch::Body(ApplyPatchArgs { hunks, patch: _ }) => {
assert_eq!(
hunks,
vec![Hunk::AddFile {
path: PathBuf::from("foo"),
contents: "hi\n".to_string()
}]
);
}
result => panic!("expected MaybeApplyPatch::Body got {result:?}"),
}
}
#[test]
fn test_heredoc_applypatch() {
let args = strs_to_strings(&[
"bash",
"-lc",
r#"applypatch <<'PATCH'
*** Begin Patch
*** Add File: foo
+hi
*** End Patch
PATCH"#,
]);
match maybe_parse_apply_patch(&args) {
MaybeApplyPatch::Body(ApplyPatchArgs { hunks, patch: _ }) => {
assert_eq!(
hunks,
vec![Hunk::AddFile {
path: PathBuf::from("foo"),
contents: "hi\n".to_string()
}]
);
}
result => panic!("expected MaybeApplyPatch::Body got {result:?}"),
}
}
#[test]
fn test_add_file_hunk_creates_file_with_contents() {
let dir = tempdir().unwrap();
let path = dir.path().join("add.txt");
let patch = wrap_patch(&format!(
r#"*** Add File: {}
+ab
+cd"#,
path.display()
));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
// Verify expected stdout and stderr outputs.
let stdout_str = String::from_utf8(stdout).unwrap();
let stderr_str = String::from_utf8(stderr).unwrap();
let expected_out = format!(
"Success. Updated the following files:\nA {}\n",
path.display()
);
assert_eq!(stdout_str, expected_out);
assert_eq!(stderr_str, "");
let contents = fs::read_to_string(path).unwrap();
assert_eq!(contents, "ab\ncd\n");
}
#[test]
fn test_delete_file_hunk_removes_file() {
let dir = tempdir().unwrap();
let path = dir.path().join("del.txt");
fs::write(&path, "x").unwrap();
let patch = wrap_patch(&format!("*** Delete File: {}", path.display()));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
let stdout_str = String::from_utf8(stdout).unwrap();
let stderr_str = String::from_utf8(stderr).unwrap();
let expected_out = format!(
"Success. Updated the following files:\nD {}\n",
path.display()
);
assert_eq!(stdout_str, expected_out);
assert_eq!(stderr_str, "");
assert!(!path.exists());
}
#[test]
fn test_update_file_hunk_modifies_content() {
let dir = tempdir().unwrap();
let path = dir.path().join("update.txt");
fs::write(&path, "foo\nbar\n").unwrap();
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
foo
-bar
+baz"#,
path.display()
));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
// Validate modified file contents and expected stdout/stderr.
let stdout_str = String::from_utf8(stdout).unwrap();
let stderr_str = String::from_utf8(stderr).unwrap();
let expected_out = format!(
"Success. Updated the following files:\nM {}\n",
path.display()
);
assert_eq!(stdout_str, expected_out);
assert_eq!(stderr_str, "");
let contents = fs::read_to_string(&path).unwrap();
assert_eq!(contents, "foo\nbaz\n");
}
#[test]
fn test_update_file_hunk_can_move_file() {
let dir = tempdir().unwrap();
let src = dir.path().join("src.txt");
let dest = dir.path().join("dst.txt");
fs::write(&src, "line\n").unwrap();
let patch = wrap_patch(&format!(
r#"*** Update File: {}
*** Move to: {}
@@
-line
+line2"#,
src.display(),
dest.display()
));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
// Validate move semantics and expected stdout/stderr.
let stdout_str = String::from_utf8(stdout).unwrap();
let stderr_str = String::from_utf8(stderr).unwrap();
let expected_out = format!(
"Success. Updated the following files:\nM {}\n",
dest.display()
);
assert_eq!(stdout_str, expected_out);
assert_eq!(stderr_str, "");
assert!(!src.exists());
let contents = fs::read_to_string(&dest).unwrap();
assert_eq!(contents, "line2\n");
}
/// Verify that a single `Update File` hunk with multiple change chunks can update different
/// parts of a file and that the file is listed only once in the summary.
#[test]
fn test_multiple_update_chunks_apply_to_single_file() {
// Start with a file containing four lines.
let dir = tempdir().unwrap();
let path = dir.path().join("multi.txt");
fs::write(&path, "foo\nbar\nbaz\nqux\n").unwrap();
// Construct an update patch with two separate change chunks.
// The first chunk uses the line `foo` as context and transforms `bar` into `BAR`.
// The second chunk uses `baz` as context and transforms `qux` into `QUX`.
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
foo
-bar
+BAR
@@
baz
-qux
+QUX"#,
path.display()
));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
let stdout_str = String::from_utf8(stdout).unwrap();
let stderr_str = String::from_utf8(stderr).unwrap();
let expected_out = format!(
"Success. Updated the following files:\nM {}\n",
path.display()
);
assert_eq!(stdout_str, expected_out);
assert_eq!(stderr_str, "");
let contents = fs::read_to_string(&path).unwrap();
assert_eq!(contents, "foo\nBAR\nbaz\nQUX\n");
}
/// A more involved `Update File` hunk that exercises additions, deletions and
/// replacements in separate chunks that appear in non‑adjacent parts of the
/// file. Verifies that all edits are applied and that the summary lists the
/// file only once.
#[test]
fn test_update_file_hunk_interleaved_changes() {
let dir = tempdir().unwrap();
let path = dir.path().join("interleaved.txt");
// Original file: six numbered lines.
fs::write(&path, "a\nb\nc\nd\ne\nf\n").unwrap();
// Patch performs:
// • Replace `b` → `B`
// • Replace `e` → `E` (using surrounding context)
// • Append new line `g` at the end‑of‑file
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
a
-b
+B
@@
c
d
-e
+E
@@
f
+g
*** End of File"#,
path.display()
));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
let stdout_str = String::from_utf8(stdout).unwrap();
let stderr_str = String::from_utf8(stderr).unwrap();
let expected_out = format!(
"Success. Updated the following files:\nM {}\n",
path.display()
);
assert_eq!(stdout_str, expected_out);
assert_eq!(stderr_str, "");
let contents = fs::read_to_string(&path).unwrap();
assert_eq!(contents, "a\nB\nc\nd\nE\nf\ng\n");
}
/// Ensure that patches authored with ASCII characters can update lines that
/// contain typographic Unicode punctuation (e.g. EN DASH, NON-BREAKING
/// HYPHEN). Historically `git apply` succeeds in such scenarios but our
/// internal matcher failed requiring an exact byte-for-byte match. The
/// fuzzy-matching pass that normalises common punctuation should now bridge
/// the gap.
#[test]
fn test_update_line_with_unicode_dash() {
let dir = tempdir().unwrap();
let path = dir.path().join("unicode.py");
// Original line contains EN DASH (\u{2013}) and NON-BREAKING HYPHEN (\u{2011}).
let original = "import asyncio # local import \u{2013} avoids top\u{2011}level dep\n";
std::fs::write(&path, original).unwrap();
// Patch uses plain ASCII dash / hyphen.
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
-import asyncio # local import - avoids top-level dep
+import asyncio # HELLO"#,
path.display()
));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
// File should now contain the replaced comment.
let expected = "import asyncio # HELLO\n";
let contents = std::fs::read_to_string(&path).unwrap();
assert_eq!(contents, expected);
// Ensure success summary lists the file as modified.
let stdout_str = String::from_utf8(stdout).unwrap();
let expected_out = format!(
"Success. Updated the following files:\nM {}\n",
path.display()
);
assert_eq!(stdout_str, expected_out);
// No stderr expected.
assert_eq!(String::from_utf8(stderr).unwrap(), "");
}
#[test]
fn test_unified_diff() {
// Start with a file containing four lines.
let dir = tempdir().unwrap();
let path = dir.path().join("multi.txt");
fs::write(&path, "foo\nbar\nbaz\nqux\n").unwrap();
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
foo
-bar
+BAR
@@
baz
-qux
+QUX"#,
path.display()
));
let patch = parse_patch(&patch).unwrap();
let update_file_chunks = match patch.hunks.as_slice() {
[Hunk::UpdateFile { chunks, .. }] => chunks,
_ => panic!("Expected a single UpdateFile hunk"),
};
let diff = unified_diff_from_chunks(&path, update_file_chunks).unwrap();
let expected_diff = r#"@@ -1,4 +1,4 @@
foo
-bar
+BAR
baz
-qux
+QUX
"#;
let expected = ApplyPatchFileUpdate {
unified_diff: expected_diff.to_string(),
content: "foo\nBAR\nbaz\nQUX\n".to_string(),
};
assert_eq!(expected, diff);
}
#[test]
fn test_unified_diff_first_line_replacement() {
// Replace the very first line of the file.
let dir = tempdir().unwrap();
let path = dir.path().join("first.txt");
fs::write(&path, "foo\nbar\nbaz\n").unwrap();
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
-foo
+FOO
bar
"#,
path.display()
));
let patch = parse_patch(&patch).unwrap();
let chunks = match patch.hunks.as_slice() {
[Hunk::UpdateFile { chunks, .. }] => chunks,
_ => panic!("Expected a single UpdateFile hunk"),
};
let diff = unified_diff_from_chunks(&path, chunks).unwrap();
let expected_diff = r#"@@ -1,2 +1,2 @@
-foo
+FOO
bar
"#;
let expected = ApplyPatchFileUpdate {
unified_diff: expected_diff.to_string(),
content: "FOO\nbar\nbaz\n".to_string(),
};
assert_eq!(expected, diff);
}
#[test]
fn test_unified_diff_last_line_replacement() {
// Replace the very last line of the file.
let dir = tempdir().unwrap();
let path = dir.path().join("last.txt");
fs::write(&path, "foo\nbar\nbaz\n").unwrap();
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
foo
bar
-baz
+BAZ
"#,
path.display()
));
let patch = parse_patch(&patch).unwrap();
let chunks = match patch.hunks.as_slice() {
[Hunk::UpdateFile { chunks, .. }] => chunks,
_ => panic!("Expected a single UpdateFile hunk"),
};
let diff = unified_diff_from_chunks(&path, chunks).unwrap();
let expected_diff = r#"@@ -2,2 +2,2 @@
bar
-baz
+BAZ
"#;
let expected = ApplyPatchFileUpdate {
unified_diff: expected_diff.to_string(),
content: "foo\nbar\nBAZ\n".to_string(),
};
assert_eq!(expected, diff);
}
#[test]
fn test_unified_diff_insert_at_eof() {
// Insert a new line at end‑of‑file.
let dir = tempdir().unwrap();
let path = dir.path().join("insert.txt");
fs::write(&path, "foo\nbar\nbaz\n").unwrap();
let patch = wrap_patch(&format!(
r#"*** Update File: {}
@@
+quux
*** End of File
"#,
path.display()
));
let patch = parse_patch(&patch).unwrap();
let chunks = match patch.hunks.as_slice() {
[Hunk::UpdateFile { chunks, .. }] => chunks,
_ => panic!("Expected a single UpdateFile hunk"),
};
let diff = unified_diff_from_chunks(&path, chunks).unwrap();
let expected_diff = r#"@@ -3 +3,2 @@
baz
+quux
"#;
let expected = ApplyPatchFileUpdate {
unified_diff: expected_diff.to_string(),
content: "foo\nbar\nbaz\nquux\n".to_string(),
};
assert_eq!(expected, diff);
}
#[test]
fn test_unified_diff_interleaved_changes() {
// Original file with six lines.
let dir = tempdir().unwrap();
let path = dir.path().join("interleaved.txt");
fs::write(&path, "a\nb\nc\nd\ne\nf\n").unwrap();
// Patch replaces two separate lines and appends a new one at EOF using
// three distinct chunks.
let patch_body = format!(
r#"*** Update File: {}
@@
a
-b
+B
@@
d
-e
+E
@@
f
+g
*** End of File"#,
path.display()
);
let patch = wrap_patch(&patch_body);
// Extract chunks then build the unified diff.
let parsed = parse_patch(&patch).unwrap();
let chunks = match parsed.hunks.as_slice() {
[Hunk::UpdateFile { chunks, .. }] => chunks,
_ => panic!("Expected a single UpdateFile hunk"),
};
let diff = unified_diff_from_chunks(&path, chunks).unwrap();
let expected_diff = r#"@@ -1,6 +1,7 @@
a
-b
+B
c
d
-e
+E
f
+g
"#;
let expected = ApplyPatchFileUpdate {
unified_diff: expected_diff.to_string(),
content: "a\nB\nc\nd\nE\nf\ng\n".to_string(),
};
assert_eq!(expected, diff);
let mut stdout = Vec::new();
let mut stderr = Vec::new();
apply_patch(&patch, &mut stdout, &mut stderr).unwrap();
let contents = fs::read_to_string(path).unwrap();
assert_eq!(
contents,
r#"a
B
c
d
E
f
g
"#
);
}
#[test]
fn test_apply_patch_should_resolve_absolute_paths_in_cwd() {
let session_dir = tempdir().unwrap();
let relative_path = "source.txt";
// Note that we need this file to exist for the patch to be "verified"
// and parsed correctly.
let session_file_path = session_dir.path().join(relative_path);
fs::write(&session_file_path, "session directory content\n").unwrap();
let argv = vec![
"apply_patch".to_string(),
r#"*** Begin Patch
*** Update File: source.txt
@@
-session directory content
+updated session directory content
*** End Patch"#
.to_string(),
];
let result = maybe_parse_apply_patch_verified(&argv, session_dir.path());
// Verify the patch contents - as otherwise we may have pulled contents
// from the wrong file (as we're using relative paths)
assert_eq!(
result,
MaybeApplyPatchVerified::Body(ApplyPatchAction {
changes: HashMap::from([(
session_dir.path().join(relative_path),
ApplyPatchFileChange::Update {
unified_diff: r#"@@ -1 +1 @@
-session directory content
+updated session directory content
"#
.to_string(),
move_path: None,
new_content: "updated session directory content\n".to_string(),
},
)]),
patch: argv[1].clone(),
cwd: session_dir.path().to_path_buf(),
})
);
}
#[test]
fn test_apply_patch_fails_on_write_error() {
let dir = tempdir().unwrap();
let path = dir.path().join("readonly.txt");
fs::write(&path, "before\n").unwrap();
let mut perms = fs::metadata(&path).unwrap().permissions();
perms.set_readonly(true);
fs::set_permissions(&path, perms).unwrap();
let patch = wrap_patch(&format!(
"*** Update File: {}\n@@\n-before\n+after\n*** End Patch",
path.display()
));
let mut stdout = Vec::new();
let mut stderr = Vec::new();
let result = apply_patch(&patch, &mut stdout, &mut stderr);
assert!(result.is_err());
}
}
================================================
FILE: codex-rs/apply-patch/src/main.rs
================================================
pub fn main() -> ! {
codex_apply_patch::main()
}
================================================
FILE: codex-rs/apply-patch/src/parser.rs
================================================
//! This module is responsible for parsing & validating a patch into a list of "hunks".
//! (It does not attempt to actually check that the patch can be applied to the filesystem.)
//!
//! The official Lark grammar for the apply-patch format is:
//!
//! start: begin_patch hunk+ end_patch
//! begin_patch: "*** Begin Patch" LF
//! end_patch: "*** End Patch" LF?
//!
//! hunk: add_hunk | delete_hunk | update_hunk
//! add_hunk: "*** Add File: " filename LF add_line+
//! delete_hunk: "*** Delete File: " filename LF
//! update_hunk: "*** Update File: " filename LF change_move? change?
//! filename: /(.+)/
//! add_line: "+" /(.+)/ LF -> line
//!
//! change_move: "*** Move to: " filename LF
//! change: (change_context | change_line)+ eof_line?
//! change_context: ("@@" | "@@ " /(.+)/) LF
//! change_line: ("+" | "-" | " ") /(.+)/ LF
//! eof_line: "*** End of File" LF
//!
//! The parser below is a little more lenient than the explicit spec and allows for
//! leading/trailing whitespace around patch markers.
use crate::ApplyPatchArgs;
use std::path::Path;
use std::path::PathBuf;
use thiserror::Error;
const BEGIN_PATCH_MARKER: &str = "*** Begin Patch";
const END_PATCH_MARKER: &str = "*** End Patch";
const ADD_FILE_MARKER: &str = "*** Add File: ";
const DELETE_FILE_MARKER: &str = "*** Delete File: ";
const UPDATE_FILE_MARKER: &str = "*** Update File: ";
const MOVE_TO_MARKER: &str = "*** Move to: ";
const EOF_MARKER: &str = "*** End of File";
const CHANGE_CONTEXT_MARKER: &str = "@@ ";
const EMPTY_CHANGE_CONTEXT_MARKER: &str = "@@";
/// Currently, the only OpenAI model that knowingly requires lenient parsing is
/// gpt-4.1. While we could try to require everyone to pass in a strictness
/// param when invoking apply_patch, it is a pain to thread it through all of
/// the call sites, so we resign ourselves allowing lenient parsing for all
/// models. See [`ParseMode::Lenient`] for details on the exceptions we make for
/// gpt-4.1.
const PARSE_IN_STRICT_MODE: bool = false;
#[derive(Debug, PartialEq, Error, Clone)]
pub enum ParseError {
#[error("invalid patch: {0}")]
InvalidPatchError(String),
#[error("invalid hunk at line {line_number}, {message}")]
InvalidHunkError { message: String, line_number: usize },
}
use ParseError::*;
#[derive(Debug, PartialEq, Clone)]
#[allow(clippy::enum_variant_names)]
pub enum Hunk {
AddFile {
path: PathBuf,
contents: String,
},
DeleteFile {
path: PathBuf,
},
UpdateFile {
path: PathBuf,
move_path: Option<PathBuf>,
/// Chunks should be in order, i.e. the `change_context` of one chunk
/// should occur later in the file than the previous chunk.
chunks: Vec<UpdateFileChunk>,
},
}
impl Hunk {
pub fn resolve_path(&self, cwd: &Path) -> PathBuf {
match self {
Hunk::AddFile { path, .. } => cwd.join(path),
Hunk::DeleteFile { path } => cwd.join(path),
Hunk::UpdateFile { path, .. } => cwd.join(path),
}
}
}
use Hunk::*;
#[derive(Debug, PartialEq, Clone)]
pub struct UpdateFileChunk {
/// A single line of context used to narrow down the position of the chunk
/// (this is usually a class, method, or function definition.)
pub change_context: Option<String>,
/// A contiguous block of lines that should be replaced with `new_lines`.
/// `old_lines` must occur strictly after `change_context`.
pub old_lines: Vec<String>,
pub new_lines: Vec<String>,
/// If set to true, `old_lines` must occur at the end of the source file.
/// (Tolerance around trailing newlines should be encouraged.)
pub is_end_of_file: bool,
}
pub fn parse_patch(patch: &str) -> Result<ApplyPatchArgs, ParseError> {
let mode = if PARSE_IN_STRICT_MODE {
ParseMode::Strict
} else {
ParseMode::Lenient
};
parse_patch_text(patch, mode)
}
enum ParseMode {
/// Parse the patch text argument as is.
Strict,
/// GPT-4.1 is known to formulate the `command` array for the `local_shell`
/// tool call for `apply_patch` call using something like the following:
///
/// ```json
/// [
/// "apply_patch",
/// "<<'EOF'\n*** Begin Patch\n*** Update File: README.md\n@@...\n*** End Patch\nEOF\n",
/// ]
/// ```
///
/// This is a problem because `local_shell` is a bit of a misnomer: the
/// `command` is not invoked by passing the arguments to a shell like Bash,
/// but are invoked using something akin to `execvpe(3)`.
///
/// This is significant in this case because where a shell would interpret
/// `<<'EOF'...` as a heredoc and pass the contents via stdin (which is
/// fine, as `apply_patch` is specified to read from stdin if no argument is
/// passed), `execvpe(3)` interprets the heredoc as a literal string. To get
/// the `local_shell` tool to run a command the way shell would, the
/// `command` array must be something like:
///
/// ```json
/// [
/// "bash",
/// "-lc",
/// "apply_patch <<'EOF'\n*** Begin Patch\n*** Update File: README.md\n@@...\n*** End Patch\nEOF\n",
/// ]
/// ```
///
/// In lenient mode, we check if the argument to `apply_patch` starts with
/// `<<'EOF'` and ends with `EOF\n`. If so, we strip off these markers,
/// trim() the result, and treat what is left as the patch text.
Lenient,
}
fn parse_patch_text(patch: &str, mode: ParseMode) -> Result<ApplyPatchArgs, ParseError> {
let lines: Vec<&str> = patch.trim().lines().collect();
let lines: &[&str] = match check_patch_boundaries_strict(&lines) {
Ok(()) => &lines,
Err(e) => match mode {
ParseMode::Strict => {
return Err(e);
}
ParseMode::Lenient => check_patch_boundaries_lenient(&lines, e)?,
},
};
let mut hunks: Vec<Hunk> = Vec::new();
// The above checks ensure that lines.len() >= 2.
let last_line_index = lines.len().saturating_sub(1);
let mut remaining_lines = &lines[1..last_line_index];
let mut line_number = 2;
while !remaining_lines.is_empty() {
let (hunk, hunk_lines) = parse_one_hunk(remaining_lines, line_number)?;
hunks.push(hunk);
line_number += hunk_lines;
remaining_lines = &remaining_lines[hunk_lines..]
}
let patch = lines.join("\n");
Ok(ApplyPatchArgs { hunks, patch })
}
/// Checks the start and end lines of the patch text for `apply_patch`,
/// returning an error if they do not match the expected markers.
fn check_patch_boundaries_strict(lines: &[&str]) -> Result<(), ParseError> {
let (first_line, last_line) = match lines {
[] => (None, None),
[first] => (Some(first), Some(first)),
[first, .., last] => (Some(first), Some(last)),
};
check_start_and_end_lines_strict(first_line, last_line)
}
/// If we are in lenient mode, we check if the first line starts with `<<EOF`
/// (possibly quoted) and the last line ends with `EOF`. There must be at least
/// 4 lines total because the heredoc markers take up 2 lines and the patch text
/// must have at least 2 lines.
///
/// If successful, returns the lines of the patch text that contain the patch
/// contents, excluding the heredoc markers.
fn check_patch_boundaries_lenient<'a>(
original_lines: &'a [&'a str],
original_parse_error: ParseError,
) -> Result<&'a [&'a str], ParseError> {
match original_lines {
[first, .., last] => {
if (first == &"<<EOF" || first == &"<<'EOF'" || first == &"<<\"EOF\"")
&& last.ends_with("EOF")
&& original_lines.len() >= 4
{
let inner_lines = &original_lines[1..original_lines.len() - 1];
match check_patch_boundaries_strict(inner_lines) {
Ok(()) => Ok(inner_lines),
Err(e) => Err(e),
}
} else {
Err(original_parse_error)
}
}
_ => Err(original_parse_error),
}
}
fn check_start_and_end_lines_strict(
first_line: Option<&&str>,
last_line: Option<&&str>,
) -> Result<(), ParseError> {
match (first_line, last_line) {
(Some(&first), Some(&last)) if first == BEGIN_PATCH_MARKER && last == END_PATCH_MARKER => {
Ok(())
}
(Some(&first), _) if first != BEGIN_PATCH_MARKER => Err(InvalidPatchError(String::from(
"The first line of the patch must be '*** Begin Patch'",
))),
_ => Err(InvalidPatchError(String::from(
"The last line of the patch must be '*** End Patch'",
))),
}
}
/// Attempts to parse a single hunk from the start of lines.
/// Returns the parsed hunk and the number of lines parsed (or a ParseError).
fn parse_one_hunk(lines: &[&str], line_number: usize) -> Result<(Hunk, usize), ParseError> {
// Be tolerant of case mismatches and extra padding around marker strings.
let first_line = lines[0].trim();
if let Some(path) = first_line.strip_prefix(ADD_FILE_MARKER) {
// Add File
let mut contents = String::new();
let mut parsed_lines = 1;
for add_line in &lines[1..] {
if let Some(line_to_add) = add_line.strip_prefix('+') {
contents.push_str(line_to_add);
contents.push('\n');
parsed_lines += 1;
} else {
break;
}
}
return Ok((
AddFile {
path: PathBuf::from(path),
contents,
},
parsed_lines,
));
} else if let Some(path) = first_line.strip_prefix(DELETE_FILE_MARKER) {
// Delete File
return Ok((
DeleteFile {
path: PathBuf::from(path),
},
1,
));
} else if let Some(path) = first_line.strip_prefix(UPDATE_FILE_MARKER) {
// Update File
let mut remaining_lines = &lines[1..];
let mut parsed_lines = 1;
// Optional: move file line
let move_path = remaining_lines
.first()
.and_then(|x| x.strip_prefix(MOVE_TO_MARKER));
if move_path.is_some() {
remaining_lines = &remaining_lines[1..];
parsed_lines += 1;
}
let mut chunks = Vec::new();
// NOTE: we need to know to stop once we reach the next special marker header.
while !remaining_lines.is_empty() {
// Skip over any completely blank lines that may separate chunks.
if remaining_lines[0].trim().is_empty() {
parsed_lines += 1;
remaining_lines = &remaining_lines[1..];
continue;
}
if remaining_lines[0].starts_with("***") {
break;
}
let (chunk, chunk_lines) = parse_update_file_chunk(
remaining_lines,
line_number + parsed_lines,
chunks.is_empty(),
)?;
chunks.push(chunk);
parsed_lines += chunk_lines;
remaining_lines = &remaining_lines[chunk_lines..]
}
if chunks.is_empty() {
return Err(InvalidHunkError {
message: format!("Update file hunk for path '{path}' is empty"),
line_number,
});
}
return Ok((
UpdateFile {
path: PathBuf::from(path),
move_path: move_path.map(PathBuf::from),
chunks,
},
parsed_lines,
));
}
Err(InvalidHunkError {
message: format!(
"'{first_line}' is not a valid hunk header. Valid hunk headers: '*** Add File: {{path}}', '*** Delete File: {{path}}', '*** Update File: {{path}}'"
),
line_number,
})
}
fn parse_update_file_chunk(
lines: &[&str],
line_number: usize,
allow_missing_context: bool,
) -> Result<(UpdateFileChunk, usize), ParseError> {
if lines.is_empty() {
return Err(InvalidHunkError {
message: "Update hunk does not contain any lines".to_string(),
line_number,
});
}
// If we see an explicit context marker @@ or @@ <context>, consume it; otherwise, optionally
// allow treating the chunk as starting directly with diff lines.
let (change_context, start_index) = if lines[0] == EMPTY_CHANGE_CONTEXT_MARKER {
(None, 1)
} else if let Some(context) = lines[0].strip_prefix(CHANGE_CONTEXT_MARKER) {
(Some(context.to_string()), 1)
} else {
if !allow_missing_context {
return Err(InvalidHunkError {
message: format!(
"Expected update hunk to start with a @@ context marker, got: '{}'",
lines[0]
),
line_number,
});
}
(None, 0)
};
if start_index >= lines.len() {
return Err(InvalidHunkError {
message: "Update hunk does not contain any lines".to_string(),
line_number: line_number + 1,
});
}
let mut chunk = UpdateFileChunk {
change_context,
old_lines: Vec::new(),
new_lines: Vec::new(),
is_end_of_file: false,
};
let mut parsed_lines = 0;
for line in &lines[start_index..] {
match *line {
EOF_MARKER => {
if parsed_lines == 0 {
return Err(InvalidHunkError {
message: "Update hunk does not contain any lines".to_string(),
line_number: line_number + 1,
});
}
chunk.is_end_of_file = true;
parsed_lines += 1;
break;
}
line_contents => {
match line_contents.chars().next() {
None => {
// Interpret this as an empty line.
chunk.old_lines.push(String::new());
chunk.new_lines.push(String::new());
}
Some(' ') => {
chunk.old_lines.push(line_contents[1..].to_string());
chunk.new_lines.push(line_contents[1..].to_string());
}
Some('+') => {
chunk.new_lines.push(line_contents[1..].to_string());
}
Some('-') => {
chunk.old_lines.push(line_contents[1..].to_string());
}
_ => {
if parsed_lines == 0 {
return Err(InvalidHunkError {
message: format!(
"Unexpected line found in update hunk: '{line_contents}'. Every line should start with ' ' (context line), '+' (added line), or '-' (removed line)"
),
line_number: line_number + 1,
});
}
// Assume this is the start of the next hunk.
break;
}
}
parsed_lines += 1;
}
}
}
Ok((chunk, parsed_lines + start_index))
}
#[test]
fn test_parse_patch() {
assert_eq!(
parse_patch_text("bad", ParseMode::Strict),
Err(InvalidPatchError(
"The first line of the patch must be '*** Begin Patch'".to_string()
))
);
assert_eq!(
parse_patch_text("*** Begin Patch\nbad", ParseMode::Strict),
Err(InvalidPatchError(
"The last line of the patch must be '*** End Patch'".to_string()
))
);
assert_eq!(
parse_patch_text(
"*** Begin Patch\n\
*** Update File: test.py\n\
*** End Patch",
ParseMode::Strict
),
Err(InvalidHunkError {
message: "Update file hunk for path 'test.py' is empty".to_string(),
line_number: 2,
})
);
assert_eq!(
parse_patch_text(
"*** Begin Patch\n\
*** End Patch",
ParseMode::Strict
)
.unwrap()
.hunks,
Vec::new()
);
assert_eq!(
parse_patch_text(
"*** Begin Patch\n\
*** Add File: path/add.py\n\
+abc\n\
+def\n\
*** Delete File: path/delete.py\n\
*** Update File: path/update.py\n\
*** Move to: path/update2.py\n\
@@ def f():\n\
- pass\n\
+ return 123\n\
*** End Patch",
ParseMode::Strict
)
.unwrap()
.hunks,
vec![
AddFile {
path: PathBuf::from("path/add.py"),
contents: "abc\ndef\n".to_string()
},
DeleteFile {
path: PathBuf::from("path/delete.py")
},
UpdateFile {
path: PathBuf::from("path/update.py"),
move_path: Some(PathBuf::from("path/update2.py")),
chunks: vec![UpdateFileChunk {
change_context: Some("def f():".to_string()),
old_lines: vec![" pass".to_string()],
new_lines: vec![" return 123".to_string()],
is_end_of_file: false
}]
}
]
);
// Update hunk followed by another hunk (Add File).
assert_eq!(
parse_patch_text(
"*** Begin Patch\n\
*** Update File: file.py\n\
@@\n\
+line\n\
*** Add File: other.py\n\
+content\n\
*** End Patch",
ParseMode::Strict
)
.unwrap()
.hunks,
vec![
UpdateFile {
path: PathBuf::from("file.py"),
move_path: None,
chunks: vec![UpdateFileChunk {
change_context: None,
old_lines: vec![],
new_lines: vec!["line".to_string()],
is_end_of_file: false
}],
},
AddFile {
path: PathBuf::from("other.py"),
contents: "content\n".to_string()
}
]
);
// Update hunk without an explicit @@ header for the first chunk should parse.
// Use a raw string to preserve the leading space diff marker on the context line.
assert_eq!(
parse_patch_text(
r#"*** Begin Patch
*** Update File: file2.py
import foo
+bar
*** End Patch"#,
ParseMode::Strict
)
.unwrap()
.hunks,
vec![UpdateFile {
path: PathBuf::from("file2.py"),
move_path: None,
chunks: vec![UpdateFileChunk {
change_context: None,
old_lines: vec!["import foo".to_string()],
new_lines: vec!["import foo".to_string(), "bar".to_string()],
is_end_of_file: false,
}],
}]
);
}
#[test]
fn test_parse_patch_lenient() {
let patch_text = r#"*** Begin Patch
*** Update File: file2.py
import foo
+bar
*** End Patch"#;
let expected_patch = vec![UpdateFile {
path: PathBuf::from("file2.py"),
move_path: None,
chunks: vec![UpdateFileChunk {
change_context: None,
old_lines: vec!["import foo".to_string()],
new_lines: vec!["import foo".to_string(), "bar".to_string()],
is_end_of_file: false,
}],
}];
let expected_error =
InvalidPatchError("The first line of the patch must be '*** Begin Patch'".to_string());
let patch_text_in_heredoc = format!("<<EOF\n{patch_text}\nEOF\n");
assert_eq!(
parse_patch_text(&patch_text_in_heredoc, ParseMode::Strict),
Err(expected_error.clone())
);
assert_eq!(
parse_patch_text(&patch_text_in_heredoc, ParseMode::Lenient),
Ok(ApplyPatchArgs {
hunks: expected_patch.clone(),
patch: patch_text.to_string()
})
);
let patch_text_in_single_quoted_heredoc = format!("<<'EOF'\n{patch_text}\nEOF\n");
assert_eq!(
parse_patch_text(&patch_text_in_single_quoted_heredoc, ParseMode::Strict),
Err(expected_error.clone())
);
assert_eq!(
parse_patch_text(&patch_text_in_single_quoted_heredoc, ParseMode::Lenient),
Ok(ApplyPatchArgs {
hunks: expected_patch.clone(),
patch: patch_text.to_string()
})
);
let patch_text_in_double_quoted_heredoc = format!("<<\"EOF\"\n{patch_text}\nEOF\n");
assert_eq!(
parse_patch_text(&patch_text_in_double_quoted_heredoc, ParseMode::Strict),
Err(expected_error.clone())
);
assert_eq!(
parse_patch_text(&patch_text_in_double_quoted_heredoc, ParseMode::Lenient),
Ok(ApplyPatchArgs {
hunks: expected_patch.clone(),
patch: patch_text.to_string()
})
);
let patch_text_in_mismatched_quotes_heredoc = format!("<<\"EOF'\n{patch_text}\nEOF\n");
assert_eq!(
parse_patch_text(&patch_text_in_mismatched_quotes_heredoc, ParseMode::Strict),
Err(expected_error.clone())
);
assert_eq!(
parse_patch_text(&patch_text_in_mismatched_quotes_heredoc, ParseMode::Lenient),
Err(expected_error.clone())
);
let patch_text_with_missing_closing_heredoc =
"<<EOF\n*** Begin Patch\n*** Update File: file2.py\nEOF\n".to_string();
assert_eq!(
parse_patch_text(&patch_text_with_missing_closing_heredoc, ParseMode::Strict),
Err(expected_error.clone())
);
assert_eq!(
parse_patch_text(&patch_text_with_missing_closing_heredoc, ParseMode::Lenient),
Err(InvalidPatchError(
"The last line of the patch must be '*** End Patch'".to_string()
))
);
}
#[test]
fn test_parse_one_hunk() {
assert_eq!(
parse_one_hunk(&["bad"], 234),
Err(InvalidHunkError {
message: "'bad' is not a valid hunk header. \
Valid hunk headers: '*** Add File: {path}', '*** Delete File: {path}', '*** Update File: {path}'".to_string(),
line_number: 234
})
);
// Other edge cases are already covered by tests above/below.
}
#[test]
fn test_update_file_chunk() {
assert_eq!(
parse_update_file_chunk(&["bad"], 123, false),
Err(InvalidHunkError {
message: "Expected update hunk to start with a @@ context marker, got: 'bad'"
.to_string(),
line_number: 123
})
);
assert_eq!(
parse_update_file_chunk(&["@@"], 123, false),
Err(InvalidHunkError {
message: "Update hunk does not contain any lines".to_string(),
line_number: 124
})
);
assert_eq!(
parse_update_file_chunk(&["@@", "bad"], 123, false),
Err(InvalidHunkError {
message: "Unexpected line found in update hunk: 'bad'. \
Every line should start with ' ' (context line), '+' (added line), or '-' (removed line)".to_string(),
line_number: 124
})
);
assert_eq!(
parse_update_file_chunk(&["@@", "*** End of File"], 123, false),
Err(InvalidHunkError {
message: "Update hunk does not contain any lines".to_string(),
line_number: 124
})
);
assert_eq!(
parse_update_file_chunk(
&[
"@@ change_context",
"",
" context",
"-remove",
"+add",
" context2",
"*** End Patch",
],
123,
false
),
Ok((
(UpdateFileChunk {
change_context: Some("change_context".to_string()),
old_lines: vec![
"".to_string(),
"context".to_string(),
"remove".to_string(),
"context2".to_string()
],
new_lines: vec![
"".to_string(),
"context".to_string(),
"add".to_string(),
"context2".to_string()
],
is_end_of_file: false
}),
6
))
);
assert_eq!(
parse_update_file_chunk(&["@@", "+line", "*** End of File"], 123, false),
Ok((
(UpdateFileChunk {
change_context: None,
old_lines: vec![],
new_lines: vec!["line".to_string()],
is_end_of_file: true
}),
3
))
);
}
================================================
FILE: codex-rs/apply-patch/src/seek_sequence.rs
================================================
/// Attempt to find the sequence of `pattern` lines within `lines` beginning at or after `start`.
/// Returns the starting index of the match or `None` if not found. Matches are attempted with
/// decreasing strictness: exact match, then ignoring trailing whitespace, then ignoring leading
/// and trailing whitespace. When `eof` is true, we first try starting at the end-of-file (so that
/// patterns intended to match file endings are applied at the end), and fall back to searching
/// from `start` if needed.
///
/// Special cases handled defensively:
/// • Empty `pattern` → returns `Some(start)` (no-op match)
/// • `pattern.len() > lines.len()` → returns `None` (cannot match, avoids
/// out‑of‑bounds panic that occurred pre‑2025‑04‑12)
pub(crate) fn seek_sequence(
lines: &[String],
pattern: &[String],
start: usize,
eof: bool,
) -> Option<usize> {
if pattern.is_empty() {
return Some(start);
}
// When the pattern is longer than the available input there is no possible
// match. Early‑return to avoid the out‑of‑bounds slice that would occur in
// the search loops below (previously caused a panic when
// `pattern.len() > lines.len()`).
if pattern.len() > lines.len() {
return None;
}
let search_start = if eof && lines.len() >= pattern.len() {
lines.len() - pattern.len()
} else {
start
};
// Exact match first.
for i in search_start..=lines.len().saturating_sub(pattern.len()) {
if lines[i..i + pattern.len()] == *pattern {
return Some(i);
}
}
// Then rstrip match.
for i in search_start..=lines.len().saturating_sub(pattern.len()) {
let mut ok = true;
for (p_idx, pat) in pattern.iter().enumerate() {
if lines[i + p_idx].trim_end() != pat.trim_end() {
ok = false;
break;
}
}
if ok {
return Some(i);
}
}
// Finally, trim both sides to allow more lenience.
for i in search_start..=lines.len().saturating_sub(pattern.len()) {
let mut ok = true;
for (p_idx, pat) in pattern.iter().enumerate() {
if lines[i + p_idx].trim() != pat.trim() {
ok = false;
break;
}
}
if ok {
return Some(i);
}
}
// ------------------------------------------------------------------
// Final, most permissive pass – attempt to match after *normalising*
// common Unicode punctuation to their ASCII equivalents so that diffs
// authored with plain ASCII characters can still be applied to source
// files that contain typographic dashes / quotes, etc. This mirrors the
// fuzzy behaviour of `git apply` which ignores minor byte-level
// differences when locating context lines.
// ------------------------------------------------------------------
fn normalise(s: &str) -> String {
s.trim()
.chars()
.map(|c| match c {
// Various dash / hyphen code-points → ASCII '-'
'\u{2010}' | '\u{2011}' | '\u{2012}' | '\u{2013}' | '\u{2014}' | '\u{2015}'
| '\u{2212}' => '-',
// Fancy single quotes → '\''
'\u{2018}' | '\u{2019}' | '\u{201A}' | '\u{201B}' => '\'',
// Fancy double quotes → '"'
'\u{201C}' | '\u{201D}' | '\u{201E}' | '\u{201F}' => '"',
// Non-breaking space and other odd spaces → normal space
'\u{00A0}' | '\u{2002}' | '\u{2003}' | '\u{2004}' | '\u{2005}' | '\u{2006}'
| '\u{2007}' | '\u{2008}' | '\u{2009}' | '\u{200A}' | '\u{202F}' | '\u{205F}'
| '\u{3000}' => ' ',
other => other,
})
.collect::<String>()
}
for i in search_start..=lines.len().saturating_sub(pattern.len()) {
let mut ok = true;
for (p_idx, pat) in pattern.iter().enumerate() {
if normalise(&lines[i + p_idx]) != normalise(pat) {
ok = false;
break;
}
}
if ok {
return Some(i);
}
}
None
}
#[cfg(test)]
mod tests {
use super::seek_sequence;
fn to_vec(strings: &[&str]) -> Vec<String> {
strings.iter().map(|s| s.to_string()).collect()
}
#[test]
fn test_exact_match_finds_sequence() {
let lines = to_vec(&["foo", "bar", "baz"]);
let pattern = to_vec(&["bar", "baz"]);
assert_eq!(seek_sequence(&lines, &pattern, 0, false), Some(1));
}
#[test]
fn test_rstrip_match_ignores_trailing_whitespace() {
let lines = to_vec(&["foo ", "bar\t\t"]);
// Pattern omits trailing whitespace.
let pattern = to_vec(&["foo", "bar"]);
assert_eq!(seek_sequence(&lines, &pattern, 0, false), Some(0));
}
#[test]
fn test_trim_match_ignores_leading_and_trailing_whitespace() {
let lines = to_vec(&[" foo ", " bar\t"]);
// Pattern omits any additional whitespace.
let pattern = to_vec(&["foo", "bar"]);
assert_eq!(seek_sequence(&lines, &pattern, 0, false), Some(0));
}
#[test]
fn test_pattern_longer_than_input_returns_none() {
let lines = to_vec(&["just one line"]);
let pattern = to_vec(&["too", "many", "lines"]);
// Should not panic – must return None when pattern cannot possibly fit.
assert_eq!(seek_sequence(&lines, &pattern, 0, false), None);
}
}
================================================
FILE: codex-rs/apply-patch/src/standalone_executable.rs
================================================
use std::io::Read;
use std::io::Write;
pub fn main() -> ! {
let exit_code = run_main();
std::process::exit(exit_code);
}
/// We would prefer to return `std::process::ExitCode`, but its `exit_process()`
/// method is still a nightly API and we want main() to return !.
pub fn run_main() -> i32 {
// Expect either one argument (the full apply_patch payload) or read it from stdin.
let mut args = std::env::args_os();
let _argv0 = args.next();
let patch_arg = match args.next() {
Some(arg) => match arg.into_string() {
Ok(s) => s,
Err(_) => {
eprintln!("Error: apply_patch requires a UTF-8 PATCH argument.");
return 1;
}
},
None => {
// No argument provided; attempt to read the patch from stdin.
let mut buf = String::new();
match std::io::stdin().read_to_string(&mut buf) {
Ok(_) => {
if buf.is_empty() {
eprintln!("Usage: apply_patch 'PATCH'\n echo 'PATCH' | apply-patch");
return 2;
}
buf
}
Err(err) => {
eprintln!("Error: Failed to read PATCH from stdin.\n{err}");
return 1;
}
}
}
};
// Refuse extra args to avoid ambiguity.
if args.next().is_some() {
eprintln!("Error: apply_patch accepts exactly one argument.");
return 2;
}
let mut stdout = std::io::stdout();
let mut stderr = std::io::stderr();
match crate::apply_patch(&patch_arg, &mut stdout, &mut stderr) {
Ok(()) => {
// Flush to ensure output ordering when used in pipelines.
let _ = stdout.flush();
0
}
Err(_) => 1,
}
}
================================================
FILE: codex-rs/apply-patch/tests/all.rs
================================================
// Single integration test binary that aggregates all test modules.
// The submodules live in `tests/suite/`.
mod suite;
================================================
FILE: codex-rs/apply-patch/tests/suite/cli.rs
================================================
use assert_cmd::prelude::*;
use std::fs;
use std::process::Command;
use tempfile::tempdir;
#[test]
fn test_apply_patch_cli_add_and_update() -> anyhow::Result<()> {
let tmp = tempdir()?;
let file = "cli_test.txt";
let absolute_path = tmp.path().join(file);
// 1) Add a file
let add_patch = format!(
r#"*** Begin Patch
*** Add File: {file}
+hello
*** End Patch"#
);
Command::cargo_bin("apply_patch")
.expect("should find apply_patch binary")
.arg(add_patch)
.current_dir(tmp.path())
.assert()
.success()
.stdout(format!("Success. Updated the following files:\nA {file}\n"));
assert_eq!(fs::read_to_string(&absolute_path)?, "hello\n");
// 2) Update the file
let update_patch = format!(
r#"*** Begin Patch
*** Update File: {file}
@@
-hello
+world
*** End Patch"#
);
Command::cargo_bin("apply_patch")
.expect("should find apply_patch binary")
.arg(update_patch)
.current_dir(tmp.path())
.assert()
.success()
.stdout(format!("Success. Updated the following files:\nM {file}\n"));
assert_eq!(fs::read_to_string(&absolute_path)?, "world\n");
Ok(())
}
#[test]
fn test_apply_patch_cli_stdin_add_and_update() -> anyhow::Result<()> {
let tmp = tempdir()?;
let file = "cli_test_stdin.txt";
let absolute_path = tmp.path().join(file);
// 1) Add a file via stdin
let add_patch = format!(
r#"*** Begin Patch
*** Add File: {file}
+hello
*** End Patch"#
);
let mut cmd =
assert_cmd::Command::cargo_bin("apply_patch").expect("should find apply_patch binary");
cmd.current_dir(tmp.path());
cmd.write_stdin(add_patch)
.assert()
.success()
.stdout(format!("Success. Updated the following files:\nA {file}\n"));
assert_eq!(fs::read_to_string(&absolute_path)?, "hello\n");
// 2) Update the file via stdin
let update_patch = format!(
r#"*** Begin Patch
*** Update File: {file}
@@
-hello
+world
*** End Patch"#
);
let mut cmd =
assert_cmd::Command::cargo_bin("apply_patch").expect("should find apply_patch binary");
cmd.current_dir(tmp.path());
cmd.write_stdin(update_patch)
.assert()
.success()
.stdout(format!("Success. Updated the following files:\nM {file}\n"));
assert_eq!(fs::read_to_string(&absolute_path)?, "world\n");
Ok(())
}
================================================
FILE: codex-rs/apply-patch/tests/suite/mod.rs
================================================
mod cli;
================================================
FILE: codex-rs/arg0/Cargo.toml
================================================
[package]
edition = "2024"
name = "codex-arg0"
version = { workspace = true }
[lib]
name = "codex_arg0"
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
anyhow = "1"
codex-apply-patch = { path = "../apply-patch" }
codex-core = { path = "../core" }
codex-linux-sandbox = { path = "../linux-sandbox" }
dotenvy = "0.15.7"
tempfile = "3"
tokio = { version = "1", features = ["rt-multi-thread"] }
================================================
FILE: codex-rs/arg0/src/lib.rs
================================================
use std::future::Future;
use std::path::Path;
use std::path::PathBuf;
use codex_core::CODEX_APPLY_PATCH_ARG1;
#[cfg(unix)]
use std::os::unix::fs::symlink;
use tempfile::TempDir;
const LINUX_SANDBOX_ARG0: &str = "codex-linux-sandbox";
const APPLY_PATCH_ARG0: &str = "apply_patch";
const MISSPELLED_APPLY_PATCH_ARG0: &str = "applypatch";
/// While we want to deploy the Codex CLI as a single executable for simplicity,
/// we also want to expose some of its functionality as distinct CLIs, so we use
/// the "arg0 trick" to determine which CLI to dispatch. This effectively allows
/// us to simulate deploying multiple executables as a single binary on Mac and
/// Linux (but not Windows).
///
/// When the current executable is invoked through the hard-link or alias named
/// `codex-linux-sandbox` we *directly* execute
/// [`codex_linux_sandbox::run_main`] (which never returns). Otherwise we:
///
/// 1. Use [`dotenvy::from_path`] and [`dotenvy::dotenv`] to modify the
/// environment before creating any threads.
/// 2. Construct a Tokio multi-thread runtime.
/// 3. Derive the path to the current executable (so children can re-invoke the
/// sandbox) when running on Linux.
/// 4. Execute the provided async `main_fn` inside that runtime, forwarding any
/// error. Note that `main_fn` receives `codex_linux_sandbox_exe:
/// Option<PathBuf>`, as an argument, which is generally needed as part of
/// constructing [`codex_core::config::Config`].
///
/// This function should be used to wrap any `main()` function in binary crates
/// in this workspace that depends on these helper CLIs.
pub fn arg0_dispatch_or_else<F, Fut>(main_fn: F) -> anyhow::Result<()>
where
F: FnOnce(Option<PathBuf>) -> Fut,
Fut: Future<Output = anyhow::Result<()>>,
{
// Determine if we were invoked via the special alias.
let mut args = std::env::args_os();
let argv0 = args.next().unwrap_or_default();
let exe_name = Path::new(&argv0)
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("");
if exe_name == LINUX_SANDBOX_ARG0 {
// Safety: [`run_main`] never returns.
codex_linux_sandbox::run_main();
} else if exe_name == APPLY_PATCH_ARG0 || exe_name == MISSPELLED_APPLY_PATCH_ARG0 {
codex_apply_patch::main();
}
let argv1 = args.next().unwrap_or_default();
if argv1 == CODEX_APPLY_PATCH_ARG1 {
let patch_arg = args.next().and_then(|s| s.to_str().map(|s| s.to_owned()));
let exit_code = match patch_arg {
Some(patch_arg) => {
let mut stdout = std::io::stdout();
let mut stderr = std::io::stderr();
match codex_apply_patch::apply_patch(&patch_arg, &mut stdout, &mut stderr) {
Ok(()) => 0,
Err(_) => 1,
}
}
None => {
eprintln!("Error: {CODEX_APPLY_PATCH_ARG1} requires a UTF-8 PATCH argument.");
1
}
};
std::process::exit(exit_code);
}
// This modifies the environment, which is not thread-safe, so do this
// before creating any threads/the Tokio runtime.
load_dotenv();
// Retain the TempDir so it exists for the lifetime of the invocation of
// this executable. Admittedly, we could invoke `keep()` on it, but it
// would be nice to avoid leaving temporary directories behind, if possible.
let _path_entry = match prepend_path_entry_for_apply_patch() {
Ok(path_entry) => Some(path_entry),
Err(err) => {
// It is possible that Codex will proceed successfully even if
// updating the PATH fails, so warn the user and move on.
eprintln!("WARNING: proceeding, even though we could not update PATH: {err}");
None
}
};
// Regular invocation – create a Tokio runtime and execute the provided
// async entry-point.
let runtime = tokio::runtime::Runtime::new()?;
runtime.block_on(async move {
let codex_linux_sandbox_exe: Option<PathBuf> = if cfg!(target_os = "linux") {
std::env::current_exe().ok()
} else {
None
};
main_fn(codex_linux_sandbox_exe).await
})
}
const ILLEGAL_ENV_VAR_PREFIX: &str = "CODEX_";
/// Load env vars from ~/.codex/.env and `$(pwd)/.env`.
///
/// Security: Do not allow `.env` files to create or modify any variables
/// with names starting with `CODEX_`.
fn load_dotenv() {
if let Ok(codex_home) = codex_core::config::find_codex_home()
&& let Ok(iter) = dotenvy::from_path_iter(codex_home.join(".env"))
{
set_filtered(iter);
}
if let Ok(iter) = dotenvy::dotenv_iter() {
set_filtered(iter);
}
}
/// Helper to set vars from a dotenvy iterator while filtering out `CODEX_` keys.
fn set_filtered<I>(iter: I)
where
I: IntoIterator<Item = Result<(String, String), dotenvy::Error>>,
{
for (key, value) in iter.into_iter().flatten() {
if !key.to_ascii_uppercase().starts_with(ILLEGAL_ENV_VAR_PREFIX) {
// It is safe to call set_var() because our process is
// single-threaded at this point in its execution.
unsafe { std::env::set_var(&key, &value) };
}
}
}
/// Creates a temporary directory with either:
///
/// - UNIX: `apply_patch` symlink to the current executable
/// - WINDOWS: `apply_patch.bat` batch script to invoke the current executable
/// with the "secret" --codex-run-as-apply-patch flag.
///
/// This temporary directory is prepended to the PATH environment variable so
/// that `apply_patch` can be on the PATH without requiring the user to
/// install a separate `apply_patch` executable, simplifying the deployment of
/// Codex CLI.
///
/// IMPORTANT: This function modifies the PATH environment variable, so it MUST
/// be called before multiple threads are spawned.
fn prepend_path_entry_for_apply_patch() -> std::io::Result<TempDir> {
let temp_dir = TempDir::new()?;
let path = temp_dir.path();
for filename in &[APPLY_PATCH_ARG0, MISSPELLED_APPLY_PATCH_ARG0] {
let exe = std::env::current_exe()?;
#[cfg(unix)]
{
let link = path.join(filename);
symlink(&exe, &link)?;
}
#[cfg(windows)]
{
let batch_script = path.join(format!("{filename}.bat"));
std::fs::write(
&batch_script,
format!(
r#"@echo off
"{}" {CODEX_APPLY_PATCH_ARG1} %*
"#,
exe.display()
),
)?;
}
}
#[cfg(unix)]
const PATH_SEPARATOR: &str = ":";
#[cfg(windows)]
const PATH_SEPARATOR: &str = ";";
let path_element = path.display();
let updated_path_env_var = match std::env::var("PATH") {
Ok(existing_path) => {
format!("{path_element}{PATH_SEPARATOR}{existing_path}")
}
Err(_) => {
format!("{path_element}")
}
};
unsafe {
std::env::set_var("PATH", updated_path_env_var);
}
Ok(temp_dir)
}
================================================
FILE: codex-rs/chatgpt/Cargo.toml
================================================
[package]
edition = "2024"
name = "codex-chatgpt"
version = { workspace = true }
[lints]
workspace = true
[dependencies]
anyhow = "1"
clap = { version = "4", features = ["derive"] }
codex-common = { path = "../common", features = ["cli"] }
codex-core = { path = "../core" }
codex-login = { path = "../login" }
reqwest = { version = "0.12", features = ["json", "stream"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["full"] }
[dev-dependencies]
tempfile = "3"
================================================
FILE: codex-rs/chatgpt/README.md
================================================
# ChatGPT
This crate pertains to first party ChatGPT APIs and products such as Codex agent.
This crate should be primarily built and maintained by OpenAI employees. Please reach out to a maintainer before making an external contribution.
================================================
FILE: codex-rs/chatgpt/src/apply_command.rs
================================================
use std::path::PathBuf;
use clap::Parser;
use codex_common::CliConfigOverrides;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use crate::chatgpt_token::init_chatgpt_token_from_auth;
use crate::get_task::GetTaskResponse;
use crate::get_task::OutputItem;
use crate::get_task::PrOutputItem;
use crate::get_task::get_task;
/// Applies the latest diff from a Codex agent task.
#[derive(Debug, Parser)]
pub struct ApplyCommand {
pub task_id: String,
#[clap(flatten)]
pub config_overrides: CliConfigOverrides,
}
pub async fn run_apply_command(
apply_cli: ApplyCommand,
cwd: Option<PathBuf>,
) -> anyhow::Result<()> {
let config = Config::load_with_cli_overrides(
apply_cli
.config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?,
ConfigOverrides::default(),
)?;
init_chatgpt_token_from_auth(&config.codex_home).await?;
let task_response = get_task(&config, apply_cli.task_id).await?;
apply_diff_from_task(task_response, cwd).await
}
pub async fn apply_diff_from_task(
task_response: GetTaskResponse,
cwd: Option<PathBuf>,
) -> anyhow::Result<()> {
let diff_turn = match task_response.current_diff_task_turn {
Some(turn) => turn,
None => anyhow::bail!("No diff turn found"),
};
let output_diff = diff_turn.output_items.iter().find_map(|item| match item {
OutputItem::Pr(PrOutputItem { output_diff }) => Some(output_diff),
_ => None,
});
match output_diff {
Some(output_diff) => apply_diff(&output_diff.diff, cwd).await,
None => anyhow::bail!("No PR output item found"),
}
}
async fn apply_diff(diff: &str, cwd: Option<PathBuf>) -> anyhow::Result<()> {
let mut cmd = tokio::process::Command::new("git");
if let Some(cwd) = cwd {
cmd.current_dir(cwd);
}
let toplevel_output = cmd
.args(vec!["rev-parse", "--show-toplevel"])
.output()
.await?;
if !toplevel_output.status.success() {
anyhow::bail!("apply must be run from a git repository.");
}
let repo_root = String::from_utf8(toplevel_output.stdout)?
.trim()
.to_string();
let mut git_apply_cmd = tokio::process::Command::new("git")
.args(vec!["apply", "--3way"])
.current_dir(&repo_root)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
if let Some(mut stdin) = git_apply_cmd.stdin.take() {
tokio::io::AsyncWriteExt::write_all(&mut stdin, diff.as_bytes()).await?;
drop(stdin);
}
let output = git_apply_cmd.wait_with_output().await?;
if !output.status.success() {
anyhow::bail!(
"Git apply failed with status {}: {}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
}
println!("Successfully applied diff");
Ok(())
}
================================================
FILE: codex-rs/chatgpt/src/chatgpt_client.rs
================================================
use codex_core::config::Config;
use codex_core::user_agent::get_codex_user_agent;
use crate::chatgpt_token::get_chatgpt_token_data;
use crate::chatgpt_token::init_chatgpt_token_from_auth;
use anyhow::Context;
use serde::de::DeserializeOwned;
/// Make a GET request to the ChatGPT backend API.
pub(crate) async fn chatgpt_get_request<T: DeserializeOwned>(
config: &Config,
path: String,
) -> anyhow::Result<T> {
let chatgpt_base_url = &config.chatgpt_base_url;
init_chatgpt_token_from_auth(&config.codex_home).await?;
// Make direct HTTP request to ChatGPT backend API with the token
let client = reqwest::Client::new();
let url = format!("{chatgpt_base_url}{path}");
let token =
get_chatgpt_token_data().ok_or_else(|| anyhow::anyhow!("ChatGPT token not available"))?;
let account_id = token.account_id.ok_or_else(|| {
anyhow::anyhow!("ChatGPT account ID not available, please re-run `codex login`")
});
let response = client
.get(&url)
.bearer_auth(&token.access_token)
.header("chatgpt-account-id", account_id?)
.header("Content-Type", "application/json")
.header("User-Agent", get_codex_user_agent(None))
.send()
.await
.context("Failed to send request")?;
if response.status().is_success() {
let result: T = response
.json()
.await
.context("Failed to parse JSON response")?;
Ok(result)
} else {
let status = response.status();
let body = response.text().await.unwrap_or_default();
anyhow::bail!("Request failed with status {}: {}", status, body)
}
}
================================================
FILE: codex-rs/chatgpt/src/chatgpt_token.rs
================================================
use codex_login::AuthMode;
use codex_login::CodexAuth;
use std::path::Path;
use std::sync::LazyLock;
use std::sync::RwLock;
use codex_login::TokenData;
static CHATGPT_TOKEN: LazyLock<RwLock<Option<TokenData>>> = LazyLock::new(|| RwLock::new(None));
pub fn get_chatgpt_token_data() -> Option<TokenData> {
CHATGPT_TOKEN.read().ok()?.clone()
}
pub fn set_chatgpt_token_data(value: TokenData) {
if let Ok(mut guard) = CHATGPT_TOKEN.write() {
*guard = Some(value);
}
}
/// Initialize the ChatGPT token from auth.json file
pub async fn init_chatgpt_token_from_auth(codex_home: &Path) -> std::io::Result<()> {
let auth = CodexAuth::from_codex_home(codex_home, AuthMode::ChatGPT)?;
if let Some(auth) = auth {
let token_data = auth.get_token_data().await?;
set_chatgpt_token_data(token_data);
}
Ok(())
}
================================================
FILE: codex-rs/chatgpt/src/get_task.rs
================================================
use codex_core::config::Config;
use serde::Deserialize;
use crate::chatgpt_client::chatgpt_get_request;
#[derive(Debug, Deserialize)]
pub struct GetTaskResponse {
pub current_diff_task_turn: Option<AssistantTurn>,
}
// Only relevant fields for our extraction
#[derive(Debug, Deserialize)]
pub struct AssistantTurn {
pub output_items: Vec<OutputItem>,
}
#[derive(Debug, Deserialize)]
#[serde(tag = "type")]
pub enum OutputItem {
#[serde(rename = "pr")]
Pr(PrOutputItem),
#[serde(other)]
Other,
}
#[derive(Debug, Deserialize)]
pub struct PrOutputItem {
pub output_diff: OutputDiff,
}
#[derive(Debug, Deserialize)]
pub struct OutputDiff {
pub diff: String,
}
pub(crate) async fn get_task(config: &Config, task_id: String) -> anyhow::Result<GetTaskResponse> {
let path = format!("/wham/tasks/{task_id}");
chatgpt_get_request(config, path).await
}
================================================
FILE: codex-rs/chatgpt/src/lib.rs
================================================
pub mod apply_command;
mod chatgpt_client;
mod chatgpt_token;
pub mod get_task;
================================================
FILE: codex-rs/chatgpt/tests/all.rs
================================================
// Single integration test binary that aggregates all test modules.
// The submodules live in `tests/suite/`.
mod suite;
================================================
FILE: codex-rs/chatgpt/tests/suite/apply_command_e2e.rs
================================================
use codex_chatgpt::apply_command::apply_diff_from_task;
use codex_chatgpt::get_task::GetTaskResponse;
use std::path::Path;
use tempfile::TempDir;
use tokio::process::Command;
/// Creates a temporary git repository with initial commit
async fn create_temp_git_repo() -> anyhow::Result<TempDir> {
let temp_dir = TempDir::new()?;
let repo_path = temp_dir.path();
let envs = vec![
("GIT_CONFIG_GLOBAL", "/dev/null"),
("GIT_CONFIG_NOSYSTEM", "1"),
];
let output = Command::new("git")
.envs(envs.clone())
.args(["init"])
.current_dir(repo_path)
.output()
.await?;
if !output.status.success() {
anyhow::bail!(
"Failed to initialize git repo: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Command::new("git")
.envs(envs.clone())
.args(["config", "user.email", "test@example.com"])
.current_dir(repo_path)
.output()
.await?;
Command::new("git")
.envs(envs.clone())
.args(["config", "user.name", "Test User"])
.current_dir(repo_path)
.output()
.await?;
std::fs::write(repo_path.join("README.md"), "# Test Repo\n")?;
Command::new("git")
.envs(envs.clone())
.args(["add", "README.md"])
.current_dir(repo_path)
.output()
.await?;
let output = Command::new("git")
.envs(envs.clone())
.args(["commit", "-m", "Initial commit"])
.current_dir(repo_path)
.output()
.await?;
if !output.status.success() {
anyhow::bail!(
"Failed to create initial commit: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Ok(temp_dir)
}
async fn mock_get_task_with_fixture() -> anyhow::Result<GetTaskResponse> {
let fixture_path = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/task_turn_fixture.json");
let fixture_content = std::fs::read_to_string(fixture_path)?;
let response: GetTaskResponse = serde_json::from_str(&fixture_content)?;
Ok(response)
}
#[tokio::test]
async fn test_apply_command_creates_fibonacci_file() {
let temp_repo = create_temp_git_repo()
.await
.expect("Failed to create temp git repo");
let repo_path = temp_repo.path();
let task_response = mock_get_task_with_fixture()
.await
.expect("Failed to load fixture");
apply_diff_from_task(task_response, Some(repo_path.to_path_buf()))
.await
.expect("Failed to apply diff from task");
// Assert that fibonacci.js was created in scripts/ directory
let fibonacci_path = repo_path.join("scripts/fibonacci.js");
assert!(fibonacci_path.exists(), "fibonacci.js was not created");
// Verify the file contents match expected
let contents = std::fs::read_to_string(&fibonacci_path).expect("Failed to read fibonacci.js");
assert!(
contents.contains("function fibonacci(n)"),
"fibonacci.js doesn't contain expected function"
);
assert!(
contents.contains("#!/usr/bin/env node"),
"fibonacci.js doesn't have shebang"
);
assert!(
contents.contains("module.exports = fibonacci;"),
"fibonacci.js doesn't export function"
);
// Verify file has correct number of lines (31 as specified in fixture)
let line_count = contents.lines().count();
assert_eq!(
line_count, 31,
"fibonacci.js should have 31 lines, got {line_count}",
);
}
#[tokio::test]
async fn test_apply_command_with_merge_conflicts() {
let temp_repo = create_temp_git_repo()
.await
.expect("Failed to create temp git repo");
let repo_path = temp_repo.path();
// Create conflicting fibonacci.js file first
let scripts_dir = repo_path.join("scripts");
std::fs::create_dir_all(&scripts_dir).expect("Failed to create scripts directory");
let conflicting_content = r#"#!/usr/bin/env node
// This is a different fibonacci implementation
function fib(num) {
if (num <= 1) return num;
return fib(num - 1) + fib(num - 2);
}
console.log("Running fibonacci...");
console.log(fib(10));
"#;
let fibonacci_path = scripts_dir.join("fibonacci.js");
std::fs::write(&fibonacci_path, conflicting_content).expect("Failed to write conflicting file");
Command::new("git")
.args(["add", "scripts/fibonacci.js"])
.current_dir(repo_path)
.output()
.await
.expect("Failed to add fibonacci.js");
Command::new("git")
.args(["commit", "-m", "Add conflicting fibonacci implementation"])
.current_dir(repo_path)
.output()
.await
.expect("Failed to commit conflicting file");
let original_dir = std::env::current_dir().expect("Failed to get current dir");
std::env::set_current_dir(repo_path).expect("Failed to change directory");
struct DirGuard(std::path::PathBuf);
impl Drop for DirGuard {
fn drop(&mut self) {
let _ = std::env::set_current_dir(&self.0);
}
}
let _guard = DirGuard(original_dir);
let task_response = mock_get_task_with_fixture()
.await
.expect("Failed to load fixture");
let apply_result = apply_diff_from_task(task_response, Some(repo_path.to_path_buf())).await;
assert!(
apply_result.is_err(),
"Expected apply to fail due to merge conflicts"
);
let contents = std::fs::read_to_string(&fibonacci_path).expect("Failed to read fibonacci.js");
assert!(
contents.contains("<<<<<<< HEAD")
|| contents.contains("=======")
|| contents.contains(">>>>>>> "),
"fibonacci.js should contain merge conflict markers, got: {contents}",
);
}
================================================
FILE: codex-rs/chatgpt/tests/suite/mod.rs
================================================
// Aggregates all former standalone integration tests as modules.
mod apply_command_e2e;
================================================
FILE: codex-rs/chatgpt/tests/task_turn_fixture.json
================================================
{
"current_diff_task_turn": {
"output_items": [
{
"type": "pr",
"pr_title": "Add fibonacci script",
"pr_message": "## Summary\n- add a basic Fibonacci script under `scripts/`\n\n## Testing\n- `node scripts/fibonacci.js 10`\n- `npm run lint` *(fails: next not found)*",
"output_diff": {
"type": "output_diff",
"repo_id": "/workspace/rddit-vercel",
"base_commit_sha": "1a2e9baf2ce2fdd0c126b47b1bcfd512de2a9f7b",
"diff": "diff --git a/scripts/fibonacci.js b/scripts/fibonacci.js\nnew file mode 100644\nindex 0000000000000000000000000000000000000000..6c9fdfdbf8669b7968936411050525b995d0a9a6\n--- /dev/null\n+++ b/scripts/fibonacci.js\n@@ -0,0 +1,31 @@\n+#!/usr/bin/env node\n+\n+function fibonacci(n) {\n+ if (n < 0) {\n+ throw new Error(\"n must be non-negative\");\n+ }\n+ let a = 0;\n+ let b = 1;\n+ for (let i = 0; i < n; i++) {\n+ const next = a + b;\n+ a = b;\n+ b = next;\n+ }\n+ return a;\n+}\n+\n+function printUsage() {\n+ console.log(\"Usage: node scripts/fibonacci.js <n>\");\n+}\n+\n+if (require.main === module) {\n+ const arg = process.argv[2];\n+ if (arg === undefined || isNaN(Number(arg))) {\n+ printUsage();\n+ process.exit(1);\n+ }\n+ const n = Number(arg);\n+ console.log(fibonacci(n));\n+}\n+\n+module.exports = fibonacci;\n",
"external_storage_diff": {
"file_id": "file_00000000114c61f786900f8c2130ace7",
"ttl": null
},
"files_modified": 1,
"lines_added": 31,
"lines_removed": 0,
"commit_message": "Add fibonacci script"
}
},
{
"type": "message",
"role": "assistant",
"content": [
{
"content_type": "text",
"text": "**Summary**\n\n- Created a command-line Fibonacci script that validates input and prints the result when executed with Node"
},
{
"content_type": "repo_file_citation",
"path": "scripts/fibonacci.js",
"line_range_start": 1,
"line_range_end": 31
},
{
"content_type": "text",
"text": "\n\n**Testing**\n\n- ❌ `npm run lint` (failed to run `next lint`)"
},
{
"content_type": "terminal_chunk_citation",
"terminal_chunk_id": "7dd543",
"line_range_start": 1,
"line_range_end": 5
},
{
"content_type": "text",
"text": "\n- ✅ `node scripts/fibonacci.js 10` produced “55”"
},
{
"content_type": "terminal_chunk_citation",
"terminal_chunk_id": "6ee559",
"line_range_start": 1,
"line_range_end": 3
},
{
"content_type": "text",
"text": "\n\nCodex couldn't run certain commands due to environment limitations. Consider configuring a setup script or internet access in your Codex environment to install dependencies."
}
]
}
]
}
}
================================================
FILE: codex-rs/cli/Cargo.toml
================================================
[package]
edition = "2024"
name = "codex-cli"
version = { workspace = true }
[[bin]]
name = "codex"
path = "src/main.rs"
[lib]
name = "codex_cli"
path = "src/lib.rs"
[lints]
workspace = true
[dependencies]
anyhow = "1"
chrono = { version = "0.4", features = ["serde", "clock"] }
chrono-tz = "0.8"
clap = { version = "4", features = ["derive"] }
clap_complete = "4"
dirs = "5.0"
futures = "0.3"
codex-arg0 = { path = "../arg0" }
codex-chatgpt = { path = "../chatgpt" }
codex-common = { path = "../common", features = ["cli"] }
codex-core = { path = "../core" }
codex-exec = { path = "../exec" }
codex-login = { path = "../login" }
codex-mcp-server = { path = "../mcp-server" }
codex-protocol = { path = "../protocol" }
codex-tui = { path = "../tui" }
mcp-types = { path = "../mcp-types" }
serde_json = "1"
tiktoken-rs = "*"
uuid = "1.0"
tokio = { version = "1", features = [
"io-std",
"macros",
"process",
"rt-multi-thread",
"signal",
] }
tracing = "0.1.41"
tracing-subscriber = "0.3.20"
codex-protocol-ts = { path = "../protocol-ts" }
================================================
FILE: codex-rs/cli/src/debug_sandbox.rs
================================================
use std::path::PathBuf;
use codex_common::CliConfigOverrides;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_core::exec_env::create_env;
use codex_core::landlock::spawn_command_under_linux_sandbox;
use codex_core::seatbelt::spawn_command_under_seatbelt;
use codex_core::spawn::StdioPolicy;
use codex_protocol::config_types::SandboxMode;
use crate::LandlockCommand;
use crate::SeatbeltCommand;
use crate::exit_status::handle_exit_status;
pub async fn run_command_under_seatbelt(
command: SeatbeltCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
let SeatbeltCommand {
full_auto,
config_overrides,
command,
} = command;
run_command_under_sandbox(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Seatbelt,
)
.await
}
pub async fn run_command_under_landlock(
command: LandlockCommand,
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
let LandlockCommand {
full_auto,
config_overrides,
command,
} = command;
run_command_under_sandbox(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
SandboxType::Landlock,
)
.await
}
enum SandboxType {
Seatbelt,
Landlock,
}
async fn run_command_under_sandbox(
full_auto: bool,
command: Vec<String>,
config_overrides: CliConfigOverrides,
codex_linux_sandbox_exe: Option<PathBuf>,
sandbox_type: SandboxType,
) -> anyhow::Result<()> {
let sandbox_mode = create_sandbox_mode(full_auto);
let cwd = std::env::current_dir()?;
let config = Config::load_with_cli_overrides(
config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?,
ConfigOverrides {
sandbox_mode: Some(sandbox_mode),
codex_linux_sandbox_exe,
..Default::default()
},
)?;
let stdio_policy = StdioPolicy::Inherit;
let env = create_env(&config.shell_environment_policy);
let mut child = match sandbox_type {
SandboxType::Seatbelt => {
spawn_command_under_seatbelt(command, &config.sandbox_policy, cwd, stdio_policy, env)
.await?
}
SandboxType::Landlock => {
#[expect(clippy::expect_used)]
let codex_linux_sandbox_exe = config
.codex_linux_sandbox_exe
.expect("codex-linux-sandbox executable not found");
spawn_command_under_linux_sandbox(
codex_linux_sandbox_exe,
command,
&config.sandbox_policy,
cwd,
stdio_policy,
env,
)
.await?
}
};
let status = child.wait().await?;
handle_exit_status(status);
}
pub fn create_sandbox_mode(full_auto: bool) -> SandboxMode {
if full_auto {
SandboxMode::WorkspaceWrite
} else {
SandboxMode::ReadOnly
}
}
================================================
FILE: codex-rs/cli/src/exit_status.rs
================================================
#[cfg(unix)]
pub(crate) fn handle_exit_status(status: std::process::ExitStatus) -> ! {
use std::os::unix::process::ExitStatusExt;
// Use ExitStatus to derive the exit code.
if let Some(code) = status.code() {
std::process::exit(code);
} else if let Some(signal) = status.signal() {
std::process::exit(128 + signal);
} else {
std::process::exit(1);
}
}
#[cfg(windows)]
pub(crate) fn handle_exit_status(status: std::process::ExitStatus) -> ! {
if let Some(code) = status.code() {
std::process::exit(code);
} else {
// Rare on Windows, but if it happens: use fallback code.
std::process::exit(1);
}
}
================================================
FILE: codex-rs/cli/src/lib.rs
================================================
pub mod debug_sandbox;
mod exit_status;
pub mod login;
pub mod proto;
use clap::Parser;
use codex_common::CliConfigOverrides;
#[derive(Debug, Parser)]
pub struct SeatbeltCommand {
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
#[arg(long = "full-auto", default_value_t = false)]
pub full_auto: bool,
#[clap(skip)]
pub config_overrides: CliConfigOverrides,
/// Full command args to run under seatbelt.
#[arg(trailing_var_arg = true)]
pub command: Vec<String>,
}
#[derive(Debug, Parser)]
pub struct LandlockCommand {
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
#[arg(long = "full-auto", default_value_t = false)]
pub full_auto: bool,
#[clap(skip)]
pub config_overrides: CliConfigOverrides,
/// Full command args to run under landlock.
#[arg(trailing_var_arg = true)]
pub command: Vec<String>,
}
================================================
FILE: codex-rs/cli/src/login.rs
================================================
use codex_common::CliConfigOverrides;
use codex_core::config::Config;
use codex_core::config::ConfigOverrides;
use codex_login::AuthMode;
use codex_login::CLIENT_ID;
use codex_login::CodexAuth;
use codex_login::OPENAI_API_KEY_ENV_VAR;
use codex_login::ServerOptions;
use codex_login::login_with_api_key;
use codex_login::logout;
use codex_login::run_login_server;
use std::env;
use std::path::PathBuf;
pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<()> {
let opts = ServerOptions::new(codex_home, CLIENT_ID.to_string());
let server = run_login_server(opts)?;
eprintln!(
"Starting local login server on http://localhost:{}.\nIf your browser did not open, navigate to this URL to authenticate:\n\n{}",
server.actual_port, server.auth_url,
);
server.block_until_done().await
}
pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides);
match login_with_chatgpt(config.codex_home).await {
Ok(_) => {
eprintln!("Successfully logged in");
std::process::exit(0);
}
Err(e) => {
eprintln!("Error logging in: {e}");
std::process::exit(1);
}
}
}
pub async fn run_login_with_api_key(
cli_config_overrides: CliConfigOverrides,
api_key: String,
) -> ! {
let config = load_config_or_exit(cli_config_overrides);
match login_with_api_key(&config.codex_home, &api_key) {
Ok(_) => {
eprintln!("Successfully logged in");
std::process::exit(0);
}
Err(e) => {
eprintln!("Error logging in: {e}");
std::process::exit(1);
}
}
}
pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides);
match CodexAuth::from_codex_home(&config.codex_home, config.preferred_auth_method) {
Ok(Some(auth)) => match auth.mode {
AuthMode::ApiKey => match auth.get_token().await {
Ok(api_key) => {
eprintln!("Logged in using an API key - {}", safe_format_key(&api_key));
if let Ok(env_api_key) = env::var(OPENAI_API_KEY_ENV_VAR)
&& env_api_key == api_key
{
eprintln!(
" API loaded from OPENAI_API_KEY environment variable or .env file"
);
}
std::process::exit(0);
}
Err(e) => {
eprintln!("Unexpected error retrieving API key: {e}");
std::process::exit(1);
}
},
AuthMode::ChatGPT => {
eprintln!("Logged in using ChatGPT");
std::process::exit(0);
}
},
Ok(None) => {
eprintln!("Not logged in");
std::process::exit(1);
}
Err(e) => {
eprintln!("Error checking login status: {e}");
std::process::exit(1);
}
}
}
pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
let config = load_config_or_exit(cli_config_overrides);
match logout(&config.codex_home) {
Ok(true) => {
eprintln!("Successfully logged out");
std::process::exit(0);
}
Ok(false) => {
eprintln!("Not logged in");
std::process::exit(0);
}
Err(e) => {
eprintln!("Error logging out: {e}");
std::process::exit(1);
}
}
}
fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config {
let cli_overrides = match cli_config_overrides.parse_overrides() {
Ok(v) => v,
Err(e) => {
eprintln!("Error parsing -c overrides: {e}");
std::process::exit(1);
}
};
let config_overrides = ConfigOverrides::default();
match Config::load_with_cli_overrides(cli_overrides, config_overrides) {
Ok(config) => config,
Err(e) => {
eprintln!("Error loading configuration: {e}");
std::process::exit(1);
}
}
}
fn safe_format_key(key: &str) -> String {
if key.len() <= 13 {
return "***".to_string();
}
let prefix = &key[..8];
let suffix = &key[key.len() - 5..];
format!("{prefix}***{suffix}")
}
#[cfg(test)]
mod tests {
use super::safe_format_key;
#[test]
fn formats_long_key() {
let key = "sk-proj-1234567890ABCDE";
assert_eq!(safe_format_key(key), "sk-proj-***ABCDE");
}
#[test]
fn short_key_returns_stars() {
let key = "sk-proj-12345";
assert_eq!(safe_format_key(key), "***");
}
}
================================================
FILE: codex-rs/cli/src/main.rs
================================================
use anyhow::Context;
use clap::CommandFactory;
use clap::Parser;
use clap_complete::Shell;
use clap_complete::generate;
use codex_arg0::arg0_dispatch_or_else;
use codex_chatgpt::apply_command::ApplyCommand;
use codex_chatgpt::apply_command::run_apply_command;
use codex_cli::LandlockCommand;
use codex_cli::SeatbeltCommand;
use codex_cli::login::run_login_status;
use codex_cli::login::run_login_with_api_key;
use codex_cli::login::run_login_with_chatgpt;
use codex_cli::login::run_logout;
use codex_cli::proto;
use codex_common::CliConfigOverrides;
use codex_exec::Cli as ExecCli;
use codex_tui::Cli as TuiCli;
use std::path::PathBuf;
use tiktoken_rs::o200k_base;
use crate::proto::ProtoCli;
/// Codex CLI
///
/// If no subcommand is specified, options will be forwarded to the interactive CLI.
#[derive(Debug, Parser)]
#[clap(
author,
version,
// If a sub‑command is given, ignore requirements of the default args.
subcommand_negates_reqs = true,
// The executable is sometimes invoked via a platform‑specific name like
// `codex-x86_64-unknown-linux-musl`, but the help output should always use
// the generic `codex` command name that users run.
bin_name = "codex"
)]
struct MultitoolCli {
#[clap(flatten)]
pub config_overrides: CliConfigOverrides,
#[clap(flatten)]
interactive: TuiCli,
#[clap(subcommand)]
subcommand: Option<Subcommand>,
}
#[derive(Debug, clap::Subcommand)]
enum Subcommand {
/// Run Codex non-interactively.
#[clap(visible_alias = "e")]
Exec(ExecCli),
/// Manage login.
Login(LoginCommand),
/// Remove stored authentication credentials.
Logout(LogoutCommand),
/// Experimental: run Codex as an MCP server.
Mcp,
/// Run Codex in autonomous mode with external LLM driver.
#[clap(visible_alias = "auto")]
Autonomous(AutonomousCommand),
/// Run the Protocol stream via stdin/stdout
#[clap(visible_alias = "p")]
Proto(ProtoCli),
/// Generate shell completion scripts.
Completion(CompletionCommand),
/// Internal debugging commands.
Debug(DebugArgs),
/// Apply the latest diff produced by Codex agent as a `git apply` to your local working tree.
#[clap(visible_alias = "a")]
Apply(ApplyCommand),
/// Internal: generate TypeScript protocol bindings.
#[clap(hide = true)]
GenerateTs(GenerateTsCommand),
}
#[derive(Debug, Parser)]
struct CompletionCommand {
/// Shell to generate completions for
#[clap(value_enum, default_value_t = Shell::Bash)]
shell: Shell,
}
#[derive(Debug, Parser)]
struct DebugArgs {
#[command(subcommand)]
cmd: DebugCommand,
}
#[derive(Debug, clap::Subcommand)]
enum DebugCommand {
/// Run a command under Seatbelt (macOS only).
Seatbelt(SeatbeltCommand),
/// Run a command under Landlock+seccomp (Linux only).
Landlock(LandlockCommand),
}
#[derive(Debug, Parser)]
struct LoginCommand {
#[clap(skip)]
config_overrides: CliConfigOverrides,
#[arg(long = "api-key", value_name = "API_KEY")]
api_key: Option<String>,
#[command(subcommand)]
action: Option<LoginSubcommand>,
}
#[derive(Debug, clap::Subcommand)]
enum LoginSubcommand {
/// Show login status.
Status,
}
#[derive(Debug, Parser)]
struct LogoutCommand {
#[clap(skip)]
config_overrides: CliConfigOverrides,
}
#[derive(Debug, Parser)]
struct GenerateTsCommand {
/// Output directory where .ts files will be written
#[arg(short = 'o', long = "out", value_name = "DIR")]
out_dir: PathBuf,
/// Optional path to the Prettier executable to format generated files
#[arg(short = 'p', long = "prettier", value_name = "PRETTIER_BIN")]
prettier: Option<PathBuf>,
}
#[derive(Debug, Parser)]
struct AutonomousCommand {
/// Path to the configuration YAML file.
#[clap(long, short = 'f', value_name = "FILE")]
config_file: PathBuf,
/// Duration to run in autonomous mode (in minutes).
#[clap(long, short = 'd', default_value = "30")]
duration: u64,
/// Model to use for the external LLM driver.
#[clap(long, short = 'm', default_value = "o3")]
driver_model: String,
/// Enable full-auto mode (skip all approvals and use workspace-write sandbox).
#[clap(long = "full-auto")]
full_auto: bool,
/// Resume from an existing autonomous session directory.
#[clap(long, value_name = "DIR")]
resume_dir: Option<PathBuf>,
/// Start hour for active operation (0-23, Pacific time).
#[clap(long, default_value = "0")]
work_start_hour: u8,
/// End hour for active operation (0-23, Pacific time).
#[clap(long, default_value = "23")]
work_end_hour: u8,
/// Ignore Pacific time work-hour pauses and run continuously.
#[clap(long)]
ignore_work_hours: bool,
/// Custom logs directory (overrides default autonomous_session_* naming).
#[clap(long, value_name = "DIR")]
logs_dir: Option<PathBuf>,
/// Mode/specialist to use for the codex instance.
#[clap(long, value_name = "MODE")]
mode: Option<String>,
#[clap(flatten)]
config_overrides: CliConfigOverrides,
}
fn main() -> anyhow::Result<()> {
arg0_dispatch_or_else(|codex_linux_sandbox_exe| async move {
cli_main(codex_linux_sandbox_exe).await?;
Ok(())
})
}
async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::Result<()> {
let cli = MultitoolCli::parse();
match cli.subcommand {
None => {
let mut tui_cli = cli.interactive;
prepend_config_flags(&mut tui_cli.config_overrides, cli.config_overrides);
let usage = codex_tui::run_main(tui_cli, codex_linux_sandbox_exe).await?;
if !usage.is_zero() {
println!("{}", codex_core::protocol::FinalOutput::from(usage));
}
}
Some(Subcommand::Exec(mut exec_cli)) => {
prepend_config_flags(&mut exec_cli.config_overrides, cli.config_overrides);
codex_exec::run_main(exec_cli, codex_linux_sandbox_exe).await?;
}
Some(Subcommand::Mcp) => {
codex_mcp_server::run_main(codex_linux_sandbox_exe, cli.config_overrides).await?;
}
Some(Subcommand::Autonomous(mut autonomous_cli)) => {
prepend_config_flags(&mut autonomous_cli.config_overrides, cli.config_overrides);
run_autonomous_mode(autonomous_cli, codex_linux_sandbox_exe).await?;
}
Some(Subcommand::Login(mut login_cli)) => {
prepend_config_flags(&mut login_cli.config_overrides, cli.config_overrides);
match login_cli.action {
Some(LoginSubcommand::Status) => {
run_login_status(login_cli.config_overrides).await;
}
None => {
if let Some(api_key) = login_cli.api_key {
run_login_with_api_key(login_cli.config_overrides, api_key).await;
} else {
run_login_with_chatgpt(login_cli.config_overrides).await;
}
}
}
}
Some(Subcommand::Logout(mut logout_cli)) => {
prepend_config_flags(&mut logout_cli.config_overrides, cli.config_overrides);
run_logout(logout_cli.config_overrides).await;
}
Some(Subcommand::Proto(mut proto_cli)) => {
prepend_config_flags(&mut proto_cli.config_overrides, cli.config_overrides);
proto::run_main(proto_cli).await?;
}
Some(Subcommand::Completion(completion_cli)) => {
print_completion(completion_cli);
}
Some(Subcommand::Debug(debug_args)) => match debug_args.cmd {
DebugCommand::Seatbelt(mut seatbelt_cli) => {
prepend_config_flags(&mut seatbelt_cli.config_overrides, cli.config_overrides);
codex_cli::debug_sandbox::run_command_under_seatbelt(
seatbelt_cli,
codex_linux_sandbox_exe,
)
.await?;
}
DebugCommand::Landlock(mut landlock_cli) => {
prepend_config_flags(&mut landlock_cli.config_overrides, cli.config_overrides);
codex_cli::debug_sandbox::run_command_under_landlock(
landlock_cli,
codex_linux_sandbox_exe,
)
.await?;
}
},
Some(Subcommand::Apply(mut apply_cli)) => {
prepend_config_flags(&mut apply_cli.config_overrides, cli.config_overrides);
run_apply_command(apply_cli, None).await?;
}
Some(Subcommand::GenerateTs(gen_cli)) => {
codex_protocol_ts::generate_ts(&gen_cli.out_dir, gen_cli.prettier.as_deref())?;
}
}
Ok(())
}
async fn run_autonomous_mode(
autonomous_cli: AutonomousCommand,
_codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
use codex_core::ConversationManager;
use codex_core::config::Config;
use codex_core::protocol::InputItem;
use codex_core::protocol::Op;
use codex_login::AuthManager;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
use tokio::time::sleep;
println!("🚀 Starting autonomous mode...");
println!("📁 Config file: {:?}", autonomous_cli.config_file);
if let Some(ref resume_dir) = autonomous_cli.resume_dir {
println!("🔄 Resuming from: {:?}", resume_dir);
}
println!("⏰ Duration: {} minutes", autonomous_cli.duration);
println!("🤖 Driver model: {}", autonomous_cli.driver_model);
// Load config file
let config_content =
std::fs::read_to_string(&autonomous_cli.config_file).with_context(|| {
format!(
"Failed to read config file: {:?}",
autonomous_cli.config_file
)
})?;
// Load prompt templates from core directory
let core_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("core");
let initial_prompt_file = core_dir.join("initial_prompt.txt");
let continuation_prompt_file = core_dir.join("continuation_prompt.txt");
let approval_prompt_file = core_dir.join("approval_prompt.txt");
let bugcrowd_approval_prompt_file = core_dir.join("bugcrowd_approval_prompt.txt");
let summarization_prompt_file = core_dir.join("summarization_prompt.txt");
let initial_prompt_template =
std::fs::read_to_string(&initial_prompt_file).with_context(|| {
format!(
"Failed to read initial prompt file: {:?}",
initial_prompt_file
)
})?;
let continuation_prompt_template = std::fs::read_to_string(&continuation_prompt_file)
.with_context(|| {
format!(
"Failed to read continuation prompt file: {:?}",
continuation_prompt_file
)
})?;
let approval_prompt_template =
std::fs::read_to_string(&approval_prompt_file).with_context(|| {
format!(
"Failed to read approval prompt file: {:?}",
approval_prompt_file
)
})?;
let bugcrowd_approval_prompt_template = std::fs::read_to_string(&bugcrowd_approval_prompt_file)
.with_context(|| {
format!(
"Failed to read bugcrowd approval prompt file: {:?}",
bugcrowd_approval_prompt_file
)
})?;
let summarization_prompt_template = std::fs::read_to_string(&summarization_prompt_file)
.with_context(|| {
format!(
"Failed to read summarization prompt file: {:?}",
summarization_prompt_file
)
})?;
println!("📋 Task config loaded");
println!("📝 Prompt templates loaded");
// Create codex config with overrides, applying full-auto settings if enabled
let mut config_overrides = codex_core::config::ConfigOverrides::default();
if autonomous_cli.full_auto {
config_overrides.approval_policy = Some(codex_core::protocol::AskForApproval::OnFailure);
config_overrides.sandbox_mode =
Some(codex_protocol::config_types::SandboxMode::WorkspaceWrite);
}
// Set specialist mode if provided
if let Some(mode) = autonomous_cli.mode {
config_overrides.specialist = Some(mode);
}
let config = Config::load_with_cli_overrides(
autonomous_cli
.config_overrides
.parse_overrides()
.map_err(anyhow::Error::msg)?,
config_overrides,
)
.with_context(|| "Failed to load codex config")?;
// Debug: Log the actual config being used
println!(
"🔧 DEBUG: Loaded config - model: {}, provider: {}",
config.model, config.model_provider.name
);
println!("🔧 DEBUG: Driver model: {}", autonomous_cli.driver_model);
println!(
"🔧 DEBUG: OPENROUTER_API_KEY: {}",
if std::env::var("OPENROUTER_API_KEY").is_ok() {
"SET"
} else {
"NOT SET"
}
);
println!(
"🔧 DEBUG: OPENAI_API_KEY: {}",
if std::env::var("OPENAI_API_KEY").is_ok() {
"SET"
} else {
"NOT SET"
}
);
// Initialize codex session
let codex_home = codex_core::config::find_codex_home()?;
let auth_manager = Arc::new(AuthManager::new(codex_home, codex_login::AuthMode::ChatGPT));
let conversation_manager = ConversationManager::new(auth_manager);
let new_conversation = conversation_manager
.new_conversation(config.clone())
.await?;
let codex = new_conversation.conversation;
println!("✅ Codex session initialized");
// Initialize context accumulator and conversation log
let mut context = String::new();
let mut conversation_log = Vec::new();
let mut iteration = 0;
// Load resume context if resume directory is provided
if let Some(ref resume_dir) = autonomous_cli.resume_dir {
println!("🔄 Loading resume context from {:?}", resume_dir);
// Load context from context_log.txt
let context_log_file = resume_dir.join("context_log.txt");
if context_log_file.exists() {
context = std::fs::read_to_string(&context_log_file)
.with_context(|| format!("Failed to read context log: {:?}", context_log_file))?;
println!("✅ Context log loaded ({} bytes)", context.len());
}
// Load conversation from latest.json
let latest_file = resume_dir.join("latest.json");
if latest_file.exists() {
let latest_content = std::fs::read_to_string(&latest_file)
.with_context(|| format!("Failed to read latest.json: {:?}", latest_file))?;
conversation_log = serde_json::from_str(&latest_content)
.with_context(|| format!("Failed to parse latest.json: {:?}", latest_file))?;
println!(
"✅ Conversation log loaded ({} messages)",
conversation_log.len()
);
}
// Determine next iteration number from existing files
let mut max_iteration = 0;
if let Ok(entries) = std::fs::read_dir(resume_dir) {
for entry in entries {
if let Ok(entry) = entry {
let filename = entry.file_name().to_string_lossy().to_string();
if filename.starts_with("iteration_") && filename.ends_with(".json") {
if let Ok(iter_num) = filename[10..13].parse::<u32>() {
max_iteration = max_iteration.max(iter_num);
}
}
}
}
}
iteration = max_iteration + 1;
println!("✅ Resuming from iteration {}", iteration);
}
let start_time = Instant::now();
let _duration = Duration::from_secs(autonomous_cli.duration * 60);
// Create or use existing session-specific logs directory
let session_timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let session_logs_dir = if let Some(ref resume_dir) = autonomous_cli.resume_dir {
// Use existing directory for resume
resume_dir.clone()
} else if let Some(ref custom_logs_dir) = autonomous_cli.logs_dir {
// Use custom logs directory (for vulnerability deep-dives)
std::fs::create_dir_all(&custom_logs_dir).with_context(|| {
format!(
"Failed to create custom logs directory: {:?}",
custom_logs_dir
)
})?;
custom_logs_dir.clone()
} else {
// Create new session directory with timestamp
let session_logs_dir =
PathBuf::from("./logs").join(format!("autonomous_session_{}", session_timestamp));
std::fs::create_dir_all(&session_logs_dir).with_context(|| {
format!(
"Failed to create session logs directory: {:?}",
session_logs_dir
)
})?;
session_logs_dir
};
println!("📁 Session logs directory: {:?}", session_logs_dir);
// Create backup directory in home directory
let backup_logs_dir = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("Could not find home directory"))?
.join("codex-logs-backup")
.join(format!("autonomous_session_{}", session_timestamp));
std::fs::create_dir_all(&backup_logs_dir).with_context(|| {
format!(
"Failed to create backup logs directory: {:?}",
backup_logs_dir
)
})?;
println!("📁 Backup logs directory: {:?}", backup_logs_dir);
// Load codex system prompt from prompt.md (only for new sessions)
if autonomous_cli.resume_dir.is_none() {
let prompt_md_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("core")
.join("prompt.md");
let system_prompt = std::fs::read_to_string(&prompt_md_path)
.with_context(|| format!("Failed to read system prompt from: {:?}", prompt_md_path))?;
// Add system message to conversation log
conversation_log.push(serde_json::json!({
"role": "system",
"content": system_prompt
}));
}
// Function to save checkpoint log files and update heartbeat
let save_checkpoint = |log: &Vec<serde_json::Value>, iteration_num: u32| {
let log_json = serde_json::to_string_pretty(log).unwrap_or_else(|_| "[]".to_string());
// Save numbered checkpoint to both locations
let checkpoint_path = session_logs_dir.join(format!("iteration_{:03}.json", iteration_num));
let backup_checkpoint_path =
backup_logs_dir.join(format!("iteration_{:03}.json", iteration_num));
if let Err(e) = std::fs::write(&checkpoint_path, &log_json) {
eprintln!("❌ Failed to save checkpoint {}: {}", iteration_num, e);
} else {
println!(
"📝 Checkpoint {} saved to: {:?}",
iteration_num, checkpoint_path
);
}
if let Err(e) = std::fs::write(&backup_checkpoint_path, &log_json) {
eprintln!(
"❌ Failed to save backup checkpoint {}: {}",
iteration_num, e
);
} else {
println!(
"📝 Backup checkpoint {} saved to: {:?}",
iteration_num, backup_checkpoint_path
);
}
// Also save as latest.json for easy access to both locations
let latest_path = session_logs_dir.join("latest.json");
let backup_latest_path = backup_logs_dir.join("latest.json");
if let Err(e) = std::fs::write(&latest_path, &log_json) {
eprintln!("❌ Failed to save latest.json: {}", e);
}
if let Err(e) = std::fs::write(&backup_latest_path, &log_json) {
eprintln!("❌ Failed to save backup latest.json: {}", e);
}
// Save session metadata to both locations
let current_time = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let metadata = serde_json::json!({
"session_start": session_timestamp,
"current_iteration": iteration_num,
"elapsed_seconds": start_time.elapsed().as_secs(),
"last_updated": current_time
});
let metadata_path = session_logs_dir.join("session_info.json");
let backup_metadata_path = backup_logs_dir.join("session_info.json");
if let Err(e) = std::fs::write(
&metadata_path,
serde_json::to_string_pretty(&metadata).unwrap_or_default(),
) {
eprintln!("❌ Failed to save session metadata: {}", e);
}
if let Err(e) = std::fs::write(
&backup_metadata_path,
serde_json::to_string_pretty(&metadata).unwrap_or_default(),
) {
eprintln!("❌ Failed to save backup session metadata: {}", e);
}
// Save heartbeat file for health monitor
let heartbeat = serde_json::json!({
"timestamp": chrono::Utc::now().to_rfc3339(),
"iteration": iteration_num,
"session_timestamp": session_timestamp,
"elapsed_seconds": start_time.elapsed().as_secs(),
"status": "running",
"pid": std::process::id(),
"config_file": autonomous_cli.config_file.to_string_lossy(),
"duration_minutes": autonomous_cli.duration,
"driver_model": &autonomous_cli.driver_model,
"full_auto": autonomous_cli.full_auto
});
let heartbeat_json = serde_json::to_string_pretty(&heartbeat).unwrap_or_default();
// Save heartbeat in session directory and backup
let heartbeat_path = session_logs_dir.join("heartbeat.json");
let backup_heartbeat_path = backup_logs_dir.join("heartbeat.json");
if let Err(e) = std::fs::write(&heartbeat_path, &heartbeat_json) {
eprintln!("❌ Failed to save heartbeat: {}", e);
}
if let Err(e) = std::fs::write(&backup_heartbeat_path, &heartbeat_json) {
eprintln!("❌ Failed to save backup heartbeat: {}", e);
}
// Also save heartbeat to global location for health monitor
let global_heartbeat_path = PathBuf::from("./logs/latest_session_heartbeat.json");
let backup_global_heartbeat_path = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("codex-logs-backup")
.join("latest_session_heartbeat.json");
if let Err(e) = std::fs::write(&global_heartbeat_path, &heartbeat_json) {
eprintln!("❌ Failed to save global heartbeat: {}", e);
}
if let Err(e) = std::fs::write(&backup_global_heartbeat_path, &heartbeat_json) {
eprintln!("❌ Failed to save backup global heartbeat: {}", e);
}
};
// Save initial checkpoint with system message
save_checkpoint(&conversation_log, 0);
println!(
"🚀 Session {} started with {} minute duration",
session_timestamp, autonomous_cli.duration
);
// Main autonomous loop with error handling
let session_finished = false;
let loop_result = async {
while !session_finished {
iteration += 1;
println!(
"\n🔄 Iteration {} ({}s elapsed)",
iteration,
start_time.elapsed().as_secs()
);
// Determine which prompt template to use
let prompt_template = if iteration == 1 {
&initial_prompt_template
} else {
&continuation_prompt_template
};
// Check context token count and summarize if needed
let mut final_context = context.clone();
let mut context_was_summarized = false;
let context_tokens = count_tokens(&context)?;
const MAX_TOKENS: usize = 200_000;
const TOKEN_BUFFER: usize = 500;
if context_tokens > (MAX_TOKENS - TOKEN_BUFFER) {
println!(
"⚠️ Context approaching token limit: {} tokens (max: {})",
context_tokens, MAX_TOKENS
);
// Summarize the formatted context string (but keep conversation_log intact)
final_context = summarize_context(
&context,
&autonomous_cli.driver_model,
&summarization_prompt_template,
)
.await?;
context_was_summarized = true;
println!(
"✅ Context summarized from {} to {} tokens",
context_tokens,
count_tokens(&final_context)?
);
}
// Inject config and context into prompt template
let driver_prompt =
inject_template_variables(prompt_template, &config_content, &final_context);
// Check final driver prompt token count
let driver_prompt_tokens = count_tokens(&driver_prompt)?;
println!("📊 Driver prompt tokens: {}", driver_prompt_tokens);
if driver_prompt_tokens > (MAX_TOKENS - TOKEN_BUFFER) {
return Err(anyhow::anyhow!(
"Driver prompt still too long after summarization: {} tokens (max: {})",
driver_prompt_tokens,
MAX_TOKENS - TOKEN_BUFFER
));
}
// Generate user prompt using external LLM
let (user_prompt, tool_results) =
generate_user_prompt(&driver_prompt, &autonomous_cli.driver_model, &session_logs_dir).await?;
println!("💭 Generated user prompt: {}", user_prompt);
// Handle supervisor LLM tool calls and generate final user prompt
let final_user_prompt = if !tool_results.is_empty() {
// Case 2: Supervisor made tool calls - need to get follow-up response
// Add user message with tool calls to conversation log
conversation_log.push(serde_json::json!({
"role": "user",
"content": user_prompt,
"tool_calls": tool_results.iter().map(|tr| {
// Find the original tool call to get the correct tool name
let tool_call_id = tr["tool_call_id"].as_str().unwrap_or("");
let tool_name = tr.get("tool_name").and_then(|n| n.as_str()).unwrap_or("unknown");
serde_json::json!({
"id": tool_call_id,
"type": "function",
"function": {
"name": tool_name,
"arguments": serde_json::json!({})
}
})
}).collect::<Vec<_>>()
}));
// Add tool results to conversation log
for tool_result in &tool_results {
conversation_log.push(serde_json::json!({
"role": "tool",
"tool_call_id": tool_result["tool_call_id"],
"content": tool_result["content"]
}));
}
// Generate follow-up prompt from supervisor with tool results
let follow_up_context = format!("{}\n\nTool Results:\n{}",
final_context,
serde_json::to_string_pretty(&tool_results).unwrap_or_default()
);
let follow_up_driver_prompt = inject_template_variables(
&continuation_prompt_template,
&config_content,
&follow_up_context,
);
let (follow_up_prompt, _) = generate_user_prompt(
&follow_up_driver_prompt,
&autonomous_cli.driver_model,
&session_logs_dir,
).await?;
println!("🔄 Supervisor follow-up prompt: {}", follow_up_prompt);
// Add follow-up user message to conversation log
conversation_log.push(serde_json::json!({
"role": "user",
"content": follow_up_prompt
}));
// Update context with follow-up conversation
final_context = format!("{}\n\nUSER: {}\n\nASSISTANT: {}",
final_context, follow_up_prompt, follow_up_prompt);
follow_up_prompt
} else {
// Case 1: No tool calls - use original supervisor message directly
// Add regular user message to conversation log
conversation_log.push(serde_json::json!({
"role": "user",
"content": user_prompt
}));
user_prompt
};
// Submit to codex
let input_items = vec![InputItem::Text {
text: final_user_prompt.clone(),
}];
let submission_id: String = codex.submit(Op::UserInput { items: input_items }).await?;
// Collect codex response and tool calls
let (codex_response, tool_calls, reasoning, tool_responses) =
collect_codex_response_with_tools(
&codex,
&submission_id,
autonomous_cli.full_auto,
&autonomous_cli.driver_model,
&approval_prompt_template,
&bugcrowd_approval_prompt_template,
&session_logs_dir,
&config_content,
)
.await?;
println!("🤖 Codex response collected");
// Add events in correct chronological order:
// 1. Assistant reasoning (if present)
if let Some(reasoning_text) = reasoning {
conversation_log.push(serde_json::json!({
"role": "assistant",
"content": "",
"reasoning": reasoning_text
}));
}
// 2. Assistant tool calls (if any)
if !tool_calls.is_empty() {
conversation_log.push(serde_json::json!({
"role": "assistant",
"content": "",
"tool_calls": tool_calls
}));
}
// 3. Tool responses
for tool_response in tool_responses {
conversation_log.push(tool_response);
}
// 4. Final assistant response
conversation_log.push(serde_json::json!({
"role": "assistant",
"content": codex_response
}));
// Build readable conversation context
let mut readable_context = String::new();
for msg in &conversation_log {
match msg.get("role").and_then(|r| r.as_str()) {
Some("system") => {
readable_context.push_str(&format!(
"SYSTEM: {}\n\n",
msg.get("content").and_then(|c| c.as_str()).unwrap_or("")
));
}
Some("user") => {
readable_context.push_str(&format!(
"USER: {}\n\n",
msg.get("content").and_then(|c| c.as_str()).unwrap_or("")
));
}
Some("assistant") => {
if let Some(reasoning) = msg.get("reasoning") {
readable_context.push_str(&format!(
"ASSISTANT_REASONING: {}\n\n",
reasoning.as_str().unwrap_or("")
));
} else if let Some(tool_calls) = msg.get("tool_calls") {
// Filter out system tool calls
let empty_vec = vec![];
let tool_calls_array = tool_calls.as_array().unwrap_or(&empty_vec);
let filtered_tool_calls: Vec<_> = tool_calls_array
.iter()
.filter(|tool_call| {
tool_call.get("type").and_then(|t| t.as_str()) != Some("system")
})
.collect();
if !filtered_tool_calls.is_empty() {
readable_context.push_str(&format!(
"ASSISTANT_TOOL_CALLS: {}\n\n",
serde_json::to_string_pretty(&filtered_tool_calls).unwrap_or_default()
));
}
} else {
readable_context.push_str(&format!(
"ASSISTANT: {}\n\n",
msg.get("content").and_then(|c| c.as_str()).unwrap_or("")
));
}
}
Some("tool") => {
readable_context.push_str(&format!(
"TOOL_RESPONSE: {}\n\n",
msg.get("content").and_then(|c| c.as_str()).unwrap_or("")
));
}
_ => {
// Skip unknown roles
}
}
}
// Use summarized context if we summarized this iteration, otherwise use rebuilt context
if context_was_summarized {
context = final_context;
} else {
context = readable_context;
}
// Save context string to file for testing
let context_log_path = session_logs_dir.join("context_log.txt");
if let Err(e) = std::fs::write(&context_log_path, &context) {
eprintln!("❌ Failed to save context log: {}", e);
}
// Save checkpoint after each iteration
save_checkpoint(&conversation_log, iteration as u32);
// Wait before next iteration
sleep(Duration::from_secs(10)).await;
}
println!(
"✅ Autonomous mode completed after {} iterations",
iteration
);
Ok::<(), anyhow::Error>(())
}
.await;
// Save final checkpoint regardless of how we exit
save_checkpoint(&conversation_log, iteration as u32);
// Update final heartbeat with completion status
let final_status = if loop_result.is_ok() {
"completed"
} else {
"error"
};
let final_heartbeat = serde_json::json!({
"timestamp": chrono::Utc::now().to_rfc3339(),
"iteration": iteration,
"session_timestamp": session_timestamp,
"elapsed_seconds": start_time.elapsed().as_secs(),
"status": final_status,
"pid": std::process::id(),
"config_file": autonomous_cli.config_file.to_string_lossy(),
"duration_minutes": autonomous_cli.duration,
"driver_model": &autonomous_cli.driver_model,
"full_auto": autonomous_cli.full_auto
});
let final_heartbeat_json = serde_json::to_string_pretty(&final_heartbeat).unwrap_or_default();
let global_heartbeat_path = PathBuf::from("./logs/latest_session_heartbeat.json");
if let Err(e) = std::fs::write(&global_heartbeat_path, &final_heartbeat_json) {
eprintln!("❌ Failed to save final heartbeat: {}", e);
}
println!(
"🏁 Final checkpoint saved for session {}",
session_timestamp
);
// Return the result
loop_result
}
async fn collect_codex_response_with_tools(
codex: &codex_core::CodexConversation,
submission_id: &str,
_full_auto: bool,
driver_model: &str,
approval_prompt_template: &str,
bugcrowd_approval_prompt_template: &str,
session_logs_dir: &std::path::Path,
config_content: &str,
) -> anyhow::Result<(
String,
Vec<serde_json::Value>,
Option<String>,
Vec<serde_json::Value>,
)> {
use codex_core::protocol::EventMsg;
let mut assistant_content = String::new();
let mut reasoning_content = String::new();
let mut tool_calls = Vec::new();
let mut tool_responses = Vec::new();
let mut task_complete = false;
let mut denied_tool_calls = std::collections::HashSet::new();
// Collect events until task is complete
while !task_complete {
match codex.next_event().await {
Ok(event) => {
if event.id == submission_id {
match event.msg {
EventMsg::AgentMessage(msg) => {
println!("🤖 Agent: {}", msg.message);
assistant_content.push_str(&msg.message);
assistant_content.push('\n');
}
EventMsg::AgentReasoning(reasoning) => {
println!("🧠 Reasoning: {}", reasoning.text);
reasoning_content.push_str(&reasoning.text);
reasoning_content.push('\n');
}
EventMsg::ExecCommandBegin(cmd) => {
println!("⚡ Executing: {:?}", cmd.command);
// Add bash command as a tool call
tool_calls.push(serde_json::json!({
"id": format!("exec_{}", cmd.call_id),
"type": "function",
"function": {
"name": "bash",
"arguments": serde_json::to_string(&serde_json::json!({
"command": cmd.command
})).unwrap_or_default()
},
"timestamp": chrono::Utc::now().to_rfc3339()
}));
}
EventMsg::ExecCommandEnd(result) => {
let stdout_preview = if result.stdout.len() > 200 {
&result.stdout[..200]
} else {
&result.stdout
};
println!("📊 Command result: {}", stdout_preview);
// Add bash command result as a tool response
tool_responses.push(serde_json::json!({
"role": "tool",
"tool_call_id": format!("exec_{}", result.call_id),
"content": serde_json::to_string(&serde_json::json!({
"exit_code": result.exit_code,
"stdout": result.stdout,
"stderr": result.stderr
})).unwrap_or_default(),
"timestamp": chrono::Utc::now().to_rfc3339()
}));
}
EventMsg::McpToolCallBegin(tool) => {
println!("🔧 Calling tool: {}", tool.invocation.tool);
// Check if this is a bugcrowd_submit call - always require external LLM approval
if tool.invocation.tool == "bugcrowd_submit" {
println!(
"🤖 Requesting approval from external LLM for bugcrowd_submit tool..."
);
// Use the specialized bugcrowd approval prompt
let tool_approval_prompt = inject_bugcrowd_approval_variables(
bugcrowd_approval_prompt_template,
&tool.invocation.tool,
&tool.invocation.arguments,
);
match generate_user_prompt(
&tool_approval_prompt,
driver_model,
&session_logs_dir,
)
.await
{
Ok((response, _)) => {
println!("🤖 External LLM response: {}", response);
let (approved, reasoning) =
parse_approval_response(&response);
if approved {
println!(
"✅ Bugcrowd submission approved by external LLM: {}",
reasoning
);
// Let the tool call proceed normally
} else {
println!(
"❌ Bugcrowd submission denied by external LLM: {}",
reasoning
);
// Track this call as denied so we ignore its McpToolCallEnd event
denied_tool_calls.insert(tool.call_id.clone());
// Create a fake tool response with the denial reasoning
// This prevents the actual MCP tool from being called
tool_responses.push(serde_json::json!({
"role": "tool",
"tool_call_id": tool.call_id,
"content": format!("❌ Bugcrowd submission denied by security review: {}", reasoning)
}));
// Skip to next event - don't let this tool call proceed
continue;
}
}
Err(e) => {
println!(
"❌ Error getting approval from external LLM: {}",
e
);
// Create a tool response with the error
tool_responses.push(serde_json::json!({
"role": "tool",
"tool_call_id": tool.call_id,
"content": format!("❌ Bugcrowd submission failed due to approval error: {}", e)
}));
// Skip to next event - don't let this tool call proceed
continue;
}
}
}
// Add tool call to OpenAI format
tool_calls.push(serde_json::json!({
"id": tool.call_id,
"type": "function",
"function": {
"name": tool.invocation.tool,
"arguments": serde_json::to_string(&tool.invocation.arguments).unwrap_or_default()
},
"timestamp": chrono::Utc::now().to_rfc3339()
}));
}
EventMsg::McpToolCallEnd(result) => {
// Skip results for denied tool calls (we already added the denial response)
if denied_tool_calls.contains(&result.call_id) {
println!(
"🚫 Ignoring result for denied tool call: {}",
result.call_id
);
continue;
}
match &result.result {
Ok(success) => {
println!("✅ Tool result: {:?}", success);
// Add tool response to conversation log
tool_responses.push(serde_json::json!({
"role": "tool",
"tool_call_id": result.call_id,
"content": serde_json::to_string(success).unwrap_or_default(),
"timestamp": chrono::Utc::now().to_rfc3339()
}));
}
Err(err) => {
println!("❌ Tool error: {}", err);
// Add tool error to conversation log
tool_responses.push(serde_json::json!({
"role": "tool",
"tool_call_id": result.call_id,
"content": format!("Error: {}", err),
"timestamp": chrono::Utc::now().to_rfc3339()
}));
}
}
}
EventMsg::ExecApprovalRequest(approval) => {
println!("🔍 Approval requested for command: {:?}", approval.command);
// Add approval request as a tool call
let approval_id = format!(
"approval_{}",
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis()
);
tool_calls.push(serde_json::json!({
"id": approval_id.clone(),
"type": "function",
"function": {
"name": "request_approval",
"arguments": serde_json::to_string(&approval).unwrap_or_default()
}
}));
// Check if it's a bugcrowd_submit call - always require external LLM approval
let is_bugcrowd_submit = approval.command.iter().any(|arg| {
arg.contains("bugcrowd_submit") || arg.contains("bugcrowd-submit")
});
// Generate approval prompt with task context
let approval_prompt = inject_approval_variables_with_context(
approval_prompt_template,
&approval.command,
&approval.cwd,
&approval.reason,
&config_content,
);
let context_info = if is_bugcrowd_submit {
" (BUGCROWD SUBMISSION - Requires careful review)"
} else {
""
};
println!(
"🤖 Requesting approval from external LLM{}...",
context_info
);
let decision = match generate_user_prompt(
&approval_prompt,
driver_model,
&session_logs_dir,
)
.await
gitextract_2yw_apz6/
├── .env.example
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── NOTICE
├── README.md
├── codex-rs/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── README.md
│ ├── ansi-escape/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ └── lib.rs
│ ├── apply-patch/
│ │ ├── Cargo.toml
│ │ ├── apply_patch_tool_instructions.md
│ │ ├── src/
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ ├── parser.rs
│ │ │ ├── seek_sequence.rs
│ │ │ └── standalone_executable.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── cli.rs
│ │ └── mod.rs
│ ├── arg0/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── chatgpt/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── src/
│ │ │ ├── apply_command.rs
│ │ │ ├── chatgpt_client.rs
│ │ │ ├── chatgpt_token.rs
│ │ │ ├── get_task.rs
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ ├── suite/
│ │ │ ├── apply_command_e2e.rs
│ │ │ └── mod.rs
│ │ └── task_turn_fixture.json
│ ├── cli/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── debug_sandbox.rs
│ │ ├── exit_status.rs
│ │ ├── lib.rs
│ │ ├── login.rs
│ │ ├── main.rs
│ │ └── proto.rs
│ ├── clippy.toml
│ ├── common/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── approval_mode_cli_arg.rs
│ │ ├── approval_presets.rs
│ │ ├── config_override.rs
│ │ ├── config_summary.rs
│ │ ├── elapsed.rs
│ │ ├── fuzzy_match.rs
│ │ ├── lib.rs
│ │ ├── model_presets.rs
│ │ ├── sandbox_mode_cli_arg.rs
│ │ └── sandbox_summary.rs
│ ├── config.md
│ ├── core/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── active_directory.md
│ │ ├── approval_prompt.txt
│ │ ├── bugcrowd_approval_prompt.txt
│ │ ├── client_side_web.md
│ │ ├── continuation_prompt.txt
│ │ ├── enumeration.md
│ │ ├── initial_prompt.txt
│ │ ├── linux_privesc.md
│ │ ├── prompt.md
│ │ ├── shelling.md
│ │ ├── src/
│ │ │ ├── apply_patch.rs
│ │ │ ├── bash.rs
│ │ │ ├── chat_completions.rs
│ │ │ ├── client.rs
│ │ │ ├── client_common.rs
│ │ │ ├── codex.rs
│ │ │ ├── codex_conversation.rs
│ │ │ ├── config.rs
│ │ │ ├── config_profile.rs
│ │ │ ├── config_types.rs
│ │ │ ├── conversation_history.rs
│ │ │ ├── conversation_manager.rs
│ │ │ ├── environment_context.rs
│ │ │ ├── error.rs
│ │ │ ├── exec.rs
│ │ │ ├── exec_command/
│ │ │ │ ├── exec_command_params.rs
│ │ │ │ ├── exec_command_session.rs
│ │ │ │ ├── mod.rs
│ │ │ │ ├── responses_api.rs
│ │ │ │ ├── session_id.rs
│ │ │ │ └── session_manager.rs
│ │ │ ├── exec_env.rs
│ │ │ ├── flags.rs
│ │ │ ├── git_info.rs
│ │ │ ├── is_safe_command.rs
│ │ │ ├── landlock.rs
│ │ │ ├── lib.rs
│ │ │ ├── mcp_connection_manager.rs
│ │ │ ├── mcp_tool_call.rs
│ │ │ ├── message_history.rs
│ │ │ ├── model_family.rs
│ │ │ ├── model_provider_info.rs
│ │ │ ├── openai_model_info.rs
│ │ │ ├── openai_tools.rs
│ │ │ ├── parse_command.rs
│ │ │ ├── plan_tool.rs
│ │ │ ├── project_doc.rs
│ │ │ ├── prompt_for_compact_command.md
│ │ │ ├── rollout.rs
│ │ │ ├── safety.rs
│ │ │ ├── seatbelt.rs
│ │ │ ├── seatbelt_base_policy.sbpl
│ │ │ ├── shell.rs
│ │ │ ├── spawn.rs
│ │ │ ├── terminal.rs
│ │ │ ├── tool_apply_patch.rs
│ │ │ ├── turn_diff_tracker.rs
│ │ │ ├── user_agent.rs
│ │ │ ├── user_notification.rs
│ │ │ └── util.rs
│ │ ├── summarization_prompt.txt
│ │ ├── tests/
│ │ │ ├── all.rs
│ │ │ ├── cli_responses_fixture.sse
│ │ │ ├── common/
│ │ │ │ ├── Cargo.toml
│ │ │ │ └── lib.rs
│ │ │ ├── fixtures/
│ │ │ │ ├── completed_template.json
│ │ │ │ └── incomplete_sse.json
│ │ │ └── suite/
│ │ │ ├── cli_stream.rs
│ │ │ ├── client.rs
│ │ │ ├── compact.rs
│ │ │ ├── exec.rs
│ │ │ ├── exec_stream_events.rs
│ │ │ ├── live_cli.rs
│ │ │ ├── mod.rs
│ │ │ ├── prompt_caching.rs
│ │ │ ├── seatbelt.rs
│ │ │ ├── stream_error_allows_next_turn.rs
│ │ │ └── stream_no_completed.rs
│ │ ├── web.md
│ │ ├── web_enumeration.md
│ │ └── windows_privesc.md
│ ├── cve-prompt.md
│ ├── default.nix
│ ├── disclosure-email-prompt.md
│ ├── docs/
│ │ └── protocol_v1.md
│ ├── exec/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── cli.rs
│ │ │ ├── event_processor.rs
│ │ │ ├── event_processor_with_human_output.rs
│ │ │ ├── event_processor_with_json_output.rs
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ └── realtime_logger.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ ├── fixtures/
│ │ │ ├── apply_patch_freeform_final.txt
│ │ │ ├── sse_apply_patch_add.json
│ │ │ ├── sse_apply_patch_freeform_add.json
│ │ │ ├── sse_apply_patch_freeform_update.json
│ │ │ ├── sse_apply_patch_update.json
│ │ │ └── sse_response_completed.json
│ │ └── suite/
│ │ ├── apply_patch.rs
│ │ ├── common.rs
│ │ ├── mod.rs
│ │ └── sandbox.rs
│ ├── execpolicy/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── build.rs
│ │ ├── src/
│ │ │ ├── arg_matcher.rs
│ │ │ ├── arg_resolver.rs
│ │ │ ├── arg_type.rs
│ │ │ ├── default.policy
│ │ │ ├── error.rs
│ │ │ ├── exec_call.rs
│ │ │ ├── execv_checker.rs
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ ├── opt.rs
│ │ │ ├── policy.rs
│ │ │ ├── policy_parser.rs
│ │ │ ├── program.rs
│ │ │ ├── sed_command.rs
│ │ │ └── valid_exec.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── bad.rs
│ │ ├── cp.rs
│ │ ├── good.rs
│ │ ├── head.rs
│ │ ├── literal.rs
│ │ ├── ls.rs
│ │ ├── mod.rs
│ │ ├── parse_sed_command.rs
│ │ ├── pwd.rs
│ │ └── sed.rs
│ ├── file-search/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── cli.rs
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── justfile
│ ├── linux-sandbox/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── src/
│ │ │ ├── landlock.rs
│ │ │ ├── lib.rs
│ │ │ ├── linux_run_main.rs
│ │ │ └── main.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── landlock.rs
│ │ └── mod.rs
│ ├── login/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── assets/
│ │ │ │ └── success.html
│ │ │ ├── auth_manager.rs
│ │ │ ├── lib.rs
│ │ │ ├── pkce.rs
│ │ │ ├── server.rs
│ │ │ └── token_data.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── login_server_e2e.rs
│ │ └── mod.rs
│ ├── mcp-client/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ ├── main.rs
│ │ └── mcp_client.rs
│ ├── mcp-server/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── codex_message_processor.rs
│ │ │ ├── codex_tool_config.rs
│ │ │ ├── codex_tool_runner.rs
│ │ │ ├── error_code.rs
│ │ │ ├── exec_approval.rs
│ │ │ ├── json_to_toml.rs
│ │ │ ├── lib.rs
│ │ │ ├── main.rs
│ │ │ ├── message_processor.rs
│ │ │ ├── outgoing_message.rs
│ │ │ ├── patch_approval.rs
│ │ │ └── tool_handlers/
│ │ │ └── mod.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ ├── common/
│ │ │ ├── Cargo.toml
│ │ │ ├── lib.rs
│ │ │ ├── mcp_process.rs
│ │ │ ├── mock_model_server.rs
│ │ │ └── responses.rs
│ │ └── suite/
│ │ ├── auth.rs
│ │ ├── codex_message_processor_flow.rs
│ │ ├── codex_tool.rs
│ │ ├── config.rs
│ │ ├── create_conversation.rs
│ │ ├── interrupt.rs
│ │ ├── login.rs
│ │ ├── mod.rs
│ │ └── send_message.rs
│ ├── mcp-types/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── generate_mcp_types.py
│ │ ├── schema/
│ │ │ ├── 2025-03-26/
│ │ │ │ └── schema.json
│ │ │ └── 2025-06-18/
│ │ │ └── schema.json
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── tests/
│ │ ├── all.rs
│ │ └── suite/
│ │ ├── initialize.rs
│ │ ├── mod.rs
│ │ └── progress_notification.rs
│ ├── ollama/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── client.rs
│ │ ├── lib.rs
│ │ ├── parser.rs
│ │ ├── pull.rs
│ │ └── url.rs
│ ├── protocol/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── config_types.rs
│ │ ├── lib.rs
│ │ ├── mcp_protocol.rs
│ │ ├── message_history.rs
│ │ ├── models.rs
│ │ ├── parse_command.rs
│ │ ├── plan_tool.rs
│ │ └── protocol.rs
│ ├── protocol-ts/
│ │ ├── Cargo.toml
│ │ ├── generate-ts
│ │ └── src/
│ │ ├── lib.rs
│ │ └── main.rs
│ ├── rust-toolchain.toml
│ ├── rustfmt.toml
│ ├── scripts/
│ │ └── create_github_release.sh
│ └── tui/
│ ├── Cargo.toml
│ ├── prompt_for_init_command.md
│ ├── src/
│ │ ├── app.rs
│ │ ├── app_backtrack.rs
│ │ ├── app_event.rs
│ │ ├── app_event_sender.rs
│ │ ├── backtrack_helpers.rs
│ │ ├── bottom_pane/
│ │ │ ├── approval_modal_view.rs
│ │ │ ├── bottom_pane_view.rs
│ │ │ ├── chat_composer.rs
│ │ │ ├── chat_composer_history.rs
│ │ │ ├── command_popup.rs
│ │ │ ├── file_search_popup.rs
│ │ │ ├── list_selection_view.rs
│ │ │ ├── mod.rs
│ │ │ ├── popup_consts.rs
│ │ │ ├── scroll_state.rs
│ │ │ ├── selection_popup_common.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__backspace_after_pastes.snap
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__empty.snap
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__large.snap
│ │ │ │ ├── codex_tui__bottom_pane__chat_composer__tests__multiple_pastes.snap
│ │ │ │ └── codex_tui__bottom_pane__chat_composer__tests__small.snap
│ │ │ └── textarea.rs
│ │ ├── chatwidget/
│ │ │ ├── agent.rs
│ │ │ ├── interrupts.rs
│ │ │ ├── snapshots/
│ │ │ │ ├── codex_tui__chatwidget__tests__approval_modal_exec.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__approval_modal_patch.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_idle_h1.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_idle_h2.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_idle_h3.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_running_h1.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_running_h2.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__chat_small_running_h3.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__deltas_then_same_final_message_are_rendered_snapshot.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__final_reasoning_then_message_without_deltas_are_rendered.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__interrupt_exec_marks_failed.snap
│ │ │ │ ├── codex_tui__chatwidget__tests__status_widget_active.snap
│ │ │ │ └── codex_tui__chatwidget__tests__status_widget_and_approval_modal.snap
│ │ │ └── tests.rs
│ │ ├── chatwidget.rs
│ │ ├── chatwidget_stream_tests.rs
│ │ ├── citation_regex.rs
│ │ ├── cli.rs
│ │ ├── clipboard_paste.rs
│ │ ├── common.rs
│ │ ├── custom_terminal.rs
│ │ ├── diff_render.rs
│ │ ├── exec_command.rs
│ │ ├── file_search.rs
│ │ ├── get_git_diff.rs
│ │ ├── history_cell.rs
│ │ ├── insert_history.rs
│ │ ├── lib.rs
│ │ ├── live_wrap.rs
│ │ ├── main.rs
│ │ ├── markdown.rs
│ │ ├── markdown_stream.rs
│ │ ├── onboarding/
│ │ │ ├── auth.rs
│ │ │ ├── mod.rs
│ │ │ ├── onboarding_screen.rs
│ │ │ ├── trust_directory.rs
│ │ │ └── welcome.rs
│ │ ├── pager_overlay.rs
│ │ ├── render/
│ │ │ ├── line_utils.rs
│ │ │ ├── markdown_utils.rs
│ │ │ └── mod.rs
│ │ ├── session_log.rs
│ │ ├── shimmer.rs
│ │ ├── slash_command.rs
│ │ ├── snapshots/
│ │ │ ├── codex_tui__diff_render__tests__add_details.snap
│ │ │ ├── codex_tui__diff_render__tests__blank_context_line.snap
│ │ │ ├── codex_tui__diff_render__tests__single_line_replacement_counts.snap
│ │ │ ├── codex_tui__diff_render__tests__update_details_with_rename.snap
│ │ │ ├── codex_tui__diff_render__tests__vertical_ellipsis_between_hunks.snap
│ │ │ ├── codex_tui__diff_render__tests__wrap_behavior_insert.snap
│ │ │ ├── codex_tui__pager_overlay__tests__static_overlay_snapshot_basic.snap
│ │ │ ├── codex_tui__pager_overlay__tests__transcript_overlay_snapshot_basic.snap
│ │ │ ├── codex_tui__status_indicator_widget__tests__renders_truncated.snap
│ │ │ ├── codex_tui__status_indicator_widget__tests__renders_with_queued_messages.snap
│ │ │ └── codex_tui__status_indicator_widget__tests__renders_with_working_header.snap
│ │ ├── status_indicator_widget.rs
│ │ ├── streaming/
│ │ │ ├── controller.rs
│ │ │ └── mod.rs
│ │ ├── text_formatting.rs
│ │ ├── tui.rs
│ │ ├── updates.rs
│ │ └── user_approval_widget.rs
│ ├── styles.md
│ └── tests/
│ ├── all.rs
│ ├── fixtures/
│ │ ├── binary-size-log.jsonl
│ │ ├── ideal-binary-response.txt
│ │ └── oss-story.jsonl
│ └── suite/
│ ├── mod.rs
│ ├── status_indicator.rs
│ ├── vt100_history.rs
│ ├── vt100_live_commit.rs
│ └── vt100_streaming_no_dup.rs
├── configs/
│ ├── stanford/
│ │ └── example.yaml
│ └── tests/
│ ├── ctf_easy.yaml
│ └── simple.yaml
├── docs/
│ ├── SYSTEM_ARCHITECTURES.md
│ ├── TRIAGE_ARCHITECTURE.md
│ ├── license.md
│ └── supervisor-usage.md
├── pyproject.toml
├── run_docker.sh
├── supervisor/
│ ├── __init__.py
│ ├── config.py
│ ├── context_manager.py
│ ├── orchestration/
│ │ ├── __init__.py
│ │ ├── instance_manager.py
│ │ ├── log_reader.py
│ │ ├── orchestrator.py
│ │ ├── prompt_generator.py
│ │ └── router.py
│ ├── prompts/
│ │ ├── __init__.py
│ │ ├── continuation_context_prompt.py
│ │ ├── router_prompt.py
│ │ ├── summarization_prompt.py
│ │ └── supervisor_prompt.py
│ ├── submissions/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── ctf.py
│ │ ├── registry.py
│ │ └── vulnerability.py
│ ├── supervisor.py
│ ├── todo_generator.py
│ ├── tools.py
│ ├── triage/
│ │ ├── prompts/
│ │ │ ├── __init__.py
│ │ │ ├── initial_review_prompt.py
│ │ │ ├── severity_prompt.py
│ │ │ ├── system_prompt.py
│ │ │ └── validation_prompt.py
│ │ ├── triage_manager.py
│ │ └── triage_tools.py
│ ├── vulnerability_storage.py
│ └── working_hours.py
└── test_files/
└── it_has_begun/
└── script.sh
Showing preview only (280K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (2993 symbols across 258 files)
FILE: codex-rs/ansi-escape/src/lib.rs
function ansi_escape_line (line 9) | pub fn ansi_escape_line(s: &str) -> Line<'static> {
function ansi_escape (line 21) | pub fn ansi_escape(s: &str) -> Text<'static> {
FILE: codex-rs/apply-patch/src/lib.rs
constant APPLY_PATCH_TOOL_INSTRUCTIONS (line 26) | pub const APPLY_PATCH_TOOL_INSTRUCTIONS: &str = include_str!("../apply_p...
constant APPLY_PATCH_COMMANDS (line 28) | const APPLY_PATCH_COMMANDS: [&str; 2] = ["apply_patch", "applypatch"];
type ApplyPatchError (line 31) | pub enum ApplyPatchError {
method from (line 42) | fn from(err: std::io::Error) -> Self {
method from (line 51) | fn from(err: &std::io::Error) -> Self {
type IoError (line 61) | pub struct IoError {
method eq (line 68) | fn eq(&self, other: &Self) -> bool {
type MaybeApplyPatch (line 74) | pub enum MaybeApplyPatch {
type ApplyPatchArgs (line 84) | pub struct ApplyPatchArgs {
function maybe_parse_apply_patch (line 89) | pub fn maybe_parse_apply_patch(argv: &[String]) -> MaybeApplyPatch {
type ApplyPatchFileChange (line 115) | pub enum ApplyPatchFileChange {
type MaybeApplyPatchVerified (line 129) | pub enum MaybeApplyPatchVerified {
type ApplyPatchAction (line 146) | pub struct ApplyPatchAction {
method is_empty (line 159) | pub fn is_empty(&self) -> bool {
method changes (line 164) | pub fn changes(&self) -> &HashMap<PathBuf, ApplyPatchFileChange> {
method new_add_for_test (line 170) | pub fn new_add_for_test(path: &Path, content: String) -> Self {
function maybe_parse_apply_patch_verified (line 202) | pub fn maybe_parse_apply_patch_verified(argv: &[String], cwd: &Path) -> ...
function extract_heredoc_body_from_apply_patch_command (line 268) | fn extract_heredoc_body_from_apply_patch_command(
type ExtractHeredocError (line 311) | pub enum ExtractHeredocError {
function apply_patch (line 320) | pub fn apply_patch(
function apply_hunks (line 353) | pub fn apply_hunks(
type AffectedPaths (line 408) | pub struct AffectedPaths {
function apply_hunks_to_files (line 416) | fn apply_hunks_to_files(hunks: &[Hunk]) -> anyhow::Result<AffectedPaths> {
type AppliedPatch (line 478) | struct AppliedPatch {
function derive_new_contents_from_chunks (line 485) | fn derive_new_contents_from_chunks(
function compute_replacements (line 526) | fn compute_replacements(
function apply_replacements (line 616) | fn apply_replacements(
type ApplyPatchFileUpdate (line 644) | pub struct ApplyPatchFileUpdate {
function unified_diff_from_chunks (line 649) | pub fn unified_diff_from_chunks(
function unified_diff_from_chunks_with_context (line 656) | pub fn unified_diff_from_chunks_with_context(
function print_summary (line 675) | pub fn print_summary(
function wrap_patch (line 700) | fn wrap_patch(body: &str) -> String {
function strs_to_strings (line 704) | fn strs_to_strings(strs: &[&str]) -> Vec<String> {
function test_literal (line 709) | fn test_literal() {
function test_literal_applypatch (line 734) | fn test_literal_applypatch() {
function test_heredoc (line 759) | fn test_heredoc() {
function test_heredoc_applypatch (line 786) | fn test_heredoc_applypatch() {
function test_add_file_hunk_creates_file_with_contents (line 813) | fn test_add_file_hunk_creates_file_with_contents() {
function test_delete_file_hunk_removes_file (line 839) | fn test_delete_file_hunk_removes_file() {
function test_update_file_hunk_modifies_content (line 859) | fn test_update_file_hunk_modifies_content() {
function test_update_file_hunk_can_move_file (line 888) | fn test_update_file_hunk_can_move_file() {
function test_multiple_update_chunks_apply_to_single_file (line 922) | fn test_multiple_update_chunks_apply_to_single_file() {
function test_update_file_hunk_interleaved_changes (line 962) | fn test_update_file_hunk_interleaved_changes() {
function test_update_line_with_unicode_dash (line 1016) | fn test_update_line_with_unicode_dash() {
function test_unified_diff (line 1055) | fn test_unified_diff() {
function test_unified_diff_first_line_replacement (line 1095) | fn test_unified_diff_first_line_replacement() {
function test_unified_diff_last_line_replacement (line 1131) | fn test_unified_diff_last_line_replacement() {
function test_unified_diff_insert_at_eof (line 1168) | fn test_unified_diff_insert_at_eof() {
function test_unified_diff_interleaved_changes (line 1202) | fn test_unified_diff_interleaved_changes() {
function test_apply_patch_should_resolve_absolute_paths_in_cwd (line 1274) | fn test_apply_patch_should_resolve_absolute_paths_in_cwd() {
function test_apply_patch_fails_on_write_error (line 1320) | fn test_apply_patch_fails_on_write_error() {
FILE: codex-rs/apply-patch/src/main.rs
function main (line 1) | pub fn main() -> ! {
FILE: codex-rs/apply-patch/src/parser.rs
constant BEGIN_PATCH_MARKER (line 31) | const BEGIN_PATCH_MARKER: &str = "*** Begin Patch";
constant END_PATCH_MARKER (line 32) | const END_PATCH_MARKER: &str = "*** End Patch";
constant ADD_FILE_MARKER (line 33) | const ADD_FILE_MARKER: &str = "*** Add File: ";
constant DELETE_FILE_MARKER (line 34) | const DELETE_FILE_MARKER: &str = "*** Delete File: ";
constant UPDATE_FILE_MARKER (line 35) | const UPDATE_FILE_MARKER: &str = "*** Update File: ";
constant MOVE_TO_MARKER (line 36) | const MOVE_TO_MARKER: &str = "*** Move to: ";
constant EOF_MARKER (line 37) | const EOF_MARKER: &str = "*** End of File";
constant CHANGE_CONTEXT_MARKER (line 38) | const CHANGE_CONTEXT_MARKER: &str = "@@ ";
constant EMPTY_CHANGE_CONTEXT_MARKER (line 39) | const EMPTY_CHANGE_CONTEXT_MARKER: &str = "@@";
constant PARSE_IN_STRICT_MODE (line 47) | const PARSE_IN_STRICT_MODE: bool = false;
type ParseError (line 50) | pub enum ParseError {
type Hunk (line 60) | pub enum Hunk {
method resolve_path (line 79) | pub fn resolve_path(&self, cwd: &Path) -> PathBuf {
type UpdateFileChunk (line 91) | pub struct UpdateFileChunk {
function parse_patch (line 106) | pub fn parse_patch(patch: &str) -> Result<ApplyPatchArgs, ParseError> {
type ParseMode (line 115) | enum ParseMode {
function parse_patch_text (line 154) | fn parse_patch_text(patch: &str, mode: ParseMode) -> Result<ApplyPatchAr...
function check_patch_boundaries_strict (line 183) | fn check_patch_boundaries_strict(lines: &[&str]) -> Result<(), ParseErro...
function check_patch_boundaries_lenient (line 199) | fn check_patch_boundaries_lenient<'a>(
function check_start_and_end_lines_strict (line 222) | fn check_start_and_end_lines_strict(
function parse_one_hunk (line 241) | fn parse_one_hunk(lines: &[&str], line_number: usize) -> Result<(Hunk, u...
function parse_update_file_chunk (line 336) | fn parse_update_file_chunk(
function test_parse_patch (line 430) | fn test_parse_patch() {
function test_parse_patch_lenient (line 561) | fn test_parse_patch_lenient() {
function test_parse_one_hunk (line 644) | fn test_parse_one_hunk() {
function test_update_file_chunk (line 657) | fn test_update_file_chunk() {
FILE: codex-rs/apply-patch/src/seek_sequence.rs
function seek_sequence (line 12) | pub(crate) fn seek_sequence(
function to_vec (line 116) | fn to_vec(strings: &[&str]) -> Vec<String> {
function test_exact_match_finds_sequence (line 121) | fn test_exact_match_finds_sequence() {
function test_rstrip_match_ignores_trailing_whitespace (line 128) | fn test_rstrip_match_ignores_trailing_whitespace() {
function test_trim_match_ignores_leading_and_trailing_whitespace (line 136) | fn test_trim_match_ignores_leading_and_trailing_whitespace() {
function test_pattern_longer_than_input_returns_none (line 144) | fn test_pattern_longer_than_input_returns_none() {
FILE: codex-rs/apply-patch/src/standalone_executable.rs
function main (line 4) | pub fn main() -> ! {
function run_main (line 11) | pub fn run_main() -> i32 {
FILE: codex-rs/apply-patch/tests/suite/cli.rs
function test_apply_patch_cli_add_and_update (line 7) | fn test_apply_patch_cli_add_and_update() -> anyhow::Result<()> {
function test_apply_patch_cli_stdin_add_and_update (line 50) | fn test_apply_patch_cli_stdin_add_and_update() -> anyhow::Result<()> {
FILE: codex-rs/arg0/src/lib.rs
constant LINUX_SANDBOX_ARG0 (line 10) | const LINUX_SANDBOX_ARG0: &str = "codex-linux-sandbox";
constant APPLY_PATCH_ARG0 (line 11) | const APPLY_PATCH_ARG0: &str = "apply_patch";
constant MISSPELLED_APPLY_PATCH_ARG0 (line 12) | const MISSPELLED_APPLY_PATCH_ARG0: &str = "applypatch";
function arg0_dispatch_or_else (line 36) | pub fn arg0_dispatch_or_else<F, Fut>(main_fn: F) -> anyhow::Result<()>
constant ILLEGAL_ENV_VAR_PREFIX (line 107) | const ILLEGAL_ENV_VAR_PREFIX: &str = "CODEX_";
function load_dotenv (line 113) | fn load_dotenv() {
function set_filtered (line 126) | fn set_filtered<I>(iter: I)
function prepend_path_entry_for_apply_patch (line 152) | fn prepend_path_entry_for_apply_patch() -> std::io::Result<TempDir> {
FILE: codex-rs/chatgpt/src/apply_command.rs
type ApplyCommand (line 16) | pub struct ApplyCommand {
function run_apply_command (line 22) | pub async fn run_apply_command(
function apply_diff_from_task (line 40) | pub async fn apply_diff_from_task(
function apply_diff (line 58) | async fn apply_diff(diff: &str, cwd: Option<PathBuf>) -> anyhow::Result<...
FILE: codex-rs/chatgpt/src/chatgpt_client.rs
function chatgpt_get_request (line 11) | pub(crate) async fn chatgpt_get_request<T: DeserializeOwned>(
FILE: codex-rs/chatgpt/src/chatgpt_token.rs
function get_chatgpt_token_data (line 11) | pub fn get_chatgpt_token_data() -> Option<TokenData> {
function set_chatgpt_token_data (line 15) | pub fn set_chatgpt_token_data(value: TokenData) {
function init_chatgpt_token_from_auth (line 22) | pub async fn init_chatgpt_token_from_auth(codex_home: &Path) -> std::io:...
FILE: codex-rs/chatgpt/src/get_task.rs
type GetTaskResponse (line 7) | pub struct GetTaskResponse {
type AssistantTurn (line 13) | pub struct AssistantTurn {
type OutputItem (line 19) | pub enum OutputItem {
type PrOutputItem (line 28) | pub struct PrOutputItem {
type OutputDiff (line 33) | pub struct OutputDiff {
function get_task (line 37) | pub(crate) async fn get_task(config: &Config, task_id: String) -> anyhow...
FILE: codex-rs/chatgpt/tests/suite/apply_command_e2e.rs
function create_temp_git_repo (line 8) | async fn create_temp_git_repo() -> anyhow::Result<TempDir> {
function mock_get_task_with_fixture (line 70) | async fn mock_get_task_with_fixture() -> anyhow::Result<GetTaskResponse> {
function test_apply_command_creates_fibonacci_file (line 78) | async fn test_apply_command_creates_fibonacci_file() {
function test_apply_command_with_merge_conflicts (line 120) | async fn test_apply_command_with_merge_conflicts() {
FILE: codex-rs/cli/src/debug_sandbox.rs
function run_command_under_seatbelt (line 16) | pub async fn run_command_under_seatbelt(
function run_command_under_landlock (line 35) | pub async fn run_command_under_landlock(
type SandboxType (line 54) | enum SandboxType {
function run_command_under_sandbox (line 59) | async fn run_command_under_sandbox(
function create_sandbox_mode (line 107) | pub fn create_sandbox_mode(full_auto: bool) -> SandboxMode {
FILE: codex-rs/cli/src/exit_status.rs
function handle_exit_status (line 2) | pub(crate) fn handle_exit_status(status: std::process::ExitStatus) -> ! {
function handle_exit_status (line 16) | pub(crate) fn handle_exit_status(status: std::process::ExitStatus) -> ! {
FILE: codex-rs/cli/src/lib.rs
type SeatbeltCommand (line 10) | pub struct SeatbeltCommand {
type LandlockCommand (line 24) | pub struct LandlockCommand {
FILE: codex-rs/cli/src/login.rs
function login_with_chatgpt (line 15) | pub async fn login_with_chatgpt(codex_home: PathBuf) -> std::io::Result<...
function run_login_with_chatgpt (line 27) | pub async fn run_login_with_chatgpt(cli_config_overrides: CliConfigOverr...
function run_login_with_api_key (line 42) | pub async fn run_login_with_api_key(
function run_login_status (line 60) | pub async fn run_login_status(cli_config_overrides: CliConfigOverrides) ...
function run_logout (line 99) | pub async fn run_logout(cli_config_overrides: CliConfigOverrides) -> ! {
function load_config_or_exit (line 118) | fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Conf...
function safe_format_key (line 137) | fn safe_format_key(key: &str) -> String {
function formats_long_key (line 151) | fn formats_long_key() {
function short_key_returns_stars (line 157) | fn short_key_returns_stars() {
FILE: codex-rs/cli/src/main.rs
type MultitoolCli (line 38) | struct MultitoolCli {
type Subcommand (line 50) | enum Subcommand {
type CompletionCommand (line 88) | struct CompletionCommand {
type DebugArgs (line 95) | struct DebugArgs {
type DebugCommand (line 101) | enum DebugCommand {
type LoginCommand (line 110) | struct LoginCommand {
type LoginSubcommand (line 122) | enum LoginSubcommand {
type LogoutCommand (line 128) | struct LogoutCommand {
type GenerateTsCommand (line 134) | struct GenerateTsCommand {
type AutonomousCommand (line 145) | struct AutonomousCommand {
function main (line 189) | fn main() -> anyhow::Result<()> {
function cli_main (line 196) | async fn cli_main(codex_linux_sandbox_exe: Option<PathBuf>) -> anyhow::R...
function run_autonomous_mode (line 275) | async fn run_autonomous_mode(
function collect_codex_response_with_tools (line 972) | async fn collect_codex_response_with_tools(
function inject_template_variables (line 1439) | fn inject_template_variables(template: &str, config_yaml: &str, context:...
function _inject_approval_variables (line 1445) | fn _inject_approval_variables(
function inject_approval_variables_with_context (line 1461) | fn inject_approval_variables_with_context(
function inject_patch_approval_variables_with_context (line 1479) | fn inject_patch_approval_variables_with_context(
function inject_bugcrowd_approval_variables (line 1499) | fn inject_bugcrowd_approval_variables(
function parse_approval_response (line 1514) | fn parse_approval_response(response: &str) -> (bool, String) {
function count_tokens (line 1556) | fn count_tokens(text: &str) -> anyhow::Result<usize> {
function summarize_context (line 1562) | async fn summarize_context(
function generate_user_prompt (line 1588) | async fn generate_user_prompt(
function handle_supervisor_tool_calls (line 1921) | async fn handle_supervisor_tool_calls(
function prepend_config_flags (line 2124) | fn prepend_config_flags(
function print_completion (line 2133) | fn print_completion(cmd: CompletionCommand) {
FILE: codex-rs/cli/src/proto.rs
type ProtoCli (line 19) | pub struct ProtoCli {
function run_main (line 24) | pub async fn run_main(opts: ProtoCli) -> anyhow::Result<()> {
FILE: codex-rs/common/src/approval_mode_cli_arg.rs
type ApprovalModeCliArg (line 10) | pub enum ApprovalModeCliArg {
method from (line 30) | fn from(value: ApprovalModeCliArg) -> Self {
FILE: codex-rs/common/src/approval_presets.rs
type ApprovalPreset (line 6) | pub struct ApprovalPreset {
function builtin_approval_presets (line 22) | pub fn builtin_approval_presets() -> Vec<ApprovalPreset> {
FILE: codex-rs/common/src/config_override.rs
type CliConfigOverrides (line 19) | pub struct CliConfigOverrides {
method parse_overrides (line 42) | pub fn parse_overrides(&self) -> Result<Vec<(String, Value)>, String> {
method apply_on_value (line 82) | pub fn apply_on_value(&self, target: &mut Value) -> Result<(), String> {
function apply_single_override (line 93) | fn apply_single_override(root: &mut Value, path: &str, value: Value) {
function parse_toml_value (line 135) | fn parse_toml_value(raw: &str) -> Result<Value, toml::de::Error> {
function parses_basic_scalar (line 149) | fn parses_basic_scalar() {
function fails_on_unquoted_string (line 155) | fn fails_on_unquoted_string() {
function parses_array (line 160) | fn parses_array() {
function parses_inline_table (line 167) | fn parses_inline_table() {
FILE: codex-rs/common/src/config_summary.rs
function create_config_summary_entries (line 7) | pub fn create_config_summary_entries(config: &Config) -> Vec<(&'static s...
FILE: codex-rs/common/src/elapsed.rs
function format_elapsed (line 6) | pub fn format_elapsed(start_time: Instant) -> String {
function format_duration (line 16) | pub fn format_duration(duration: Duration) -> String {
function format_elapsed_millis (line 21) | fn format_elapsed_millis(millis: i64) -> String {
function test_format_duration_subsecond (line 38) | fn test_format_duration_subsecond() {
function test_format_duration_seconds (line 49) | fn test_format_duration_seconds() {
function test_format_duration_minutes (line 61) | fn test_format_duration_minutes() {
FILE: codex-rs/common/src/fuzzy_match.rs
function fuzzy_match (line 12) | pub fn fuzzy_match(haystack: &str, needle: &str) -> Option<(Vec<usize>, ...
function fuzzy_indices (line 72) | pub fn fuzzy_indices(haystack: &str, needle: &str) -> Option<Vec<usize>> {
function ascii_basic_indices (line 85) | fn ascii_basic_indices() {
function unicode_dotted_i_istanbul_highlighting (line 96) | fn unicode_dotted_i_istanbul_highlighting() {
function unicode_german_sharp_s_casefold (line 107) | fn unicode_german_sharp_s_casefold() {
function prefer_contiguous_match_over_spread (line 112) | fn prefer_contiguous_match_over_spread() {
function start_of_string_bonus_applies (line 129) | fn start_of_string_bonus_applies() {
function empty_needle_matches_with_max_score_and_no_indices (line 146) | fn empty_needle_matches_with_max_score_and_no_indices() {
function case_insensitive_matching_basic (line 156) | fn case_insensitive_matching_basic() {
function indices_are_deduped_for_multichar_lowercase_expansion (line 167) | fn indices_are_deduped_for_multichar_lowercase_expansion() {
FILE: codex-rs/common/src/model_presets.rs
type ModelPreset (line 5) | pub struct ModelPreset {
function builtin_model_presets (line 21) | pub fn builtin_model_presets() -> &'static [ModelPreset] {
FILE: codex-rs/common/src/sandbox_mode_cli_arg.rs
type SandboxModeCliArg (line 14) | pub enum SandboxModeCliArg {
method from (line 21) | fn from(value: SandboxModeCliArg) -> Self {
FILE: codex-rs/common/src/sandbox_summary.rs
function summarize_sandbox_policy (line 3) | pub fn summarize_sandbox_policy(sandbox_policy: &SandboxPolicy) -> String {
FILE: codex-rs/core/src/apply_patch.rs
constant CODEX_APPLY_PATCH_ARG1 (line 14) | pub const CODEX_APPLY_PATCH_ARG1: &str = "--codex-run-as-apply-patch";
type InternalApplyPatchInvocation (line 16) | pub(crate) enum InternalApplyPatchInvocation {
method from (line 37) | fn from(item: ResponseInputItem) -> Self {
type ApplyPatchExec (line 31) | pub(crate) struct ApplyPatchExec {
function apply_patch (line 42) | pub(crate) async fn apply_patch(
function convert_apply_patch_to_protocol (line 102) | pub(crate) fn convert_apply_patch_to_protocol(
FILE: codex-rs/core/src/bash.rs
function try_parse_bash (line 7) | pub fn try_parse_bash(bash_lc_arg: &str) -> Option<Tree> {
function try_parse_word_only_commands_sequence (line 23) | pub fn try_parse_word_only_commands_sequence(tree: &Tree, src: &str) -> ...
function parse_plain_command_from_node (line 87) | fn parse_plain_command_from_node(cmd: tree_sitter::Node, src: &str) -> O...
function parse_seq (line 137) | fn parse_seq(src: &str) -> Option<Vec<Vec<String>>> {
function accepts_single_simple_command (line 143) | fn accepts_single_simple_command() {
function accepts_multiple_commands_with_allowed_operators (line 149) | fn accepts_multiple_commands_with_allowed_operators() {
function extracts_double_and_single_quoted_strings (line 162) | fn extracts_double_and_single_quoted_strings() {
function accepts_numbers_as_words (line 177) | fn accepts_numbers_as_words() {
function rejects_parentheses_and_subshells (line 190) | fn rejects_parentheses_and_subshells() {
function rejects_redirections_and_unsupported_operators (line 196) | fn rejects_redirections_and_unsupported_operators() {
function rejects_command_and_process_substitutions_and_expansions (line 202) | fn rejects_command_and_process_substitutions_and_expansions() {
function rejects_variable_assignment_prefix (line 210) | fn rejects_variable_assignment_prefix() {
function rejects_trailing_operator_parse_error (line 215) | fn rejects_trailing_operator_parse_error() {
FILE: codex-rs/core/src/chat_completions.rs
function stream_chat_completions (line 32) | pub(crate) async fn stream_chat_completions(
function process_chat_sse (line 214) | async fn process_chat_sse<S>(
type AggregateMode (line 477) | enum AggregateMode {
type AggregatedChatStream (line 481) | pub(crate) struct AggregatedChatStream<S> {
type Item (line 493) | type Item = Result<ResponseEvent>;
method poll_next (line 495) | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<...
type AggregateStreamExt (line 639) | pub(crate) trait AggregateStreamExt: Stream<Item = Result<ResponseEvent>...
method aggregate (line 659) | fn aggregate(self) -> AggregatedChatStream<Self> {
function new (line 667) | fn new(inner: S, mode: AggregateMode) -> Self {
function streaming_mode (line 677) | pub(crate) fn streaming_mode(inner: S) -> Self {
FILE: codex-rs/core/src/client.rs
type ErrorResponse (line 49) | struct ErrorResponse {
type Error (line 54) | struct Error {
type ModelClient (line 64) | pub struct ModelClient {
method new (line 76) | pub fn new(
method get_model_context_window (line 97) | pub fn get_model_context_window(&self) -> Option<u64> {
method stream (line 106) | pub async fn stream(&self, prompt: &Prompt) -> Result<ResponseStream> {
method stream_responses (line 149) | async fn stream_responses(&self, prompt: &Prompt) -> Result<ResponseSt...
method get_provider (line 368) | pub fn get_provider(&self) -> ModelProviderInfo {
method get_model (line 373) | pub fn get_model(&self) -> String {
method get_model_family (line 378) | pub fn get_model_family(&self) -> ModelFamily {
method get_reasoning_effort (line 383) | pub fn get_reasoning_effort(&self) -> ReasoningEffortConfig {
method get_reasoning_summary (line 388) | pub fn get_reasoning_summary(&self) -> ReasoningSummaryConfig {
method get_auth_manager (line 392) | pub fn get_auth_manager(&self) -> Option<Arc<AuthManager>> {
type SseEvent (line 398) | struct SseEvent {
type ResponseCreated (line 407) | struct ResponseCreated {}
type ResponseCompleted (line 410) | struct ResponseCompleted {
type ResponseCompletedUsage (line 416) | struct ResponseCompletedUsage {
method from (line 425) | fn from(val: ResponseCompletedUsage) -> Self {
type ResponseCompletedInputTokensDetails (line 437) | struct ResponseCompletedInputTokensDetails {
type ResponseCompletedOutputTokensDetails (line 442) | struct ResponseCompletedOutputTokensDetails {
function process_sse (line 446) | async fn process_sse<S>(
function stream_from_fixture (line 650) | async fn stream_from_fixture(
function collect_events (line 689) | async fn collect_events(
function run_sse (line 712) | async fn run_sse(
function parses_items_and_completed (line 745) | async fn parses_items_and_completed() {
function error_when_missing_completed (line 824) | async fn error_when_missing_completed() {
function error_when_error_event (line 866) | async fn error_when_error_event() {
function table_driven_event_kinds (line 908) | async fn table_driven_event_kinds() {
FILE: codex-rs/core/src/client_common.rs
constant BASE_INSTRUCTIONS (line 21) | const BASE_INSTRUCTIONS: &str = include_str!("../prompt.md");
constant ACTIVE_DIRECTORY_INSTRUCTIONS (line 24) | const ACTIVE_DIRECTORY_INSTRUCTIONS: &str = include_str!("../active_dire...
constant CLIENT_SIDE_WEB_INSTRUCTIONS (line 25) | const CLIENT_SIDE_WEB_INSTRUCTIONS: &str = include_str!("../client_side_...
constant ENUMERATION_INSTRUCTIONS (line 26) | const ENUMERATION_INSTRUCTIONS: &str = include_str!("../enumeration.md");
constant LINUX_PRIVESC_INSTRUCTIONS (line 27) | const LINUX_PRIVESC_INSTRUCTIONS: &str = include_str!("../linux_privesc....
constant SHELLING_INSTRUCTIONS (line 28) | const SHELLING_INSTRUCTIONS: &str = include_str!("../shelling.md");
constant WEB_ENUMERATION_INSTRUCTIONS (line 29) | const WEB_ENUMERATION_INSTRUCTIONS: &str = include_str!("../web_enumerat...
constant WEB_INSTRUCTIONS (line 30) | const WEB_INSTRUCTIONS: &str = include_str!("../web.md");
constant WINDOWS_PRIVESC_INSTRUCTIONS (line 31) | const WINDOWS_PRIVESC_INSTRUCTIONS: &str = include_str!("../windows_priv...
constant USER_INSTRUCTIONS_START (line 34) | const USER_INSTRUCTIONS_START: &str = "<user_instructions>\n\n";
constant USER_INSTRUCTIONS_END (line 35) | const USER_INSTRUCTIONS_END: &str = "\n\n</user_instructions>";
type Prompt (line 39) | pub struct Prompt {
method get_full_instructions (line 55) | pub(crate) fn get_full_instructions(
method get_formatted_input (line 93) | pub(crate) fn get_formatted_input(&self) -> Vec<ResponseItem> {
method format_user_instructions_message (line 98) | pub(crate) fn format_user_instructions_message(ui: &str) -> ResponseIt...
type ResponseEvent (line 110) | pub enum ResponseEvent {
type Reasoning (line 128) | pub(crate) struct Reasoning {
type TextControls (line 135) | pub(crate) struct TextControls {
type OpenAiVerbosity (line 142) | pub(crate) enum OpenAiVerbosity {
method from (line 150) | fn from(v: VerbosityConfig) -> Self {
type ResponsesApiRequest (line 162) | pub(crate) struct ResponsesApiRequest<'a> {
function create_reasoning_param_for_request (line 183) | pub(crate) fn create_reasoning_param_for_request(
function create_text_param_for_request (line 195) | pub(crate) fn create_text_param_for_request(
type ResponseStream (line 203) | pub struct ResponseStream {
type Item (line 208) | type Item = Result<ResponseEvent>;
method poll_next (line 210) | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Opt...
function get_full_instructions_no_user_content (line 222) | fn get_full_instructions_no_user_content() {
function serializes_text_verbosity_when_set (line 233) | fn serializes_text_verbosity_when_set() {
function omits_text_when_not_set (line 263) | fn omits_text_when_not_set() {
FILE: codex-rs/core/src/codex.rs
type MutexExt (line 126) | trait MutexExt<T> {
method lock_unchecked (line 127) | fn lock_unchecked(&self) -> MutexGuard<'_, T>;
function lock_unchecked (line 131) | fn lock_unchecked(&self) -> MutexGuard<'_, T> {
type Codex (line 139) | pub struct Codex {
method spawn (line 165) | pub async fn spawn(
method submit (line 225) | pub async fn submit(&self, op: Op) -> CodexResult<String> {
method submit_with_id (line 237) | pub async fn submit_with_id(&self, sub: Submission) -> CodexResult<()> {
method next_event (line 245) | pub async fn next_event(&self) -> CodexResult<Event> {
type CodexSpawnOk (line 148) | pub struct CodexSpawnOk {
constant INITIAL_SUBMIT_ID (line 153) | pub(crate) const INITIAL_SUBMIT_ID: &str = "";
constant SUBMISSION_CHANNEL_CAPACITY (line 154) | pub(crate) const SUBMISSION_CHANNEL_CAPACITY: usize = 64;
constant MODEL_FORMAT_MAX_BYTES (line 157) | pub(crate) const MODEL_FORMAT_MAX_BYTES: usize = 10 * 1024;
constant MODEL_FORMAT_MAX_LINES (line 158) | pub(crate) const MODEL_FORMAT_MAX_LINES: usize = 256;
constant MODEL_FORMAT_HEAD_LINES (line 159) | pub(crate) const MODEL_FORMAT_HEAD_LINES: usize = MODEL_FORMAT_MAX_LINES...
constant MODEL_FORMAT_TAIL_LINES (line 160) | pub(crate) const MODEL_FORMAT_TAIL_LINES: usize = MODEL_FORMAT_MAX_LINES...
constant MODEL_FORMAT_HEAD_BYTES (line 161) | pub(crate) const MODEL_FORMAT_HEAD_BYTES: usize = MODEL_FORMAT_MAX_BYTES...
type State (line 257) | struct State {
type Session (line 268) | pub(crate) struct Session {
method new (line 356) | async fn new(
method set_task (line 578) | pub fn set_task(&self, task: AgentTask) {
method remove_task (line 586) | pub fn remove_task(&self, sub_id: &str) {
method send_event (line 597) | pub(crate) async fn send_event(&self, event: Event) {
method request_command_approval (line 603) | pub async fn request_command_approval(
method request_patch_approval (line 629) | pub async fn request_patch_approval(
method notify_approval (line 655) | pub fn notify_approval(&self, sub_id: &str, decision: ReviewDecision) {
method add_approved_command (line 662) | pub fn add_approved_command(&self, cmd: Vec<String>) {
method record_conversation_items (line 669) | async fn record_conversation_items(&self, items: &[ResponseItem]) {
method record_state_snapshot (line 676) | async fn record_state_snapshot(&self, items: &[ResponseItem]) {
method on_exec_command_begin (line 694) | async fn on_exec_command_begin(
method on_exec_command_end (line 736) | async fn on_exec_command_end(
method run_exec_with_events (line 800) | async fn run_exec_with_events<'a>(
method notify_background_event (line 851) | async fn notify_background_event(&self, sub_id: &str, message: impl In...
method notify_stream_error (line 861) | async fn notify_stream_error(&self, sub_id: &str, message: impl Into<S...
method turn_input_with_history (line 873) | pub fn turn_input_with_history(&self, extra: Vec<ResponseItem>) -> Vec...
method inject_input (line 878) | pub fn inject_input(&self, input: Vec<InputItem>) -> Result<(), Vec<In...
method get_pending_input (line 888) | pub fn get_pending_input(&self) -> Vec<ResponseInputItem> {
method call_tool (line 899) | pub async fn call_tool(
method interrupt_task (line 911) | fn interrupt_task(&self) {
method maybe_notify (line 924) | fn maybe_notify(&self, notification: UserNotification) {
type TurnContext (line 291) | pub(crate) struct TurnContext {
method resolve_path (line 307) | fn resolve_path(&self, path: Option<String>) -> PathBuf {
type ConfigureSession (line 315) | struct ConfigureSession {
method drop (line 952) | fn drop(&mut self) {
type ExecCommandContext (line 958) | pub(crate) struct ExecCommandContext {
type ApplyPatchCommandContext (line 967) | pub(crate) struct ApplyPatchCommandContext {
type AgentTask (line 973) | pub(crate) struct AgentTask {
method spawn (line 980) | fn spawn(
method compact (line 1000) | fn compact(
method abort (line 1023) | fn abort(self, reason: TurnAbortReason) {
function submission_loop (line 1039) | async fn submission_loop(
function run_task (line 1373) | async fn run_task(
function run_turn (line 1576) | async fn run_turn(
type ProcessedResponseItem (line 1642) | struct ProcessedResponseItem {
function try_run_turn (line 1647) | async fn try_run_turn(
function run_compact_task (line 1821) | async fn run_compact_task(
function handle_response_item (line 1906) | async fn handle_response_item(
function handle_function_call (line 2056) | async fn handle_function_call(
function handle_custom_tool_call (line 2188) | async fn handle_custom_tool_call(
function to_exec_params (line 2240) | fn to_exec_params(params: ShellToolCallParams, turn_context: &TurnContex...
function parse_container_exec_arguments (line 2251) | fn parse_container_exec_arguments(
type ExecInvokeArgs (line 2273) | pub struct ExecInvokeArgs<'a> {
function maybe_translate_shell_command (line 2281) | fn maybe_translate_shell_command(
function handle_container_exec_with_params (line 2299) | async fn handle_container_exec_with_params(
function handle_sandbox_error (line 2518) | async fn handle_sandbox_error(
function format_exec_output_str (line 2656) | fn format_exec_output_str(exec_output: &ExecToolCallOutput) -> String {
function take_bytes_at_char_boundary (line 2720) | fn take_bytes_at_char_boundary(s: &str, maxb: usize) -> &str {
function take_last_bytes_at_char_boundary (line 2737) | fn take_last_bytes_at_char_boundary(s: &str, maxb: usize) -> &str {
function format_exec_output (line 2758) | fn format_exec_output(exec_output: &ExecToolCallOutput) -> String {
function get_last_assistant_message_from_turn (line 2794) | fn get_last_assistant_message_from_turn(responses: &[ResponseItem]) -> O...
function drain_to_completed (line 2814) | async fn drain_to_completed(
function convert_call_tool_result_to_function_call_output_payload (line 2858) | fn convert_call_tool_result_to_function_call_output_payload(
function text_block (line 2902) | fn text_block(s: &str) -> ContentBlock {
function prefers_structured_content_when_present (line 2911) | fn prefers_structured_content_when_present() {
function model_truncation_head_tail_by_lines (line 2936) | fn model_truncation_head_tail_by_lines() {
function model_truncation_respects_byte_budget (line 2976) | fn model_truncation_respects_byte_budget() {
function falls_back_to_content_when_structured_is_null (line 3012) | fn falls_back_to_content_when_structured_is_null() {
function success_flag_reflects_is_error_true (line 3030) | fn success_flag_reflects_is_error_true() {
function success_flag_true_with_no_error_and_content_used (line 3047) | fn success_flag_true_with_no_error_and_content_used() {
FILE: codex-rs/core/src/codex_conversation.rs
type CodexConversation (line 7) | pub struct CodexConversation {
method new (line 14) | pub(crate) fn new(codex: Codex) -> Self {
method submit (line 18) | pub async fn submit(&self, op: Op) -> CodexResult<String> {
method submit_with_id (line 23) | pub async fn submit_with_id(&self, sub: Submission) -> CodexResult<()> {
method next_event (line 27) | pub async fn next_event(&self) -> CodexResult<Event> {
FILE: codex-rs/core/src/config.rs
constant OPENAI_DEFAULT_MODEL (line 31) | const OPENAI_DEFAULT_MODEL: &str = "gpt-5";
constant PROJECT_DOC_MAX_BYTES (line 36) | pub(crate) const PROJECT_DOC_MAX_BYTES: usize = 32 * 1024;
constant CONFIG_TOML_FILE (line 38) | const CONFIG_TOML_FILE: &str = "config.toml";
constant DEFAULT_RESPONSES_ORIGINATOR_HEADER (line 40) | const DEFAULT_RESPONSES_ORIGINATOR_HEADER: &str = "codex_cli_rs";
type Config (line 44) | pub struct Config {
method load_with_cli_overrides (line 193) | pub fn load_with_cli_overrides(
method load_from_base_config_with_overrides (line 601) | pub fn load_from_base_config_with_overrides(
method load_instructions (line 797) | fn load_instructions(codex_dir: Option<&Path>) -> Option<String> {
method get_base_instructions (line 814) | fn get_base_instructions(
function load_config_as_toml_with_cli_overrides (line 222) | pub fn load_config_as_toml_with_cli_overrides(
function load_config_as_toml (line 242) | pub fn load_config_as_toml(codex_home: &Path) -> std::io::Result<TomlVal...
function set_project_trusted (line 265) | pub fn set_project_trusted(codex_home: &Path, project_path: &Path) -> an...
function apply_toml_override (line 344) | fn apply_toml_override(root: &mut TomlValue, path: &str, value: TomlValu...
type ConfigToml (line 388) | pub struct ConfigToml {
method derive_sandbox_policy (line 507) | fn derive_sandbox_policy(&self, sandbox_mode_override: Option<SandboxM...
method is_cwd_trusted (line 531) | pub fn is_cwd_trusted(&self, resolved_cwd: &Path) -> bool {
method get_config_profile (line 557) | pub fn get_config_profile(
type ProjectConfig (line 494) | pub struct ProjectConfig {
type ToolsToml (line 499) | pub struct ToolsToml {
type ConfigOverrides (line 581) | pub struct ConfigOverrides {
function default_model (line 857) | fn default_model() -> String {
function find_codex_home (line 869) | pub fn find_codex_home() -> std::io::Result<PathBuf> {
function log_dir (line 890) | pub fn log_dir(cfg: &Config) -> std::io::Result<PathBuf> {
function test_toml_parsing (line 905) | fn test_toml_parsing() {
function test_sandbox_config_parsing (line 937) | fn test_sandbox_config_parsing() {
type PrecedenceTestFixture (line 992) | struct PrecedenceTestFixture {
method cwd (line 1002) | fn cwd(&self) -> PathBuf {
method codex_home (line 1006) | fn codex_home(&self) -> PathBuf {
function create_test_fixture (line 1011) | fn create_test_fixture() -> std::io::Result<PrecedenceTestFixture> {
function test_precedence_fixture_with_o3_profile (line 1111) | fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
function test_precedence_fixture_with_gpt3_profile (line 1169) | fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
function test_precedence_fixture_with_zdr_profile (line 1241) | fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
function test_set_project_trusted_writes_explicit_tables (line 1299) | fn test_set_project_trusted_writes_explicit_tables() -> anyhow::Result<(...
function test_set_project_trusted_converts_inline_to_explicit (line 1327) | fn test_set_project_trusted_converts_inline_to_explicit() -> anyhow::Res...
FILE: codex-rs/core/src/config_profile.rs
type ConfigProfile (line 12) | pub struct ConfigProfile {
FILE: codex-rs/core/src/config_types.rs
type McpServerConfig (line 15) | pub struct McpServerConfig {
type UriBasedFileOpener (line 26) | pub enum UriBasedFileOpener {
method get_scheme (line 45) | pub fn get_scheme(&self) -> Option<&str> {
type History (line 58) | pub struct History {
type HistoryPersistence (line 69) | pub enum HistoryPersistence {
type Tui (line 79) | pub struct Tui {}
type SandboxWorkspaceWrite (line 82) | pub struct SandboxWorkspaceWrite {
type ShellEnvironmentPolicyInherit (line 95) | pub enum ShellEnvironmentPolicyInherit {
type ShellEnvironmentPolicyToml (line 111) | pub struct ShellEnvironmentPolicyToml {
type EnvironmentVariablePattern (line 127) | pub type EnvironmentVariablePattern = WildMatchPattern<'*', '?'>;
type ShellEnvironmentPolicy (line 137) | pub struct ShellEnvironmentPolicy {
method from (line 159) | fn from(toml: ShellEnvironmentPolicyToml) -> Self {
type ReasoningEffort (line 193) | pub enum ReasoningEffort {
type ReasoningSummary (line 208) | pub enum ReasoningSummary {
type Verbosity (line 222) | pub enum Verbosity {
FILE: codex-rs/core/src/conversation_history.rs
type ConversationHistory (line 5) | pub(crate) struct ConversationHistory {
method new (line 11) | pub(crate) fn new() -> Self {
method contents (line 16) | pub(crate) fn contents(&self) -> Vec<ResponseItem> {
method record_items (line 21) | pub(crate) fn record_items<I>(&mut self, items: I)
method keep_last_messages (line 35) | pub(crate) fn keep_last_messages(&mut self, n: usize) {
function is_api_message (line 66) | fn is_api_message(message: &ResponseItem) -> bool {
function assistant_msg (line 84) | fn assistant_msg(text: &str) -> ResponseItem {
function user_msg (line 94) | fn user_msg(text: &str) -> ResponseItem {
function filters_non_api_messages (line 105) | fn filters_non_api_messages() {
FILE: codex-rs/core/src/conversation_manager.rs
type NewConversation (line 23) | pub struct NewConversation {
type ConversationManager (line 31) | pub struct ConversationManager {
method new (line 37) | pub fn new(auth_manager: Arc<AuthManager>) -> Self {
method with_auth (line 46) | pub fn with_auth(auth: CodexAuth) -> Self {
method new_conversation (line 50) | pub async fn new_conversation(&self, config: Config) -> CodexResult<Ne...
method spawn_conversation (line 55) | async fn spawn_conversation(
method finalize_spawn (line 70) | async fn finalize_spawn(
method get_conversation (line 102) | pub async fn get_conversation(
method remove_conversation (line 113) | pub async fn remove_conversation(&self, conversation_id: Uuid) {
method fork_conversation (line 121) | pub async fn fork_conversation(
function truncate_after_dropping_last_messages (line 144) | fn truncate_after_dropping_last_messages(items: Vec<ResponseItem>, n: us...
function user_msg (line 179) | fn user_msg(text: &str) -> ResponseItem {
function assistant_msg (line 188) | fn assistant_msg(text: &str) -> ResponseItem {
function drops_from_last_user_only (line 199) | fn drops_from_last_user_only() {
FILE: codex-rs/core/src/environment_context.rs
constant ENVIRONMENT_CONTEXT_START (line 14) | pub(crate) const ENVIRONMENT_CONTEXT_START: &str = "<environment_context>";
constant ENVIRONMENT_CONTEXT_END (line 15) | pub(crate) const ENVIRONMENT_CONTEXT_END: &str = "</environment_context>";
type NetworkAccess (line 20) | pub enum NetworkAccess {
type EnvironmentContext (line 26) | pub(crate) struct EnvironmentContext {
method new (line 35) | pub fn new(
method serialize_to_xml (line 81) | pub fn serialize_to_xml(self) -> String {
method from (line 112) | fn from(ec: EnvironmentContext) -> Self {
FILE: codex-rs/core/src/error.rs
type Result (line 9) | pub type Result<T> = std::result::Result<T, CodexErr>;
type SandboxErr (line 12) | pub enum SandboxErr {
type CodexErr (line 41) | pub enum CodexErr {
method downcast_ref (line 219) | pub fn downcast_ref<T: std::any::Any>(&self) -> Option<&T> {
type UsageLimitReachedError (line 129) | pub struct UsageLimitReachedError {
method fmt (line 135) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function format_reset_duration (line 165) | fn format_reset_duration(total_secs: u64) -> String {
type EnvVarError (line 196) | pub struct EnvVarError {
method fmt (line 206) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function get_error_message_ui (line 224) | pub fn get_error_message_ui(e: &CodexErr) -> String {
function usage_limit_reached_error_formats_plus_plan (line 238) | fn usage_limit_reached_error_formats_plus_plan() {
function usage_limit_reached_error_formats_default_when_none (line 250) | fn usage_limit_reached_error_formats_default_when_none() {
function usage_limit_reached_error_formats_default_for_other_plans (line 262) | fn usage_limit_reached_error_formats_default_for_other_plans() {
function usage_limit_reached_includes_minutes_when_available (line 274) | fn usage_limit_reached_includes_minutes_when_available() {
function usage_limit_reached_includes_hours_and_minutes (line 286) | fn usage_limit_reached_includes_hours_and_minutes() {
function usage_limit_reached_includes_days_hours_minutes (line 298) | fn usage_limit_reached_includes_days_hours_minutes() {
function usage_limit_reached_less_than_minute (line 310) | fn usage_limit_reached_less_than_minute() {
FILE: codex-rs/core/src/exec.rs
constant DEFAULT_TIMEOUT_MS (line 31) | const DEFAULT_TIMEOUT_MS: u64 = 10_000;
constant SIGKILL_CODE (line 35) | const SIGKILL_CODE: i32 = 9;
constant TIMEOUT_CODE (line 36) | const TIMEOUT_CODE: i32 = 64;
constant EXIT_CODE_SIGNAL_BASE (line 37) | const EXIT_CODE_SIGNAL_BASE: i32 = 128;
constant READ_CHUNK_SIZE (line 40) | const READ_CHUNK_SIZE: usize = 8192;
constant AGGREGATE_BUFFER_INITIAL_CAPACITY (line 41) | const AGGREGATE_BUFFER_INITIAL_CAPACITY: usize = 8 * 1024;
constant MAX_EXEC_OUTPUT_DELTAS_PER_CALL (line 45) | pub(crate) const MAX_EXEC_OUTPUT_DELTAS_PER_CALL: usize = 10_000;
type ExecParams (line 48) | pub struct ExecParams {
method timeout_duration (line 58) | pub fn timeout_duration(&self) -> Duration {
type SandboxType (line 64) | pub enum SandboxType {
type StdoutStream (line 75) | pub struct StdoutStream {
function process_exec_tool_call (line 81) | pub async fn process_exec_tool_call(
function is_likely_sandbox_denied (line 175) | fn is_likely_sandbox_denied(sandbox_type: SandboxType, exit_code: i32) -...
type StreamOutput (line 191) | pub struct StreamOutput<T> {
type RawExecToolCallOutput (line 196) | struct RawExecToolCallOutput {
function new (line 204) | pub fn new(text: String) -> Self {
function from_utf8_lossy (line 213) | pub fn from_utf8_lossy(&self) -> StreamOutput<String> {
function append_all (line 222) | fn append_all(dst: &mut Vec<u8>, src: &[u8]) {
type ExecToolCallOutput (line 227) | pub struct ExecToolCallOutput {
function exec (line 235) | async fn exec(
function consume_truncated_output (line 267) | async fn consume_truncated_output(
function read_capped (line 343) | async fn read_capped<R: AsyncRead + Unpin + Send + 'static>(
function synthetic_exit_status (line 398) | fn synthetic_exit_status(code: i32) -> ExitStatus {
function synthetic_exit_status (line 404) | fn synthetic_exit_status(code: i32) -> ExitStatus {
FILE: codex-rs/core/src/exec_command/exec_command_params.rs
type ExecCommandParams (line 7) | pub struct ExecCommandParams {
function default_yield_time (line 23) | fn default_yield_time() -> u64 {
function max_output_tokens (line 27) | fn max_output_tokens() -> u64 {
function default_login (line 31) | fn default_login() -> bool {
function default_shell (line 35) | fn default_shell() -> String {
type WriteStdinParams (line 40) | pub struct WriteStdinParams {
function write_stdin_default_yield_time_ms (line 51) | fn write_stdin_default_yield_time_ms() -> u64 {
function write_stdin_default_max_output_tokens (line 55) | fn write_stdin_default_max_output_tokens() -> u64 {
FILE: codex-rs/core/src/exec_command/exec_command_session.rs
type ExecCommandSession (line 8) | pub(crate) struct ExecCommandSession {
method new (line 30) | pub(crate) fn new(
method writer_sender (line 48) | pub(crate) fn writer_sender(&self) -> mpsc::Sender<Vec<u8>> {
method output_receiver (line 52) | pub(crate) fn output_receiver(&self) -> broadcast::Receiver<Vec<u8>> {
method drop (line 58) | fn drop(&mut self) {
FILE: codex-rs/core/src/exec_command/responses_api.rs
constant EXEC_COMMAND_TOOL_NAME (line 6) | pub const EXEC_COMMAND_TOOL_NAME: &str = "exec_command";
constant WRITE_STDIN_TOOL_NAME (line 7) | pub const WRITE_STDIN_TOOL_NAME: &str = "write_stdin";
function create_exec_command_tool_for_responses_api (line 9) | pub fn create_exec_command_tool_for_responses_api() -> ResponsesApiTool {
function create_write_stdin_tool_for_responses_api (line 57) | pub fn create_write_stdin_tool_for_responses_api() -> ResponsesApiTool {
FILE: codex-rs/core/src/exec_command/session_id.rs
type SessionId (line 5) | pub(crate) struct SessionId(pub u32);
FILE: codex-rs/core/src/exec_command/session_manager.rs
type SessionManager (line 25) | pub struct SessionManager {
method handle_exec_command_request (line 84) | pub async fn handle_exec_command_request(
method handle_write_stdin_request (line 179) | pub async fn handle_write_stdin_request(
type ExecCommandOutput (line 31) | pub struct ExecCommandOutput {
method to_text_output (line 39) | fn to_text_output(&self) -> String {
type ExitStatus (line 64) | pub enum ExitStatus {
function result_into_payload (line 69) | pub fn result_into_payload(result: Result<ExecCommandOutput, String>) ->...
function create_exec_command_session (line 244) | async fn create_exec_command_session(
function truncate_middle (line 354) | fn truncate_middle(s: &str, max_bytes: usize) -> (String, Option<u64>) {
function session_manager_streams_and_truncates_from_now (line 477) | async fn session_manager_streams_and_truncates_from_now() {
function extract_monotonic_numbers (line 579) | fn extract_monotonic_numbers(s: &str) -> Vec<i64> {
function to_text_output_exited_no_truncation (line 597) | fn to_text_output_exited_no_truncation() {
function to_text_output_ongoing_with_truncation (line 613) | fn to_text_output_ongoing_with_truncation() {
function truncate_middle_no_newlines_fallback (line 630) | fn truncate_middle_no_newlines_fallback() {
function truncate_middle_prefers_newline_boundaries (line 643) | fn truncate_middle_prefers_newline_boundaries() {
FILE: codex-rs/core/src/exec_env.rs
function create_env (line 14) | pub fn create_env(policy: &ShellEnvironmentPolicy) -> HashMap<String, St...
function populate_env (line 18) | fn populate_env<I>(vars: I, policy: &ShellEnvironmentPolicy) -> HashMap<...
function make_vars (line 77) | fn make_vars(pairs: &[(&str, &str)]) -> Vec<(String, String)> {
function test_core_inherit_and_default_excludes (line 85) | fn test_core_inherit_and_default_excludes() {
function test_include_only (line 105) | fn test_include_only() {
function test_set_overrides (line 125) | fn test_set_overrides() {
function test_inherit_all (line 145) | fn test_inherit_all() {
function test_inherit_all_with_default_excludes (line 160) | fn test_inherit_all_with_default_excludes() {
function test_inherit_none (line 176) | fn test_inherit_none() {
FILE: codex-rs/core/src/git_info.rs
constant GIT_COMMAND_TIMEOUT (line 16) | const GIT_COMMAND_TIMEOUT: TokioDuration = TokioDuration::from_secs(5);
type GitInfo (line 19) | pub struct GitInfo {
type GitDiffToRemote (line 32) | pub struct GitDiffToRemote {
function collect_git_info (line 41) | pub async fn collect_git_info(cwd: &Path) -> Option<GitInfo> {
function git_diff_to_remote (line 96) | pub async fn git_diff_to_remote(cwd: &Path) -> Option<GitDiffToRemote> {
function run_git_command_with_timeout (line 113) | async fn run_git_command_with_timeout(args: &[&str], cwd: &Path) -> Opti...
function get_git_remotes (line 126) | async fn get_git_remotes(cwd: &Path) -> Option<Vec<String>> {
function get_default_branch (line 149) | async fn get_default_branch(cwd: &Path) -> Option<String> {
function branch_ancestry (line 213) | async fn branch_ancestry(cwd: &Path) -> Option<Vec<String>> {
function branch_remote_and_distance (line 284) | async fn branch_remote_and_distance(
function find_closest_sha (line 363) | async fn find_closest_sha(cwd: &Path, branches: &[String], remotes: &[St...
function diff_against_sha (line 387) | async fn diff_against_sha(cwd: &Path, sha: &GitSha) -> Option<String> {
function resolve_root_git_project_for_trust (line 445) | pub fn resolve_root_git_project_for_trust(cwd: &Path) -> Option<PathBuf> {
function create_test_git_repo (line 483) | async fn create_test_git_repo(temp_dir: &TempDir) -> PathBuf {
function create_test_git_repo_with_remote (line 540) | async fn create_test_git_repo_with_remote(temp_dir: &TempDir) -> (PathBu...
function test_collect_git_info_non_git_directory (line 576) | async fn test_collect_git_info_non_git_directory() {
function test_collect_git_info_git_repository (line 583) | async fn test_collect_git_info_git_repository() {
function test_collect_git_info_with_remote (line 607) | async fn test_collect_git_info_with_remote() {
function test_collect_git_info_detached_head (line 636) | async fn test_collect_git_info_detached_head() {
function test_collect_git_info_with_branch (line 668) | async fn test_collect_git_info_with_branch() {
function test_get_git_working_tree_state_clean_repo (line 689) | async fn test_get_git_working_tree_state_clean_repo() {
function test_get_git_working_tree_state_with_changes (line 712) | async fn test_get_git_working_tree_state_with_changes() {
function test_get_git_working_tree_state_branch_fallback (line 740) | async fn test_get_git_working_tree_state_branch_fallback() {
function resolve_root_git_project_for_trust_returns_none_outside_repo (line 782) | fn resolve_root_git_project_for_trust_returns_none_outside_repo() {
function resolve_root_git_project_for_trust_regular_repo_returns_repo_root (line 788) | async fn resolve_root_git_project_for_trust_regular_repo_returns_repo_ro...
function resolve_root_git_project_for_trust_detects_worktree_and_returns_main_root (line 806) | async fn resolve_root_git_project_for_trust_detects_worktree_and_returns...
function resolve_root_git_project_for_trust_non_worktrees_gitdir_returns_none (line 836) | fn resolve_root_git_project_for_trust_non_worktrees_gitdir_returns_none() {
function test_get_git_working_tree_state_unpushed_commit (line 856) | async fn test_get_git_working_tree_state_unpushed_commit() {
function test_git_info_serialization (line 893) | fn test_git_info_serialization() {
function test_git_info_serialization_with_nones (line 912) | fn test_git_info_serialization_with_nones() {
FILE: codex-rs/core/src/is_safe_command.rs
function is_known_safe_command (line 4) | pub fn is_known_safe_command(command: &[String]) -> bool {
function is_safe_to_call_with_exec (line 31) | fn is_safe_to_call_with_exec(command: &[String]) -> bool {
function is_valid_sed_n_arg (line 128) | fn is_valid_sed_n_arg(arg: Option<&str>) -> bool {
function vec_str (line 164) | fn vec_str(args: &[&str]) -> Vec<String> {
function known_safe_examples (line 169) | fn known_safe_examples() {
function unknown_or_partial (line 188) | fn unknown_or_partial() {
function ripgrep_rules (line 217) | fn ripgrep_rules() {
function bash_lc_safe_examples (line 251) | fn bash_lc_safe_examples() {
function bash_lc_safe_examples_with_operators (line 283) | fn bash_lc_safe_examples_with_operators() {
function bash_lc_unsafe_examples (line 307) | fn bash_lc_unsafe_examples() {
FILE: codex-rs/core/src/landlock.rs
function spawn_command_under_linux_sandbox (line 16) | pub async fn spawn_command_under_linux_sandbox<P>(
function create_linux_sandbox_command_args (line 42) | fn create_linux_sandbox_command_args(
FILE: codex-rs/core/src/mcp_connection_manager.rs
constant MCP_TOOL_NAME_DELIMITER (line 36) | const MCP_TOOL_NAME_DELIMITER: &str = "__";
constant MAX_TOOL_NAME_LENGTH (line 37) | const MAX_TOOL_NAME_LENGTH: usize = 64;
constant LIST_TOOLS_TIMEOUT (line 40) | const LIST_TOOLS_TIMEOUT: Duration = Duration::from_secs(10);
type ClientStartErrors (line 44) | pub type ClientStartErrors = HashMap<String, anyhow::Error>;
function qualify_tools (line 46) | fn qualify_tools(tools: Vec<ToolInfo>) -> HashMap<String, ToolInfo> {
type ToolInfo (line 78) | struct ToolInfo {
type McpConnectionManager (line 86) | pub(crate) struct McpConnectionManager {
method new (line 106) | pub async fn new(
method list_all_tools (line 196) | pub fn list_all_tools(&self) -> HashMap<String, Tool> {
method call_tool (line 204) | pub async fn call_tool(
method parse_tool_name (line 223) | pub fn parse_tool_name(&self, tool_name: &str) -> Option<(String, Stri...
function list_all_tools (line 232) | async fn list_all_tools(
function is_valid_mcp_server_name (line 276) | fn is_valid_mcp_server_name(server_name: &str) -> bool {
function create_test_tool (line 288) | fn create_test_tool(server_name: &str, tool_name: &str) -> ToolInfo {
function test_qualify_tools_short_non_duplicated_names (line 308) | fn test_qualify_tools_short_non_duplicated_names() {
function test_qualify_tools_duplicated_names_skipped (line 322) | fn test_qualify_tools_duplicated_names_skipped() {
function test_qualify_tools_long_names_same_server (line 336) | fn test_qualify_tools_long_names_same_server() {
FILE: codex-rs/core/src/mcp_tool_call.rs
function handle_mcp_tool_call (line 17) | pub(crate) async fn handle_mcp_tool_call(
function notify_mcp_tool_call_event (line 76) | async fn notify_mcp_tool_call_event(sess: &Session, sub_id: &str, event:...
FILE: codex-rs/core/src/message_history.rs
constant HISTORY_FILENAME (line 39) | const HISTORY_FILENAME: &str = "history.jsonl";
constant MAX_RETRIES (line 41) | const MAX_RETRIES: usize = 10;
constant RETRY_SLEEP (line 42) | const RETRY_SLEEP: Duration = Duration::from_millis(100);
type HistoryEntry (line 45) | pub struct HistoryEntry {
function history_filepath (line 51) | fn history_filepath(config: &Config) -> PathBuf {
function append_entry (line 60) | pub(crate) async fn append_entry(text: &str, session_id: &Uuid, config: ...
function acquire_exclusive_lock_with_retry (line 128) | async fn acquire_exclusive_lock_with_retry(file: &File) -> Result<()> {
function history_metadata (line 151) | pub(crate) async fn history_metadata(config: &Config) -> (u64, usize) {
function lookup (line 198) | pub(crate) fn lookup(log_id: u64, offset: usize, config: &Config) -> Opt...
function lookup (line 256) | pub(crate) fn lookup(log_id: u64, offset: usize, config: &Config) -> Opt...
function acquire_shared_lock_with_retry (line 262) | fn acquire_shared_lock_with_retry(file: &File) -> Result<()> {
function ensure_owner_only_permissions (line 284) | async fn ensure_owner_only_permissions(file: &File) -> Result<()> {
function ensure_owner_only_permissions (line 298) | async fn ensure_owner_only_permissions(_file: &File) -> Result<()> {
FILE: codex-rs/core/src/model_family.rs
type ModelFamily (line 5) | pub struct ModelFamily {
function find_family_for_model (line 72) | pub fn find_family_for_model(slug: &str) -> Option<ModelFamily> {
FILE: codex-rs/core/src/model_provider_info.rs
constant DEFAULT_STREAM_IDLE_TIMEOUT_MS (line 17) | const DEFAULT_STREAM_IDLE_TIMEOUT_MS: u64 = 300_000;
constant DEFAULT_STREAM_MAX_RETRIES (line 18) | const DEFAULT_STREAM_MAX_RETRIES: u64 = 5;
constant DEFAULT_REQUEST_MAX_RETRIES (line 19) | const DEFAULT_REQUEST_MAX_RETRIES: u64 = 4;
constant MAX_STREAM_MAX_RETRIES (line 21) | const MAX_STREAM_MAX_RETRIES: u64 = 100;
constant MAX_REQUEST_MAX_RETRIES (line 23) | const MAX_REQUEST_MAX_RETRIES: u64 = 100;
type WireApi (line 33) | pub enum WireApi {
type ModelProviderInfo (line 44) | pub struct ModelProviderInfo {
method create_request_builder (line 97) | pub async fn create_request_builder<'a>(
method get_query_string (line 125) | fn get_query_string(&self) -> String {
method get_full_url (line 138) | pub(crate) fn get_full_url(&self, auth: &Option<CodexAuth>) -> String {
method apply_http_headers (line 165) | fn apply_http_headers(&self, mut builder: reqwest::RequestBuilder) -> ...
method api_key (line 187) | pub fn api_key(&self) -> crate::error::Result<Option<String>> {
method request_max_retries (line 211) | pub fn request_max_retries(&self) -> u64 {
method stream_max_retries (line 218) | pub fn stream_max_retries(&self) -> u64 {
method stream_idle_timeout (line 225) | pub fn stream_idle_timeout(&self) -> Duration {
constant DEFAULT_OLLAMA_PORT (line 232) | const DEFAULT_OLLAMA_PORT: u32 = 11434;
constant BUILT_IN_OSS_MODEL_PROVIDER_ID (line 234) | pub const BUILT_IN_OSS_MODEL_PROVIDER_ID: &str = "oss";
function built_in_model_providers (line 237) | pub fn built_in_model_providers() -> HashMap<String, ModelProviderInfo> {
function create_oss_provider (line 291) | pub fn create_oss_provider() -> ModelProviderInfo {
function create_oss_provider_with_base_url (line 312) | pub fn create_oss_provider_with_base_url(base_url: &str) -> ModelProvide...
function test_deserialize_ollama_model_provider_toml (line 335) | fn test_deserialize_ollama_model_provider_toml() {
function test_deserialize_azure_model_provider_toml (line 360) | fn test_deserialize_azure_model_provider_toml() {
function test_deserialize_example_model_provider_toml (line 389) | fn test_deserialize_example_model_provider_toml() {
FILE: codex-rs/core/src/openai_model_info.rs
type ModelInfo (line 9) | pub(crate) struct ModelInfo {
function get_model_info (line 17) | pub(crate) fn get_model_info(model_family: &ModelFamily) -> Option<Model...
FILE: codex-rs/core/src/openai_tools.rs
type ResponsesApiTool (line 17) | pub struct ResponsesApiTool {
type FreeformTool (line 28) | pub struct FreeformTool {
type FreeformToolFormat (line 35) | pub struct FreeformToolFormat {
type OpenAiTool (line 45) | pub enum OpenAiTool {
type ConfigShellToolType (line 57) | pub enum ConfigShellToolType {
type ToolsConfig (line 65) | pub struct ToolsConfig {
method new (line 83) | pub fn new(params: &ToolsConfigParams) -> Self {
type ToolsConfigParams (line 72) | pub struct ToolsConfigParams<'a> {
type JsonSchema (line 130) | pub(crate) enum JsonSchema {
function create_shell_tool (line 163) | fn create_shell_tool() -> OpenAiTool {
function create_shell_tool_for_sandbox (line 197) | fn create_shell_tool_for_sandbox(sandbox_policy: &SandboxPolicy) -> Open...
type ApplyPatchToolArgs (line 297) | pub(crate) struct ApplyPatchToolArgs {
function create_tools_json_for_responses_api (line 304) | pub fn create_tools_json_for_responses_api(
function create_tools_json_for_chat_completions_api (line 319) | pub(crate) fn create_tools_json_for_chat_completions_api(
function mcp_tool_to_openai_tool (line 347) | pub(crate) fn mcp_tool_to_openai_tool(
function sanitize_json_schema (line 389) | fn sanitize_json_schema(value: &mut JsonValue) {
function get_openai_tools (line 499) | pub fn get_openai_tools(
function assert_eq_tool_names (line 571) | fn assert_eq_tool_names(tools: &[OpenAiTool], expected_names: &[&str]) {
function test_get_openai_tools (line 596) | fn test_get_openai_tools() {
function test_get_openai_tools_default_shell (line 614) | fn test_get_openai_tools_default_shell() {
function test_get_openai_tools_mcp_tools (line 631) | fn test_get_openai_tools_mcp_tools() {
function test_get_openai_tools_mcp_tools_sorted_by_name (line 730) | fn test_get_openai_tools_mcp_tools_sorted_by_name() {
function test_mcp_tool_property_missing_type_defaults_to_string (line 805) | fn test_mcp_tool_property_missing_type_defaults_to_string() {
function test_mcp_tool_integer_normalized_to_number (line 863) | fn test_mcp_tool_integer_normalized_to_number() {
function test_mcp_tool_array_without_items_gets_default_string_items (line 916) | fn test_mcp_tool_array_without_items_gets_default_string_items() {
function test_mcp_tool_anyof_defaults_to_string (line 972) | fn test_mcp_tool_anyof_defaults_to_string() {
FILE: codex-rs/core/src/parse_command.rs
type ParsedCommand (line 9) | pub enum ParsedCommand {
function from (line 47) | fn from(v: ParsedCommand) -> Self {
function shlex_join (line 62) | fn shlex_join(tokens: &[String]) -> String {
function parse_command (line 77) | pub fn parse_command(command: &[String]) -> Vec<ParsedCommand> {
function shlex_split_safe (line 96) | fn shlex_split_safe(s: &str) -> Vec<String> {
function vec_str (line 100) | fn vec_str(args: &[&str]) -> Vec<String> {
function assert_parsed (line 104) | fn assert_parsed(args: &[String], expected: Vec<ParsedCommand>) {
function git_status_is_unknown (line 110) | fn git_status_is_unknown() {
function handles_git_pipe_wc (line 120) | fn handles_git_pipe_wc() {
function bash_lc_redirect_not_quoted (line 131) | fn bash_lc_redirect_not_quoted() {
function handles_complex_bash_command_head (line 142) | fn handles_complex_bash_command_head() {
function supports_searching_for_navigate_to_route (line 173) | fn supports_searching_for_navigate_to_route() -> anyhow::Result<()> {
function handles_complex_bash_command (line 187) | fn handles_complex_bash_command() {
function supports_rg_files_with_path_and_pipe (line 205) | fn supports_rg_files_with_path_and_pipe() {
function supports_rg_files_then_head (line 218) | fn supports_rg_files_then_head() {
function supports_cat (line 236) | fn supports_cat() {
function supports_ls_with_pipe (line 248) | fn supports_ls_with_pipe() {
function supports_head_n (line 260) | fn supports_head_n() {
function supports_cat_sed_n (line 272) | fn supports_cat_sed_n() {
function supports_tail_n_plus (line 284) | fn supports_tail_n_plus() {
function supports_tail_n_last_lines (line 296) | fn supports_tail_n_last_lines() {
function supports_npm_run_build_is_unknown (line 309) | fn supports_npm_run_build_is_unknown() {
function supports_npm_run_with_forwarded_args (line 319) | fn supports_npm_run_with_forwarded_args() {
function supports_grep_recursive_current_dir (line 340) | fn supports_grep_recursive_current_dir() {
function supports_grep_recursive_specific_file (line 352) | fn supports_grep_recursive_specific_file() {
function supports_grep_query_with_slashes_not_shortened (line 370) | fn supports_grep_query_with_slashes_not_shortened() {
function supports_grep_weird_backtick_in_query (line 384) | fn supports_grep_weird_backtick_in_query() {
function supports_cd_and_rg_files (line 396) | fn supports_cd_and_rg_files() {
function echo_then_cargo_test_sequence (line 413) | fn echo_then_cargo_test_sequence() {
function supports_cargo_fmt_and_test_with_config (line 423) | fn supports_cargo_fmt_and_test_with_config() {
function recognizes_rustfmt_and_clippy (line 442) | fn recognizes_rustfmt_and_clippy() {
function recognizes_pytest_go_and_tools (line 463) | fn recognizes_pytest_go_and_tools() {
function recognizes_jest_and_vitest_filters (line 510) | fn recognizes_jest_and_vitest_filters() {
function recognizes_npx_and_scripts (line 527) | fn recognizes_npx_and_scripts() {
function small_formatting_always_true_commands (line 572) | fn small_formatting_always_true_commands() {
function head_behavior (line 584) | fn head_behavior() {
function tail_behavior (line 600) | fn tail_behavior() {
function sed_behavior (line 622) | fn sed_behavior() {
function empty_tokens_is_not_small (line 644) | fn empty_tokens_is_not_small() {
function supports_nl_then_sed_reading (line 650) | fn supports_nl_then_sed_reading() {
function supports_sed_n (line 662) | fn supports_sed_n() {
function filters_out_printf (line 674) | fn filters_out_printf() {
function drops_yes_in_pipelines (line 687) | fn drops_yes_in_pipelines() {
function supports_sed_n_then_nl_as_search (line 701) | fn supports_sed_n_then_nl_as_search() {
function preserves_rg_with_spaces (line 716) | fn preserves_rg_with_spaces() {
function ls_with_glob (line 728) | fn ls_with_glob() {
function trim_on_semicolon (line 739) | fn trim_on_semicolon() {
function split_on_or_connector (line 756) | fn split_on_or_connector() {
function strips_true_in_sequence (line 774) | fn strips_true_in_sequence() {
function strips_true_inside_bash_lc (line 796) | fn strips_true_inside_bash_lc() {
function shorten_path_on_windows (line 819) | fn shorten_path_on_windows() {
function head_with_no_space (line 830) | fn head_with_no_space() {
function bash_dash_c_pipeline_parsing (line 841) | fn bash_dash_c_pipeline_parsing() {
function tail_with_no_space (line 860) | fn tail_with_no_space() {
function pnpm_test_is_parsed_as_test (line 871) | fn pnpm_test_is_parsed_as_test() {
function pnpm_exec_vitest_is_unknown (line 881) | fn pnpm_exec_vitest_is_unknown() {
function cargo_test_with_crate (line 898) | fn cargo_test_with_crate() {
function cargo_test_with_crate_2 (line 908) | fn cargo_test_with_crate_2() {
function cargo_test_with_crate_3 (line 920) | fn cargo_test_with_crate_3() {
function cargo_test_with_crate_4 (line 930) | fn cargo_test_with_crate_4() {
function recognizes_black_and_ruff (line 941) | fn recognizes_black_and_ruff() {
function recognizes_pnpm_monorepo_test_and_npm_format_script (line 974) | fn recognizes_pnpm_monorepo_test_and_npm_format_script() {
function yarn_test_is_parsed_as_test (line 995) | fn yarn_test_is_parsed_as_test() {
function pytest_file_only_and_go_run_regex (line 1005) | fn pytest_file_only_and_go_run_regex() {
function grep_with_query_and_path (line 1024) | fn grep_with_query_and_path() {
function rg_with_equals_style_flags (line 1036) | fn rg_with_equals_style_flags() {
function cat_with_double_dash_and_sed_ranges (line 1048) | fn cat_with_double_dash_and_sed_ranges() {
function drop_trailing_nl_in_pipeline (line 1069) | fn drop_trailing_nl_in_pipeline() {
function ls_with_time_style_and_path (line 1082) | fn ls_with_time_style_and_path() {
function eslint_with_config_path_and_target (line 1094) | fn eslint_with_config_path_and_target() {
function npx_eslint_with_config_path_and_target (line 1106) | fn npx_eslint_with_config_path_and_target() {
function fd_file_finder_variants (line 1118) | fn fd_file_finder_variants() {
function find_basic_name_filter (line 1140) | fn find_basic_name_filter() {
function find_type_only_path (line 1152) | fn find_type_only_path() {
function parse_command_impl (line 1164) | pub fn parse_command_impl(command: &[String]) -> Vec<ParsedCommand> {
function simplify_once (line 1193) | fn simplify_once(commands: &[ParsedCommand]) -> Option<Vec<ParsedCommand...
function is_valid_sed_n_arg (line 1255) | fn is_valid_sed_n_arg(arg: Option<&str>) -> bool {
function normalize_tokens (line 1280) | fn normalize_tokens(cmd: &[String]) -> Vec<String> {
function contains_connectors (line 1298) | fn contains_connectors(tokens: &[String]) -> bool {
function split_on_connectors (line 1304) | fn split_on_connectors(tokens: &[String]) -> Vec<Vec<String>> {
function trim_at_connector (line 1322) | fn trim_at_connector(tokens: &[String]) -> Vec<String> {
function short_display_path (line 1335) | fn short_display_path(path: &str) -> String {
function skip_flag_values (line 1349) | fn skip_flag_values<'a>(args: &'a [String], flags_with_vals: &[&str]) ->...
constant ESLINT_FLAGS_WITH_VALUES (line 1382) | const ESLINT_FLAGS_WITH_VALUES: &[&str] = &[
function collect_non_flag_targets (line 1393) | fn collect_non_flag_targets(args: &[String]) -> Option<Vec<String>> {
function collect_non_flag_targets_with_flags (line 1433) | fn collect_non_flag_targets_with_flags(
function is_pathish (line 1449) | fn is_pathish(s: &str) -> bool {
function parse_fd_query_and_path (line 1458) | fn parse_fd_query_and_path(tail: &[String]) -> (Option<String>, Option<S...
function parse_find_query_and_path (line 1491) | fn parse_find_query_and_path(tail: &[String]) -> (Option<String>, Option...
function classify_npm_like (line 1517) | fn classify_npm_like(tool: &str, tail: &[String], full_cmd: &[String]) -...
function parse_bash_lc_commands (line 1558) | fn parse_bash_lc_commands(original: &[String]) -> Option<Vec<ParsedComma...
function is_small_formatting_command (line 1692) | fn is_small_formatting_command(tokens: &[String]) -> bool {
function drop_small_formatting_commands (line 1724) | fn drop_small_formatting_commands(mut commands: Vec<Vec<String>>) -> Vec...
function summarize_main_tokens (line 1729) | fn summarize_main_tokens(main_cmd: &[String]) -> ParsedCommand {
FILE: codex-rs/core/src/plan_tool.rs
function handle_update_plan (line 66) | pub(crate) async fn handle_update_plan(
function parse_update_plan_arguments (line 93) | fn parse_update_plan_arguments(
FILE: codex-rs/core/src/project_doc.rs
constant CANDIDATE_FILENAMES (line 21) | const CANDIDATE_FILENAMES: &[&str] = &["AGENTS.md"];
constant PROJECT_DOC_SEPARATOR (line 25) | const PROJECT_DOC_SEPARATOR: &str = "\n\n--- project-doc ---\n\n";
function get_user_instructions (line 29) | pub(crate) async fn get_user_instructions(config: &Config) -> Option<Str...
function read_project_docs (line 51) | pub async fn read_project_docs(config: &Config) -> std::io::Result<Optio...
function discover_project_doc_paths (line 109) | pub fn discover_project_doc_paths(config: &Config) -> std::io::Result<Ve...
function make_config (line 189) | fn make_config(root: &TempDir, limit: usize, instructions: Option<&str>)...
function no_doc_file_returns_none (line 207) | async fn no_doc_file_returns_none() {
function doc_smaller_than_limit_is_returned (line 220) | async fn doc_smaller_than_limit_is_returned() {
function doc_larger_than_limit_is_truncated (line 236) | async fn doc_larger_than_limit_is_truncated() {
function finds_doc_in_repo_root (line 254) | async fn finds_doc_in_repo_root() {
function zero_byte_limit_disables_docs (line 281) | async fn zero_byte_limit_disables_docs() {
function merges_existing_instructions_with_project_doc (line 295) | async fn merges_existing_instructions_with_project_doc() {
function keeps_existing_instructions_when_doc_missing (line 313) | async fn keeps_existing_instructions_when_doc_missing() {
function concatenates_root_and_cwd_docs (line 326) | async fn concatenates_root_and_cwd_docs() {
FILE: codex-rs/core/src/rollout.rs
constant SESSIONS_SUBDIR (line 27) | const SESSIONS_SUBDIR: &str = "sessions";
type SessionMeta (line 30) | pub struct SessionMeta {
type SessionMetaWithGit (line 37) | struct SessionMetaWithGit {
type SessionStateSnapshot (line 45) | pub struct SessionStateSnapshot {}
type SavedSession (line 48) | pub struct SavedSession {
type RolloutRecorder (line 67) | pub(crate) struct RolloutRecorder {
method new (line 81) | pub async fn new(
method record_items (line 124) | pub(crate) async fn record_items(&self, items: &[ResponseItem]) -> std...
method record_state (line 153) | pub(crate) async fn record_state(&self, state: SessionStateSnapshot) -...
method resume (line 160) | pub async fn resume(
method shutdown (line 233) | pub async fn shutdown(&self) -> std::io::Result<()> {
type RolloutCmd (line 71) | enum RolloutCmd {
type LogFileInfo (line 249) | struct LogFileInfo {
function create_log_file (line 260) | fn create_log_file(config: &Config, session_id: Uuid) -> std::io::Result...
function rollout_writer (line 294) | async fn rollout_writer(
type JsonlWriter (line 356) | struct JsonlWriter {
method write_line (line 361) | async fn write_line(&mut self, item: &impl serde::Serialize) -> std::i...
FILE: codex-rs/core/src/safety.rs
type SafetyCheck (line 15) | pub enum SafetyCheck {
function assess_patch_safety (line 21) | pub fn assess_patch_safety(
function assess_command_safety (line 74) | pub fn assess_command_safety(
function assess_safety_for_untrusted_command (line 103) | pub(crate) fn assess_safety_for_untrusted_command(
function get_platform_sandbox (line 162) | pub fn get_platform_sandbox() -> Option<SandboxType> {
function is_write_patch_constrained_to_writable_paths (line 173) | fn is_write_patch_constrained_to_writable_paths(
function test_writable_roots_constraint (line 253) | fn test_writable_roots_constraint() {
function test_request_escalated_privileges (line 303) | fn test_request_escalated_privileges() {
function test_request_escalated_privileges_no_sandbox_fallback (line 323) | fn test_request_escalated_privileges_no_sandbox_fallback() {
FILE: codex-rs/core/src/seatbelt.rs
constant MACOS_SEATBELT_BASE_POLICY (line 11) | const MACOS_SEATBELT_BASE_POLICY: &str = include_str!("seatbelt_base_pol...
constant MACOS_PATH_TO_SEATBELT_EXECUTABLE (line 17) | const MACOS_PATH_TO_SEATBELT_EXECUTABLE: &str = "/usr/bin/sandbox-exec";
function spawn_command_under_seatbelt (line 19) | pub async fn spawn_command_under_seatbelt(
function create_seatbelt_command_args (line 41) | fn create_seatbelt_command_args(
function create_seatbelt_args_with_read_only_git_subpath (line 135) | fn create_seatbelt_args_with_read_only_git_subpath() {
function create_seatbelt_args_for_cwd_as_git_repo (line 211) | fn create_seatbelt_args_for_cwd_as_git_repo() {
type PopulatedTmp (line 303) | struct PopulatedTmp {
function populate_tmpdir (line 311) | fn populate_tmpdir(tmp: &Path) -> PopulatedTmp {
FILE: codex-rs/core/src/shell.rs
type ZshShell (line 7) | pub struct ZshShell {
type PowerShellConfig (line 13) | pub struct PowerShellConfig {
type Shell (line 19) | pub enum Shell {
method format_default_shell_invocation (line 26) | pub fn format_default_shell_invocation(&self, command: Vec<String>) ->...
method name (line 95) | pub fn name(&self) -> Option<String> {
function strip_bash_lc (line 106) | fn strip_bash_lc(command: &Vec<String>) -> Option<String> {
function default_user_shell (line 120) | pub async fn default_user_shell() -> Shell {
function default_user_shell (line 155) | pub async fn default_user_shell() -> Shell {
function default_user_shell (line 160) | pub async fn default_user_shell() -> Shell {
function test_current_shell_detects_zsh (line 206) | async fn test_current_shell_detects_zsh() {
function test_run_with_profile_zshrc_not_exists (line 227) | async fn test_run_with_profile_zshrc_not_exists() {
function test_run_with_profile_escaping_and_execution (line 237) | async fn test_run_with_profile_escaping_and_execution() {
function test_format_default_shell_invocation_powershell (line 346) | fn test_format_default_shell_invocation_powershell() {
FILE: codex-rs/core/src/spawn.rs
constant CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR (line 18) | pub const CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR: &str = "CODEX_SANDBOX_...
constant CODEX_SANDBOX_ENV_VAR (line 23) | pub const CODEX_SANDBOX_ENV_VAR: &str = "CODEX_SANDBOX";
type StdioPolicy (line 26) | pub enum StdioPolicy {
function spawn_child_async (line 38) | pub(crate) async fn spawn_child_async(
FILE: codex-rs/core/src/terminal.rs
function user_agent (line 5) | pub fn user_agent() -> String {
function is_valid_header_value_char (line 16) | fn is_valid_header_value_char(c: char) -> bool {
function sanitize_header_value (line 20) | fn sanitize_header_value(value: String) -> String {
function detect_terminal (line 24) | fn detect_terminal() -> String {
FILE: codex-rs/core/src/tool_apply_patch.rs
type ApplyPatchToolArgs (line 12) | pub(crate) struct ApplyPatchToolArgs {
type ApplyPatchToolType (line 18) | pub enum ApplyPatchToolType {
function create_apply_patch_freeform_tool (line 25) | pub(crate) fn create_apply_patch_freeform_tool() -> OpenAiTool {
function create_apply_patch_json_tool (line 58) | pub(crate) fn create_apply_patch_json_tool() -> OpenAiTool {
FILE: codex-rs/core/src/turn_diff_tracker.rs
constant ZERO_OID (line 15) | const ZERO_OID: &str = "0000000000000000000000000000000000000000";
constant DEV_NULL (line 16) | const DEV_NULL: &str = "/dev/null";
type BaselineFileInfo (line 18) | struct BaselineFileInfo {
type TurnDiffTracker (line 33) | pub struct TurnDiffTracker {
method new (line 46) | pub fn new() -> Self {
method on_patch_begin (line 54) | pub fn on_patch_begin(&mut self, changes: &HashMap<PathBuf, FileChange...
method get_path_for_internal (line 130) | fn get_path_for_internal(&self, internal: &str) -> Option<PathBuf> {
method find_git_root_cached (line 143) | fn find_git_root_cached(&mut self, start: &Path) -> Option<PathBuf> {
method relative_to_git_root_str (line 188) | fn relative_to_git_root_str(&mut self, path: &Path) -> String {
method git_blob_oid_for_path (line 203) | fn git_blob_oid_for_path(&mut self, path: &Path) -> Option<String> {
method get_unified_diff (line 225) | pub fn get_unified_diff(&mut self) -> Result<Option<String>> {
method get_file_diff (line 252) | fn get_file_diff(&mut self, internal_file_name: &str) -> String {
function git_blob_sha1_hex_bytes (line 372) | fn git_blob_sha1_hex_bytes(data: &[u8]) -> Output<sha1::Sha1> {
type FileMode (line 383) | enum FileMode {
method as_str (line 391) | fn as_str(&self) -> &'static str {
method fmt (line 402) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function file_mode_for_path (line 408) | fn file_mode_for_path(path: &Path) -> Option<FileMode> {
function file_mode_for_path (line 425) | fn file_mode_for_path(_path: &Path) -> Option<FileMode> {
function blob_bytes (line 430) | fn blob_bytes(path: &Path, mode: &FileMode) -> Option<Vec<u8>> {
function symlink_blob_bytes (line 446) | fn symlink_blob_bytes(path: &Path) -> Option<Vec<u8>> {
function symlink_blob_bytes (line 453) | fn symlink_blob_bytes(_path: &Path) -> Option<Vec<u8>> {
function is_windows_drive_or_unc_root (line 458) | fn is_windows_drive_or_unc_root(p: &std::path::Path) -> bool {
function git_blob_sha1_hex (line 475) | fn git_blob_sha1_hex(data: &str) -> String {
function normalize_diff_for_test (line 479) | fn normalize_diff_for_test(input: &str, root: &Path) -> String {
function accumulates_add_and_update (line 507) | fn accumulates_add_and_update() {
function accumulates_delete (line 575) | fn accumulates_delete() {
function accumulates_move_and_update (line 606) | fn accumulates_move_and_update() {
function move_without_1change_yields_no_diff (line 646) | fn move_without_1change_yields_no_diff() {
function move_declared_but_file_only_appears_at_dest_is_add (line 670) | fn move_declared_but_file_only_appears_at_dest_is_add() {
function update_persists_across_new_baseline_for_new_file (line 705) | fn update_persists_across_new_baseline_for_new_file() {
function binary_files_differ_update (line 778) | fn binary_files_differ_update() {
function filenames_with_spaces_add_and_update (line 820) | fn filenames_with_spaces_add_and_update() {
FILE: codex-rs/core/src/user_agent.rs
constant DEFAULT_ORIGINATOR (line 1) | const DEFAULT_ORIGINATOR: &str = "codex_cli_rs";
function get_codex_user_agent (line 3) | pub fn get_codex_user_agent(originator: Option<&str>) -> String {
function test_get_codex_user_agent (line 21) | fn test_get_codex_user_agent() {
function test_macos (line 28) | fn test_macos() {
FILE: codex-rs/core/src/user_notification.rs
type UserNotification (line 8) | pub(crate) enum UserNotification {
function test_user_notification (line 26) | fn test_user_notification() {
FILE: codex-rs/core/src/util.rs
constant INITIAL_DELAY_MS (line 6) | const INITIAL_DELAY_MS: u64 = 200;
constant BACKOFF_FACTOR (line 7) | const BACKOFF_FACTOR: f64 = 2.0;
function backoff (line 9) | pub(crate) fn backoff(attempt: u64) -> Duration {
function is_inside_git_repo (line 28) | pub fn is_inside_git_repo(base_dir: &Path) -> bool {
FILE: codex-rs/core/tests/common/lib.rs
function load_default_config_for_test (line 13) | pub fn load_default_config_for_test(codex_home: &TempDir) -> Config {
function load_sse_fixture (line 30) | pub fn load_sse_fixture(path: impl AsRef<std::path::Path>) -> String {
function load_sse_fixture_with_id_from_str (line 50) | pub fn load_sse_fixture_with_id_from_str(raw: &str, id: &str) -> String {
function load_sse_fixture_with_id (line 74) | pub fn load_sse_fixture_with_id(path: impl AsRef<std::path::Path>, id: &...
function wait_for_event (line 95) | pub async fn wait_for_event<F>(
function wait_for_event_with_timeout (line 106) | pub async fn wait_for_event_with_timeout<F>(
FILE: codex-rs/core/tests/suite/cli_stream.rs
function chat_mode_stream_cli (line 21) | async fn chat_mode_stream_cli() {
function exec_cli_applies_experimental_instructions_file (line 86) | async fn exec_cli_applies_experimental_instructions_file() {
function responses_api_stream_cli (line 179) | async fn responses_api_stream_cli() {
function integration_creates_and_checks_session_file (line 215) | async fn integration_creates_and_checks_session_file() {
function integration_git_info_unit_test (line 452) | async fn integration_git_info_unit_test() {
FILE: codex-rs/core/tests/suite/client.rs
function sse_completed (line 26) | fn sse_completed(id: &str) -> String {
function assert_message_role (line 31) | fn assert_message_role(request_body: &serde_json::Value, role: &str) {
function assert_message_starts_with (line 36) | fn assert_message_starts_with(request_body: &serde_json::Value, text: &s...
function assert_message_ends_with (line 48) | fn assert_message_ends_with(request_body: &serde_json::Value, text: &str) {
function write_auth_json (line 62) | fn write_auth_json(
function includes_session_id_and_model_headers_in_request (line 112) | async fn includes_session_id_and_model_headers_in_request() {
function includes_base_instructions_override_in_request (line 185) | async fn includes_base_instructions_override_in_request() {
function originator_config_override_is_used (line 242) | async fn originator_config_override_is_used() {
function chatgpt_auth_sends_correct_request (line 292) | async fn chatgpt_auth_sends_correct_request() {
function prefers_chatgpt_token_when_config_prefers_chatgpt (line 372) | async fn prefers_chatgpt_token_when_config_prefers_chatgpt() {
function prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tokens (line 453) | async fn prefers_apikey_when_config_prefers_apikey_even_with_chatgpt_tok...
function includes_user_instructions_message_in_request (line 534) | async fn includes_user_instructions_message_in_request() {
function azure_overrides_assign_properties_used_for_responses_url (line 595) | async fn azure_overrides_assign_properties_used_for_responses_url() {
function env_var_overrides_loaded_auth (line 671) | async fn env_var_overrides_loaded_auth() {
function create_dummy_codex_auth (line 746) | fn create_dummy_codex_auth() -> CodexAuth {
function history_dedupes_streamed_and_final_messages_across_turns (line 757) | async fn history_dedupes_streamed_and_final_messages_across_turns() {
FILE: codex-rs/core/tests/suite/compact.rs
function sse (line 26) | fn sse(events: Vec<Value>) -> String {
function ev_completed (line 42) | fn ev_completed(id: &str) -> Value {
function ev_assistant_message (line 53) | fn ev_assistant_message(id: &str, text: &str) -> Value {
function sse_response (line 65) | fn sse_response(body: String) -> ResponseTemplate {
function mount_sse_once (line 71) | async fn mount_sse_once<M>(server: &MockServer, matcher: M, body: String)
constant FIRST_REPLY (line 84) | const FIRST_REPLY: &str = "FIRST_REPLY";
constant SUMMARY_TEXT (line 85) | const SUMMARY_TEXT: &str = "SUMMARY_ONLY_CONTEXT";
constant SUMMARIZE_TRIGGER (line 86) | const SUMMARIZE_TRIGGER: &str = "Start Summarization";
constant THIRD_USER_MSG (line 87) | const THIRD_USER_MSG: &str = "next turn";
function summarize_context_three_requests_and_instructions (line 90) | async fn summarize_context_three_requests_and_instructions() {
FILE: codex-rs/core/tests/suite/exec.rs
function skip_test (line 17) | fn skip_test() -> bool {
function run_test_cmd (line 27) | async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result<ExecToolCa...
function exit_code_0_succeeds (line 47) | async fn exit_code_0_succeeds() {
function truncates_output_lines (line 63) | async fn truncates_output_lines() {
function truncates_output_bytes (line 83) | async fn truncates_output_bytes() {
function exit_command_not_found_is_ok (line 100) | async fn exit_command_not_found_is_ok() {
function write_file_fails_as_sandbox_error (line 112) | async fn write_file_fails_as_sandbox_error() {
FILE: codex-rs/core/tests/suite/exec_stream_events.rs
function collect_stdout_events (line 17) | fn collect_stdout_events(rx: Receiver<Event>) -> Vec<u8> {
function test_exec_stdout_stream_events_echo (line 33) | async fn test_exec_stdout_stream_events_echo() {
function test_exec_stderr_stream_events_echo (line 83) | async fn test_exec_stderr_stream_events_echo() {
function test_aggregated_output_interleaves_in_order (line 144) | async fn test_aggregated_output_interleaves_in_order() {
FILE: codex-rs/core/tests/suite/live_cli.rs
function require_api_key (line 13) | fn require_api_key() -> String {
function run_live (line 19) | fn run_live(prompt: &str) -> (assert_cmd::assert::Assert, TempDir) {
function live_create_file_hello_txt (line 115) | fn live_create_file_hello_txt() {
function live_print_working_directory (line 137) | fn live_print_working_directory() {
FILE: codex-rs/core/tests/suite/prompt_caching.rs
function sse_completed (line 27) | fn sse_completed(id: &str) -> String {
function assert_tool_names (line 31) | fn assert_tool_names(body: &serde_json::Value, expected_names: &[&str]) {
function codex_mini_latest_tools (line 44) | async fn codex_mini_latest_tools() {
function prompt_tools_are_consistent_across_requests (line 128) | async fn prompt_tools_are_consistent_across_requests() {
function prefixes_context_and_instructions_once_and_consistently_across_requests (line 211) | async fn prefixes_context_and_instructions_once_and_consistently_across_...
function overrides_turn_context_but_keeps_cached_prefix_and_key_constant (line 333) | async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant...
function per_turn_overrides_keep_cached_prefix_and_key_constant (line 456) | async fn per_turn_overrides_keep_cached_prefix_and_key_constant() {
FILE: codex-rs/core/tests/suite/seatbelt.rs
type TestScenario (line 16) | struct TestScenario {
method run_test (line 31) | async fn run_test(&self, policy: &SandboxPolicy, expectations: TestExp...
type TestExpectations (line 24) | struct TestExpectations {
function if_parent_of_repo_is_writable_then_dot_git_folder_is_writable (line 75) | async fn if_parent_of_repo_is_writable_then_dot_git_folder_is_writable() {
function if_git_repo_is_writable_root_then_dot_git_folder_is_read_only (line 101) | async fn if_git_repo_is_writable_root_then_dot_git_folder_is_read_only() {
function danger_full_access_allows_all_writes (line 126) | async fn danger_full_access_allows_all_writes() {
function read_only_forbids_all_writes (line 145) | async fn read_only_forbids_all_writes() {
function create_test_scenario (line 163) | fn create_test_scenario(tmp: &TempDir) -> TestScenario {
function touch (line 182) | async fn touch(path: &Path, policy: &SandboxPolicy) -> bool {
FILE: codex-rs/core/tests/suite/stream_error_allows_next_turn.rs
function sse_completed (line 22) | fn sse_completed(id: &str) -> String {
function continue_after_stream_error (line 27) | async fn continue_after_stream_error() {
FILE: codex-rs/core/tests/suite/stream_no_completed.rs
function sse_incomplete (line 26) | fn sse_incomplete() -> String {
function sse_completed (line 30) | fn sse_completed(id: &str) -> String {
function retries_on_early_close (line 35) | async fn retries_on_early_close() {
FILE: codex-rs/exec/src/cli.rs
type Cli (line 8) | pub struct Cli {
type Color (line 90) | pub enum Color {
type Mode (line 99) | pub enum Mode {
method fmt (line 114) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: codex-rs/exec/src/event_processor.rs
type CodexStatus (line 6) | pub(crate) enum CodexStatus {
type EventProcessor (line 12) | pub(crate) trait EventProcessor {
method print_config_summary (line 14) | fn print_config_summary(&mut self, config: &Config, prompt: &str);
method process_event (line 17) | fn process_event(&mut self, event: Event) -> CodexStatus;
function handle_last_message (line 20) | pub(crate) fn handle_last_message(last_agent_message: Option<&str>, outp...
function write_last_message_file (line 31) | fn write_last_message_file(contents: &str, last_message_path: Option<&Pa...
FILE: codex-rs/exec/src/event_processor_with_human_output.rs
constant MAX_OUTPUT_LINES_FOR_EXEC_TOOL_CALL (line 43) | const MAX_OUTPUT_LINES_FOR_EXEC_TOOL_CALL: usize = 20;
type EventProcessorWithHumanOutput (line 44) | pub(crate) struct EventProcessorWithHumanOutput {
method create_with_ansi (line 70) | pub(crate) fn create_with_ansi(
type ExecCommandBegin (line 118) | struct ExecCommandBegin {
type PatchApplyBegin (line 122) | struct PatchApplyBegin {
method print_config_summary (line 142) | fn print_config_summary(&mut self, config: &Config, prompt: &str) {
method process_event (line 169) | fn process_event(&mut self, event: Event) -> CodexStatus {
function escape_command (line 551) | fn escape_command(command: &[String]) -> String {
function format_file_change (line 555) | fn format_file_change(change: &FileChange) -> &'static str {
function format_mcp_invocation (line 568) | fn format_mcp_invocation(invocation: &McpInvocation) -> String {
FILE: codex-rs/exec/src/event_processor_with_json_output.rs
type EventProcessorWithJsonOutput (line 15) | pub(crate) struct EventProcessorWithJsonOutput {
method new (line 20) | pub fn new(last_message_path: Option<PathBuf>) -> Self {
method print_config_summary (line 26) | fn print_config_summary(&mut self, config: &Config, prompt: &str) {
method process_event (line 42) | fn process_event(&mut self, event: Event) -> CodexStatus {
FILE: codex-rs/exec/src/lib.rs
function get_specialist_system_prompt (line 39) | fn get_specialist_system_prompt(specialist: &str) -> String {
function get_default_system_prompt (line 55) | fn get_default_system_prompt() -> String {
function run_main (line 59) | pub async fn run_main(cli: Cli, codex_linux_sandbox_exe: Option<PathBuf>...
function handle_last_message (line 465) | fn handle_last_message(
function wait_for_supervisor_followup (line 487) | async fn wait_for_supervisor_followup(
FILE: codex-rs/exec/src/main.rs
type TopCli (line 19) | struct TopCli {
function main (line 27) | fn main() -> anyhow::Result<()> {
FILE: codex-rs/exec/src/realtime_logger.rs
type RealtimeLogger (line 13) | pub struct RealtimeLogger {
method new (line 26) | pub fn new(
method log_event (line 131) | pub async fn log_event(&self, event: &Event) -> anyhow::Result<()> {
method append_context (line 460) | async fn append_context(&self, text: &str) -> anyhow::Result<()> {
method save_final_result (line 467) | async fn save_final_result(&self, status: &str) -> anyhow::Result<()> {
FILE: codex-rs/exec/tests/suite/apply_patch.rs
function test_standalone_exec_cli_can_use_apply_patch (line 14) | fn test_standalone_exec_cli_can_use_apply_patch() -> anyhow::Result<()> {
function test_apply_patch_tool (line 45) | async fn test_apply_patch_tool() -> anyhow::Result<()> {
function test_apply_patch_freeform_tool (line 77) | async fn test_apply_patch_freeform_tool() -> anyhow::Result<()> {
FILE: codex-rs/exec/tests/suite/common.rs
type SeqResponder (line 19) | struct SeqResponder {
method respond (line 25) | fn respond(&self, _: &wiremock::Request) -> wiremock::ResponseTemplate {
function run_e2e_exec_test (line 42) | pub(crate) async fn run_e2e_exec_test(cwd: &Path, response_streams: Vec<...
FILE: codex-rs/exec/tests/suite/sandbox.rs
function spawn_command_under_sandbox (line 12) | async fn spawn_command_under_sandbox(
function spawn_command_under_sandbox (line 24) | async fn spawn_command_under_sandbox(
function python_multiprocessing_lock_works_under_sandbox (line 45) | async fn python_multiprocessing_lock_works_under_sandbox() {
function unix_sock_body (line 95) | fn unix_sock_body() {
function allow_unix_socketpair_recvfrom (line 169) | async fn allow_unix_socketpair_recvfrom() {
constant IN_SANDBOX_ENV_VAR (line 179) | const IN_SANDBOX_ENV_VAR: &str = "IN_SANDBOX";
function run_code_under_sandbox (line 182) | pub async fn run_code_under_sandbox<F, Fut>(
FILE: codex-rs/execpolicy/build.rs
function main (line 1) | fn main() {
FILE: codex-rs/execpolicy/src/arg_matcher.rs
type ArgMatcher (line 20) | pub enum ArgMatcher {
method cardinality (line 51) | pub fn cardinality(&self) -> ArgMatcherCardinality {
method arg_type (line 66) | pub fn arg_type(&self) -> ArgType {
method alloc_value (line 98) | fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
type Canonical (line 105) | type Canonical = ArgMatcher;
type Error (line 109) | type Error = starlark::Error;
method unpack_value_impl (line 111) | fn unpack_value_impl(value: Value<'v>) -> starlark::Result<Option<Self...
type ArgMatcherCardinality (line 81) | pub enum ArgMatcherCardinality {
method is_exact (line 88) | pub fn is_exact(&self) -> Option<usize> {
FILE: codex-rs/execpolicy/src/arg_resolver.rs
type PositionalArg (line 10) | pub struct PositionalArg {
function resolve_observed_args_with_patterns (line 15) | pub fn resolve_observed_args_with_patterns(
type ParitionedArgs (line 148) | struct ParitionedArgs {
function partition_args (line 156) | fn partition_args(program: &str, arg_patterns: &Vec<ArgMatcher>) -> Resu...
function get_range_checked (line 190) | fn get_range_checked<T>(vec: &[T], range: std::ops::Range<usize>) -> Res...
FILE: codex-rs/execpolicy/src/arg_type.rs
type ArgType (line 15) | pub enum ArgType {
method validate (line 32) | pub fn validate(&self, value: &str) -> Result<()> {
method might_write_file (line 72) | pub fn might_write_file(&self) -> bool {
type Canonical (line 86) | type Canonical = ArgType;
FILE: codex-rs/execpolicy/src/error.rs
type Result (line 10) | pub type Result<T> = std::result::Result<T, Error>;
type Error (line 15) | pub enum Error {
FILE: codex-rs/execpolicy/src/exec_call.rs
type ExecCall (line 6) | pub struct ExecCall {
method new (line 12) | pub fn new(program: &str, args: &[&str]) -> Self {
method fmt (line 21) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: codex-rs/execpolicy/src/execv_checker.rs
type ExecvChecker (line 28) | pub struct ExecvChecker {
method new (line 33) | pub fn new(execv_policy: Policy) -> Self {
method r#match (line 37) | pub fn r#match(&self, exec_call: &ExecCall) -> Result<MatchedExec> {
method check (line 43) | pub fn check(
function ensure_absolute_path (line 100) | fn ensure_absolute_path(path: &str, cwd: &Option<OsString>) -> Result<Pa...
function is_executable_file (line 118) | fn is_executable_file(path: &str) -> bool {
function setup (line 149) | fn setup(fake_cp: &Path) -> ExecvChecker {
function test_check_valid_input_files (line 165) | fn test_check_valid_input_files() -> Result<()> {
FILE: codex-rs/execpolicy/src/lib.rs
constant DEFAULT_POLICY (line 40) | const DEFAULT_POLICY: &str = include_str!("default.policy");
function get_default_policy (line 42) | pub fn get_default_policy() -> starlark::Result<Policy> {
FILE: codex-rs/execpolicy/src/main.rs
constant MATCHED_BUT_WRITES_FILES_EXIT_CODE (line 16) | const MATCHED_BUT_WRITES_FILES_EXIT_CODE: i32 = 12;
constant MIGHT_BE_SAFE_EXIT_CODE (line 17) | const MIGHT_BE_SAFE_EXIT_CODE: i32 = 13;
constant FORBIDDEN_EXIT_CODE (line 18) | const FORBIDDEN_EXIT_CODE: i32 = 14;
type Args (line 22) | pub struct Args {
type Command (line 37) | pub enum Command {
type ExecArg (line 54) | pub struct ExecArg {
function main (line 61) | fn main() -> Result<()> {
function check_command (line 96) | fn check_command(
type Output (line 128) | pub enum Output {
function deserialize_from_json (line 150) | fn deserialize_from_json<'de, D>(deserializer: D) -> Result<ExecArg, D::...
type Err (line 161) | type Err = anyhow::Error;
method from_str (line 163) | fn from_str(s: &str) -> Result<Self, Self::Err> {
FILE: codex-rs/execpolicy/src/opt.rs
type Opt (line 19) | pub struct Opt {
method new (line 40) | pub fn new(opt: String, meta: OptMeta, required: bool) -> Self {
method name (line 48) | pub fn name(&self) -> &str {
type Canonical (line 55) | type Canonical = Opt;
type Error (line 59) | type Error = starlark::Error;
method unpack_value_impl (line 61) | fn unpack_value_impl(value: Value<'v>) -> starlark::Result<Option<Self...
method alloc_value (line 69) | fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
type OptMeta (line 31) | pub enum OptMeta {
type Canonical (line 76) | type Canonical = OptMeta;
FILE: codex-rs/execpolicy/src/policy.rs
type Policy (line 15) | pub struct Policy {
method new (line 22) | pub fn new(
method check (line 44) | pub fn check(&self, exec_call: &ExecCall) -> Result<MatchedExec> {
method check_each_good_list_individually (line 88) | pub fn check_each_good_list_individually(&self) -> Vec<PositiveExample...
method check_each_bad_list_individually (line 96) | pub fn check_each_bad_list_individually(&self) -> Vec<NegativeExampleP...
FILE: codex-rs/execpolicy/src/policy_parser.rs
type PolicyParser (line 24) | pub struct PolicyParser {
method new (line 30) | pub fn new(policy_source: &str, unparsed_policy: &str) -> Self {
method parse (line 37) | pub fn parse(&self) -> starlark::Result<Policy> {
type ForbiddenProgramRegex (line 75) | pub struct ForbiddenProgramRegex {
type PolicyBuilder (line 81) | struct PolicyBuilder {
method new (line 88) | fn new() -> Self {
method build (line 96) | fn build(self) -> Result<Policy, regex_lite::Error> {
method add_program_spec (line 103) | fn add_program_spec(&self, program_spec: ProgramSpec) {
method add_forbidden_substrings (line 110) | fn add_forbidden_substrings(&self, substrings: &[String]) {
method add_forbidden_program_regex (line 115) | fn add_forbidden_program_regex(&self, regex: Regex, reason: String) {
function policy_builtins (line 122) | fn policy_builtins(builder: &mut GlobalsBuilder) {
FILE: codex-rs/execpolicy/src/program.rs
type ProgramSpec (line 19) | pub struct ProgramSpec {
method new (line 33) | pub fn new(
method check (line 94) | pub fn check(&self, exec_call: &ExecCall) -> Result<MatchedExec> {
method verify_should_match_list (line 197) | pub fn verify_should_match_list(&self) -> Vec<PositiveExampleFailedChe...
method verify_should_not_match_list (line 218) | pub fn verify_should_not_match_list(&self) -> Vec<NegativeExamplePasse...
type MatchedExec (line 70) | pub enum MatchedExec {
type Forbidden (line 76) | pub enum Forbidden {
type PositiveExampleFailedCheck (line 237) | pub struct PositiveExampleFailedCheck {
type NegativeExamplePassedCheck (line 244) | pub struct NegativeExamplePassedCheck {
FILE: codex-rs/execpolicy/src/sed_command.rs
function parse_sed_command (line 4) | pub fn parse_sed_command(sed_command: &str) -> Result<()> {
FILE: codex-rs/execpolicy/src/valid_exec.rs
type ValidExec (line 7) | pub struct ValidExec {
method new (line 21) | pub fn new(program: &str, args: Vec<MatchedArg>, system_path: &[&str])...
method might_write_files (line 33) | pub fn might_write_files(&self) -> bool {
type MatchedArg (line 40) | pub struct MatchedArg {
method new (line 47) | pub fn new(index: usize, r#type: ArgType, value: &str) -> Result<Self> {
type MatchedOpt (line 59) | pub struct MatchedOpt {
method new (line 69) | pub fn new(name: &str, value: &str, r#type: ArgType) -> Result<Self> {
method name (line 78) | pub fn name(&self) -> &str {
type MatchedFlag (line 84) | pub struct MatchedFlag {
method new (line 90) | pub fn new(name: &str) -> Self {
FILE: codex-rs/execpolicy/tests/suite/bad.rs
function verify_everything_in_bad_list_is_rejected (line 5) | fn verify_everything_in_bad_list_is_rejected() {
FILE: codex-rs/execpolicy/tests/suite/cp.rs
function setup (line 15) | fn setup() -> Policy {
function test_cp_no_args (line 20) | fn test_cp_no_args() {
function test_cp_one_arg (line 34) | fn test_cp_one_arg() {
function test_cp_one_file (line 48) | fn test_cp_one_file() -> Result<()> {
function test_cp_multiple_files (line 68) | fn test_cp_multiple_files() -> Result<()> {
FILE: codex-rs/execpolicy/tests/suite/good.rs
function verify_everything_in_good_list_is_allowed (line 5) | fn verify_everything_in_good_list_is_allowed() {
FILE: codex-rs/execpolicy/tests/suite/head.rs
function setup (line 16) | fn setup() -> Policy {
function test_head_no_args (line 21) | fn test_head_no_args() {
function test_head_one_file_no_flags (line 42) | fn test_head_one_file_no_flags() -> Result<()> {
function test_head_one_flag_one_file (line 63) | fn test_head_one_flag_one_file() -> Result<()> {
function test_head_invalid_n_as_0 (line 89) | fn test_head_invalid_n_as_0() {
function test_head_invalid_n_as_nonint_float (line 101) | fn test_head_invalid_n_as_nonint_float() {
function test_head_invalid_n_as_float (line 113) | fn test_head_invalid_n_as_float() {
function test_head_invalid_n_as_negative_int (line 125) | fn test_head_invalid_n_as_negative_int() {
FILE: codex-rs/execpolicy/tests/suite/literal.rs
function test_invalid_subcommand (line 13) | fn test_invalid_subcommand() -> Result<()> {
FILE: codex-rs/execpolicy/tests/suite/ls.rs
function setup (line 15) | fn setup() -> Policy {
function test_ls_no_args (line 20) | fn test_ls_no_args() {
function test_ls_dash_a_dash_l (line 32) | fn test_ls_dash_a_dash_l() {
function test_ls_dash_z (line 50) | fn test_ls_dash_z() {
function test_ls_dash_al (line 66) | fn test_ls_dash_al() {
function test_ls_one_file_arg (line 81) | fn test_ls_one_file_arg() -> Result<()> {
function test_ls_multiple_file_args (line 99) | fn test_ls_multiple_file_args() -> Result<()> {
function test_ls_multiple_flags_and_file_args (line 121) | fn test_ls_multiple_flags_and_file_args() -> Result<()> {
function test_flags_after_file_args (line 145) | fn test_flags_after_file_args() -> Result<()> {
FILE: codex-rs/execpolicy/tests/suite/parse_sed_command.rs
function parses_simple_print_command (line 5) | fn parses_simple_print_command() {
function rejects_malformed_print_command (line 10) | fn rejects_malformed_print_command() {
FILE: codex-rs/execpolicy/tests/suite/pwd.rs
function setup (line 15) | fn setup() -> Policy {
function test_pwd_no_args (line 20) | fn test_pwd_no_args() {
function test_pwd_capital_l (line 35) | fn test_pwd_capital_l() {
function test_pwd_capital_p (line 51) | fn test_pwd_capital_p() {
function test_pwd_extra_args (line 67) | fn test_pwd_extra_args() {
FILE: codex-rs/execpolicy/tests/suite/sed.rs
function setup (line 16) | fn setup() -> Policy {
function test_sed_print_specific_lines (line 21) | fn test_sed_print_specific_lines() -> Result<()> {
function test_sed_print_specific_lines_with_e_flag (line 43) | fn test_sed_print_specific_lines_with_e_flag() -> Result<()> {
function test_sed_reject_dangerous_command (line 65) | fn test_sed_reject_dangerous_command() {
function test_sed_verify_e_or_pattern_is_required (line 77) | fn test_sed_verify_e_or_pattern_is_required() {
FILE: codex-rs/file-search/src/cli.rs
type Cli (line 10) | pub struct Cli {
FILE: codex-rs/file-search/src/lib.rs
type FileMatch (line 36) | pub struct FileMatch {
type FileSearchResults (line 43) | pub struct FileSearchResults {
type Reporter (line 48) | pub trait Reporter {
method report_match (line 49) | fn report_match(&self, file_match: &FileMatch);
method warn_matches_truncated (line 50) | fn warn_matches_truncated(&self, total_match_count: usize, shown_match...
method warn_no_search_pattern (line 51) | fn warn_no_search_pattern(&self, search_directory: &Path);
function run_main (line 54) | pub async fn run_main<T: Reporter>(
function run (line 124) | pub fn run(
function sort_matches (line 283) | fn sort_matches(matches: &mut [(u32, String)]) {
type BestMatchesList (line 291) | struct BestMatchesList {
method new (line 303) | fn new(max_count: usize, pattern: Pattern, matcher: Matcher) -> Self {
method insert (line 314) | fn insert(&mut self, line: &str) {
type WorkerCount (line 333) | struct WorkerCount {
function create_worker_count (line 338) | fn create_worker_count(num_workers: NonZero<usize>) -> WorkerCount {
function create_pattern (line 357) | fn create_pattern(pattern: &str) -> Pattern {
function verify_score_is_none_for_non_match (line 371) | fn verify_score_is_none_for_non_match() {
function tie_breakers_sort_by_path_when_scores_equal (line 382) | fn tie_breakers_sort_by_path_when_scores_equal() {
FILE: codex-rs/file-search/src/main.rs
function main (line 12) | async fn main() -> anyhow::Result<()> {
type StdioReporter (line 22) | struct StdioReporter {
method report_match (line 28) | fn report_match(&self, file_match: &FileMatch) {
method warn_matches_truncated (line 61) | fn warn_matches_truncated(&self, total_match_count: usize, shown_match_c...
method warn_no_search_pattern (line 72) | fn warn_no_search_pattern(&self, search_directory: &Path) {
FILE: codex-rs/linux-sandbox/src/landlock.rs
function apply_sandbox_policy_to_current_thread (line 30) | pub(crate) fn apply_sandbox_policy_to_current_thread(
function install_filesystem_landlock_rules_on_current_thread (line 59) | fn install_filesystem_landlock_rules_on_current_thread(writable_roots: V...
function install_network_seccomp_filter_on_current_thread (line 87) | fn install_network_seccomp_filter_on_current_thread() -> std::result::Re...
FILE: codex-rs/linux-sandbox/src/lib.rs
function run_main (line 7) | pub fn run_main() -> ! {
function run_main (line 12) | pub fn run_main() -> ! {
FILE: codex-rs/linux-sandbox/src/linux_run_main.rs
type LandlockCommand (line 8) | pub struct LandlockCommand {
function run_main (line 20) | pub fn run_main() -> ! {
FILE: codex-rs/linux-sandbox/src/main.rs
function main (line 4) | fn main() -> ! {
FILE: codex-rs/linux-sandbox/tests/suite/landlock.rs
constant SHORT_TIMEOUT_MS (line 17) | const SHORT_TIMEOUT_MS: u64 = 200;
constant SHORT_TIMEOUT_MS (line 19) | const SHORT_TIMEOUT_MS: u64 = 5_000;
constant LONG_TIMEOUT_MS (line 22) | const LONG_TIMEOUT_MS: u64 = 1_000;
constant LONG_TIMEOUT_MS (line 24) | const LONG_TIMEOUT_MS: u64 = 5_000;
constant NETWORK_TIMEOUT_MS (line 27) | const NETWORK_TIMEOUT_MS: u64 = 2_000;
constant NETWORK_TIMEOUT_MS (line 29) | const NETWORK_TIMEOUT_MS: u64 = 10_000;
function create_env_from_core_vars (line 31) | fn create_env_from_core_vars() -> HashMap<String, String> {
function run_cmd (line 37) | async fn run_cmd(cmd: &[&str], writable_roots: &[PathBuf], timeout_ms: u...
function test_root_read (line 76) | async fn test_root_read() {
function test_root_write (line 82) | async fn test_root_write() {
function test_dev_null_write (line 94) | async fn test_dev_null_write() {
function test_writable_root (line 106) | async fn test_writable_root() {
function test_timeout (line 125) | async fn test_timeout() {
function assert_network_blocked (line 134) | async fn assert_network_blocked(cmd: &[&str]) {
function sandbox_blocks_curl (line 183) | async fn sandbox_blocks_curl() {
function sandbox_blocks_wget (line 188) | async fn sandbox_blocks_wget() {
function sandbox_blocks_ping (line 193) | async fn sandbox_blocks_ping() {
function sandbox_blocks_nc (line 199) | async fn sandbox_blocks_nc() {
function sandbox_blocks_ssh (line 205) | async fn sandbox_blocks_ssh() {
function sandbox_blocks_getent (line 220) | async fn sandbox_blocks_getent() {
function sandbox_blocks_dev_tcp_redirection (line 225) | async fn sandbox_blocks_dev_tcp_redirection() {
FILE: codex-rs/login/src/auth_manager.rs
type CachedAuth (line 10) | struct CachedAuth {
type AuthManager (line 24) | pub struct AuthManager {
method new (line 34) | pub fn new(codex_home: PathBuf, preferred_auth_mode: AuthMode) -> Self {
method from_auth_for_testing (line 48) | pub fn from_auth_for_testing(auth: CodexAuth) -> Arc<Self> {
method auth (line 61) | pub fn auth(&self) -> Option<CodexAuth> {
method preferred_auth_method (line 66) | pub fn preferred_auth_method(&self) -> AuthMode {
method reload (line 75) | pub fn reload(&self) -> bool {
method auths_equal (line 89) | fn auths_equal(a: &Option<CodexAuth>, b: &Option<CodexAuth>) -> bool {
method shared (line 98) | pub fn shared(codex_home: PathBuf, preferred_auth_mode: AuthMode) -> A...
method refresh_token (line 104) | pub async fn refresh_token(&self) -> std::io::Result<Option<String>> {
method logout (line 123) | pub fn logout(&self) -> std::io::Result<bool> {
FILE: codex-rs/login/src/lib.rs
constant CLIENT_ID (line 31) | pub const CLIENT_ID: &str = "app_EMoamEEZ73f0CkXaXp7hrann";
constant OPENAI_API_KEY_ENV_VAR (line 32) | pub const OPENAI_API_KEY_ENV_VAR: &str = "OPENAI_API_KEY";
type CodexAuth (line 37) | pub struct CodexAuth {
method from_api_key (line 52) | pub fn from_api_key(api_key: &str) -> Self {
method refresh_token (line 61) | pub async fn refresh_token(&self) -> Result<String, std::io::Error> {
method from_codex_home (line 96) | pub fn from_codex_home(
method get_token_data (line 103) | pub async fn get_token_data(&self) -> Result<TokenData, std::io::Error> {
method get_token (line 148) | pub async fn get_token(&self) -> Result<String, std::io::Error> {
method get_account_id (line 159) | pub fn get_account_id(&self) -> Option<String> {
method get_plan_type (line 164) | pub fn get_plan_type(&self) -> Option<String> {
method get_current_auth_json (line 169) | fn get_current_auth_json(&self) -> Option<AuthDotJson> {
method get_current_token_data (line 174) | fn get_current_token_data(&self) -> Option<TokenData> {
method create_dummy_chatgpt_auth_for_testing (line 179) | pub fn create_dummy_chatgpt_auth_for_testing() -> Self {
method eq (line 46) | fn eq(&self, other: &Self) -> bool {
function load_auth (line 201) | fn load_auth(
function read_openai_api_key_from_env (line 271) | fn read_openai_api_key_from_env() -> Option<String> {
function get_auth_file (line 277) | pub fn get_auth_file(codex_home: &Path) -> PathBuf {
function logout (line 283) | pub fn logout(codex_home: &Path) -> std::io::Result<bool> {
function login_with_api_key (line 292) | pub fn login_with_api_key(codex_home: &Path, api_key: &str) -> std::io::...
function try_read_auth_json (line 303) | pub fn try_read_auth_json(auth_file: &Path) -> std::io::Result<AuthDotJs...
function write_auth_json (line 312) | fn write_auth_json(auth_file: &Path, auth_dot_json: &AuthDotJson) -> std...
function update_tokens (line 326) | async fn update_tokens(
function try_refresh_token (line 347) | async fn try_refresh_token(refresh_token: String) -> std::io::Result<Ref...
type RefreshRequest (line 379) | struct RefreshRequest {
type RefreshResponse (line 387) | struct RefreshResponse {
type AuthDotJson (line 395) | pub struct AuthDotJson {
constant LAST_REFRESH (line 417) | const LAST_REFRESH: &str = "2025-08-06T20:41:36.232376Z";
function writes_api_key_and_loads_auth (line 420) | fn writes_api_key_and_loads_auth() {
function loads_from_env_var_if_env_var_exists (line 431) | fn loads_from_env_var_if_env_var_exists() {
function roundtrip_auth_dot_json (line 446) | async fn roundtrip_auth_dot_json() {
function pro_account_with_no_api_key_uses_chatgpt_auth (line 466) | async fn pro_account_with_no_api_key_uses_chatgpt_auth() {
function pro_account_with_api_key_still_uses_chatgpt_auth (line 517) | async fn pro_account_with_api_key_still_uses_chatgpt_auth() {
function enterprise_account_with_api_key_uses_chatgpt_auth (line 567) | async fn enterprise_account_with_api_key_uses_chatgpt_auth() {
type AuthFileParams (line 593) | struct AuthFileParams {
function write_auth_file (line 598) | fn write_auth_file(params: AuthFileParams, codex_home: &Path) -> std::io...
function id_token_info_handles_missing_fields (line 642) | fn id_token_info_handles_missing_fields() {
function loads_api_key_from_auth_json (line 659) | async fn loads_api_key_from_auth_json() {
function logout_removes_auth_file (line 684) | fn logout_removes_auth_file() -> Result<(), std::io::Error> {
FILE: codex-rs/login/src/pkce.rs
type PkceCodes (line 7) | pub struct PkceCodes {
function generate_pkce (line 12) | pub fn generate_pkce() -> PkceCodes {
FILE: codex-rs/login/src/server.rs
constant DEFAULT_ISSUER (line 20) | const DEFAULT_ISSUER: &str = "https://auth.openai.com";
constant DEFAULT_PORT (line 21) | const DEFAULT_PORT: u16 = 1455;
type ServerOptions (line 24) | pub struct ServerOptions {
method new (line 34) | pub fn new(codex_home: PathBuf, client_id: String) -> Self {
type LoginServer (line 46) | pub struct LoginServer {
method block_until_done (line 54) | pub async fn block_until_done(self) -> io::Result<()> {
method cancel (line 60) | pub fn cancel(&self) {
method cancel_handle (line 64) | pub fn cancel_handle(&self) -> ShutdownHandle {
type ShutdownHandle (line 70) | pub struct ShutdownHandle {
method shutdown (line 75) | pub fn shutdown(&self) {
function run_login_server (line 80) | pub fn run_login_server(opts: ServerOptions) -> io::Result<LoginServer> {
type HandledRequest (line 170) | enum HandledRequest {
function process_request (line 176) | async fn process_request(
function build_authorize_url (line 274) | fn build_authorize_url(
function generate_state (line 300) | fn generate_state() -> String {
type ExchangedTokens (line 306) | struct ExchangedTokens {
function exchange_code_for_tokens (line 312) | async fn exchange_code_for_tokens(
function persist_tokens_async (line 356) | async fn persist_tokens_async(
function read_or_default (line 401) | fn read_or_default(path: &Path) -> AuthDotJson {
function compose_success_url (line 412) | fn compose_success_url(port: u16, issuer: &str, id_token: &str, access_t...
function jwt_auth_claims (line 460) | fn jwt_auth_claims(jwt: &str) -> serde_json::Map<String, serde_json::Val...
function obtain_api_key (line 491) | async fn obtain_api_key(issuer: &str, client_id: &str, id_token: &str) -...
FILE: codex-rs/login/src/token_data.rs
type TokenData (line 9) | pub struct TokenData {
method should_use_api_key (line 28) | pub(crate) fn should_use_api_key(
method is_openai_email (line 47) | pub fn is_openai_email(&self) -> bool {
type IdTokenInfo (line 57) | pub struct IdTokenInfo {
method get_chatgpt_plan_type (line 67) | pub fn get_chatgpt_plan_type(&self) -> Option<String> {
type PlanType (line 77) | pub(crate) enum PlanType {
method is_plan_that_should_use_api_key (line 83) | fn is_plan_that_should_use_api_key(&self) -> bool {
method as_string (line 96) | pub fn as_string(&self) -> String {
type KnownPlan (line 106) | pub(crate) enum KnownPlan {
type IdClaims (line 117) | struct IdClaims {
type AuthClaims (line 125) | struct AuthClaims {
type IdTokenInfoError (line 131) | pub enum IdTokenInfoError {
function parse_id_token (line 140) | pub(crate) fn parse_id_token(id_token: &str) -> Result<IdTokenInfo, IdTo...
function deserialize_id_token (line 158) | fn deserialize_id_token<'de, D>(deserializer: D) -> Result<IdTokenInfo, ...
function serialize_id_token (line 166) | fn serialize_id_token<S>(id_token: &IdTokenInfo, serializer: S) -> Resul...
function id_token_info_parses_email_and_plan (line 179) | fn id_token_info_parses_email_and_plan() {
FILE: codex-rs/login/tests/suite/login_server_e2e.rs
constant CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR (line 12) | pub const CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR: &str = "CODEX_SANDBOX_...
function start_mock_issuer (line 14) | fn start_mock_issuer() -> (SocketAddr, thread::JoinHandle<()>) {
function end_to_end_login_flow_persists_auth_json (line 77) | async fn end_to_end_login_flow_persists_auth_json() {
function creates_missing_codex_home_dir (line 136) | async fn creates_missing_codex_home_dir() {
FILE: codex-rs/mcp-client/src/main.rs
function main (line 27) | async fn main() -> Result<()> {
FILE: codex-rs/mcp-client/src/mcp_client.rs
constant CHANNEL_CAPACITY (line 57) | const CHANNEL_CAPACITY: usize = 128;
type PendingSender (line 60) | type PendingSender = oneshot::Sender<JSONRPCMessage>;
type McpClient (line 63) | pub struct McpClient {
method new_stdio_client (line 85) | pub async fn new_stdio_client(
method send_request (line 195) | pub async fn send_request<R>(
method send_notification (line 286) | pub async fn send_notification<N>(&self, params: N::Params) -> Result<()>
method initialize (line 318) | pub async fn initialize(
method list_tools (line 333) | pub async fn list_tools(
method call_tool (line 342) | pub async fn call_tool(
method dispatch_response (line 354) | async fn dispatch_response(
method dispatch_error (line 377) | async fn dispatch_error(
method drop (line 393) | fn drop(&mut self) {
constant DEFAULT_ENV_VARS (line 406) | const DEFAULT_ENV_VARS: &[&str] = &[
constant DEFAULT_ENV_VARS (line 432) | const DEFAULT_ENV_VARS: &[&str] = &[
function create_env_for_mcp_server (line 445) | fn create_env_for_mcp_server(
function test_create_env_for_mcp_server (line 463) | fn test_create_env_for_mcp_server() {
FILE: codex-rs/mcp-server/src/codex_message_processor.rs
constant LOGIN_CHATGPT_TIMEOUT (line 68) | const LOGIN_CHATGPT_TIMEOUT: Duration = Duration::from_secs(10 * 60);
type ActiveLogin (line 70) | struct ActiveLogin {
method drop (line 76) | fn drop(&self) {
type CodexMessageProcessor (line 82) | pub(crate) struct CodexMessageProcessor {
method new (line 95) | pub fn new(
method process_request (line 114) | pub async fn process_request(&mut self, request: ClientRequest) {
method login_chatgpt (line 158) | async fn login_chatgpt(&mut self, request_id: RequestId) {
method cancel_login_chatgpt (line 260) | async fn cancel_login_chatgpt(&mut self, request_id: RequestId, login_...
method logout_chatgpt (line 284) | async fn logout_chatgpt(&mut self, request_id: RequestId) {
method get_auth_status (line 321) | async fn get_auth_status(
method get_config_toml (line 363) | async fn get_config_toml(&self, request_id: RequestId) {
method process_new_conversation (line 419) | async fn process_new_conversation(&self, request_id: RequestId, params...
method send_user_message (line 457) | async fn send_user_message(&self, request_id: RequestId, params: SendU...
method send_user_turn (line 498) | async fn send_user_turn(&self, request_id: RequestId, params: SendUser...
method interrupt_conversation (line 550) | async fn interrupt_conversation(
method add_conversation_listener (line 580) | async fn add_conversation_listener(
method remove_conversation_listener (line 655) | async fn remove_conversation_listener(
method git_diff_to_origin (line 679) | async fn git_diff_to_origin(&self, request_id: RequestId, cwd: PathBuf) {
function apply_bespoke_event_handling (line 701) | async fn apply_bespoke_event_handling(
function derive_config_from_params (line 775) | fn derive_config_from_params(
function on_patch_approval_response (line 816) | async fn on_patch_approval_response(
function on_exec_approval_response (line 858) | async fn on_exec_approval_response(
FILE: codex-rs/mcp-server/src/codex_tool_config.rs
type CodexToolCallParam (line 19) | pub struct CodexToolCallParam {
method into_config (line 136) | pub fn into_config(
type CodexToolCallApprovalPolicy (line 63) | pub enum CodexToolCallApprovalPolicy {
method from (line 71) | fn from(value: CodexToolCallApprovalPolicy) -> Self {
type CodexToolCallSandboxMode (line 85) | pub enum CodexToolCallSandboxMode {
method from (line 92) | fn from(value: CodexToolCallSandboxMode) -> Self {
function create_tool_for_codex_tool_call_param (line 102) | pub(crate) fn create_tool_for_codex_tool_call_param() -> Tool {
type CodexToolCallReplyParam (line 184) | pub struct CodexToolCallReplyParam {
function create_tool_for_codex_tool_call_reply_param (line 193) | pub(crate) fn create_tool_for_codex_tool_call_reply_param() -> Tool {
function verify_codex_tool_json_schema (line 240) | fn verify_codex_tool_json_schema() {
function verify_codex_tool_reply_json_schema (line 308) | fn verify_codex_tool_reply_json_schema() {
FILE: codex-rs/mcp-server/src/codex_tool_runner.rs
constant INVALID_PARAMS_ERROR_CODE (line 34) | pub(crate) const INVALID_PARAMS_ERROR_CODE: i64 = -32602;
function run_codex_tool_session (line 40) | pub async fn run_codex_tool_session(
function run_codex_tool_session_reply (line 117) | pub async fn run_codex_tool_session_reply(
function run_codex_tool_session_inner (line 153) | async fn run_codex_tool_session_inner(
FILE: codex-rs/mcp-server/src/error_code.rs
constant INVALID_REQUEST_ERROR_CODE (line 1) | pub(crate) const INVALID_REQUEST_ERROR_CODE: i64 = -32600;
constant INTERNAL_ERROR_CODE (line 2) | pub(crate) const INTERNAL_ERROR_CODE: i64 = -32603;
FILE: codex-rs/mcp-server/src/exec_approval.rs
type ExecApprovalElicitRequestParams (line 22) | pub struct ExecApprovalElicitRequestParams {
type ExecApprovalResponse (line 45) | pub struct ExecApprovalResponse {
function handle_exec_approval_request (line 50) | pub(crate) async fn handle_exec_approval_request(
function on_exec_approval_response (line 116) | async fn on_exec_approval_response(
FILE: codex-rs/mcp-server/src/json_to_toml.rs
function json_to_toml (line 5) | pub(crate) fn json_to_toml(v: JsonValue) -> TomlValue {
function json_number_to_toml (line 37) | fn json_number_to_toml() {
function json_array_to_toml (line 43) | fn json_array_to_toml() {
function json_bool_to_toml (line 52) | fn json_bool_to_toml() {
function json_float_to_toml (line 58) | fn json_float_to_toml() {
function json_null_to_toml (line 64) | fn json_null_to_toml() {
function json_object_nested (line 70) | fn json_object_nested() {
FILE: codex-rs/mcp-server/src/lib.rs
constant CHANNEL_CAPACITY (line 47) | const CHANNEL_CAPACITY: usize = 128;
function run_main (line 49) | pub async fn run_main(
FILE: codex-rs/mcp-server/src/main.rs
function main (line 5) | fn main() -> anyhow::Result<()> {
FILE: codex-rs/mcp-server/src/message_processor.rs
type MessageProcessor (line 38) | pub(crate) struct MessageProcessor {
method new (line 50) | pub(crate) fn new(
method process_request (line 76) | pub(crate) async fn process_request(&mut self, request: JSONRPCRequest) {
method process_response (line 144) | pub(crate) async fn process_response(&mut self, response: JSONRPCRespo...
method process_notification (line 151) | pub(crate) async fn process_notification(&mut self, notification: JSON...
method process_error (line 188) | pub(crate) fn process_error(&mut self, err: JSONRPCError) {
method handle_initialize (line 192) | async fn handle_initialize(
method send_response (line 237) | async fn send_response<T>(&self, id: RequestId, result: T::Result)
method handle_ping (line 244) | async fn handle_ping(
method handle_list_resources (line 255) | fn handle_list_resources(
method handle_list_resource_templates (line 262) | fn handle_list_resource_templates(
method handle_read_resource (line 270) | fn handle_read_resource(
method handle_subscribe (line 277) | fn handle_subscribe(
method handle_unsubscribe (line 284) | fn handle_unsubscribe(
method handle_list_prompts (line 291) | fn handle_list_prompts(
method handle_get_prompt (line 298) | fn handle_get_prompt(
method handle_list_tools (line 305) | async fn handle_list_tools(
method handle_call_tool (line 323) | async fn handle_call_tool(
method handle_tool_call_codex (line 352) | async fn handle_tool_call_codex(&self, id: RequestId, arguments: Optio...
method handle_tool_call_codex_session_reply (line 428) | async fn handle_tool_call_codex_session_reply(
method handle_set_level (line 535) | fn handle_set_level(
method handle_complete (line 542) | fn handle_complete(
method handle_cancelled_notification (line 553) | async fn handle_cancelled_notification(
method handle_progress_notification (line 604) | fn handle_progress_notification(
method handle_resource_list_changed (line 611) | fn handle_resource_list_changed(
method handle_resource_updated (line 621) | fn handle_resource_updated(
method handle_prompt_list_changed (line 628) | fn handle_prompt_list_changed(
method handle_tool_list_changed (line 635) | fn handle_tool_list_changed(
method handle_logging_message (line 642) | fn handle_logging_message(
FILE: codex-rs/mcp-server/src/outgoing_message.rs
type OutgoingMessageSender (line 25) | pub(crate) struct OutgoingMessageSender {
method new (line 32) | pub(crate) fn new(sender: mpsc::Sender<OutgoingMessage>) -> Self {
method send_request (line 40) | pub(crate) async fn send_request(
method notify_client_response (line 62) | pub(crate) async fn notify_client_response(&self, id: RequestId, resul...
method send_response (line 80) | pub(crate) async fn send_response<T: Serialize>(&self, id: RequestId, ...
method send_event_as_notification (line 100) | pub(crate) async fn send_event_as_notification(
method send_server_notification (line 125) | pub(crate) async fn send_server_notification(&self, notification: Serv...
method send_notification (line 136) | pub(crate) async fn send_notification(&self, notification: OutgoingNot...
method send_error (line 141) | pub(crate) async fn send_error(&self, id: RequestId, error: JSONRPCErr...
type OutgoingMessage (line 148) | pub(crate) enum OutgoingMessage {
method from (line 156) | fn from(val: OutgoingMessage) -> Self {
type OutgoingRequest (line 191) | pub(crate) struct OutgoingRequest {
type OutgoingNotification (line 199) | pub(crate) struct OutgoingNotification {
type OutgoingNotificationParams (line 206) | pub(crate) struct OutgoingNotificationParams {
type OutgoingNotificationMeta (line 219) | pub(crate) struct OutgoingNotificationMeta {
method new (line 224) | pub(crate) fn new(request_id: Option<RequestId>) -> Self {
type OutgoingResponse (line 230) | pub(crate) struct OutgoingResponse {
type OutgoingError (line 236) | pub(crate) struct OutgoingError {
function test_send_event_as_notification (line 252) | async fn test_send_event_as_notification() {
function test_send_event_as_notification_with_meta (line 283) | async fn test_send_event_as_notification_with_meta() {
FILE: codex-rs/mcp-server/src/patch_approval.rs
type PatchApprovalElicitRequestParams (line 23) | pub struct PatchApprovalElicitRequestParams {
type PatchApprovalResponse (line 39) | pub struct PatchApprovalResponse {
function handle_patch_approval_request (line 44) | pub(crate) async fn handle_patch_approval_request(
function on_patch_approval_response (line 111) | pub(crate) async fn on_patch_approval_response(
FILE: codex-rs/mcp-server/tests/common/lib.rs
function to_response (line 13) | pub fn to_response<T: DeserializeOwned>(response: JSONRPCResponse) -> an...
FILE: codex-rs/mcp-server/tests/common/mcp_process.rs
type McpProcess (line 41) | pub struct McpProcess {
method new (line 53) | pub async fn new(codex_home: &Path) -> anyhow::Result<Self> {
method initialize (line 89) | pub async fn initialize(&mut self) -> anyhow::Result<()> {
method send_codex_tool_call (line 151) | pub async fn send_codex_tool_call(
method send_new_conversation_request (line 167) | pub async fn send_new_conversation_request(
method send_add_conversation_listener_request (line 176) | pub async fn send_add_conversation_listener_request(
method send_send_user_message_request (line 185) | pub async fn send_send_user_message_request(
method send_remove_conversation_listener_request (line 195) | pub async fn send_remove_conversation_listener_request(
method send_send_user_turn_request (line 205) | pub async fn send_send_user_turn_request(
method send_interrupt_conversation_request (line 214) | pub async fn send_interrupt_conversation_request(
method send_get_auth_status_request (line 223) | pub async fn send_get_auth_status_request(
method send_get_config_toml_request (line 232) | pub async fn send_get_config_toml_request(&mut self) -> anyhow::Result...
method send_login_chat_gpt_request (line 237) | pub async fn send_login_chat_gpt_request(&mut self) -> anyhow::Result<...
method send_cancel_login_chat_gpt_request (line 242) | pub async fn send_cancel_login_chat_gpt_request(
method send_logout_chat_gpt_request (line 251) | pub async fn send_logout_chat_gpt_request(&mut self) -> anyhow::Result...
method send_request (line 255) | async fn send_request(
method send_response (line 272) | pub async fn send_response(
method send_jsonrpc_message (line 285) | async fn send_jsonrpc_message(&mut self, message: JSONRPCMessage) -> a...
method read_jsonrpc_message (line 293) | async fn read_jsonrpc_message(&mut self) -> anyhow::Result<JSONRPCMess...
method read_stream_until_request_message (line 300) | pub async fn read_stream_until_request_message(&mut self) -> anyhow::R...
method read_stream_until_response_message (line 322) | pub async fn read_stream_until_response_message(
method read_stream_until_error_message (line 349) | pub async fn read_stream_until_error_message(
method read_stream_until_notification_message (line 376) | pub async fn read_stream_until_notification_message(
method read_stream_until_legacy_task_complete_notification (line 405) | pub async fn read_stream_until_legacy_task_complete_notification(
FILE: codex-rs/mcp-server/tests/common/mock_model_server.rs
function create_mock_chat_completions_server (line 13) | pub async fn create_mock_chat_completions_server(responses: Vec<String>)...
type SeqResponder (line 32) | struct SeqResponder {
method respond (line 38) | fn respond(&self, _: &wiremock::Request) -> ResponseTemplate {
FILE: codex-rs/mcp-server/tests/common/responses.rs
function create_shell_sse_response (line 4) | pub fn create_shell_sse_response(
function create_final_assistant_message_sse_response (line 42) | pub fn create_final_assistant_message_sse_response(message: &str) -> any...
function create_apply_patch_sse_response (line 61) | pub fn create_apply_patch_sse_response(
FILE: codex-rs/mcp-server/tests/suite/auth.rs
constant DEFAULT_READ_TIMEOUT (line 15) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function create_config_toml (line 18) | fn create_config_toml(codex_home: &Path) -> std::io::Result<()> {
function get_auth_status_no_auth (line 40) | async fn get_auth_status_no_auth() {
function get_auth_status_with_api_key (line 73) | async fn get_auth_status_with_api_key() {
function get_auth_status_with_api_key_no_include_token (line 108) | async fn get_auth_status_with_api_key_no_include_token() {
FILE: codex-rs/mcp-server/tests/suite/codex_message_processor_flow.rs
constant DEFAULT_READ_TIMEOUT (line 32) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function test_codex_jsonrpc_conversation_flow (line 35) | async fn test_codex_jsonrpc_conversation_flow() {
function test_send_user_turn_changes_approval_policy_behavior (line 172) | async fn test_send_user_turn_changes_approval_policy_behavior() {
function create_config_toml (line 350) | fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::R...
FILE: codex-rs/mcp-server/tests/suite/codex_tool.rs
constant DEFAULT_READ_TIMEOUT (line 33) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function test_shell_command_approval_triggers_elicitation (line 39) | async fn test_shell_command_approval_triggers_elicitation() {
function shell_command_approval_triggers_elicitation (line 54) | async fn shell_command_approval_triggers_elicitation() -> anyhow::Result...
function create_expected_elicitation_request (line 156) | fn create_expected_elicitation_request(
function test_patch_approval_triggers_elicitation (line 192) | async fn test_patch_approval_triggers_elicitation() {
function patch_approval_triggers_elicitation (line 205) | async fn patch_approval_triggers_elicitation() -> anyhow::Result<()> {
function test_codex_tool_passes_base_instructions (line 299) | async fn test_codex_tool_passes_base_instructions() {
function codex_tool_passes_base_instructions (line 314) | async fn codex_tool_passes_base_instructions() -> anyhow::Result<()> {
function create_expected_patch_approval_elicitation_request (line 367) | fn create_expected_patch_approval_elicitation_request(
type McpHandle (line 405) | pub struct McpHandle {
function create_mcp_process (line 415) | async fn create_mcp_process(responses: Vec<String>) -> anyhow::Result<Mc...
function create_config_toml (line 431) | fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::R...
FILE: codex-rs/mcp-server/tests/suite/config.rs
constant DEFAULT_READ_TIMEOUT (line 17) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function create_config_toml (line 19) | fn create_config_toml(codex_home: &Path) -> std::io::Result<()> {
function get_config_toml_returns_subset (line 39) | async fn get_config_toml_returns_subset() {
FILE: codex-rs/mcp-server/tests/suite/create_conversation.rs
constant DEFAULT_READ_TIMEOUT (line 21) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function test_conversation_create_and_send_message_ok (line 24) | async fn test_conversation_create_and_send_message_ok() {
function create_config_toml (line 134) | fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::R...
FILE: codex-rs/mcp-server/tests/suite/interrupt.rs
constant DEFAULT_READ_TIMEOUT (line 25) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function test_shell_command_interruption (line 28) | async fn test_shell_command_interruption() {
function shell_command_interruption (line 41) | async fn shell_command_interruption() -> anyhow::Result<()> {
function create_config_toml (line 143) | fn create_config_toml(codex_home: &Path, server_uri: String) -> std::io:...
FILE: codex-rs/mcp-server/tests/suite/login.rs
constant DEFAULT_READ_TIMEOUT (line 18) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function create_config_toml (line 21) | fn create_config_toml(codex_home: &Path) -> std::io::Result<()> {
function logout_chatgpt_removes_auth (line 43) | async fn logout_chatgpt_removes_auth() {
function login_and_cancel_chatgpt (line 96) | async fn login_and_cancel_chatgpt() {
FILE: codex-rs/mcp-server/tests/suite/send_message.rs
constant DEFAULT_READ_TIMEOUT (line 22) | const DEFAULT_READ_TIMEOUT: std::time::Duration = std::time::Duration::f...
function test_send_message_success (line 25) | async fn test_send_message_success() {
function send_message (line 84) | async fn send_message(message: &str, conversation_id: ConversationId, mc...
function test_send_message_session_not_found (line 130) | async fn test_send_message_session_not_found() {
function create_config_toml (line 165) | fn create_config_toml(codex_home: &Path, server_uri: &str) -> std::io::R...
FILE: codex-rs/mcp-types/generate_mcp_types.py
function main (line 38) | def main() -> int:
function add_definition (line 197) | def add_definition(name: str, definition: dict[str, Any], out: list[str]...
class StructField (line 263) | class StructField:
method append (line 269) | def append(self, out: list[str], supports_const: bool) -> None:
function define_struct (line 281) | def define_struct(
function infer_result_type (line 336) | def infer_result_type(request_type_name: str) -> str:
function implements_request_trait (line 347) | def implements_request_trait(name: str) -> bool:
function implements_notification_trait (line 355) | def implements_notification_trait(name: str) -> bool:
function add_trait_impl (line 362) | def add_trait_impl(
function define_string_enum (line 383) | def define_string_enum(
function define_untagged_enum (line 398) | def define_untagged_enum(name: str, type_list: list[str], out: list[str]...
function define_any_of (line 415) | def define_any_of(
function get_serde_annotation_for_anyof_type (line 507) | def get_serde_annotation_for_anyof_type(type_name: str) -> str | None:
function map_type (line 518) | def map_type(
class RustProp (line 590) | class RustProp:
function rust_prop_name (line 596) | def rust_prop_name(name: str, is_optional: bool) -> RustProp:
function to_snake_case (line 626) | def to_snake_case(name: str) -> str:
function capitalize (line 637) | def capitalize(name: str) -> str:
function check_string_list (line 642) | def check_string_list(value: Any) -> list[str] | None:
function type_from_ref (line 652) | def type_from_ref(ref: str) -> str:
function emit_doc_comment (line 658) | def emit_doc_comment(text: str | None, out: list[str]) -> None:
FILE: codex-rs/mcp-types/src/lib.rs
constant MCP_SCHEMA_VERSION (line 15) | pub const MCP_SCHEMA_VERSION: &str = "2025-06-18";
constant JSONRPC_VERSION (line 16) | pub const JSONRPC_VERSION: &str = "2.0";
type ModelContextProtocolRequest (line 19) | pub trait ModelContextProtocolRequest {
constant METHOD (line 20) | const METHOD: &'static str;
constant METHOD (line 92) | const METHOD: &'static str = "tools/call";
type Params (line 93) | type Params = CallToolRequestParams;
type Result (line 94) | type Result = CallToolResult;
constant METHOD (line 221) | const METHOD: &'static str = "completion/complete";
type Params (line 222) | type Params = CompleteRequestParams;
type Result (line 223) | type Result = CompleteResult;
constant METHOD (line 292) | const METHOD: &'static str = "sampling/createMessage";
type Params (line 293) | type Params = CreateMessageRequestParams;
type Result (line 294) | type Result = CreateMessageResult;
constant METHOD (line 369) | const METHOD: &'static str = "elicitation/create";
type Params (line 370) | type Params = ElicitRequestParams;
type Result (line 371) | type Result = ElicitResult;
constant METHOD (line 444) | const METHOD: &'static str = "prompts/get";
type Params (line 445) | type Params = GetPromptRequestParams;
type Result (line 446) | type Result = GetPromptResult;
constant METHOD (line 496) | const METHOD: &'static str = "initialize";
type Params (line 497) | type Params = InitializeRequestParams;
type Result (line 498) | type Result = InitializeResult;
constant METHOD (line 599) | const METHOD: &'static str = "prompts/list";
type Params (line 600) | type Params = Option<ListPromptsRequestParams>;
type Result (line 601) | type Result = ListPromptsResult;
constant METHOD (line 634) | const METHOD: &'static str = "resources/templates/list";
type Params (line 635) | type Params = Option<ListResourceTemplatesRequestParams>;
type Result (line 636) | type Result = ListResourceTemplatesResult;
constant METHOD (line 670) | const METHOD: &'static str = "resources/list";
type Params (line 671) | type Params = Option<ListResourcesRequestParams>;
type Result (line 672) | type Result = ListResourcesResult;
constant METHOD (line 705) | const METHOD: &'static str = "roots/list";
type Params (line 706) | type Params = Option<serde_json::Value>;
type Result (line 707) | type Result = ListRootsResult;
constant METHOD (line 730) | const METHOD: &'static str = "tools/list";
type Params (line 731) | type Params = Option<ListToolsRequestParams>;
type Result (line 732) | type Result = ListToolsResult;
constant METHOD (line 901) | const METHOD: &'static str = "ping";
type Params (line 902) | type Params = Option<serde_json::Value>;
type Result (line 903) | type Result = Result;
constant METHOD (line 998) | const METHOD: &'static str = "resources/read";
type Params (line 999) | type Params = ReadResourceRequestParams;
type Result (line 1000) | type Result = ReadResourceResult;
constant METHOD (line 1284) | const METHOD: &'static str = "logging/setLevel";
type Params (line 1285) | type Params = SetLevelRequestParams;
type Result (line 1286) | type Result = Result;
constant METHOD (line 1313) | const METHOD: &'static str = "resources/subscribe";
type Params (line 1314) | type Params = SubscribeRequestParams;
type Result (line 1315) | type Result = Result;
constant METHOD (line 1431) | const METHOD: &'static str = "resources/unsubscribe";
type Params (line 1432) | type Params = UnsubscribeRequestParams;
type Result (line 1433) | type Result = Result;
type ModelContextProtocolNotification (line 26) | pub trait ModelContextProtocolNotification {
constant METHOD (line 27) | const METHOD: &'static str;
constant METHOD (line 130) | const METHOD: &'static str = "notifications/cancelled";
type Params (line 131) | type Params = CancelledNotificationParams;
constant METHOD (line 534) | const METHOD: &'static str = "notifications/initialized";
type Params (line 535) | type Params = Option<serde_json::Value>;
constant METHOD (line 789) | const METHOD: &'static str = "notifications/message";
type Params (line 790) | type Params = LoggingMessageNotificationParams;
constant METHOD (line 921) | const METHOD: &'static str = "notifications/progress";
type Params (line 922) | type Params = ProgressNotificationParams;
constant METHOD (line 971) | const METHOD: &'static str = "notifications/prompts/list_changed";
type Params (line 972) | type Params = Option<serde_json::Value>;
constant METHOD (line 1092) | const METHOD: &'static str = "notifications/resources/list_changed";
type Params (line 1093) | type Params = Option<serde_json::Value>;
constant METHOD (line 1123) | const METHOD: &'static str = "notifications/resources/updated";
type Params (line 1124) | type Params = ResourceUpdatedNotificationParams;
constant METHOD (line 1155) | const METHOD: &'static str = "notifications/roots/list_changed";
type Params (line 1156) | type Params = Option<serde_json::Value>;
constant METHOD (line 1423) | const METHOD: &'static str = "notifications/tools/list_changed";
type Params (line 1424) | type Params = Option<serde_json::Value>;
function default_jsonrpc (line 31) | fn default_jsonrpc() -> String {
type Annotations (line 37) | pub struct Annotations {
type AudioContent (line 52) | pub struct AudioContent {
type BaseMetadata (line 63) | pub struct BaseMetadata {
type BlobResourceContents (line 70) | pub struct BlobResourceContents {
type BooleanSchema (line 78) | pub struct BooleanSchema {
type CallToolRequest (line 89) | pub enum CallToolRequest {}
type CallToolRequestParams (line 98) | pub struct CallToolRequestParams {
type CallToolResult (line 106) | pub struct CallToolResult {
function from (line 119) | fn from(value: CallToolResult) -> Self {
type CancelledNotification (line 127) | pub enum CancelledNotification {}
type CancelledNotificationParams (line 135) | pub struct CancelledNotificationParams {
type ClientCapabilities (line 144) | pub struct ClientCapabilities {
type ClientCapabilitiesRoots (line 157) | pub struct ClientCapabilitiesRoots {
type ClientNotification (line 168) | pub enum ClientNotification {
type ClientRequest (line 177) | pub enum ClientRequest {
type Error (line 1442) | type Error = serde_json::Error;
method try_from (line 1443) | fn try_from(req: JSONRPCRequest) -> std::result::Result<Self, Self::Er...
type ClientResult (line 210) | pub enum ClientResult {
type CompleteRequest (line 218) | pub enum CompleteRequest {}
type CompleteRequestParams (line 227) | pub struct CompleteRequestParams {
type CompleteRequestParamsContext (line 236) | pub struct CompleteRequestParamsContext {
type CompleteRequestParamsArgument (line 243) | pub struct CompleteRequestParamsArgument {
type CompleteRequestParamsRef (line 250) | pub enum CompleteRequestParamsRef {
type CompleteResult (line 257) | pub struct CompleteResult {
type CompleteResultCompletion (line 262) | pub struct CompleteResultCompletion {
function from (line 271) | fn from(value: CompleteResult) -> Self {
type ContentBlock (line 280) | pub enum ContentBlock {
type CreateMessageRequest (line 289) | pub enum CreateMessageRequest {}
type CreateMessageRequestParams (line 298) | pub struct CreateMessageRequestParams {
type CreateMessageResult (line 334) | pub struct CreateMessageResult {
type CreateMessageResultContent (line 348) | pub enum CreateMessageResultContent {
function from (line 355) | fn from(value: CreateMessageResult) -> Self {
type Cursor (line 363) | pub struct Cursor(String);
type ElicitRequest (line 366) | pub enum ElicitRequest {}
type ElicitRequestParams (line 375) | pub struct ElicitRequestParams {
type ElicitRequestParamsRequestedSchema (line 384) | pub struct ElicitRequestParamsRequestedSchema {
type ElicitResult (line 393) | pub struct ElicitResult {
function from (line 400) | fn from(value: ElicitResult) -> Self {
type EmbeddedResource (line 412) | pub struct EmbeddedResource {
type EmbeddedResourceResource (line 421) | pub enum EmbeddedResourceResource {
type EmptyResult (line 426) | pub type EmptyResult = Result;
type EnumSchema (line 429) | pub struct EnumSchema {
type GetPromptRequest (line 441) | pub enum GetPromptRequest {}
type GetPromptRequestParams (line 450) | pub struct GetPromptRequestParams {
type GetPromptResult (line 458) | pub struct GetPromptResult {
function from (line 465) | fn from(value: GetPromptResult) -> Self {
type ImageContent (line 474) | pub struct ImageContent {
type Implementation (line 485) | pub struct Implementation {
type InitializeRequest (line 493) | pub enum InitializeRequest {}
type InitializeRequestParams (line 502) | pub struct InitializeRequestParams {
type InitializeResult (line 512) | pub struct InitializeResult {
function from (line 523) | fn from(value: InitializeResult) -> Self {
type InitializedNotification (line 531) | pub enum InitializedNotification {}
type JSONRPCError (line 540) | pub struct JSONRPCError {
type JSONRPCErrorError (line 548) | pub struct JSONRPCErrorError {
type JSONRPCMessage (line 558) | pub enum JSONRPCMessage {
type JSONRPCNotification (line 567) | pub struct JSONRPCNotification {
type JSONRPCRequest (line 577) | pub struct JSONRPCRequest {
type JSONRPCResponse (line 588) | pub struct JSONRPCResponse {
type ListPromptsRequest (line 596) | pub enum ListPromptsRequest {}
type ListPromptsRequestParams (line 605) | pub struct ListPromptsRequestParams {
type ListPromptsResult (line 612) | pub struct ListPromptsResult {
function from (line 623) | fn from(value: ListPromptsResult) -> Self {
type ListResourceTemplatesRequest (line 631) | pub enum ListResourceTemplatesRequest {}
type ListResourceTemplatesRequestParams (line 640) | pub struct ListResourceTemplatesRequestParams {
type ListResourceTemplatesResult (line 647) | pub struct ListResourceTemplatesResult {
function from (line 659) | fn from(value: ListResourceTemplatesResult) -> Self {
type ListResourcesRequest (line 667) | pub enum ListResourcesRequest {}
type ListResourcesRequestParams (line 676) | pub struct ListResourcesRequestParams {
type ListResourcesResult (line 683) | pub struct ListResourcesResult {
function from (line 694) | fn from(value: ListResourcesResult) -> Self {
type ListRootsRequest (line 702) | pub enum ListRootsRequest {}
type ListRootsResult (line 714) | pub struct ListRootsResult {
function from (line 719) | fn from(value: ListRootsResult) -> Self {
type ListToolsRequest (line 727) | pub enum ListToolsRequest {}
type ListToolsRequestParams (line 736) | pub struct ListToolsRequestParams {
type ListToolsResult (line 743) | pub struct ListToolsResult {
function from (line 754) | fn from(value: ListToolsResult) -> Self {
type LoggingLevel (line 766) | pub enum LoggingLevel {
type LoggingMessageNotification (line 786) | pub enum LoggingMessageNotification {}
type LoggingMessageNotificationParams (line 794) | pub struct LoggingMessageNotificationParams {
type ModelHint (line 806) | pub struct ModelHint {
type ModelPreferences (line 823) | pub struct ModelPreferences {
type Notification (line 847) | pub struct Notification {
type NumberSchema (line 854) | pub struct NumberSchema {
type PaginatedRequest (line 867) | pub struct PaginatedRequest {
type PaginatedRequestParams (line 874) | pub struct PaginatedRequestParams {
type PaginatedResult (line 880) | pub struct PaginatedResult {
function from (line 890) | fn from(value: PaginatedResult) -> Self {
type PingRequest (line 898) | pub enum PingRequest {}
type PrimitiveSchemaDefinition (line 910) | pub enum PrimitiveSchemaDefinition {
type ProgressNotification (line 918) | pub enum ProgressNotification {}
type ProgressNotificationParams (line 926) | pub struct ProgressNotificationParams {
type ProgressToken (line 938) | pub enum ProgressToken {
type Prompt (line 945) | pub struct Prompt {
type PromptArgument (line 957) | pub struct PromptArgument {
type PromptListChangedNotification (line 968) | pub enum PromptListChangedNotification {}
type PromptMessage (line 980) | pub struct PromptMessage {
type PromptReference (line 987) | pub struct PromptReference {
type ReadResourceRequest (line 995) | pub enum ReadResourceRequest {}
type ReadResourceRequestParams (line 1004) | pub struct ReadResourceRequestParams {
type ReadResourceResult (line 1010) | pub struct ReadResourceResult {
type ReadResourceResultContents (line 1016) | pub enum ReadResourceResultContents {
function from (line 1022) | fn from(value: ReadResourceResult) -> Self {
type Request (line 1030) | pub struct Request {
type RequestId (line 1038) | pub enum RequestId {
type Resource (line 1045) | pub struct Resource {
type ResourceContents (line 1062) | pub struct ResourceContents {
type ResourceLink (line 1072) | pub struct ResourceLink {
type ResourceListChangedNotification (line 1089) | pub enum ResourceListChangedNotification {}
type ResourceTemplate (line 1098) | pub struct ResourceTemplate {
type ResourceTemplateReference (line 1114) | pub struct ResourceTemplateReference {
type ResourceUpdatedNotification (line 1120) | pub enum ResourceUpdatedNotification {}
type ResourceUpdatedNotificationParams (line 1128) | pub struct ResourceUpdatedNotificationParams {
type Result (line 1132) | pub type Result = serde_json::Value;
type Role (line 1136) | pub enum Role {
type Root (line 1145) | pub struct Root {
type RootsListChangedNotification (line 1152) | pub enum RootsListChangedNotification {}
type SamplingMessage (line 1161) | pub struct SamplingMessage {
type SamplingMessageContent (line 1168) | pub enum SamplingMessageContent {
type ServerCapabilities (line 1176) | pub struct ServerCapabilities {
type ServerCapabilitiesTools (line 1193) | pub struct ServerCapabilitiesTools {
type ServerCapabilitiesResources (line 1204) | pub struct ServerCapabilitiesResources {
type ServerCapabilitiesPrompts (line 1217) | pub struct ServerCapabilitiesPrompts {
type ServerNotification (line 1228) | pub enum ServerNotification {
type Error (line 1532) | type Error = serde_json::Error;
method try_from (line 1533) | fn try_from(n: JSONRPCNotification) -> std::result::Result<Self, Self:...
type ServerRequest (line 1257) | pub enum ServerRequest {
type ServerResult (line 1267) | pub enum ServerResult {
type SetLevelRequest (line 1281) | pub enum SetLevelRequest {}
type SetLevelRequestParams (line 1290) | pub struct SetLevelRequestParams {
type StringSchema (line 1295) | pub struct StringSchema {
type SubscribeRequest (line 1310) | pub enum SubscribeRequest {}
type SubscribeRequestParams (line 1319) | pub struct SubscribeRequestParams {
type TextContent (line 1325) | pub struct TextContent {
type TextResourceContents (line 1333) | pub struct TextResourceContents {
type Tool (line 1342) | pub struct Tool {
type ToolOutputSchema (line 1363) | pub struct ToolOutputSchema {
type ToolInputSchema (line 1373) | pub struct ToolInputSchema {
type ToolAnnotations (line 1390) | pub struct ToolAnnotations {
type ToolListChangedNotification (line 1420) | pub enum ToolListChangedNotification {}
type UnsubscribeRequest (line 1428) | pub enum UnsubscribeRequest {}
type UnsubscribeRequestParams (line 1437) | pub struct UnsubscribeRequestParams {
FILE: codex-rs/mcp-types/tests/suite/initialize.rs
function deserialize_initialize_request (line 12) | fn deserialize_initialize_request() {
FILE: codex-rs/mcp-types/tests/suite/progress_notification.rs
function deserialize_progress_notification (line 7) | fn deserialize_progress_notification() {
FILE: codex-rs/ollama/src/client.rs
constant OLLAMA_CONNECTION_ERROR (line 18) | const OLLAMA_CONNECTION_ERROR: &str = "No running Ollama server detected...
type OllamaClient (line 21) | pub struct OllamaClient {
method try_from_oss_provider (line 31) | pub async fn try_from_oss_provider(config: &Config) -> io::Result<Self> {
method try_from_provider_with_base_url (line 49) | async fn try_from_provider_with_base_url(base_url: &str) -> io::Result...
method try_from_provider (line 55) | async fn try_from_provider(provider: &ModelProviderInfo) -> io::Result...
method probe_server (line 79) | async fn probe_server(&self) -> io::Result<()> {
method fetch_models (line 102) | pub async fn fetch_models(&self) -> io::Result<Vec<String>> {
method pull_model_stream (line 129) | pub async fn pull_model_stream(
method pull_with_reporter (line 187) | pub async fn pull_with_reporter(
method from_host_root (line 222) | fn from_host_root(host_root: impl Into<String>) -> Self {
function test_fetch_models_happy_path (line 241) | async fn test_fetch_models_happy_path() {
function test_probe_server_happy_path_openai_compat_and_native (line 272) | async fn test_probe_server_happy_path_openai_compat_and_native() {
function test_try_from_oss_provider_ok_when_server_running (line 309) | async fn test_try_from_oss_provider_ok_when_server_running() {
function test_try_from_oss_provider_err_when_server_missing (line 333) | async fn test_try_from_oss_provider_err_when_server_missing() {
FILE: codex-rs/ollama/src/lib.rs
constant DEFAULT_OSS_MODEL (line 14) | pub const DEFAULT_OSS_MODEL: &str = "gpt-oss:20b";
function ensure_oss_ready (line 20) | pub async fn ensure_oss_ready(config: &Config) -> std::io::Result<()> {
FILE: codex-rs/ollama/src/parser.rs
function pull_events_from_value (line 6) | pub(crate) fn pull_events_from_value(value: &JsonValue) -> Vec<PullEvent> {
function test_pull_events_decoder_status_and_success (line 36) | fn test_pull_events_decoder_status_and_success() {
function test_pull_events_decoder_progress (line 49) | fn test_pull_events_decoder_progress() {
FILE: codex-rs/ollama/src/pull.rs
type PullEvent (line 7) | pub enum PullEvent {
type PullProgressReporter (line 25) | pub trait PullProgressReporter {
method on_event (line 26) | fn on_event(&mut self, event: &PullEvent) -> io::Result<()>;
method on_event (line 57) | fn on_event(&mut self, event: &PullEvent) -> io::Result<()> {
method on_event (line 144) | fn on_event(&mut self, event: &PullEvent) -> io::Result<()> {
type CliProgressReporter (line 30) | pub struct CliProgressReporter {
method new (line 45) | pub fn new() -> Self {
method default (line 39) | fn default() -> Self {
type TuiProgressReporter (line 141) | pub struct TuiProgressReporter(CliProgressReporter);
FILE: codex-rs/ollama/src/url.rs
function is_openai_compatible_base_url (line 2) | pub(crate) fn is_openai_compatible_base_url(base_url: &str) -> bool {
function base_url_to_host_root (line 8) | pub fn base_url_to_host_root(base_url: &str) -> String {
function test_base_url_to_host_root (line 25) | fn test_base_url_to_host_root() {
FILE: codex-rs/protocol-ts/src/lib.rs
constant HEADER (line 13) | const HEADER: &str = "// GENERATED CODE! DO NOT MODIFY BY HAND!\n\n";
function generate_ts (line 15) | pub fn generate_ts(out_dir: &Path, prettier: Option<&Path>) -> Result<()> {
function ensure_dir (line 76) | fn ensure_dir(dir: &Path) -> Result<()> {
function prepend_header_if_missing (line 81) | fn prepend_header_if_missing(path: &Path) -> Result<()> {
function ts_files_in (line 103) | fn ts_files_in(dir: &Path) -> Result<Vec<PathBuf>> {
function generate_index_ts (line 120) | fn generate_index_ts(out_dir: &Path) -> Result<PathBuf> {
FILE: codex-rs/protocol-ts/src/main.rs
type Args (line 7) | struct Args {
function main (line 17) | fn main() -> Result<()> {
FILE: codex-rs/protocol/src/config_types.rs
type ReasoningEffort (line 15) | pub enum ReasoningEffort {
type ReasoningSummary (line 29) | pub enum ReasoningSummary {
type SandboxMode (line 41) | pub enum SandboxMode {
type ConfigProfile (line 57) | pub struct ConfigProfile {
FILE: codex-rs/protocol/src/mcp_protocol.rs
type ConversationId (line 23) | pub struct ConversationId(pub Uuid);
method fmt (line 26) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type GitSha (line 33) | pub struct GitSha(pub String);
method new (line 36) | pub fn new(sha: &str) -> Self {
type AuthMode (line 43) | pub enum AuthMode {
type ClientRequest (line 51) | pub enum ClientRequest {
type NewConversationParams (line 113) | pub struct NewConversationParams {
type NewConversationResponse (line 156) | pub struct NewConversationResponse {
type AddConversationSubscriptionResponse (line 163) | pub struct AddConversationSubscriptionResponse {
type RemoveConversationSubscriptionResponse (line 169) | pub struct RemoveConversationSubscriptionResponse {}
type LoginChatGptResponse (line 173) | pub struct LoginChatGptResponse {
type GitDiffToRemoteResponse (line 181) | pub struct GitDiffToRemoteResponse {
type CancelLoginChatGptParams (line 188) | pub struct CancelLoginChatGptParams {
type GitDiffToRemoteParams (line 194) | pub struct GitDiffToRemoteParams {
type CancelLoginChatGptResponse (line 200) | pub struct CancelLoginChatGptResponse {}
type LogoutChatGptParams (line 204) | pub struct LogoutChatGptParams {}
type LogoutChatGptResponse (line 208) | pub struct LogoutChatGptResponse {}
type GetAuthStatusParams (line 212) | pub struct GetAuthStatusParams {
type GetAuthStatusResponse (line 223) | pub struct GetAuthStatusResponse {
type GetConfigTomlResponse (line 233) | pub struct GetConfigTomlResponse {
type SendUserMessageParams (line 253) | pub struct SendUserMessageParams {
type SendUserTurnParams (line 260) | pub struct SendUserTurnParams {
type SendUserTurnResponse (line 273) | pub struct SendUserTurnResponse {}
type InterruptConversationParams (line 277) | pub struct InterruptConversationParams {
type InterruptConversationResponse (line 283) | pub struct InterruptConversationResponse {
type SendUserMessageResponse (line 289) | pub struct SendUserMessageResponse {}
type AddConversationListenerParams (line 293) | pub struct AddConversationListenerParams {
type RemoveConversationListenerParams (line 299) | pub struct RemoveConversationListenerParams {
type InputItem (line 306) | pub enum InputItem {
constant APPLY_PATCH_APPROVAL_METHOD (line 324) | pub const APPLY_PATCH_APPROVAL_METHOD: &str = "applyPatchApproval";
constant EXEC_COMMAND_APPROVAL_METHOD (line 325) | pub const EXEC_COMMAND_APPROVAL_METHOD: &str = "execCommandApproval";
type ServerRequest (line 330) | pub enum ServerRequest {
type ApplyPatchApprovalParams (line 346) | pub struct ApplyPatchApprovalParams {
type ExecCommandApprovalParams (line 362) | pub struct ExecCommandApprovalParams {
type ExecCommandApprovalResponse (line 374) | pub struct ExecCommandApprovalResponse {
type ApplyPatchApprovalResponse (line 379) | pub struct ApplyPatchApprovalResponse {
type LoginChatGptCompleteNotification (line 385) | pub struct LoginChatGptCompleteNotification {
type AuthStatusChangeNotification (line 394) | pub struct AuthStatusChangeNotification {
type ServerNotification (line 403) | pub enum ServerNotification {
function serialize_new_conversation (line 418) | fn serialize_new_conversation() {
FILE: codex-rs/protocol/src/message_history.rs
type HistoryEntry (line 5) | pub struct HistoryEntry {
FILE: codex-rs/protocol/src/models.rs
type ResponseInputItem (line 14) | pub enum ResponseInputItem {
method from (line 179) | fn from(items: Vec<InputItem>) -> Self {
type ContentItem (line 35) | pub enum ContentItem {
type ResponseItem (line 43) | pub enum ResponseItem {
method from (line 112) | fn from(item: ResponseInputItem) -> Self {
function should_serialize_reasoning_content (line 102) | fn should_serialize_reasoning_content(content: &Option<Vec<ReasoningItem...
type LocalShellStatus (line 144) | pub enum LocalShellStatus {
type LocalShellAction (line 152) | pub enum LocalShellAction {
type LocalShellExecAction (line 157) | pub struct LocalShellExecAction {
type ReasoningItemReasoningSummary (line 167) | pub enum ReasoningItemReasoningSummary {
type ReasoningItemContent (line 173) | pub enum ReasoningItemContent {
type ShellToolCallParams (line 216) | pub struct ShellToolCallParams {
type FunctionCallOutputPayload (line 230) | pub struct FunctionCallOutputPayload {
method deserialize (line 257) | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
method fmt (line 274) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Target (line 280) | type Target = str;
method deref (line 281) | fn deref(&self) -> &Self::Target {
method serialize (line 242) | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
function serializes_success_as_plain_string (line 291) | fn serializes_success_as_plain_string() {
function serializes_failure_as_string (line 308) | fn serializes_failure_as_string() {
function deserialize_shell_tool_call_params (line 324) | fn deserialize_shell_tool_call_params() {
FILE: codex-rs/protocol/src/parse_command.rs
type ParsedCommand (line 6) | pub enum ParsedCommand {
FILE: codex-rs/protocol/src/plan_tool.rs
type StepStatus (line 7) | pub enum StepStatus {
type PlanItemArg (line 15) | pub struct PlanItemArg {
type UpdatePlanArgs (line 22) | pub struct UpdatePlanArgs {
FILE: codex-rs/protocol/src/protocol.rs
type Submission (line 30) | pub struct Submission {
type Op (line 42) | pub enum Op {
type AskForApproval (line 161) | pub enum AskForApproval {
type SandboxPolicy (line 188) | pub enum SandboxPolicy {
method new_read_only_policy (line 265) | pub fn new_read_only_policy() -> Self {
method new_workspace_write_policy (line 271) | pub fn new_workspace_write_policy() -> Self {
method has_full_disk_read_access (line 281) | pub fn has_full_disk_read_access(&self) -> bool {
method has_full_disk_write_access (line 285) | pub fn has_full_disk_write_access(&self) -> bool {
method has_full_network_access (line 293) | pub fn has_full_network_access(&self) -> bool {
method get_writable_roots_with_cwd (line 304) | pub fn get_writable_roots_with_cwd(&self, cwd: &Path) -> Vec<WritableR...
type WritableRoot (line 229) | pub struct WritableRoot {
method is_path_writable (line 238) | pub fn is_path_writable(&self, path: &Path) -> bool {
type Err (line 256) | type Err = serde_json::Error;
method from_str (line 258) | fn from_str(s: &str) -> Result<Self, Self::Err> {
type InputItem (line 368) | pub enum InputItem {
type Event (line 386) | pub struct Event {
type EventMsg (line 397) | pub enum EventMsg {
type ErrorEvent (line 486) | pub struct ErrorEvent {
type TaskCompleteEvent (line 491) | pub struct TaskCompleteEvent {
type TaskStartedEvent (line 496) | pub struct TaskStartedEvent {
type TokenUsage (line 501) | pub struct TokenUsage {
method is_zero (line 510) | pub fn is_zero(&self) -> bool {
method cached_input (line 514) | pub fn cached_input(&self) -> u64 {
method non_cached_input (line 518) | pub fn non_cached_input(&self) -> u64 {
method blended_total (line 523) | pub fn blended_total(&self) -> u64 {
method tokens_in_context_window (line 531) | pub fn tokens_in_context_window(&self) -> u64 {
method percent_of_context_window_remaining (line 546) | pub fn percent_of_context_window_remaining(
type FinalOutput (line 565) | pub struct FinalOutput {
method from (line 570) | fn from(token_usage: TokenUsage) -> Self {
method fmt (line 576) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type AgentMessageEvent (line 598) | pub struct AgentMessageEvent {
type AgentMessageDeltaEvent (line 603) | pub struct AgentMessageDeltaEvent {
type AgentReasoningEvent (line 608) | pub struct AgentReasoningEvent {
type AgentReasoningRawContentEvent (line 613) | pub struct AgentReasoningRawContentEvent {
type AgentReasoningRawContentDeltaEvent (line 618) | pub struct AgentReasoningRawContentDeltaEvent {
type AgentReasoningSectionBreakEvent (line 623) | pub struct AgentReasoningSectionBreakEvent {}
type AgentReasoningDeltaEvent (line 626) | pub struct AgentReasoningDeltaEvent {
type McpInvocation (line 631) | pub struct McpInvocation {
type McpToolCallBeginEvent (line 641) | pub struct McpToolCallBeginEvent {
type McpToolCallEndEvent (line 648) | pub struct McpToolCallEndEvent {
method is_success (line 658) | pub fn is_success(&self) -> bool {
type WebSearchBeginEvent (line 667) | pub struct WebSearchBeginEvent {
type ConversationHistoryResponseEvent (line 675) | pub struct ConversationHistoryResponseEvent {
type ExecCommandBeginEvent (line 681) | pub struct ExecCommandBeginEvent {
type ExecCommandEndEvent (line 692) | pub struct ExecCommandEndEvent {
type ExecOutputStream (line 712) | pub enum ExecOutputStream {
type ExecCommandOutputDeltaEvent (line 718) | pub struct ExecCommandOutputDeltaEvent {
type ExecApprovalRequestEvent (line 729) | pub struct ExecApprovalRequestEvent {
type ApplyPatchApprovalRequestEvent (line 742) | pub struct ApplyPatchApprovalRequestEvent {
type BackgroundEventEvent (line 755) | pub struct BackgroundEventEvent {
type StreamErrorEvent (line 760) | pub struct StreamErrorEvent {
type PatchApplyBeginEvent (line 765) | pub struct PatchApplyBeginEvent {
type PatchApplyEndEvent (line 775) | pub struct PatchApplyEndEvent {
type TurnDiffEvent (line 787) | pub struct TurnDiffEvent {
type GetHistoryEntryResponseEvent (line 792) | pub struct GetHistoryEntryResponseEvent {
type McpListToolsResponseEvent (line 802) | pub struct McpListToolsResponseEvent {
type SessionConfiguredEvent (line 808) | pub struct SessionConfiguredEvent {
type ReviewDecision (line 825) | pub enum ReviewDecision {
type FileChange (line 846) | pub enum FileChange {
type Chunk (line 858) | pub struct Chunk {
type TurnAbortedEvent (line 866) | pub struct TurnAbortedEvent {
type TurnAbortReason (line 872) | pub enum TurnAbortReason {
function serialize_event (line 884) | fn serialize_event() {
FILE: codex-rs/tui/src/app.rs
type App (line 31) | pub(crate) struct App {
method run (line 57) | pub async fn run(
method handle_tui_event (line 115) | pub(crate) async fn handle_tui_event(
method handle_event (line 160) | async fn handle_event(&mut self, tui: &mut tui::Tui, event: AppEvent) ...
method token_usage (line 274) | pub(crate) fn token_usage(&self) -> codex_core::protocol::TokenUsage {
method handle_key_event (line 278) | async fn handle_key_event(&mut self, tui: &mut tui::Tui, key_event: Ke...
FILE: codex-rs/tui/src/app_backtrack.rs
type BacktrackState (line 13) | pub(crate) struct BacktrackState {
method handle_backtrack_overlay_event (line 31) | pub(crate) async fn handle_backtrack_overlay_event(
method handle_backtrack_esc_key (line 77) | pub(crate) fn handle_backtrack_esc_key(&mut self, tui: &mut tui::Tui) {
method request_backtrack (line 91) | pub(crate) fn request_backtrack(
method open_transcript_overlay (line 104) | pub(crate) fn open_transcript_overlay(&mut self, tui: &mut tui::Tui) {
method close_transcript_overlay (line 111) | pub(crate) fn close_transcript_overlay(&mut self, tui: &mut tui::Tui) {
method render_transcript_once (line 128) | pub(crate) fn render_transcript_once(&mut self, tui: &mut tui::Tui) {
method prime_backtrack (line 135) | fn prime_backtrack(&mut self) {
method open_backtrack_preview (line 143) | fn open_backtrack_preview(&mut self, tui: &mut tui::Tui) {
method begin_overlay_backtrack_preview (line 152) | fn begin_overlay_backtrack_preview(&mut self, tui: &mut tui::Tui) {
method step_backtrack_and_highlight (line 162) | fn step_backtrack_and_highlight(&mut self, tui: &mut tui::Tui) {
method compute_backtrack_selection (line 170) | fn compute_backtrack_selection(
method apply_backtrack_selection (line 190) | fn apply_backtrack_selection(
method overlay_forward_event (line 205) | fn overlay_forward_event(&mut self, tui: &mut tui::Tui, event: TuiEvent)...
method overlay_confirm_backtrack (line 217) | fn overlay_confirm_backtrack(&mut self, tui: &mut tui::Tui) {
method overlay_step_backtrack (line 230) | fn overlay_step_backtrack(&mut self, tui: &mut tui::Tui, event: TuiEvent...
method confirm_backtrack_from_main (line 241) | pub(crate) fn confirm_backtrack_from_main(&mut self) {
method reset_backtrack_state (line 253) | pub(crate) fn reset_backtrack_state(&mut self) {
method on_conversation_history_for_backtrack (line 263) | pub(crate) async fn on_conversation_history_for_backtrack(
method fork_and_switch_to_new_conversation (line 279) | async fn fork_and_switch_to_new_conversation(
method perform_fork (line 300) | async fn perform_fork(
method install_forked_conversation (line 312) | fn install_forked_conversation(
method trim_transcript_for_backtrack (line 340) | fn trim_transcript_for_backtrack(&mut self, drop_count: usize) {
FILE: codex-rs/tui/src/app_event.rs
type AppEvent (line 14) | pub(crate) enum AppEvent {
FILE: codex-rs/tui/src/app_event_sender.rs
type AppEventSender (line 7) | pub(crate) struct AppEventSender {
method new (line 12) | pub(crate) fn new(app_event_tx: UnboundedSender<AppEvent>) -> Self {
method send (line 18) | pub(crate) fn send(&self, event: AppEvent) {
FILE: codex-rs/tui/src/backtrack_helpers.rs
function highlight_range_for_nth_last_user (line 4) | pub(crate) fn highlight_range_for_nth_last_user(
function wrapped_offset_before (line 13) | pub(crate) fn wrapped_offset_before(lines: &[Line<'_>], header_idx: usiz...
function find_nth_last_user_header_index (line 20) | pub(crate) fn find_nth_last_user_header_index(lines: &[Line<'_>], n: usi...
function normalize_backtrack_n (line 46) | pub(crate) fn normalize_backtrack_n(lines: &[Line<'_>], n: usize) -> usi...
function nth_last_user_text (line 63) | pub(crate) fn nth_last_user_text(lines: &[Line<'_>], n: usize) -> Option...
function extract_message_text_after_header (line 69) | fn extract_message_text_after_header(lines: &[Line<'_>], header_idx: usi...
function highlight_range_from_header (line 98) | fn highlight_range_from_header(lines: &[Line<'_>], header_idx: usize) ->...
function line (line 118) | fn line(s: &str) -> Line<'static> {
function transcript_with_users (line 122) | fn transcript_with_users(count: usize) -> Vec<Line<'static>> {
function normalize_wraps_to_one_when_past_oldest (line 134) | fn normalize_wraps_to_one_when_past_oldest() {
function normalize_returns_zero_when_no_user_messages (line 143) | fn normalize_returns_zero_when_no_user_messages() {
function normalize_keeps_valid_n (line 150) | fn normalize_keeps_valid_n() {
FILE: codex-rs/tui/src/bottom_pane/approval_modal_view.rs
type ApprovalModalView (line 15) | pub(crate) struct ApprovalModalView {
method new (line 22) | pub fn new(request: ApprovalRequest, app_event_tx: AppEventSender) -> ...
method enqueue_request (line 30) | pub fn enqueue_request(&mut self, req: ApprovalRequest) {
method maybe_advance (line 35) | fn maybe_advance(&mut self) {
method handle_key_event (line 45) | fn handle_key_event(&mut self, _pane: &mut BottomPane, key_event: KeyEve...
method on_ctrl_c (line 50) | fn on_ctrl_c(&mut self, _pane: &mut BottomPane) -> CancellationEvent {
method is_complete (line 56) | fn is_complete(&self) -> bool {
method desired_height (line 60) | fn desired_height(&self, width: u16) -> u16 {
method render (line 64) | fn render(&self, area: Rect, buf: &mut Buffer) {
method try_consume_approval_request (line 68) | fn try_consume_approval_request(&mut self, req: ApprovalRequest) -> Opti...
function make_exec_request (line 80) | fn make_exec_request() -> ApprovalRequest {
function ctrl_c_aborts_and_clears_queue (line 89) | fn ctrl_c_aborts_and_clears_queue() {
FILE: codex-rs/tui/src/bottom_pane/bottom_pane_view.rs
type BottomPaneView (line 10) | pub(crate) trait BottomPaneView {
method handle_key_event (line 13) | fn handle_key_event(&mut self, _pane: &mut BottomPane, _key_event: Key...
method is_complete (line 16) | fn is_complete(&self) -> bool {
method on_ctrl_c (line 21) | fn on_ctrl_c(&mut self, _pane: &mut BottomPane) -> CancellationEvent {
method desired_height (line 26) | fn desired_height(&self, width: u16) -> u16;
method render (line 29) | fn render(&self, area: Rect, buf: &mut Buffer);
method try_consume_approval_request (line 33) | fn try_consume_approval_request(
FILE: codex-rs/tui/src/bottom_pane/chat_composer.rs
constant PASTE_BURST_MIN_CHARS (line 44) | const PASTE_BURST_MIN_CHARS: u16 = 3;
constant PASTE_BURST_CHAR_INTERVAL (line 45) | const PASTE_BURST_CHAR_INTERVAL: Duration = Duration::from_millis(8);
constant PASTE_ENTER_SUPPRESS_WINDOW (line 46) | const PASTE_ENTER_SUPPRESS_WINDOW: Duration = Duration::from_millis(120);
constant LARGE_PASTE_CHAR_THRESHOLD (line 50) | const LARGE_PASTE_CHAR_THRESHOLD: usize = 1000;
type InputResult (line 53) | pub enum InputResult {
type AttachedImage (line 60) | struct AttachedImage {
type TokenUsageInfo (line 65) | struct TokenUsageInfo {
type ChatComposer (line 80) | pub(crate) struct ChatComposer {
method new (line 113) | pub fn new(
method desired_height (line 145) | pub fn desired_height(&self, width: u16) -> u16 {
method cursor_pos (line 154) | pub fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> {
method is_empty (line 170) | pub(crate) fn is_empty(&self) -> bool {
method set_token_usage (line 177) | pub(crate) fn set_token_usage(
method set_history_metadata (line 199) | pub(crate) fn set_history_metadata(&mut self, log_id: u64, entry_count...
method on_history_entry_response (line 206) | pub(crate) fn on_history_entry_response(
method handle_paste (line 220) | pub fn handle_paste(&mut self, pasted: String) -> bool {
method handle_paste_image_path (line 240) | pub fn handle_paste_image_path(&mut self, pasted: String) -> bool {
method set_text_content (line 260) | pub(crate) fn set_text_content(&mut self, text: String) {
method current_text (line 269) | pub(crate) fn current_text(&self) -> String {
method attach_image (line 273) | pub fn attach_image(&mut self, path: PathBuf, width: u32, height: u32,...
method take_recent_submission_images (line 282) | pub fn take_recent_submission_images(&mut self) -> Vec<PathBuf> {
method on_file_search_result (line 288) | pub(crate) fn on_file_search_result(&mut self, query: String, matches:...
method set_ctrl_c_quit_hint (line 304) | pub fn set_ctrl_c_quit_hint(&mut self, show: bool, has_focus: bool) {
method insert_str (line 309) | pub(crate) fn insert_str(&mut self, text: &str) {
method handle_key_event (line 316) | pub fn handle_key_event(&mut self, key_event: KeyEvent) -> (InputResul...
method popup_active (line 335) | pub(crate) fn popup_active(&self) -> bool {
method handle_key_event_with_slash_popup (line 340) | fn handle_key_event_with_slash_popup(&mut self, key_event: KeyEvent) -...
method clamp_to_char_boundary (line 411) | fn clamp_to_char_boundary(text: &str, pos: usize) -> usize {
method handle_non_ascii_char (line 425) | fn handle_non_ascii_char(&mut self, input: KeyEvent) -> (InputResult, ...
method handle_key_event_with_file_popup (line 439) | fn handle_key_event_with_file_popup(&mut self, key_event: KeyEvent) ->...
method is_image_path (line 541) | fn is_image_path(path: &str) -> bool {
method current_at_token (line 557) | fn current_at_token(textarea: &TextArea) -> Option<String> {
method insert_selected_path (line 653) | fn insert_selected_path(&mut self, path: &str) {
method handle_key_event_without_popup (line 690) | fn handle_key_event_without_popup(&mut self, key_event: KeyEvent) -> (...
method handle_input_basic (line 809) | fn handle_input_basic(&mut self, input: KeyEvent) -> (InputResult, boo...
method try_remove_any_placeholder_at_cursor (line 982) | fn try_remove_any_placeholder_at_cursor(&mut self) -> bool {
method sync_command_popup (line 1125) | fn sync_command_popup(&mut self) {
method sync_file_search_popup (line 1148) | fn sync_file_search_popup(&mut self) {
method set_has_focus (line 1192) | fn set_has_focus(&mut self, has_focus: bool) {
method set_esc_backtrack_hint (line 1196) | pub(crate) fn set_esc_backtrack_hint(&mut self, show: bool) {
type ActivePopup (line 106) | enum ActivePopup {
method render_ref (line 1202) | fn render_ref(&self, area: Rect, buf: &mut Buffer) {
function test_current_at_token_basic_cases (line 1327) | fn test_current_at_token_basic_cases() {
function test_current_at_token_cursor_positions (line 1385) | fn test_current_at_token_cursor_positions() {
function test_current_at_token_whitespace_boundaries (line 1419) | fn test_current_at_token_whitespace_boundaries() {
function handle_paste_small_inserts_text (line 1476) | fn handle_paste_small_inserts_text() {
function handle_paste_large_uses_placeholder_and_replaces_on_submit (line 1500) | fn handle_paste_large_uses_placeholder_and_replaces_on_submit() {
function edit_clears_pending_paste (line 1529) | fn edit_clears_pending_paste() {
function ui_snapshots (line 1549) | fn ui_snapshots() {
function slash_init_dispatches_command_and_does_not_submit_literal_text (line 1609) | fn slash_init_dispatches_command_and_does_not_submit_literal_text() {
function slash_tab_completion_moves_cursor_to_end (line 1645) | fn slash_tab_completion_moves_cursor_to_end() {
function slash_mention_dispatches_command_and_inserts_at (line 1667) | fn slash_mention_dispatches_command_and_inserts_at() {
function test_multiple_pastes_submission (line 1699) | fn test_multiple_pastes_submission() {
function test_placeholder_deletion (line 1773) | fn test_placeholder_deletion() {
function test_partial_placeholder_deletion (line 1840) | fn test_partial_placeholder_deletion() {
function attach_image_and_submit_includes_image_paths (line 1887) | fn attach_image_and_submit_includes_image_paths() {
function attach_image_without_text_submits_empty_text_and_images (line 1906) | fn attach_image_without_text_submits_empty_text_and_images() {
function image_placeholder_backspace_behaves_like_text_placeholder (line 1926) | fn image_placeholder_backspace_behaves_like_text_placeholder() {
function backspace_with_multibyte_text_before_placeholder_does_not_panic (line 1958) | fn backspace_with_multibyte_text_before_placeholder_does_not_panic() {
function deleting_one_of_duplicate_image_placeholders_removes_matching_entry (line 1983) | fn deleting_one_of_duplicate_image_placeholders_removes_matching_entry() {
function pasting_filepath_attaches_image (line 2019) | fn pasting_filepath_attaches_image() {
FILE: codex-rs/tui/src/bottom_pane/chat_composer_history.rs
type ChatComposerHistory (line 10) | pub(crate) struct ChatComposerHistory {
method new (line 34) | pub fn new() -> Self {
method set_metadata (line 46) | pub fn set_metadata(&mut self, log_id: u64, entry_count: usize) {
method record_local_submission (line 57) | pub fn record_local_submission(&mut self, text: &str) {
method should_handle_navigation (line 74) | pub fn should_handle_navigation(&self, text: &str, cursor: usize) -> b...
method navigate_up (line 95) | pub fn navigate_up(&mut self, app_event_tx: &AppEventSender) -> Option...
method navigate_down (line 112) | pub fn navigate_down(&mut self, app_event_tx: &AppEventSender) -> Opti...
method on_entry_response (line 139) | pub fn on_entry_response(
method populate_history_at_index (line 162) | fn populate_history_at_index(
function duplicate_submissions_are_not_recorded (line 198) | fn duplicate_submissions_are_not_recorded() {
function navigation_with_async_fetch (line 221) | fn navigation_with_async_fetch() {
FILE: codex-rs/tui/src/bottom_pane/command_popup.rs
type CommandPopup (line 13) | pub(crate) struct CommandPopup {
method new (line 20) | pub(crate) fn new() -> Self {
method on_composer_text_change (line 32) | pub(crate) fn on_composer_text_change(&mut self, text: String) {
method calculate_required_height (line 61) | pub(crate) fn calculate_required_height(&self) -> u16 {
method filtered (line 67) | fn filtered(&self) -> Vec<(&SlashCommand, Option<Vec<usize>>, i32)> {
method filtered_commands (line 88) | fn filtered_commands(&self) -> Vec<&SlashCommand> {
method move_up (line 93) | pub(crate) fn move_up(&mut self) {
method move_down (line 101) | pub(crate) fn move_down(&mut self) {
method selected_command (line 110) | pub(crate) fn selected_command(&self) -> Option<&SlashCommand> {
method render_ref (line 119) | fn render_ref(&self, area: Rect, buf: &mut Buffer) {
function filter_includes_init_when_typing_prefix (line 143) | fn filter_includes_init_when_typing_prefix() {
function selecting_init_by_exact_match (line 159) | fn selecting_init_by_exact_match() {
FILE: codex-rs/tui/src/bottom_pane/file_search_popup.rs
type FileSearchPopup (line 12) | pub(crate) struct FileSearchPopup {
method new (line 27) | pub(crate) fn new() -> Self {
method set_query (line 38) | pub(crate) fn set_query(&mut self, query: &str) {
method set_empty_prompt (line 59) | pub(crate) fn set_empty_prompt(&mut self) {
method set_matches (line 70) | pub(crate) fn set_matches(&mut self, query: &str, matches: Vec<FileMat...
method move_up (line 84) | pub(crate) fn move_up(&mut self) {
method move_down (line 91) | pub(crate) fn move_down(&mut self) {
method selected_match (line 97) | pub(crate) fn selected_match(&self) -> Option<&str> {
method calculate_required_height (line 104) | pub(crate) fn calculate_required_height(&self) -> u16 {
method render_ref (line 116) | fn render_ref(&self, area: Rect, buf: &mut Buffer) {
FILE: codex-rs/tui/src/bottom_pane/list_selection_view.rs
type SelectionAction (line 24) | pub(crate) type SelectionAction = Box<dyn Fn(&AppEventSender) + Send + S...
type SelectionItem (line 26) | pub(crate) struct SelectionItem {
type ListSelectionView (line 33) | pub(crate) struct ListSelectionView {
method dim_prefix_span (line 44) | fn dim_prefix_span() -> Span<'static> {
method render_dim_prefix_line (line 48) | fn render_dim_prefix_line(area: Rect, buf: &mut Buffer) {
method new (line 52) | pub fn new(
method move_up (line 77) | fn move_up(&mut self) {
method move_down (line 83) | fn move_down(&mut self) {
method accept (line 89) | fn accept(&mut self) {
method cancel (line 102) | fn cancel(&mut self) {
method handle_key_event (line 109) | fn handle_key_event(&mut self, _pane: &mut BottomPane, key_event: KeyEve...
method is_complete (line 130) | fn is_complete(&self) -> bool {
method on_ctrl_c (line 134) | fn on_ctrl_c(&mut self, _pane: &mut BottomPane) -> CancellationEvent {
method desired_height (line 139) | fn desired_height(&self, _width: u16) -> u16 {
method render (line 153) | fn render(&self, area: Rect, buf: &mut Buffer) {
FILE: codex-rs/tui/src/bottom_pane/mod.rs
type CancellationEvent (line 30) | pub(crate) enum CancellationEvent {
type BottomPane (line 44) | pub(crate) struct BottomPane {
constant BOTTOM_PAD_LINES (line 75) | const BOTTOM_PAD_LINES: u16 = 1;
method new (line 76) | pub fn new(params: BottomPaneParams) -> Self {
method desired_height (line 97) | pub fn desired_height(&self, width: u16) -> u16 {
method layout (line 118) | fn layout(&self, area: Rect) -> [Rect; 2] {
method cursor_pos (line 147) | pub fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> {
method handle_key_event (line 161) | pub fn handle_key_event(&mut self, key_event: KeyEvent) -> InputResult {
method on_ctrl_c (line 191) | pub(crate) fn on_ctrl_c(&mut self) -> CancellationEvent {
method handle_paste (line 212) | pub fn handle_paste(&mut self, pasted: String) {
method insert_str (line 221) | pub(crate) fn insert_str(&mut self, text: &str) {
method set_composer_text (line 227) | pub(crate) fn set_composer_text(&mut self, text: String) {
method composer_text (line 234) | pub(crate) fn composer_text(&self) -> String {
method update_status_header (line 241) | pub(crate) fn update_status_header(&mut self, header: String) {
method show_ctrl_c_quit_hint (line 248) | pub(crate) fn show_ctrl_c_quit_hint(&mut self) {
method clear_ctrl_c_quit_hint (line 255) | pub(crate) fn clear_ctrl_c_quit_hint(&mut self) {
method ctrl_c_quit_hint_visible (line 264) | pub(crate) fn ctrl_c_quit_hint_visible(&self) -> bool {
method show_esc_backtrack_hint (line 268) | pub(crate) fn show_esc_backtrack_hint(&mut self) {
method clear_esc_backtrack_hint (line 274) | pub(crate) fn clear_esc_backtrack_hint(&mut self) {
method set_task_running (line 284) | pub fn set_task_running(&mut self, running: bool) {
method show_selection_view (line 305) | pub(crate) fn show_selection_view(
method set_queued_user_messages (line 324) | pub(crate) fn set_queued_user_messages(&mut self, queued: Vec<String>) {
method composer_is_empty (line 332) | pub(crate) fn composer_is_empty(&self) -> bool {
method is_task_running (line 336) | pub(crate) fn is_task_running(&self) -> bool {
method is_normal_backtrack_mode (line 343) | pub(crate) fn is_normal_backtrack_mode(&self) -> bool {
method set_token_usage (line 349) | pub(crate) fn set_token_usage(
method push_approval_request (line 361) | pub fn push_approval_request(&mut self, request: ApprovalRequest) {
method request_redraw (line 381) | pub(crate) fn request_redraw(&self) {
method set_history_metadata (line 387) | pub(crate) fn set_history_metadata(&mut self, log_id: u64, entry_count...
method on_history_entry_response (line 391) | pub(crate) fn on_history_entry_response(
method on_file_search_result (line 406) | pub(crate) fn on_file_search_result(&mut self, query: String, matches:...
method attach_image (line 411) | pub(crate) fn attach_image(
method take_recent_submission_images (line 425) | pub(crate) fn take_recent_submission_images(&mut self) -> Vec<PathBuf> {
type BottomPaneParams (line 66) | pub(crate) struct BottomPaneParams {
method render_ref (line 431) | fn render_ref(&self, area: Rect, buf: &mut Buffer) {
function exec_request (line 458) | fn exec_request() -> ApprovalRequest {
function ctrl_c_on_modal_consumes_and_shows_quit_hint (line 467) | fn ctrl_c_on_modal_consumes_and_shows_quit_hint() {
function overlay_not_shown_above_approval_modal (line 486) | fn overlay_not_shown_above_approval_modal() {
function composer_shown_after_denied_while_task_running (line 516) | fn composer_shown_after_denied_while_task_running() {
function status_indicator_visible_during_command_execution (line 583) | fn status_indicator_visible_during_command_execution() {
function bottom_padding_present_with_status_above_composer (line 613) | fn bottom_padding_present_with_status_above_composer() {
function bottom_padding_shrinks_when_tiny (line 663) | fn bottom_padding_shrinks_when_tiny() {
FILE: codex-rs/tui/src/bottom_pane/popup_consts.rs
constant MAX_POPUP_ROWS (line 5) | pub(crate) const MAX_POPUP_ROWS: usize = 8;
FILE: codex-rs/tui/src/bottom_pane/scroll_state.rs
type ScrollState (line 8) | pub(crate) struct ScrollState {
method new (line 14) | pub fn new() -> Self {
method reset (line 22) | pub fn reset(&mut self) {
method clamp_selection (line 28) | pub fn clamp_selection(&mut self, len: usize) {
method move_up_wrap (line 39) | pub fn move_up_wrap(&mut self, len: usize) {
method move_down_wrap (line 53) | pub fn move_down_wrap(&mut self, len: usize) {
method ensure_visible (line 67) | pub fn ensure_visible(&mut self, len: usize, visible_rows: usize) {
function wrap_navigation_and_visibility (line 92) | fn wrap_navigation_and_visibility() {
FILE: codex-rs/tui/src/bottom_pane/selection_popup_common.rs
type GenericDisplayRow (line 20) | pub(crate) struct GenericDisplayRow {
function render_rows (line 31) | pub(crate) fn render_rows(
FILE: codex-rs/tui/src/bottom_pane/textarea.rs
type TextElement (line 18) | struct TextElement {
type TextArea (line 23) | pub(crate) struct TextArea {
method new (line 44) | pub fn new() -> Self {
method set_text (line 54) | pub fn set_text(&mut self, text: &str) {
method text (line 62) | pub fn text(&self) -> &str {
method insert_str (line 66) | pub fn insert_str(&mut self, text: &str) {
method insert_str_at (line 70) | pub fn insert_str_at(&mut self, pos: usize, text: &str) {
method replace_range (line 81) | pub fn replace_range(&mut self, range: std::ops::Range<usize>, text: &...
method replace_range_raw (line 86) | fn replace_range_raw(&mut self, range: std::ops::Range<usize>, text: &...
method cursor (line 119) | pub fn cursor(&self) -> usize {
method set_cursor (line 123) | pub fn set_cursor(&mut self, pos: usize) {
method desired_height (line 129) | pub fn desired_height(&self, width: u16) -> u16 {
method cursor_pos (line 134) | pub fn cursor_pos(&self, area: Rect) -> Option<(u16, u16)> {
method cursor_pos_with_state (line 139) | pub fn cursor_pos_with_state(&self, area: Rect, state: &TextAreaState)...
method is_empty (line 152) | pub fn is_empty(&self) -> bool {
method current_display_col (line 156) | fn current_display_col(&self) -> usize {
method wrapped_line_index_by_start (line 161) | fn wrapped_line_index_by_start(lines: &[Range<usize>], pos: usize) -> ...
method move_to_display_col_on_line (line 168) | fn move_to_display_col_on_line(
method beginning_of_line (line 188) | fn beginning_of_line(&self, pos: usize) -> usize {
method beginning_of_current_line (line 191) | fn beginning_of_current_line(&self) -> usize {
method end_of_line (line 195) | fn end_of_line(&self, pos: usize) -> usize {
method end_of_current_line (line 201) | fn end_of_current_line(&self) -> usize {
method input (line 205) | pub fn input(&mut self, event: KeyEvent) {
method delete_backward (line 406) | pub fn delete_backward(&mut self, n: usize) {
method delete_forward (line 420) | pub fn delete_forward(&mut self, n: usize) {
method delete_backward_word (line 434) | pub fn delete_backward_word(&mut self) {
method kill_to_end_of_line (line 439) | pub fn kill_to_end_of_line(&mut self) {
method kill_to_beginning_of_line (line 450) | pub fn kill_to_beginning_of_line(&mut self) {
method move_cursor_left (line 462) | pub fn move_cursor_left(&mut self) {
method move_cursor_right (line 468) | pub fn move_cursor_right(&mut self) {
method move_cursor_up (line 473) | pub fn move_cursor_up(&mut self) {
method move_cursor_down (line 536) | pub fn move_cursor_down(&mut self) {
method move_cursor_to_beginning_of_line (line 604) | pub fn move_cursor_to_beginning_of_line(&mut self, move_up_at_bol: boo...
method move_cursor_to_end_of_line (line 614) | pub fn move_cursor_to_end_of_line(&mut self, move_down_at_eol: bool) {
method insert_element (line 626) | pub fn insert_element(&mut self, text: &str) {
method add_element (line 635) | fn add_element(&mut self, range: Range<usize>) {
method find_element_containing (line 643) | fn find_element_containing(&self, pos: usize) -> Option<usize> {
method clamp_pos_to_nearest_boundary (line 649) | fn clamp_pos_to_nearest_boundary(&self, mut pos: usize) -> usize {
method clamp_pos_for_insertion (line 667) | fn clamp_pos_for_insertion(&self, pos: usize) -> usize {
method expand_range_to_element_boundaries (line 684) | fn expand_range_to_element_boundaries(&self, mut range: Range<usize>) ...
method shift_elements (line 706) | fn shift_elements(&mut self, at: usize, removed: usize, inserted: usiz...
method update_elements_after_replace (line 731) | fn update_elements_after_replace(&mut self, start: usize, end: usize, ...
method prev_atomic_boundary (line 735) | fn prev_atomic_boundary(&self, pos: usize) -> usize {
method next_atomic_boundary (line 761) | fn next_atomic_boundary(&self, pos: usize) -> usize {
method beginning_of_previous_word (line 787) | pub(crate) fn beginning_of_previous_word(&self) -> usize {
method end_of_next_word (line 800) | pub(crate) fn end_of_next_word(&self) -> usize {
method adjust_pos_out_of_elements (line 813) | fn adjust_pos_out_of_elements(&self, pos: usize, prefer_start: bool) -...
method wrapped_lines (line 827) | fn wrapped_lines(&self, width: u16) -> Ref<'_, Vec<Range<usize>>> {
method effective_scroll (line 868) | fn effective_scroll(
method render_lines (line 919) | fn render_lines(
type WrapCache (line 32) | struct WrapCache {
type TextAreaState (line 38) | pub(crate) struct TextAreaState {
method render_ref (line 898) | fn render_ref(&self, area: Rect, buf: &mut Buffer) {
type State (line 905) | type State = TextAreaState;
method render_ref (line 907) | fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::Sta...
function rand_grapheme (line 956) | fn rand_grapheme(rng: &mut rand::rngs::StdRng) -> String {
function ta_with (line 997) | fn ta_with(text: &str) -> TextArea {
function insert_and_replace_update_cursor_and_text (line 1004) | fn insert_and_replace_update_cursor_and_text() {
function delete_backward_and_forward_edges (line 1047) | fn delete_backward_and_forward_edges() {
function delete_backward_word_and_kill_line_variants (line 1073) | fn delete_backward_word_and_kill_line_variants() {
function cursor_left_and_right_handle_graphemes (line 1124) | fn cursor_left_and_right_handle_graphemes() {
function control_b_and_f_move_cursor (line 1147) | fn control_b_and_f_move_cursor() {
function control_b_f_fallback_control_chars_move_cursor (line 1159) | fn control_b_f_fallback_control_chars_move_cursor() {
function delete_backward_word_alt_keys (line 1174) | fn delete_backward_word_alt_keys() {
function control_h_backspace (line 1194) | fn control_h_backspace() {
function cursor_vertical_movement_across_lines_and_bounds (line 1216) | fn cursor_vertical_movement_across_lines_and_bounds() {
function home_end_and_emacs_style_home_end (line 1248) | fn home_end_and_emacs_style_home_end() {
function end_of_line_or_down_at_end_of_text (line 1273) | fn end_of_line_or_down_at_end_of_text() {
function word_navigation_helpers (line 1289) | fn word_navigation_helpers() {
function wrapping_and_cursor_positions (line 1308) | fn wrapping_and_cursor_positions() {
function cursor_pos_with_state_basic_and_scroll_behaviors (line 1336) | fn cursor_pos_with_state_basic_and_scroll_behaviors() {
function wrapped_navigation_across_visual_lines (line 1377) | fn wrapped_navigation_across_visual_lines() {
function cursor_pos_with_state_after_movements (line 1415) | fn cursor_pos_with_state_after_movements() {
function wrapped_navigation_with_newlines_and_spaces (line 1459) | fn wrapped_navigation_with_newlines_and_spaces() {
function wrapped_navigation_with_wide_graphemes (line 1484) | fn wrapped_navigation_with_wide_graphemes() {
function fuzz_textarea_randomized (line 1504) | fn fuzz_textarea_randomized() {
FILE: codex-rs/tui/src/chatwidget.rs
type RunningCommand (line 85) | struct RunningCommand {
type ChatWidget (line 90) | pub(crate) struct ChatWidget {
method flush_answer_stream_with_separator (line 142) | fn flush_answer_stream_with_separator(&mut self) {
method on_session_configured (line 147) | fn on_session_configured(&mut self, event: codex_core::protocol::Sessi...
method on_agent_message (line 162)
Condensed preview — 421 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (8,150K chars).
[
{
"path": ".env.example",
"chars": 719,
"preview": "# OpenRouter API Configuration\nOPENROUTER_API_KEY=your-openrouter-api-key-here\n\n# Optional: Override default models\n# SU"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1802,
"preview": "name: CI\n\non:\n push:\n branches: [ main ]\n pull_request:\n branches: [ main ]\n\njobs:\n build-and-test:\n strateg"
},
{
"path": ".gitignore",
"chars": 837,
"preview": "# deps\n# Node.js dependencies\nnode_modules\n.pnpm-store\n.pnpm-debug.log\n\n# Keep pnpm-lock.yaml\n!pnpm-lock.yaml\n\n# build\nd"
},
{
"path": "Dockerfile",
"chars": 691,
"preview": "FROM ghcr.io/astral-sh/uv:python3.11-bookworm\n\nENV DEBIAN_FRONTEND=noninteractive\n\nRUN apt-get update && \\\n apt-get i"
},
{
"path": "LICENSE",
"chars": 10926,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "NOTICE",
"chars": 240,
"preview": "OpenAI Codex\nCopyright 2025 OpenAI\n\nThis project includes code derived from [Ratatui](https://github.com/ratatui/ratatui"
},
{
"path": "README.md",
"chars": 4233,
"preview": "<h1 align=\"center\">🏹 ARTEMIS</h1>\n<p align=\"center\"><strong>A</strong>utomated <strong>R</strong>ed <strong>T</strong>ea"
},
{
"path": "codex-rs/.gitignore",
"chars": 212,
"preview": "/target/\n\n# Recommended value of CARGO_TARGET_DIR when using Docker as explained in .devcontainer/README.md.\n/target-amd"
},
{
"path": "codex-rs/Cargo.toml",
"chars": 1117,
"preview": "[workspace]\nmembers = [\n \"ansi-escape\",\n \"apply-patch\",\n \"arg0\",\n \"cli\",\n \"common\",\n \"core\",\n \"exec"
},
{
"path": "codex-rs/README.md",
"chars": 5579,
"preview": "# Codex CLI (Rust Implementation)\n\nWe provide Codex CLI as a standalone, native executable to ensure a zero-dependency i"
},
{
"path": "codex-rs/ansi-escape/Cargo.toml",
"chars": 341,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-ansi-escape\"\nversion = { workspace = true }\n\n[lib]\nname = \"codex_ansi_escape\"\np"
},
{
"path": "codex-rs/ansi-escape/README.md",
"chars": 425,
"preview": "# oai-codex-ansi-escape\n\nSmall helper functions that wrap functionality from\n<https://crates.io/crates/ansi-to-tui>:\n\n``"
},
{
"path": "codex-rs/ansi-escape/src/lib.rs",
"chars": 1292,
"preview": "use ansi_to_tui::Error;\nuse ansi_to_tui::IntoText;\nuse ratatui::text::Line;\nuse ratatui::text::Text;\n\n/// This function "
},
{
"path": "codex-rs/apply-patch/Cargo.toml",
"chars": 420,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-apply-patch\"\nversion = { workspace = true }\n\n[lib]\nname = \"codex_apply_patch\"\np"
},
{
"path": "codex-rs/apply-patch/apply_patch_tool_instructions.md",
"chars": 3070,
"preview": "## `apply_patch`\n\nUse the `apply_patch` shell command to edit files.\nYour patch language is a stripped‑down, file‑orient"
},
{
"path": "codex-rs/apply-patch/src/lib.rs",
"chars": 42769,
"preview": "mod parser;\nmod seek_sequence;\nmod standalone_executable;\n\nuse std::collections::HashMap;\nuse std::path::Path;\nuse std::"
},
{
"path": "codex-rs/apply-patch/src/main.rs",
"chars": 53,
"preview": "pub fn main() -> ! {\n codex_apply_patch::main()\n}\n"
},
{
"path": "codex-rs/apply-patch/src/parser.rs",
"chars": 25044,
"preview": "//! This module is responsible for parsing & validating a patch into a list of \"hunks\".\n//! (It does not attempt to actu"
},
{
"path": "codex-rs/apply-patch/src/seek_sequence.rs",
"chars": 5578,
"preview": "/// Attempt to find the sequence of `pattern` lines within `lines` beginning at or after `start`.\n/// Returns the starti"
},
{
"path": "codex-rs/apply-patch/src/standalone_executable.rs",
"chars": 1885,
"preview": "use std::io::Read;\nuse std::io::Write;\n\npub fn main() -> ! {\n let exit_code = run_main();\n std::process::exit(exit"
},
{
"path": "codex-rs/apply-patch/tests/all.rs",
"chars": 121,
"preview": "// Single integration test binary that aggregates all test modules.\n// The submodules live in `tests/suite/`.\nmod suite;"
},
{
"path": "codex-rs/apply-patch/tests/suite/cli.rs",
"chars": 2437,
"preview": "use assert_cmd::prelude::*;\nuse std::fs;\nuse std::process::Command;\nuse tempfile::tempdir;\n\n#[test]\nfn test_apply_patch_"
},
{
"path": "codex-rs/apply-patch/tests/suite/mod.rs",
"chars": 9,
"preview": "mod cli;\n"
},
{
"path": "codex-rs/arg0/Cargo.toml",
"chars": 406,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-arg0\"\nversion = { workspace = true }\n\n[lib]\nname = \"codex_arg0\"\npath = \"src/lib"
},
{
"path": "codex-rs/arg0/src/lib.rs",
"chars": 7130,
"preview": "use std::future::Future;\nuse std::path::Path;\nuse std::path::PathBuf;\n\nuse codex_core::CODEX_APPLY_PATCH_ARG1;\n#[cfg(uni"
},
{
"path": "codex-rs/chatgpt/Cargo.toml",
"chars": 522,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-chatgpt\"\nversion = { workspace = true }\n\n[lints]\nworkspace = true\n\n[dependencie"
},
{
"path": "codex-rs/chatgpt/README.md",
"chars": 240,
"preview": "# ChatGPT\n\nThis crate pertains to first party ChatGPT APIs and products such as Codex agent.\n\nThis crate should be prima"
},
{
"path": "codex-rs/chatgpt/src/apply_command.rs",
"chars": 2999,
"preview": "use std::path::PathBuf;\n\nuse clap::Parser;\nuse codex_common::CliConfigOverrides;\nuse codex_core::config::Config;\nuse cod"
},
{
"path": "codex-rs/chatgpt/src/chatgpt_client.rs",
"chars": 1674,
"preview": "use codex_core::config::Config;\nuse codex_core::user_agent::get_codex_user_agent;\n\nuse crate::chatgpt_token::get_chatgpt"
},
{
"path": "codex-rs/chatgpt/src/chatgpt_token.rs",
"chars": 852,
"preview": "use codex_login::AuthMode;\nuse codex_login::CodexAuth;\nuse std::path::Path;\nuse std::sync::LazyLock;\nuse std::sync::RwLo"
},
{
"path": "codex-rs/chatgpt/src/get_task.rs",
"chars": 891,
"preview": "use codex_core::config::Config;\nuse serde::Deserialize;\n\nuse crate::chatgpt_client::chatgpt_get_request;\n\n#[derive(Debug"
},
{
"path": "codex-rs/chatgpt/src/lib.rs",
"chars": 80,
"preview": "pub mod apply_command;\nmod chatgpt_client;\nmod chatgpt_token;\npub mod get_task;\n"
},
{
"path": "codex-rs/chatgpt/tests/all.rs",
"chars": 121,
"preview": "// Single integration test binary that aggregates all test modules.\n// The submodules live in `tests/suite/`.\nmod suite;"
},
{
"path": "codex-rs/chatgpt/tests/suite/apply_command_e2e.rs",
"chars": 5779,
"preview": "use codex_chatgpt::apply_command::apply_diff_from_task;\nuse codex_chatgpt::get_task::GetTaskResponse;\nuse std::path::Pat"
},
{
"path": "codex-rs/chatgpt/tests/suite/mod.rs",
"chars": 89,
"preview": "// Aggregates all former standalone integration tests as modules.\nmod apply_command_e2e;\n"
},
{
"path": "codex-rs/chatgpt/tests/task_turn_fixture.json",
"chars": 3703,
"preview": "{\n \"current_diff_task_turn\": {\n \"output_items\": [\n {\n \"type\": \"pr\",\n "
},
{
"path": "codex-rs/cli/Cargo.toml",
"chars": 1059,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-cli\"\nversion = { workspace = true }\n\n[[bin]]\nname = \"codex\"\npath = \"src/main.rs"
},
{
"path": "codex-rs/cli/src/debug_sandbox.rs",
"chars": 3077,
"preview": "use std::path::PathBuf;\n\nuse codex_common::CliConfigOverrides;\nuse codex_core::config::Config;\nuse codex_core::config::C"
},
{
"path": "codex-rs/cli/src/exit_status.rs",
"chars": 684,
"preview": "#[cfg(unix)]\npub(crate) fn handle_exit_status(status: std::process::ExitStatus) -> ! {\n use std::os::unix::process::E"
},
{
"path": "codex-rs/cli/src/lib.rs",
"chars": 1031,
"preview": "pub mod debug_sandbox;\nmod exit_status;\npub mod login;\npub mod proto;\n\nuse clap::Parser;\nuse codex_common::CliConfigOver"
},
{
"path": "codex-rs/cli/src/login.rs",
"chars": 4853,
"preview": "use codex_common::CliConfigOverrides;\nuse codex_core::config::Config;\nuse codex_core::config::ConfigOverrides;\nuse codex"
},
{
"path": "codex-rs/cli/src/main.rs",
"chars": 88180,
"preview": "use anyhow::Context;\nuse clap::CommandFactory;\nuse clap::Parser;\nuse clap_complete::Shell;\nuse clap_complete::generate;\n"
},
{
"path": "codex-rs/cli/src/proto.rs",
"chars": 4424,
"preview": "use std::io::IsTerminal;\n\nuse clap::Parser;\nuse codex_common::CliConfigOverrides;\nuse codex_core::ConversationManager;\nu"
},
{
"path": "codex-rs/clippy.toml",
"chars": 827,
"preview": "allow-expect-in-tests = true\nallow-unwrap-in-tests = true\ndisallowed-methods = [\n { path = \"ratatui::style::Color::Rg"
},
{
"path": "codex-rs/common/Cargo.toml",
"chars": 506,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-common\"\nversion = { workspace = true }\n\n[lints]\nworkspace = true\n\n[dependencies"
},
{
"path": "codex-rs/common/README.md",
"chars": 314,
"preview": "# codex-common\n\nThis crate is designed for utilities that need to be shared across other crates in the workspace, but sh"
},
{
"path": "codex-rs/common/src/approval_mode_cli_arg.rs",
"chars": 1354,
"preview": "//! Standard type to use with the `--approval-mode` CLI option.\n//! Available when the `cli` feature is enabled for the "
},
{
"path": "codex-rs/common/src/approval_presets.rs",
"chars": 1846,
"preview": "use codex_core::protocol::AskForApproval;\nuse codex_core::protocol::SandboxPolicy;\n\n/// A simple preset pairing an appro"
},
{
"path": "codex-rs/common/src/config_override.rs",
"chars": 6102,
"preview": "//! Support for `-c key=value` overrides shared across Codex CLI tools.\n//!\n//! This module provides a [`CliConfigOverri"
},
{
"path": "codex-rs/common/src/config_summary.rs",
"chars": 988,
"preview": "use codex_core::WireApi;\nuse codex_core::config::Config;\n\nuse crate::sandbox_summary::summarize_sandbox_policy;\n\n/// Bui"
},
{
"path": "codex-rs/common/src/elapsed.rs",
"chars": 2260,
"preview": "use std::time::Duration;\nuse std::time::Instant;\n\n/// Returns a string representing the elapsed time since `start_time` "
},
{
"path": "codex-rs/common/src/fuzzy_match.rs",
"chars": 6195,
"preview": "/// Simple case-insensitive subsequence matcher used for fuzzy filtering.\n///\n/// Returns the indices (character positio"
},
{
"path": "codex-rs/common/src/lib.rs",
"chars": 988,
"preview": "#[cfg(feature = \"cli\")]\nmod approval_mode_cli_arg;\n\n#[cfg(feature = \"elapsed\")]\npub mod elapsed;\n\n#[cfg(feature = \"cli\")"
},
{
"path": "codex-rs/common/src/model_presets.rs",
"chars": 2036,
"preview": "use codex_core::protocol_config_types::ReasoningEffort;\n\n/// A simple preset pairing a model slug with a reasoning effor"
},
{
"path": "codex-rs/common/src/sandbox_mode_cli_arg.rs",
"chars": 988,
"preview": "//! Standard type to use with the `--sandbox` (`-s`) CLI option.\n//!\n//! This mirrors the variants of [`codex_core::prot"
},
{
"path": "codex-rs/common/src/sandbox_summary.rs",
"chars": 1270,
"preview": "use codex_core::protocol::SandboxPolicy;\n\npub fn summarize_sandbox_policy(sandbox_policy: &SandboxPolicy) -> String {\n "
},
{
"path": "codex-rs/config.md",
"chars": 245,
"preview": "# Configuration docs moved\n\nThis file has moved. Please see the latest configuration documentation here:\n\n- Full config "
},
{
"path": "codex-rs/core/Cargo.toml",
"chars": 2088,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-core\"\nversion = { workspace = true }\n\n[lib]\nname = \"codex_core\"\npath = \"src/lib"
},
{
"path": "codex-rs/core/README.md",
"chars": 711,
"preview": "# codex-core\n\nThis crate implements the business logic for Codex. It is designed to be used by the various Codex UIs wri"
},
{
"path": "codex-rs/core/active_directory.md",
"chars": 1955,
"preview": "You are Codex, the Active Directory Enumeration Agent running inside a fully-equipped Kali Linux container. Your mission"
},
{
"path": "codex-rs/core/approval_prompt.txt",
"chars": 1514,
"preview": "You are reviewing a command execution request from an autonomous security testing agent.\n\nCOMMAND DETAILS:\nCommand: {com"
},
{
"path": "codex-rs/core/bugcrowd_approval_prompt.txt",
"chars": 1822,
"preview": "You are reviewing a bug bounty submission request from an autonomous security testing agent.\n\nSUBMISSION DETAILS:\nTool: "
},
{
"path": "codex-rs/core/client_side_web.md",
"chars": 3771,
"preview": "You are Codex, a deployed cybersecurity red-teaming agent operating inside a fully-equipped Kali Linux container. You ar"
},
{
"path": "codex-rs/core/continuation_prompt.txt",
"chars": 3435,
"preview": "You are an autonomous security testing agent, part of a complex multi-agent system designed for pentesting approved targ"
},
{
"path": "codex-rs/core/enumeration.md",
"chars": 3697,
"preview": "You are Codex, a deployed cybersecurity red-teaming agent operating inside a fully-equipped Kali Linux container. You ar"
},
{
"path": "codex-rs/core/initial_prompt.txt",
"chars": 3644,
"preview": "You are an autonomous security testing agent, part of a complex multi-agent system designed for pentesting approved targ"
},
{
"path": "codex-rs/core/linux_privesc.md",
"chars": 3451,
"preview": "You are Codex, a deployed cybersecurity red-teaming agent operating inside a fully-equipped Kali Linux container. You ar"
},
{
"path": "codex-rs/core/prompt.md",
"chars": 22369,
"preview": "You are a coding agent running in the Codex CLI, a terminal-based coding assistant. Codex CLI is an open source project "
},
{
"path": "codex-rs/core/shelling.md",
"chars": 2395,
"preview": "You are Codex, the Shelling Agent deployed inside a fully-equipped Kali Linux container. Your mission is to turn any exp"
},
{
"path": "codex-rs/core/src/apply_patch.rs",
"chars": 4828,
"preview": "use crate::codex::Session;\nuse crate::codex::TurnContext;\nuse crate::protocol::FileChange;\nuse crate::protocol::ReviewDe"
},
{
"path": "codex-rs/core/src/bash.rs",
"chars": 7178,
"preview": "use tree_sitter::Parser;\nuse tree_sitter::Tree;\nuse tree_sitter_bash::LANGUAGE as BASH;\n\n/// Parse the provided bash sou"
},
{
"path": "codex-rs/core/src/chat_completions.rs",
"chars": 27549,
"preview": "use std::time::Duration;\n\nuse bytes::Bytes;\nuse eventsource_stream::Eventsource;\nuse futures::Stream;\nuse futures::Strea"
},
{
"path": "codex-rs/core/src/client.rs",
"chars": 37090,
"preview": "use std::io::BufRead;\nuse std::path::Path;\nuse std::time::Duration;\n\nuse bytes::Bytes;\nuse codex_login::AuthManager;\nuse"
},
{
"path": "codex-rs/core/src/client_common.rs",
"chars": 9593,
"preview": "use crate::config_types::Verbosity as VerbosityConfig;\nuse crate::error::Result;\nuse crate::model_family::ModelFamily;\nu"
},
{
"path": "codex-rs/core/src/codex.rs",
"chars": 113316,
"preview": "use std::borrow::Cow;\nuse std::collections::HashMap;\nuse std::collections::HashSet;\nuse std::path::PathBuf;\nuse std::syn"
},
{
"path": "codex-rs/core/src/codex_conversation.rs",
"chars": 802,
"preview": "use crate::codex::Codex;\nuse crate::error::Result as CodexResult;\nuse crate::protocol::Event;\nuse crate::protocol::Op;\nu"
},
{
"path": "codex-rs/core/src/config.rs",
"chars": 50515,
"preview": "use crate::config_profile::ConfigProfile;\nuse crate::config_types::History;\nuse crate::config_types::McpServerConfig;\nus"
},
{
"path": "codex-rs/core/src/config_profile.rs",
"chars": 940,
"preview": "use serde::Deserialize;\nuse std::path::PathBuf;\n\nuse crate::config_types::Verbosity;\nuse crate::protocol::AskForApproval"
},
{
"path": "codex-rs/core/src/config_types.rs",
"chars": 7216,
"preview": "//! Types used to define the fields of [`crate::config::Config`].\n\n// Note this file should generally be restricted to s"
},
{
"path": "codex-rs/core/src/conversation_history.rs",
"chars": 4337,
"preview": "use codex_protocol::models::ResponseItem;\n\n/// Transcript of conversation history\n#[derive(Debug, Clone, Default)]\npub(c"
},
{
"path": "codex-rs/core/src/conversation_manager.rs",
"chars": 7547,
"preview": "use std::collections::HashMap;\nuse std::sync::Arc;\n\nuse codex_login::AuthManager;\nuse codex_login::CodexAuth;\nuse tokio:"
},
{
"path": "codex-rs/core/src/environment_context.rs",
"chars": 4314,
"preview": "use serde::Deserialize;\nuse serde::Serialize;\nuse strum_macros::Display as DeriveDisplay;\n\nuse crate::protocol::AskForAp"
},
{
"path": "codex-rs/core/src/error.rs",
"chars": 10093,
"preview": "use reqwest::StatusCode;\nuse serde_json;\nuse std::io;\nuse std::time::Duration;\nuse thiserror::Error;\nuse tokio::task::Jo"
},
{
"path": "codex-rs/core/src/exec.rs",
"chars": 12232,
"preview": "#[cfg(unix)]\nuse std::os::unix::process::ExitStatusExt;\n\nuse std::collections::HashMap;\nuse std::io;\nuse std::path::Path"
},
{
"path": "codex-rs/core/src/exec_command/exec_command_params.rs",
"chars": 1162,
"preview": "use serde::Deserialize;\nuse serde::Serialize;\n\nuse crate::exec_command::session_id::SessionId;\n\n#[derive(Debug, Clone, D"
},
{
"path": "codex-rs/core/src/exec_command/exec_command_session.rs",
"chars": 2663,
"preview": "use std::sync::Mutex as StdMutex;\n\nuse tokio::sync::broadcast;\nuse tokio::sync::mpsc;\nuse tokio::task::JoinHandle;\n\n#[de"
},
{
"path": "codex-rs/core/src/exec_command/mod.rs",
"chars": 538,
"preview": "mod exec_command_params;\nmod exec_command_session;\nmod responses_api;\nmod session_id;\nmod session_manager;\n\npub use exec"
},
{
"path": "codex-rs/core/src/exec_command/responses_api.rs",
"chars": 3281,
"preview": "use std::collections::BTreeMap;\n\nuse crate::openai_tools::JsonSchema;\nuse crate::openai_tools::ResponsesApiTool;\n\npub co"
},
{
"path": "codex-rs/core/src/exec_command/session_id.rs",
"chars": 160,
"preview": "use serde::Deserialize;\nuse serde::Serialize;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)"
},
{
"path": "codex-rs/core/src/exec_command/session_manager.rs",
"chars": 24841,
"preview": "use std::collections::HashMap;\nuse std::io::ErrorKind;\nuse std::io::Read;\nuse std::sync::Arc;\nuse std::sync::Mutex as St"
},
{
"path": "codex-rs/core/src/exec_env.rs",
"chars": 6551,
"preview": "use crate::config_types::EnvironmentVariablePattern;\nuse crate::config_types::ShellEnvironmentPolicy;\nuse crate::config_"
},
{
"path": "codex-rs/core/src/flags.rs",
"chars": 479,
"preview": "use std::time::Duration;\n\nuse env_flags::env_flags;\n\nenv_flags! {\n pub OPENAI_API_BASE: &str = \"https://api.openai.co"
},
{
"path": "codex-rs/core/src/git_info.rs",
"chars": 32379,
"preview": "use std::collections::HashSet;\nuse std::path::Path;\nuse std::path::PathBuf;\n\nuse codex_protocol::mcp_protocol::GitSha;\nu"
},
{
"path": "codex-rs/core/src/is_safe_command.rs",
"chars": 11271,
"preview": "use crate::bash::try_parse_bash;\nuse crate::bash::try_parse_word_only_commands_sequence;\n\npub fn is_known_safe_command(c"
},
{
"path": "codex-rs/core/src/landlock.rs",
"chars": 2045,
"preview": "use crate::protocol::SandboxPolicy;\nuse crate::spawn::StdioPolicy;\nuse crate::spawn::spawn_child_async;\nuse std::collect"
},
{
"path": "codex-rs/core/src/lib.rs",
"chars": 2018,
"preview": "//! Root of the `codex-core` library.\n\n// Prevent accidental direct writes to stdout/stderr in library code. All\n// user"
},
{
"path": "codex-rs/core/src/mcp_connection_manager.rs",
"chars": 12621,
"preview": "//! Connection manager for Model Context Protocol (MCP) servers.\n//!\n//! The [`McpConnectionManager`] owns one [`codex_m"
},
{
"path": "codex-rs/core/src/mcp_tool_call.rs",
"chars": 2616,
"preview": "use std::time::Duration;\nuse std::time::Instant;\n\nuse tracing::error;\n\nuse crate::codex::Session;\nuse crate::protocol::E"
},
{
"path": "codex-rs/core/src/message_history.rs",
"chars": 9579,
"preview": "//! Persistence layer for the global, append-only *message history* file.\n//!\n//! The history is stored at `~/.codex/his"
},
{
"path": "codex-rs/core/src/model_family.rs",
"chars": 3853,
"preview": "use crate::tool_apply_patch::ApplyPatchToolType;\n\n/// A model family is a group of models that share certain characteris"
},
{
"path": "codex-rs/core/src/model_provider_info.rs",
"chars": 15420,
"preview": "//! Registry of model providers supported by Codex.\n//!\n//! Providers can be defined in two places:\n//! 1. Built-in de"
},
{
"path": "codex-rs/core/src/openai_model_info.rs",
"chars": 3288,
"preview": "use crate::model_family::ModelFamily;\n\n/// Metadata about a model, particularly OpenAI models.\n/// We may want to consid"
},
{
"path": "codex-rs/core/src/openai_tools.rs",
"chars": 38266,
"preview": "use serde::Deserialize;\nuse serde::Serialize;\nuse serde_json::Value as JsonValue;\nuse serde_json::json;\nuse std::collect"
},
{
"path": "codex-rs/core/src/parse_command.rs",
"chars": 69561,
"preview": "use crate::bash::try_parse_bash;\nuse crate::bash::try_parse_word_only_commands_sequence;\nuse serde::Deserialize;\nuse ser"
},
{
"path": "codex-rs/core/src/plan_tool.rs",
"chars": 3941,
"preview": "use std::collections::BTreeMap;\nuse std::sync::LazyLock;\n\nuse crate::codex::Session;\nuse crate::openai_tools::JsonSchema"
},
{
"path": "codex-rs/core/src/project_doc.rs",
"chars": 12506,
"preview": "//! Project-level documentation discovery.\n//!\n//! Project-level documentation can be stored in files named `AGENTS.md`."
},
{
"path": "codex-rs/core/src/prompt_for_compact_command.md",
"chars": 728,
"preview": "You are a summarization assistant. A conversation follows between a user and a coding-focused AI (Codex). Your task is t"
},
{
"path": "codex-rs/core/src/rollout.rs",
"chars": 12401,
"preview": "//! Persist Codex session rollouts (.jsonl) so sessions can be replayed or inspected later.\n\nuse std::fs::File;\nuse std:"
},
{
"path": "codex-rs/core/src/safety.rs",
"chars": 12018,
"preview": "use std::collections::HashSet;\nuse std::path::Component;\nuse std::path::Path;\nuse std::path::PathBuf;\n\nuse codex_apply_p"
},
{
"path": "codex-rs/core/src/seatbelt.rs",
"chars": 11974,
"preview": "use std::collections::HashMap;\nuse std::path::Path;\nuse std::path::PathBuf;\nuse tokio::process::Child;\n\nuse crate::proto"
},
{
"path": "codex-rs/core/src/seatbelt_base_policy.sbpl",
"chars": 2338,
"preview": "(version 1)\n\n; inspired by Chrome's sandbox policy:\n; https://source.chromium.org/chromium/chromium/src/+/main:sandbox/p"
},
{
"path": "codex-rs/core/src/shell.rs",
"chars": 15017,
"preview": "use serde::Deserialize;\nuse serde::Serialize;\nuse shlex;\nuse std::path::PathBuf;\n\n#[derive(Debug, PartialEq, Eq, Clone, "
},
{
"path": "codex-rs/core/src/spawn.rs",
"chars": 4119,
"preview": "use std::collections::HashMap;\nuse std::path::PathBuf;\nuse std::process::Stdio;\nuse tokio::process::Child;\nuse tokio::pr"
},
{
"path": "codex-rs/core/src/terminal.rs",
"chars": 2416,
"preview": "use std::sync::OnceLock;\n\nstatic TERMINAL: OnceLock<String> = OnceLock::new();\n\npub fn user_agent() -> String {\n TERM"
},
{
"path": "codex-rs/core/src/tool_apply_patch.rs",
"chars": 5342,
"preview": "use serde::Deserialize;\nuse serde::Serialize;\nuse std::collections::BTreeMap;\n\nuse crate::openai_tools::FreeformTool;\nus"
},
{
"path": "codex-rs/core/src/turn_diff_tracker.rs",
"chars": 30560,
"preview": "use std::collections::HashMap;\nuse std::fs;\nuse std::path::Path;\nuse std::path::PathBuf;\nuse std::process::Command;\n\nuse"
},
{
"path": "codex-rs/core/src/user_agent.rs",
"chars": 1025,
"preview": "const DEFAULT_ORIGINATOR: &str = \"codex_cli_rs\";\n\npub fn get_codex_user_agent(originator: Option<&str>) -> String {\n "
},
{
"path": "codex-rs/core/src/user_notification.rs",
"chars": 1431,
"preview": "use serde::Serialize;\n\n/// User can configure a program that will receive notifications. Each\n/// notification is serial"
},
{
"path": "codex-rs/core/src/util.rs",
"chars": 1462,
"preview": "use std::path::Path;\nuse std::time::Duration;\n\nuse rand::Rng;\n\nconst INITIAL_DELAY_MS: u64 = 200;\nconst BACKOFF_FACTOR: "
},
{
"path": "codex-rs/core/summarization_prompt.txt",
"chars": 1623,
"preview": "You are a context summarization expert. Your task is to create a concise summary of the provided conversation context th"
},
{
"path": "codex-rs/core/tests/all.rs",
"chars": 119,
"preview": "// Single integration test binary that aggregates all test modules.\n// The submodules live in `tests/all/`.\nmod suite;\n"
},
{
"path": "codex-rs/core/tests/cli_responses_fixture.sse",
"chars": 365,
"preview": "event: response.created\ndata: {\"type\":\"response.created\",\"response\":{\"id\":\"resp1\"}}\n\nevent: response.output_item.done\nda"
},
{
"path": "codex-rs/core/tests/common/Cargo.toml",
"chars": 235,
"preview": "[package]\nname = \"core_test_support\"\nversion = { workspace = true }\nedition = \"2024\"\n\n[lib]\npath = \"lib.rs\"\n\n[dependenci"
},
{
"path": "codex-rs/core/tests/common/lib.rs",
"chars": 4511,
"preview": "#![expect(clippy::expect_used)]\n\nuse tempfile::TempDir;\n\nuse codex_core::CodexConversation;\nuse codex_core::config::Conf"
},
{
"path": "codex-rs/core/tests/fixtures/completed_template.json",
"chars": 295,
"preview": "[\n {\n \"type\": \"response.completed\",\n \"response\": {\n \"id\": \"__ID__\",\n \"usage\": {\n \"input_tokens\":"
},
{
"path": "codex-rs/core/tests/fixtures/incomplete_sse.json",
"chars": 44,
"preview": "[\n {\"type\": \"response.output_item.done\"}\n]\n"
},
{
"path": "codex-rs/core/tests/suite/cli_stream.rs",
"chars": 21051,
"preview": "use assert_cmd::Command as AssertCommand;\nuse codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;\nuse std::time::"
},
{
"path": "codex-rs/core/tests/suite/client.rs",
"chars": 30465,
"preview": "use codex_core::ConversationManager;\nuse codex_core::ModelProviderInfo;\nuse codex_core::NewConversation;\nuse codex_core:"
},
{
"path": "codex-rs/core/tests/suite/compact.rs",
"chars": 8996,
"preview": "#![expect(clippy::unwrap_used)]\n\nuse codex_core::ConversationManager;\nuse codex_core::ModelProviderInfo;\nuse codex_core:"
},
{
"path": "codex-rs/core/tests/suite/exec.rs",
"chars": 3627,
"preview": "#![cfg(target_os = \"macos\")]\n\nuse std::collections::HashMap;\n\nuse codex_core::exec::ExecParams;\nuse codex_core::exec::Ex"
},
{
"path": "codex-rs/core/tests/suite/exec_stream_events.rs",
"chars": 4926,
"preview": "#![cfg(unix)]\n\nuse std::collections::HashMap;\nuse std::path::PathBuf;\n\nuse async_channel::Receiver;\nuse codex_core::exec"
},
{
"path": "codex-rs/core/tests/suite/live_cli.rs",
"chars": 5048,
"preview": "#![expect(clippy::expect_used)]\n\n//! Optional smoke tests that hit the real OpenAI /v1/responses endpoint. They are `#[i"
},
{
"path": "codex-rs/core/tests/suite/mod.rs",
"chars": 250,
"preview": "// Aggregates all former standalone integration tests as modules.\n\nmod cli_stream;\nmod client;\nmod compact;\nmod exec;\nmo"
},
{
"path": "codex-rs/core/tests/suite/prompt_caching.rs",
"chars": 19073,
"preview": "#![allow(clippy::unwrap_used)]\n\nuse codex_core::ConversationManager;\nuse codex_core::ModelProviderInfo;\nuse codex_core::"
},
{
"path": "codex-rs/core/tests/suite/seatbelt.rs",
"chars": 6685,
"preview": "#![cfg(target_os = \"macos\")]\n\n//! Tests for the macOS sandboxing that are specific to Seatbelt.\n//! Tests that apply to "
},
{
"path": "codex-rs/core/tests/suite/stream_error_allows_next_turn.rs",
"chars": 4476,
"preview": "use std::time::Duration;\n\nuse codex_core::ConversationManager;\nuse codex_core::ModelProviderInfo;\nuse codex_core::WireAp"
},
{
"path": "codex-rs/core/tests/suite/stream_no_completed.rs",
"chars": 4093,
"preview": "//! Verifies that the agent retries when the SSE stream terminates before\n//! delivering a `response.completed` event.\n\n"
},
{
"path": "codex-rs/core/web.md",
"chars": 2201,
"preview": "You are Codex, a deployed cybersecurity red-teaming agent operating inside a fully-equipped Kali Linux container. You ar"
},
{
"path": "codex-rs/core/web_enumeration.md",
"chars": 4118,
"preview": "You are Codex, a deployed cybersecurity red-teaming agent operating inside a fully-equipped Kali Linux container. You ar"
},
{
"path": "codex-rs/core/windows_privesc.md",
"chars": 1682,
"preview": "You are Codex, the Windows Privilege Escalation Agent inside Kali. Once you have any code execution, your goal is to bec"
},
{
"path": "codex-rs/cve-prompt.md",
"chars": 2362,
"preview": "📝 CVE Drafting Prompt (Fill-In Template for Agent)\n\nInstruction:\nGiven the vulnerability context that follows this promp"
},
{
"path": "codex-rs/default.nix",
"chars": 1006,
"preview": "{ pkgs, monorep-deps ? [], ... }:\nlet\n env = {\n PKG_CONFIG_PATH = \"${pkgs.openssl.dev}/lib/pkgconfig:$PKG_CONFIG_PAT"
},
{
"path": "codex-rs/disclosure-email-prompt.md",
"chars": 2154,
"preview": "📧 Vulnerability Disclosure Email Template (Friendly Tone)\n\nInstruction:\nGiven the vulnerability context that follows thi"
},
{
"path": "codex-rs/docs/protocol_v1.md",
"chars": 7889,
"preview": "Overview of Protocol Defined in [protocol.rs](../core/src/protocol.rs) and [agent.rs](../core/src/agent.rs).\n\nThe goal o"
},
{
"path": "codex-rs/exec/Cargo.toml",
"chars": 1041,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-exec\"\nversion = { workspace = true }\n\n[[bin]]\nname = \"codex-exec\"\npath = \"src/m"
},
{
"path": "codex-rs/exec/src/cli.rs",
"chars": 4354,
"preview": "use clap::Parser;\nuse clap::ValueEnum;\nuse codex_common::CliConfigOverrides;\nuse std::path::PathBuf;\n\n#[derive(Parser, D"
},
{
"path": "codex-rs/exec/src/event_processor.rs",
"chars": 1108,
"preview": "use std::path::Path;\n\nuse codex_core::config::Config;\nuse codex_core::protocol::Event;\n\npub(crate) enum CodexStatus {\n "
},
{
"path": "codex-rs/exec/src/event_processor_with_human_output.rs",
"chars": 22949,
"preview": "use codex_common::elapsed::format_duration;\nuse codex_common::elapsed::format_elapsed;\nuse codex_core::config::Config;\nu"
},
{
"path": "codex-rs/exec/src/event_processor_with_json_output.rs",
"chars": 2163,
"preview": "use std::collections::HashMap;\nuse std::path::PathBuf;\n\nuse codex_core::config::Config;\nuse codex_core::protocol::Event;"
},
{
"path": "codex-rs/exec/src/lib.rs",
"chars": 21604,
"preview": "mod cli;\nmod event_processor;\nmod event_processor_with_human_output;\nmod event_processor_with_json_output;\nmod realtime_"
},
{
"path": "codex-rs/exec/src/main.rs",
"chars": 1362,
"preview": "//! Entry-point for the `codex-exec` binary.\n//!\n//! When this CLI is invoked normally, it parses the standard `codex-ex"
},
{
"path": "codex-rs/exec/src/realtime_logger.rs",
"chars": 17574,
"preview": "use chrono::DateTime;\nuse chrono::Utc;\nuse codex_core::protocol::Event;\nuse codex_core::protocol::EventMsg;\nuse std::fs:"
},
{
"path": "codex-rs/exec/tests/all.rs",
"chars": 121,
"preview": "// Single integration test binary that aggregates all test modules.\n// The submodules live in `tests/suite/`.\nmod suite;"
},
{
"path": "codex-rs/exec/tests/fixtures/apply_patch_freeform_final.txt",
"chars": 50,
"preview": "class BaseClass:\n def method():\n\n return True\n"
},
{
"path": "codex-rs/exec/tests/fixtures/sse_apply_patch_add.json",
"chars": 540,
"preview": "[\n {\n \"type\": \"response.output_item.done\",\n \"item\": {\n \"type\": \"custom_tool_call\",\n \"name\": \"apply_patc"
},
{
"path": "codex-rs/exec/tests/fixtures/sse_apply_patch_freeform_add.json",
"chars": 581,
"preview": "[\n {\n \"type\": \"response.output_item.done\",\n \"item\": {\n \"type\": \"custom_tool_call\",\n \"name\": \"apply_patc"
},
{
"path": "codex-rs/exec/tests/fixtures/sse_apply_patch_freeform_update.json",
"chars": 587,
"preview": "[\n {\n \"type\": \"response.output_item.done\",\n \"item\": {\n \"type\": \"custom_tool_call\",\n \"name\": \"apply_patc"
},
{
"path": "codex-rs/exec/tests/fixtures/sse_apply_patch_update.json",
"chars": 589,
"preview": "[\n {\n \"type\": \"response.output_item.done\",\n \"item\": {\n \"type\": \"function_call\",\n \"name\": \"apply_patch\","
},
{
"path": "codex-rs/exec/tests/fixtures/sse_response_completed.json",
"chars": 295,
"preview": "[\n {\n \"type\": \"response.completed\",\n \"response\": {\n \"id\": \"__ID__\",\n \"usage\": {\n \"input_tokens\":"
},
{
"path": "codex-rs/exec/tests/suite/apply_patch.rs",
"chars": 3691,
"preview": "#![allow(clippy::expect_used, clippy::unwrap_used)]\n\nuse anyhow::Context;\nuse assert_cmd::prelude::*;\nuse codex_core::CO"
},
{
"path": "codex-rs/exec/tests/suite/common.rs",
"chars": 2436,
"preview": "// this file is only used for e2e tests which are currently disabled on windows\n#![cfg(not(target_os = \"windows\"))]\n#![a"
},
{
"path": "codex-rs/exec/tests/suite/mod.rs",
"chars": 108,
"preview": "// Aggregates all former standalone integration tests as modules.\nmod apply_patch;\nmod common;\nmod sandbox;\n"
},
{
"path": "codex-rs/exec/tests/suite/sandbox.rs",
"chars": 6643,
"preview": "#![cfg(unix)]\nuse codex_core::protocol::SandboxPolicy;\nuse codex_core::spawn::StdioPolicy;\nuse std::collections::HashMap"
},
{
"path": "codex-rs/execpolicy/Cargo.toml",
"chars": 664,
"preview": "[package]\nname = \"codex-execpolicy\"\nversion = { workspace = true }\nedition = \"2024\"\n\n[[bin]]\nname = \"codex-execpolicy\"\np"
},
{
"path": "codex-rs/execpolicy/README.md",
"chars": 6514,
"preview": "# codex_execpolicy\n\nThe goal of this library is to classify a proposed [`execv(3)`](https://linux.die.net/man/3/execv) c"
},
{
"path": "codex-rs/execpolicy/build.rs",
"chars": 73,
"preview": "fn main() {\n println!(\"cargo:rerun-if-changed=src/default.policy\");\n}\n"
},
{
"path": "codex-rs/execpolicy/src/arg_matcher.rs",
"chars": 3721,
"preview": "#![allow(clippy::needless_lifetimes)]\n\nuse crate::arg_type::ArgType;\nuse crate::starlark::values::ValueLike;\nuse allocat"
},
{
"path": "codex-rs/execpolicy/src/arg_resolver.rs",
"chars": 7087,
"preview": "use serde::Serialize;\n\nuse crate::arg_matcher::ArgMatcher;\nuse crate::arg_matcher::ArgMatcherCardinality;\nuse crate::err"
},
{
"path": "codex-rs/execpolicy/src/arg_type.rs",
"chars": 2863,
"preview": "#![allow(clippy::needless_lifetimes)]\n\nuse crate::error::Error;\nuse crate::error::Result;\nuse crate::sed_command::parse_"
},
{
"path": "codex-rs/execpolicy/src/default.policy",
"chars": 5307,
"preview": "\"\"\"\ndefine_program() supports the following arguments:\n- program: the name of the program\n- system_path: list of absolut"
},
{
"path": "codex-rs/execpolicy/src/error.rs",
"chars": 2128,
"preview": "use std::path::PathBuf;\n\nuse serde::Serialize;\n\nuse crate::arg_matcher::ArgMatcher;\nuse crate::arg_resolver::PositionalA"
},
{
"path": "codex-rs/execpolicy/src/exec_call.rs",
"chars": 621,
"preview": "use std::fmt::Display;\n\nuse serde::Serialize;\n\n#[derive(Clone, Debug, Eq, PartialEq, Serialize)]\npub struct ExecCall {\n "
},
{
"path": "codex-rs/execpolicy/src/execv_checker.rs",
"chars": 8811,
"preview": "use std::ffi::OsString;\nuse std::path::Path;\nuse std::path::PathBuf;\n\nuse crate::ArgType;\nuse crate::Error::CannotCanoni"
},
{
"path": "codex-rs/execpolicy/src/lib.rs",
"chars": 1130,
"preview": "#![allow(clippy::type_complexity)]\n#![allow(clippy::too_many_arguments)]\n#[macro_use]\nextern crate starlark;\n\nmod arg_ma"
},
{
"path": "codex-rs/execpolicy/src/main.rs",
"chars": 4901,
"preview": "use anyhow::Result;\nuse clap::Parser;\nuse clap::Subcommand;\nuse codex_execpolicy::ExecCall;\nuse codex_execpolicy::Matche"
},
{
"path": "codex-rs/execpolicy/src/opt.rs",
"chars": 2124,
"preview": "#![allow(clippy::needless_lifetimes)]\n\nuse crate::ArgType;\nuse crate::starlark::values::ValueLike;\nuse allocative::Alloc"
},
{
"path": "codex-rs/execpolicy/src/policy.rs",
"chars": 3426,
"preview": "use multimap::MultiMap;\nuse regex_lite::Error as RegexError;\nuse regex_lite::Regex;\n\nuse crate::ExecCall;\nuse crate::For"
},
{
"path": "codex-rs/execpolicy/src/policy_parser.rs",
"chars": 7636,
"preview": "#![allow(clippy::needless_lifetimes)]\n\nuse crate::Opt;\nuse crate::Policy;\nuse crate::ProgramSpec;\nuse crate::arg_matcher"
},
{
"path": "codex-rs/execpolicy/src/program.rs",
"chars": 8026,
"preview": "use serde::Serialize;\nuse std::collections::HashMap;\nuse std::collections::HashSet;\n\nuse crate::ArgType;\nuse crate::Exec"
},
{
"path": "codex-rs/execpolicy/src/sed_command.rs",
"chars": 497,
"preview": "use crate::error::Error;\nuse crate::error::Result;\n\npub fn parse_sed_command(sed_command: &str) -> Result<()> {\n // F"
},
{
"path": "codex-rs/execpolicy/src/valid_exec.rs",
"chars": 2625,
"preview": "use crate::arg_type::ArgType;\nuse crate::error::Result;\nuse serde::Serialize;\n\n/// exec() invocation that has been accep"
},
{
"path": "codex-rs/execpolicy/tests/all.rs",
"chars": 121,
"preview": "// Single integration test binary that aggregates all test modules.\n// The submodules live in `tests/suite/`.\nmod suite;"
},
{
"path": "codex-rs/execpolicy/tests/suite/bad.rs",
"chars": 365,
"preview": "use codex_execpolicy::NegativeExamplePassedCheck;\nuse codex_execpolicy::get_default_policy;\n\n#[test]\nfn verify_everythin"
},
{
"path": "codex-rs/execpolicy/tests/suite/cp.rs",
"chars": 2293,
"preview": "extern crate codex_execpolicy;\n\nuse codex_execpolicy::ArgMatcher;\nuse codex_execpolicy::ArgType;\nuse codex_execpolicy::E"
},
{
"path": "codex-rs/execpolicy/tests/suite/good.rs",
"chars": 366,
"preview": "use codex_execpolicy::PositiveExampleFailedCheck;\nuse codex_execpolicy::get_default_policy;\n\n#[test]\nfn verify_everythin"
},
{
"path": "codex-rs/execpolicy/tests/suite/head.rs",
"chars": 3904,
"preview": "use codex_execpolicy::ArgMatcher;\nuse codex_execpolicy::ArgType;\nuse codex_execpolicy::Error;\nuse codex_execpolicy::Exec"
},
{
"path": "codex-rs/execpolicy/tests/suite/literal.rs",
"chars": 1590,
"preview": "use codex_execpolicy::ArgType;\nuse codex_execpolicy::Error;\nuse codex_execpolicy::ExecCall;\nuse codex_execpolicy::Matche"
},
{
"path": "codex-rs/execpolicy/tests/suite/ls.rs",
"chars": 4934,
"preview": "extern crate codex_execpolicy;\n\nuse codex_execpolicy::ArgType;\nuse codex_execpolicy::Error;\nuse codex_execpolicy::ExecCa"
},
{
"path": "codex-rs/execpolicy/tests/suite/mod.rs",
"chars": 165,
"preview": "// Aggregates all former standalone integration tests as modules.\nmod bad;\nmod cp;\nmod good;\nmod head;\nmod literal;\nmod "
},
{
"path": "codex-rs/execpolicy/tests/suite/parse_sed_command.rs",
"chars": 545,
"preview": "use codex_execpolicy::Error;\nuse codex_execpolicy::parse_sed_command;\n\n#[test]\nfn parses_simple_print_command() {\n as"
},
{
"path": "codex-rs/execpolicy/tests/suite/pwd.rs",
"chars": 2059,
"preview": "extern crate codex_execpolicy;\n\nuse std::vec;\n\nuse codex_execpolicy::Error;\nuse codex_execpolicy::ExecCall;\nuse codex_ex"
},
{
"path": "codex-rs/execpolicy/tests/suite/sed.rs",
"chars": 2603,
"preview": "extern crate codex_execpolicy;\n\nuse codex_execpolicy::ArgType;\nuse codex_execpolicy::Error;\nuse codex_execpolicy::ExecCa"
},
{
"path": "codex-rs/file-search/Cargo.toml",
"chars": 435,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-file-search\"\nversion = { workspace = true }\n\n[[bin]]\nname = \"codex-file-search\""
},
{
"path": "codex-rs/file-search/README.md",
"chars": 364,
"preview": "# codex_file_search\n\nFast fuzzy file search tool for Codex.\n\nUses <https://crates.io/crates/ignore> under the hood (whic"
},
{
"path": "codex-rs/file-search/src/cli.rs",
"chars": 1289,
"preview": "use std::num::NonZero;\nuse std::path::PathBuf;\n\nuse clap::ArgAction;\nuse clap::Parser;\n\n/// Fuzzy matches filenames unde"
},
{
"path": "codex-rs/file-search/src/lib.rs",
"chars": 12983,
"preview": "use ignore::WalkBuilder;\nuse ignore::overrides::OverrideBuilder;\nuse nucleo_matcher::Matcher;\nuse nucleo_matcher::Utf32S"
},
{
"path": "codex-rs/file-search/src/main.rs",
"chars": 2712,
"preview": "use std::io::IsTerminal;\nuse std::path::Path;\n\nuse clap::Parser;\nuse codex_file_search::Cli;\nuse codex_file_search::File"
},
{
"path": "codex-rs/justfile",
"chars": 569,
"preview": "set positional-arguments\n\n# Display help\nhelp:\n just -l\n\n# `codex`\ncodex *args:\n cargo run --bin codex -- \"$@\"\n\n# "
},
{
"path": "codex-rs/linux-sandbox/Cargo.toml",
"chars": 680,
"preview": "[package]\nedition = \"2024\"\nname = \"codex-linux-sandbox\"\nversion = { workspace = true }\n\n[[bin]]\nname = \"codex-linux-sand"
},
{
"path": "codex-rs/linux-sandbox/README.md",
"chars": 455,
"preview": "# codex-linux-sandbox\n\nThis crate is responsible for producing:\n\n- a `codex-linux-sandbox` standalone executable for Lin"
}
]
// ... and 221 more files (download for full content)
About this extraction
This page contains the full source code of the Stanford-Trinity/ARTEMIS GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 421 files (7.0 MB), approximately 1.9M tokens, and a symbol index with 2993 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.