Commit Graph

29 Commits

  • Introduce --oss flag to use gpt-oss models (#1848)
    This adds support for easily running Codex backed by a local Ollama
    instance running our new open source models. See
    https://github.com/openai/gpt-oss for details.
    
    If you pass in `--oss` you'll be prompted to install/launch ollama, and
    it will automatically download the 20b model and attempt to use it.
    
    We'll likely want to expand this with some options later to make the
    experience smoother for users who can't run the 20b or want to run the
    120b.
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • Auto format toml (#1745)
    Add recommended extension and configure it to auto format prompt.
  • fix: correctly wrap history items (#1685)
    The overall idea here is: skip ratatui for writing into scrollback,
    because its primitives are wrong. We want to render full lines of text,
    that will be wrapped natively by the terminal, and which we never plan
    to update using ratatui (so the `Buffer` struct is overhead and in fact
    an inhibition).
    
    Instead, we use ANSI scrolling regions (link reference doc to come).
    Essentially, we:
    1. Define a scrolling region that extends from the top of the prompt
    area all the way to the top of scrollback
    2. Scroll that region up by N < (screen_height - viewport_height) lines,
    in this PR N=1
    3. Put our cursor at the top of the newly empty region
    4. Print out our new text like normal
    
    The terminal interactions here (write_spans and its dependencies) are
    mostly extracted from ratatui.
  • feat: standalone file search CLI (#1386)
    Standalone fuzzy filename search library that should be helpful in
    addressing https://github.com/openai/codex/issues/1261.
  • feat: add support for login with ChatGPT (#1212)
    This does not implement the full Login with ChatGPT experience, but it
    should unblock people.
    
    **What works**
    
    * The `codex` multitool now has a `login` subcommand, so you can run
    `codex login`, which should write `CODEX_HOME/auth.json` if you complete
    the flow successfully. The TUI will now read the `OPENAI_API_KEY` from
    `auth.json`.
    * The TUI should refresh the token if it has expired and the necessary
    information is in `auth.json`.
    * There is a `LoginScreen` in the TUI that tells you to run `codex
    login` if both (1) your model provider expects to use `OPENAI_API_KEY`
    as its env var, and (2) `OPENAI_API_KEY` is not set.
    
    **What does not work**
    
    * The `LoginScreen` does not support the login flow from within the TUI.
    Instead, it tells you to quit, run `codex login`, and then run `codex`
    again.
    * `codex exec` does read from `auth.json` yet, nor does it direct the
    user to go through the login flow if `OPENAI_API_KEY` is not be found.
    * The `maybeRedeemCredits()` function from `get-api-key.tsx` has not
    been ported from TypeScript to `login_with_chatgpt.py` yet:
    
    
    https://github.com/openai/codex/blob/a67a67f3258fc21e147b6786a143fe3e15e6d5ba/codex-cli/src/utils/get-api-key.tsx#L84-L89
    
    **Implementation**
    
    Currently, the OAuth flow requires running a local webserver on
    `127.0.0.1:1455`. It seemed wasteful to incur the additional binary cost
    of a webserver dependency in the Rust CLI just to support login, so
    instead we implement this logic in Python, as Python has a `http.server`
    module as part of its standard library. Specifically, we bundle the
    contents of a single Python file as a string in the Rust CLI and then
    use it to spawn a subprocess as `python3 -c
    {{SOURCE_FOR_PYTHON_SERVER}}`.
    
    As such, the most significant files in this PR are:
    
    ```
    codex-rs/login/src/login_with_chatgpt.py
    codex-rs/login/src/lib.rs
    ```
    
    Now that the CLI may load `OPENAI_API_KEY` from the environment _or_
    `CODEX_HOME/auth.json`, we need a new abstraction for reading/writing
    this variable, so we introduce:
    
    ```
    codex-rs/core/src/openai_api_key.rs
    ```
    
    Note that `std::env::set_var()` is [rightfully] `unsafe` in Rust 2024,
    so we use a LazyLock<RwLock<Option<String>>> to store `OPENAI_API_KEY`
    so it is read in a thread-safe manner.
    
    Ultimately, it should be possible to go through the entire login flow
    from the TUI. This PR introduces a placeholder `LoginScreen` UI for that
    right now, though the new `codex login` subcommand introduced in this PR
    should be a viable workaround until the UI is ready.
    
    **Testing**
    
    Because the login flow is currently implemented in a standalone Python
    file, you can test it without building any Rust code as follows:
    
    ```
    rm -rf /tmp/codex_home && mkdir /tmp/codex_home
    CODEX_HOME=/tmp/codex_home python3 codex-rs/login/src/login_with_chatgpt.py
    ```
    
    For reference:
    
    * the original TypeScript implementation was introduced in
    https://github.com/openai/codex/pull/963
    * support for redeeming credits was later added in
    https://github.com/openai/codex/pull/974
  • fix: overhaul how we spawn commands under seccomp/landlock on Linux (#1086)
    Historically, we spawned the Seatbelt and Landlock sandboxes in
    substantially different ways:
    
    For **Seatbelt**, we would run `/usr/bin/sandbox-exec` with our policy
    specified as an arg followed by the original command:
    
    
    https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/core/src/exec.rs#L147-L219
    
    For **Landlock/Seccomp**, we would do
    `tokio::runtime::Builder::new_current_thread()`, _invoke
    Landlock/Seccomp APIs to modify the permissions of that new thread_, and
    then spawn the command:
    
    
    https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/core/src/exec_linux.rs#L28-L49
    
    While it is neat that Landlock/Seccomp supports applying a policy to
    only one thread without having to apply it to the entire process, it
    requires us to maintain two different codepaths and is a bit harder to
    reason about. The tipping point was
    https://github.com/openai/codex/pull/1061, in which we had to start
    building up the `env` in an unexpected way for the existing
    Landlock/Seccomp approach to continue to work.
    
    This PR overhauls things so that we do similar things for Mac and Linux.
    It turned out that we were already building our own "helper binary"
    comparable to Mac's `sandbox-exec` as part of the `cli` crate:
    
    
    https://github.com/openai/codex/blob/d1de7bb383552e8fadd94be79d65d188e00fd562/codex-rs/cli/Cargo.toml#L10-L12
    
    We originally created this to build a small binary to include with the
    Node.js version of the Codex CLI to provide support for Linux
    sandboxing.
    
    Though the sticky bit is that, at this point, we still want to deploy
    the Rust version of Codex as a single, standalone binary rather than a
    CLI and a supporting sandboxing binary. To satisfy this goal, we use
    "the arg0 trick," in which we:
    
    * use `std::env::current_exe()` to get the path to the CLI that is
    currently running
    * use the CLI as the `program` for the `Command`
    * set `"codex-linux-sandbox"` as arg0 for the `Command`
    
    A CLI that supports sandboxing should check arg0 at the start of the
    program. If it is `"codex-linux-sandbox"`, it must invoke
    `codex_linux_sandbox::run_main()`, which runs the CLI as if it were
    `codex-linux-sandbox`. When acting as `codex-linux-sandbox`, we make the
    appropriate Landlock/Seccomp API calls and then use `execvp(3)` to spawn
    the original command, so do _replace_ the process rather than spawn a
    subprocess. Incidentally, we do this before starting the Tokio runtime,
    so the process should only have one thread when `execvp(3)` is called.
    
    Because the `core` crate that needs to spawn the Linux sandboxing is not
    a CLI in its own right, this means that every CLI that includes `core`
    and relies on this behavior has to (1) implement it and (2) provide the
    path to the sandboxing executable. While the path is almost always
    `std::env::current_exe()`, we needed to make this configurable for
    integration tests, so `Config` now has a `codex_linux_sandbox_exe:
    Option<PathBuf>` property to facilitate threading this through,
    introduced in https://github.com/openai/codex/pull/1089.
    
    This common pattern is now captured in
    `codex_linux_sandbox::run_with_sandbox()` and all of the `main.rs`
    functions that should use it have been updated as part of this PR.
    
    The `codex-linux-sandbox` crate added to the Cargo workspace as part of
    this PR now has the bulk of the Landlock/Seccomp logic, which makes
    `core` a bit simpler. Indeed, `core/src/exec_linux.rs` and
    `core/src/landlock.rs` were removed/ported as part of this PR. I also
    moved the unit tests for this code into an integration test,
    `linux-sandbox/tests/landlock.rs`, in which I use
    `env!("CARGO_BIN_EXE_codex-linux-sandbox")` as the value for
    `codex_linux_sandbox_exe` since `std::env::current_exe()` is not
    appropriate in that case.
  • Disallow expect via lints (#865)
    Adds `expect()` as a denied lint. Same deal applies with `unwrap()`
    where we now need to put `#[expect(...` on ones that we legit want. Took
    care to enable `expect()` in test contexts.
    
    # Tests
    
    ```
    cargo fmt
    cargo clippy --all-features --all-targets --no-deps -- -D warnings
    cargo test
    ```
  • Workspace lints and disallow unwrap (#855)
    Sets submodules to use workspace lints. Added denying unwrap as a
    workspace level lint, which found a couple of cases where we could have
    propagated errors. Also manually labeled ones that were fine by my eye.
  • Update cargo to 2024 edition (#842)
    Some effects of this change:
    - New formatting changes across many files. No functionality changes
    should occur from that.
    - Calls to `set_env` are considered unsafe, since this only happens in
    tests we wrap them in `unsafe` blocks
  • chore: introduce codex-common crate (#843)
    I started this PR because I wanted to share the `format_duration()`
    utility function in `codex-rs/exec/src/event_processor.rs` with the TUI.
    The question was: where to put it?
    
    `core` should have as few dependencies as possible, so moving it there
    would introduce a dependency on `chrono`, which seemed undesirable.
    `core` already had this `cli` feature to deal with a similar situation
    around sharing common utility functions, so I decided to:
    
    * make `core` feature-free
    * introduce `common`
    * `common` can have as many "special interest" features as it needs,
    each of which can declare their own deps
    * the first two features of common are `cli` and `elapsed`
    
    In practice, this meant updating a number of `Cargo.toml` files,
    replacing this line:
    
    ```toml
    codex-core = { path = "../core", features = ["cli"] }
    ```
    
    with these:
    
    ```toml
    codex-core = { path = "../core" }
    codex-common = { path = "../common", features = ["cli"] }
    ```
    
    Moving `format_duration()` into its own file gave it some "breathing
    room" to add a unit test, so I had Codex generate some tests and new
    support for durations over 1 minute.
  • feat: initial McpClient for Rust (#822)
    This PR introduces an initial `McpClient` that we will use to give Codex
    itself programmatic access to foreign MCPs. This does not wire it up in
    Codex itself yet, but the new `mcp-client` crate includes a `main.rs`
    for basic testing for now.
    
    Manually tested by sending a `tools/list` request to Codex's own MCP
    server:
    
    ```
    codex-rs$ cargo build
    codex-rs$ cargo run --bin codex-mcp-client ./target/debug/codex-mcp-server
    {
      "tools": [
        {
          "description": "Run a Codex session. Accepts configuration parameters matching the Codex Config struct.",
          "inputSchema": {
            "properties": {
              "approval-policy": {
                "description": "Execution approval policy expressed as the kebab-case variant name (`unless-allow-listed`, `auto-edit`, `on-failure`, `never`).",
                "enum": [
                  "auto-edit",
                  "unless-allow-listed",
                  "on-failure",
                  "never"
                ],
                "type": "string"
              },
              "cwd": {
                "description": "Working directory for the session. If relative, it is resolved against the server process's current working directory.",
                "type": "string"
              },
              "disable-response-storage": {
                "description": "Disable server-side response storage.",
                "type": "boolean"
              },
              "model": {
                "description": "Optional override for the model name (e.g. \"o3\", \"o4-mini\")",
                "type": "string"
              },
              "prompt": {
                "description": "The *initial user prompt* to start the Codex conversation.",
                "type": "string"
              },
              "sandbox-permissions": {
                "description": "Sandbox permissions using the same string values accepted by the CLI (e.g. \"disk-write-cwd\", \"network-full-access\").",
                "items": {
                  "enum": [
                    "disk-full-read-access",
                    "disk-write-cwd",
                    "disk-write-platform-user-temp-folder",
                    "disk-write-platform-global-temp-folder",
                    "disk-full-write-access",
                    "network-full-access"
                  ],
                  "type": "string"
                },
                "type": "array"
              }
            },
            "required": [
              "prompt"
            ],
            "type": "object"
          },
          "name": "codex"
        }
      ]
    }
    ```
  • feat: introduce mcp-server crate (#792)
    This introduces the `mcp-server` crate, which contains a barebones MCP
    server that provides an `echo` tool that echoes the user's request back
    to them.
    
    To test it out, I launched
    [modelcontextprotocol/inspector](https://github.com/modelcontextprotocol/inspector)
    like so:
    
    ```
    mcp-server$ npx @modelcontextprotocol/inspector cargo run --
    ```
    
    and opened up `http://127.0.0.1:6274` in my browser:
    
    
    ![image](https://github.com/user-attachments/assets/83fc55d4-25c2-4497-80cd-e9702283ff93)
    
    I also had to make a small fix to `mcp-types`, adding
    `#[serde(untagged)]` to a number of `enum`s.
  • feat: introduce mcp-types crate (#787)
    This adds our own `mcp-types` crate to our Cargo workspace. We vendor in
    the
    [`2025-03-26/schema.json`](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/05f204513641c05bd78d056791af99c6c84520fa/schema/2025-03-26/schema.json)
    from the MCP repo and introduce a `generate_mcp_types.py` script to
    codegen the `lib.rs` from the JSON schema.
    
    Test coverage is currently light, but I plan to refine things as we
    start making use of this crate.
    
    And yes, I am aware that
    https://github.com/modelcontextprotocol/rust-sdk exists, though the
    published https://crates.io/crates/rmcp appears to be a competing
    effort. While things are up in the air, it seems better for us to
    control our own version of this code.
    
    Incidentally, Codex did a lot of the work for this PR. I told it to
    never edit `lib.rs` directly and instead to update
    `generate_mcp_types.py` and then re-run it to update `lib.rs`. It
    followed these instructions and once things were working end-to-end, I
    iteratively asked for changes to the tests until the API looked
    reasonable (and the code worked). Codex was responsible for figuring out
    what to do to `generate_mcp_types.py` to achieve the requested test/API
    changes.
  • chore: script to create a Rust release (#759)
    For now, keep things simple such that we never update the `version` in
    the `Cargo.toml` for the workspace root on the `main` branch. Instead,
    create a new branch for a release, push one commit that updates the
    `version`, and then tag that branch to kick off a release.
    
    To test, I ran this script and created this release job:
    
    https://github.com/openai/codex/actions/runs/14762580641
  • chore: Rust release, set prerelease:false and version=0.0.2504301132 (#755)
    The generated DotSlash file has URLs that refer to
    `https://github.com/openai/codex/releases/`, so let's set
    `prerelease:false` (but keep `draft:true` for now) so those URLs should
    work.
    
    Also updated `version` in Cargo workspace so I will kick off a build
    once this lands.
  • chore: remove the REPL crate/subcommand (#754)
    @oai-ragona and I discussed it, and we feel the REPL crate has served
    its purpose, so we're going to delete the code and future archaeologists
    can find it in Git history.
  • chore: fix errors in .github/workflows/rust-release.yml and prep 0.0.2504292006 release (#745)
    Apparently I made two key mistakes in
    https://github.com/openai/codex/pull/740 (fixed in this PR):
    
    * I forgot to redefine `$dest` in the `Stage Linux-only artifacts` step
    * I did not define the `if` check correctly in the `Stage Linux-only
    artifacts` step
    
    This fixes both of those issues and bumps the workspace version to
    `0.0.2504292006` in preparation for another release attempt.
  • feat: codex-linux-sandbox standalone executable (#740)
    This introduces a standalone executable that run the equivalent of the
    `codex debug landlock` subcommand and updates `rust-release.yml` to
    include it in the release.
    
    The idea is that we will include this small binary with the TypeScript
    CLI to provide support for Linux sandboxing.
  • [codex-rs] Add rust-release action (#671)
    Taking a pass at building artifacts per platform so we can consider
    different distribution strategies that don't require users to install
    the full `cargo` toolchain.
    
    Right now this grabs just the `codex-repl` and `codex-tui` bins for 5
    different targets and bundles them into a draft release. I think a
    clearly marked pre-release set of artifacts will unblock the next step
    of testing.
  • fix: make the TUI the default/"interactive" CLI in Rust (#711)
    Originally, the `interactive` crate was going to be a placeholder for
    building out a UX that was comparable to that of the existing TypeScript
    CLI. Though after researching how Ratatui works, that seems difficult to
    do because it is designed around the idea that it will redraw the full
    screen buffer each time (and so any scrolling should be "internal" to
    your Ratatui app) whereas the TypeScript CLI expects to render the full
    history of the conversation every time(*) (which is why you can use your
    terminal scrollbar to scroll it).
    
    While it is possible to use Ratatui in a way that acts more like what
    the TypeScript CLI is doing, it is awkward and seemingly results in
    tedious code, so I think we should abandon that approach. As such, this
    PR deletes the `interactive/` folder and the code that depended on it.
    
    Further, since we added support for mousewheel scrolling in the TUI in
    https://github.com/openai/codex/pull/641, it certainly feels much better
    and the need for scroll support via the terminal scrollbar is greatly
    diminished. This is now a more appropriate default UX for the
    "multitool" CLI.
    
    (*) Incidentally, I haven't verified this, but I think this results in
    O(N^2) work in rendering, which seems potentially problematic for long
    conversations.
  • feat: introduce codex_execpolicy crate for defining "safe" commands (#634)
    As described in detail in `codex-rs/execpolicy/README.md` introduced in
    this PR, `execpolicy` is a tool that lets you define a set of _patterns_
    used to match [`execv(3)`](https://linux.die.net/man/3/execv)
    invocations. When a pattern is matched, `execpolicy` returns the parsed
    version in a structured form that is amenable to static analysis.
    
    The primary use case is to define patterns match commands that should be
    auto-approved by a tool such as Codex. This supports a richer pattern
    matching mechanism that the sort of prefix-matching we have done to
    date, e.g.:
    
    
    https://github.com/openai/codex/blob/5e40d9d2211737f46136610497bcd9a8271009e0/codex-cli/src/approvals.ts#L333-L354
    
    Note we are still playing with the API and the `system_path` option in
    particular still needs some work.
  • feat: initial import of Rust implementation of Codex CLI in codex-rs/ (#629)
    As stated in `codex-rs/README.md`:
    
    Today, Codex CLI is written in TypeScript and requires Node.js 22+ to
    run it. For a number of users, this runtime requirement inhibits
    adoption: they would be better served by a standalone executable. As
    maintainers, we want Codex to run efficiently in a wide range of
    environments with minimal overhead. We also want to take advantage of
    operating system-specific APIs to provide better sandboxing, where
    possible.
    
    To that end, we are moving forward with a Rust implementation of Codex
    CLI contained in this folder, which has the following benefits:
    
    - The CLI compiles to small, standalone, platform-specific binaries.
    - Can make direct, native calls to
    [seccomp](https://man7.org/linux/man-pages/man2/seccomp.2.html) and
    [landlock](https://man7.org/linux/man-pages/man7/landlock.7.html) in
    order to support sandboxing on Linux.
    - No runtime garbage collection, resulting in lower memory consumption
    and better, more predictable performance.
    
    Currently, the Rust implementation is materially behind the TypeScript
    implementation in functionality, so continue to use the TypeScript
    implmentation for the time being. We will publish native executables via
    GitHub Releases as soon as we feel the Rust version is usable.