Commit Graph

52 Commits

  • Split features into codex-features crate (#15253)
    - Split the feature system into a new `codex-features` crate.
    - Cut `codex-core` and workspace consumers over to the new config and
    warning APIs.
    
    Co-authored-by: Ahmed Ibrahim <219906144+aibrahim-oai@users.noreply.github.com>
    Co-authored-by: Codex <noreply@openai.com>
  • Move terminal module to terminal-detection crate (#15216)
    - Move core/src/terminal.rs and its tests into a standalone
    terminal-detection workspace crate.
    - Update direct consumers to depend on codex-terminal-detection and
    import terminal APIs directly.
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • Move TUI on top of app server (parallel code) (#14717)
    This PR replicates the `tui` code directory and creates a temporary
    parallel `tui_app_server` directory. It also implements a new feature
    flag `tui_app_server` to select between the two tui implementations.
    
    Once the new app-server-based TUI is stabilized, we'll delete the old
    `tui` directory and feature flag.
  • feat: add auth login diagnostics (#13797)
    ## Problem
    
    Browser login failures historically leave support with an incomplete
    picture. HARs can show that the browser completed OAuth and reached the
    localhost callback, but they do not explain why the native client failed
    on the final `/oauth/token` exchange. Direct `codex login` also relied
    mostly on terminal stderr and the browser error page, so even when the
    login crate emitted better sign-in diagnostics through TUI or app-server
    flows, the one-shot CLI path still did not leave behind an easy artifact
    to collect.
    
    ## Mental model
    
    This implementation treats the browser page, the returned `io::Error`,
    and the normal structured log as separate surfaces with different safety
    requirements. The browser page and returned error preserve the detail
    that operators need to diagnose failures. The structured log stays
    narrower: it records reviewed lifecycle events, parsed safe fields, and
    redacted transport errors without becoming a sink for secrets or
    arbitrary backend bodies.
    
    Direct `codex login` now adds a fourth support surface: a small
    file-backed log at `codex-login.log` under the configured `log_dir`.
    That artifact carries the same login-target events as the other
    entrypoints without changing the existing stderr/browser UX.
    
    ## Non-goals
    
    This does not add auth logging to normal runtime requests, and it does
    not try to infer precise transport root causes from brittle string
    matching. The scope remains the browser-login callback flow in the
    `login` crate plus a direct-CLI wrapper that persists those events to
    disk.
    
    This also does not try to reuse the TUI logging stack wholesale. The TUI
    path initializes feedback, OpenTelemetry, and other session-oriented
    layers that are useful for an interactive app but unnecessary for a
    one-shot login command.
    
    ## Tradeoffs
    
    The implementation favors fidelity for caller-visible errors and
    restraint for persistent logs. Parsed JSON token-endpoint errors are
    logged safely by field. Non-JSON token-endpoint bodies remain available
    to the returned error so CLI and browser surfaces still show backend
    detail. Transport errors keep their real `reqwest` message, but attached
    URLs are surgically redacted. Custom issuer URLs are sanitized before
    logging.
    
    On the CLI side, the code intentionally duplicates a narrow slice of the
    TUI file-logging setup instead of sharing the full initializer. That
    keeps `codex login` easy to reason about and avoids coupling it to
    interactive-session layers that the command does not need.
    
    ## Architecture
    
    The core auth behavior lives in `codex-rs/login/src/server.rs`. The
    callback path now logs callback receipt, callback validation,
    token-exchange start, token-exchange success, token-endpoint non-2xx
    responses, and transport failures. App-server consumers still use this
    same login-server path via `run_login_server(...)`, so the same
    instrumentation benefits TUI, Electron, and VS Code extension flows.
    
    The direct CLI path in `codex-rs/cli/src/login.rs` now installs a small
    file-backed tracing layer for login commands only. That writes
    `codex-login.log` under `log_dir` with login-specific targets such as
    `codex_cli::login` and `codex_login::server`.
    
    ## Observability
    
    The main signals come from the `login` crate target and are
    intentionally scoped to sign-in. Structured logs include redacted issuer
    URLs, redacted transport errors, HTTP status, and parsed token-endpoint
    fields when available. The callback-layer log intentionally avoids
    `%err` on token-endpoint failures so arbitrary backend bodies do not get
    copied into the normal log file.
    
    Direct `codex login` now leaves a durable artifact for both failure and
    success cases. Example output from the new file-backed CLI path:
    
    Failing callback:
    
    ```text
    2026-03-06T22:08:54.143612Z  INFO codex_cli::login: starting browser login flow
    2026-03-06T22:09:03.431699Z  INFO codex_login::server: received login callback path=/auth/callback has_code=false has_state=true has_error=true state_valid=true
    2026-03-06T22:09:03.431745Z  WARN codex_login::server: oauth callback returned error error_code="access_denied" has_error_description=true
    ```
    
    Succeeded callback and token exchange:
    
    ```text
    2026-03-06T22:09:14.065559Z  INFO codex_cli::login: starting browser login flow
    2026-03-06T22:09:36.431678Z  INFO codex_login::server: received login callback path=/auth/callback has_code=true has_state=true has_error=false state_valid=true
    2026-03-06T22:09:36.436977Z  INFO codex_login::server: starting oauth token exchange issuer=https://auth.openai.com/ redirect_uri=http://localhost:1455/auth/callback
    2026-03-06T22:09:36.685438Z  INFO codex_login::server: oauth token exchange succeeded status=200 OK
    ```
    
    ## Tests
    
    - `cargo test -p codex-login`
    - `cargo clippy -p codex-login --tests -- -D warnings`
    - `cargo test -p codex-cli`
    - `just bazel-lock-update`
    - `just bazel-lock-check`
    - manual direct `codex login` smoke tests for both a failing callback
    and a successful browser login
    
    ---------
    
    Co-authored-by: Codex <noreply@openai.com>
  • feat: add debug clear-memories command to hard-wipe memories state (#13085)
    #### what
    adds a `codex debug clear-memories` command to help with clearing all
    memories state from disk, sqlite db, and marking threads as
    `memory_mode=disabled` so they don't get resummarized when the
    `memories` feature is re-enabled.
    
    #### tests
    add tests
  • chore: move config diagnostics out of codex-core (#12427)
    ## Why
    
    Compiling `codex-rs/core` is a bottleneck for local iteration, so this
    change continues the ongoing extraction of config-related functionality
    out of `codex-core` and into `codex-config`.
    
    The goal is not just to move code, but to reduce `codex-core` ownership
    and indirection so more code depends on `codex-config` directly.
    
    ## What Changed
    
    - Moved config diagnostics logic from
    `core/src/config_loader/diagnostics.rs` into
    `config/src/diagnostics.rs`.
    - Updated `codex-core` to use `codex-config` diagnostics types/functions
    directly where possible.
    - Removed the `core/src/config_loader/diagnostics.rs` shim module
    entirely; the remaining `ConfigToml`-specific calls are in
    `core/src/config_loader/mod.rs`.
    - Moved `CONFIG_TOML_FILE` into `codex-config` and updated existing
    references to use `codex_config::CONFIG_TOML_FILE` directly.
    - Added a direct `codex-config` dependency to `codex-cli` for its
    `CONFIG_TOML_FILE` use.
  • feat: split codex-common into smaller utils crates (#11422)
    We are removing feature-gated shared crates from the `codex-rs`
    workspace. `codex-common` grouped several unrelated utilities behind
    `[features]`, which made dependency boundaries harder to reason about
    and worked against the ongoing effort to eliminate feature flags from
    workspace crates.
    
    Splitting these utilities into dedicated crates under `utils/` aligns
    this area with existing workspace structure and keeps each dependency
    explicit at the crate boundary.
    
    ## What changed
    
    - Removed `codex-rs/common` (`codex-common`) from workspace members and
    workspace dependencies.
    - Added six new utility crates under `codex-rs/utils/`:
      - `codex-utils-cli`
      - `codex-utils-elapsed`
      - `codex-utils-sandbox-summary`
      - `codex-utils-approval-presets`
      - `codex-utils-oss`
      - `codex-utils-fuzzy-match`
    - Migrated the corresponding modules out of `codex-common` into these
    crates (with tests), and added matching `BUILD.bazel` targets.
    - Updated direct consumers to use the new crates instead of
    `codex-common`:
      - `codex-rs/cli`
      - `codex-rs/tui`
      - `codex-rs/exec`
      - `codex-rs/app-server`
      - `codex-rs/mcp-server`
      - `codex-rs/chatgpt`
      - `codex-rs/cloud-tasks`
    - Updated workspace lockfile entries to reflect the new dependency graph
    and removal of `codex-common`.
  • chore: add codex debug app-server tooling (#10367)
    codex debug app-server <user message> forwards the message through
    codex-app-server-test-client’s send_message_v2 library entry point,
    using std::env::current_exe() to resolve the codex binary.
    
    for how it looks like, see:
    
    ```
    celia@com-92114 codex-rs % cargo build -p codex-cli && target/debug/codex debug app-server --help                       
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.34s
    Tooling: helps debug the app server
    
    Usage: codex debug app-server [OPTIONS] <COMMAND>
    
    Commands:
      send-message-v2  
      help             Print this message or the help of the given subcommand(s)
    ````
    and
    ```
    celia@com-92114 codex-rs % cargo build -p codex-cli && target/debug/codex debug app-server send-message-v2 "hello world"
       Compiling codex-cli v0.0.0 (/Users/celia/code/codex/codex-rs/cli)
        Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.38s
    > {
    >   "method": "initialize",
    >   "id": "f8ba9f60-3a49-4ea9-81d6-4ab6853e3954",
    >   "params": {
    >     "clientInfo": {
    >       "name": "codex-toy-app-server",
    >       "title": "Codex Toy App Server",
    >       "version": "0.0.0"
    >     },
    >     "capabilities": {
    >       "experimentalApi": true
    >     }
    >   }
    > }
    < {
    <   "id": "f8ba9f60-3a49-4ea9-81d6-4ab6853e3954",
    <   "result": {
    <     "userAgent": "codex-toy-app-server/0.0.0 (Mac OS 26.2.0; arm64) vscode/2.4.27 (codex-toy-app-server; 0.0.0)"
    <   }
    < }
    < initialize response: InitializeResponse { user_agent: "codex-toy-app-server/0.0.0 (Mac OS 26.2.0; arm64) vscode/2.4.27 (codex-toy-app-server; 0.0.0)" }
    > {
    >   "method": "thread/start",
    >   "id": "203f1630-beee-4e60-b17b-9eff16b1638b",
    >   "params": {
    >     "model": null,
    >     "modelProvider": null,
    >     "cwd": null,
    >     "approvalPolicy": null,
    >     "sandbox": null,
    >     "config": null,
    >     "baseInstructions": null,
    >     "developerInstructions": null,
    >     "personality": null,
    >     "ephemeral": null,
    >     "dynamicTools": null,
    >     "mockExperimentalField": null,
    >     "experimentalRawEvents": false
    >   }
    > }
    ...
    ```
  • Add codex app macOS launcher (#10418)
    - Add `codex app <path>` to launch the Codex Desktop app.
    - On macOS, auto-downloads the DMG if missing; non-macOS prints a link
    to chatgpt.com/codex.
  • feat(tui): retire the tui2 experiment (#9640)
    ## Summary
    - Retire the experimental TUI2 implementation and its feature flag.
    - Remove TUI2-only config/schema/docs so the CLI stays on the
    terminal-native path.
    - Keep docs aligned with the legacy TUI while we focus on redraw-based
    improvements.
    
    ## Customer impact
    - Retires the TUI2 experiment and keeps Codex on the proven
    terminal-native UI while we invest in redraw-based improvements to the
    existing experience.
    
    ## Migration / compatibility
    - If you previously set tui2-related options in config.toml, they are
    now ignored and Codex continues using the existing terminal-native TUI
    (no action required).
    
    ## Context
    - What worked: a transcript-owned viewport delivered excellent resize
    rewrap and high-fidelity copy (especially for code).
    - Why stop: making that experience feel fully native across the
    environment matrix (terminal emulator, OS, input modality, multiplexer,
    font/theme, alt-screen behavior) creates a combinatorial explosion of
    edge cases.
    - What next: we are focusing on redraw-based improvements to the
    existing terminal-native TUI so scrolling, selection, and copy remain
    native while resize/redraw correctness improves.
    
    ## Testing
    - just write-config-schema
    - just fmt
    - cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs
    -p codex-core
    - cargo clippy --fix --all-features --tests --allow-dirty --allow-no-vcs
    -p codex-cli
    - cargo check
    - cargo test -p codex-core
    - cargo test -p codex-cli
  • fix: remove existing process hardening from Codex CLI (#8951)
    As explained in https://github.com/openai/codex/issues/8945 and
    https://github.com/openai/codex/issues/8472, there are legitimate cases
    where users expect processes spawned by Codex to inherit environment
    variables such as `LD_LIBRARY_PATH` and `DYLD_LIBRARY_PATH`, where
    failing to do so can cause significant performance issues.
    
    This PR removes the use of
    `codex_process_hardening::pre_main_hardening()` in Codex CLI (which was
    added not in response to a known security issue, but because it seemed
    like a prudent thing to do from a security perspective:
    https://github.com/openai/codex/pull/4521), but we will continue to use
    it in `codex-responses-api-proxy`. At some point, we probably want to
    introduce a slightly different version of
    `codex_process_hardening::pre_main_hardening()` in Codex CLI that
    excludes said environment variables from the Codex process itself, but
    continues to propagate them to subprocesses.
  • feat: introduce codex-utils-cargo-bin as an alternative to assert_cmd::Command (#8496)
    This PR introduces a `codex-utils-cargo-bin` utility crate that
    wraps/replaces our use of `assert_cmd::Command` and
    `escargot::CargoBuild`.
    
    As you can infer from the introduction of `buck_project_root()` in this
    PR, I am attempting to make it possible to build Codex under
    [Buck2](https://buck2.build) as well as `cargo`. With Buck2, I hope to
    achieve faster incremental local builds (largely due to Buck2's
    [dice](https://buck2.build/docs/insights_and_knowledge/modern_dice/)
    build strategy, as well as benefits from its local build daemon) as well
    as faster CI builds if we invest in remote execution and caching.
    
    See
    https://buck2.build/docs/getting_started/what_is_buck2/#why-use-buck2-key-advantages
    for more details about the performance advantages of Buck2.
    
    Buck2 enforces stronger requirements in terms of build and test
    isolation. It discourages assumptions about absolute paths (which is key
    to enabling remote execution). Because the `CARGO_BIN_EXE_*` environment
    variables that Cargo provides are absolute paths (which
    `assert_cmd::Command` reads), this is a problem for Buck2, which is why
    we need this `codex-utils-cargo-bin` utility.
    
    My WIP-Buck2 setup sets the `CARGO_BIN_EXE_*` environment variables
    passed to a `rust_test()` build rule as relative paths.
    `codex-utils-cargo-bin` will resolve these values to absolute paths,
    when necessary.
    
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/8496).
    * #8498
    * __->__ #8496
  • chore: enusre the logic that creates ConfigLayerStack has access to cwd (#8353)
    `load_config_layers_state()` should load config from a
    `.codex/config.toml` in any folder between the `cwd` for a thread and
    the project root. Though in order to do that,
    `load_config_layers_state()` needs to know what the `cwd` is, so this PR
    does the work to thread the `cwd` through for existing callsites.
    
    A notable exception is the `/config` endpoint in app server for which a
    `cwd` is not guaranteed to be associated with the query, so the `cwd`
    param is `Option<AbsolutePathBuf>` to account for this case.
    
    The logic to make use of the `cwd` will be done in a follow-up PR.
  • feat(tui2): add feature-flagged tui2 frontend (#7793)
    Introduce a new codex-tui2 crate that re-exports the existing
    interactive TUI surface and delegates run_main directly to codex-tui.
    This keeps behavior identical while giving tui2 its own crate for future
    viewport work.
    
    Wire the codex CLI to select the frontend via the tui2 feature flag.
    When the merged CLI overrides include features.tui2=true (e.g. via
    --enable tui2), interactive runs are routed through
    codex_tui2::run_main; otherwise they continue to use the original
    codex_tui::run_main.
    
    Register Feature::Tui2 in the core feature registry and add the tui2
    crate and dependency entries so the new frontend builds alongside the
    existing TUI.
    
    This is a stub that only wires up the feature flag for this.
    
    <img width="619" height="364" alt="image"
    src="https://github.com/user-attachments/assets/4893f030-932f-471e-a443-63fe6b5d8ed9"
    />
  • chore: add cargo-deny configuration (#7119)
    - add GitHub workflow running cargo-deny on push/PR
    - document cargo-deny allowlist with workspace-dep notes and advisory
    ignores
    - align workspace crates to inherit version/edition/license for
    consistent checks
  • execpolicycheck command in codex cli (#7012)
    adding execpolicycheck tool onto codex cli
    
    this is useful for validating policies (can be multiple) against
    commands.
    
    it will also surface errors in policy syntax:
    <img width="1150" height="281" alt="Screenshot 2025-11-19 at 12 46
    21 PM"
    src="https://github.com/user-attachments/assets/8f99b403-564c-4172-acc9-6574a8d13dc3"
    />
    
    this PR also changes output format when there's no match in the CLI.
    instead of returning the raw string `noMatch`, we return
    `{"noMatch":{}}`
    
    this PR is a rewrite of: https://github.com/openai/codex/pull/6932 (due
    to the numerous merge conflicts present in the original PR)
    
    ---------
    
    Co-authored-by: Michael Bolin <mbolin@openai.com>
  • add codex debug seatbelt --log-denials (#4098)
    This adds a debugging tool for analyzing why certain commands fail to
    execute under the sandbox.
    
    Example output:
    
    ```
    $ codex debug seatbelt --log-denials bash -lc "(echo foo > ~/foo.txt)"
    bash: /Users/nornagon/foo.txt: Operation not permitted
    
    === Sandbox denials ===
    (bash) file-write-data /dev/tty
    (bash) file-write-data /dev/ttys001
    (bash) sysctl-read kern.ngroups
    (bash) file-write-create /Users/nornagon/foo.txt
    ```
    
    It operates by:
    
    1. spawning `log stream` to watch system logs, and
    2. tracking all descendant PIDs using kqueue + proc_listchildpids.
    
    this is a "best-effort" technique, as `log stream` may drop logs(?), and
    kqueue + proc_listchildpids isn't atomic and can end up missing very
    short-lived processes. But it works well enough in my testing to be
    useful :)
  • fix: use generate_ts from app_server_protocol (#6407)
    Update `codex generate-ts` to use the TS export code from
    `app-server-protocol/src/export.rs`.
    
    I realized there were two duplicate implementations of Typescript export
    code:
    - `app-server-protocol/src/export.rs`
    - the `codex-protocol-ts` crate
    
    The `codex-protocol-ts` crate that `codex generate-ts` uses is out of
    date now since it doesn't handle the V2 namespace from:
    https://github.com/openai/codex/pull/6212.
  • fix: --search shouldn't show deprecation message (#6180)
    Use the new feature flags instead of the old config.
  • Windows Sandbox - Alpha version (#4905)
    - Added the new codex-windows-sandbox crate that builds both a library
    entry point (run_windows_sandbox_capture) and a CLI executable to launch
    commands inside a Windows restricted-token sandbox, including ACL
    management, capability SID provisioning, network lockdown, and output
    capture
    (windows-sandbox-rs/src/lib.rs:167, windows-sandbox-rs/src/main.rs:54).
    - Introduced the experimental WindowsSandbox feature flag and wiring so
    Windows builds can opt into the sandbox:
    SandboxType::WindowsRestrictedToken, the in-process execution path, and
    platform sandbox selection now honor the flag (core/src/features.rs:47,
    core/src/config.rs:1224, core/src/safety.rs:19,
    core/src/sandboxing/mod.rs:69, core/src/exec.rs:79,
    core/src/exec.rs:172).
    - Updated workspace metadata to include the new crate and its
    Windows-specific dependencies so the core crate can link against it
    (codex-rs/
        Cargo.toml:91, core/Cargo.toml:86).
    - Added a PowerShell bootstrap script that installs the Windows
    toolchain, required CLI utilities, and builds the workspace to ease
    development
        on the platform (scripts/setup-windows.ps1:1).
    - Landed a Python smoke-test suite that exercises
    read-only/workspace-write policies, ACL behavior, and network denial for
    the Windows sandbox
        binary (windows-sandbox-rs/sandbox_smoketests.py:1).
  • Use assert_matches (#4756)
    assert_matches is soon to be in std but is experimental for now.
  • [MCP] Add support for MCP Oauth credentials (#4517)
    This PR adds oauth login support to streamable http servers when
    `experimental_use_rmcp_client` is enabled.
    
    This PR is large but represents the minimal amount of work required for
    this to work. To keep this PR smaller, login can only be done with
    `codex mcp login` and `codex mcp logout` but it doesn't appear in `/mcp`
    or `codex mcp list` yet. Fingers crossed that this is the last large MCP
    PR and that subsequent PRs can be smaller.
    
    Under the hood, credentials are stored using platform credential
    managers using the [keyring crate](https://crates.io/crates/keyring).
    When the keyring isn't available, it falls back to storing credentials
    in `CODEX_HOME/.credentials.json` which is consistent with how other
    coding agents handle authentication.
    
    I tested this on macOS, Windows, WSL (ubuntu), and Linux. I wasn't able
    to test the dbus store on linux but did verify that the fallback works.
    
    One quirk is that if you have credentials, during development, every
    build will have its own ad-hoc binary so the keyring won't recognize the
    reader as being the same as the write so it may ask for the user's
    password. I may add an override to disable this or allow
    users/enterprises to opt-out of the keyring storage if it causes issues.
    
    <img width="5064" height="686" alt="CleanShot 2025-09-30 at 19 31 40"
    src="https://github.com/user-attachments/assets/9573f9b4-07f1-4160-83b8-2920db287e2d"
    />
    <img width="745" height="486" alt="image"
    src="https://github.com/user-attachments/assets/9562649b-ea5f-4f22-ace2-d0cb438b143e"
    />
  • fix: remove mcp-types from app server protocol (#4537)
    We continue the separation between `codex app-server` and `codex
    mcp-server`.
    
    In particular, we introduce a new crate, `codex-app-server-protocol`,
    and migrate `codex-rs/protocol/src/mcp_protocol.rs` into it, renaming it
    `codex-rs/app-server-protocol/src/protocol.rs`.
    
    Because `ConversationId` was defined in `mcp_protocol.rs`, we move it
    into its own file, `codex-rs/protocol/src/conversation_id.rs`, and
    because it is referenced in a ton of places, we have to touch a lot of
    files as part of this PR.
    
    We also decide to get away from proper JSON-RPC 2.0 semantics, so we
    also introduce `codex-rs/app-server-protocol/src/jsonrpc_lite.rs`, which
    is basically the same `JSONRPCMessage` type defined in `mcp-types`
    except with all of the `"jsonrpc": "2.0"` removed.
    
    Getting rid of `"jsonrpc": "2.0"` makes our serialization logic
    considerably simpler, as we can lean heavier on serde to serialize
    directly into the wire format that we use now.
  • Add cloud tasks (#3197)
    Adds a TUI for managing, applying, and creating cloud tasks
  • fix: separate codex mcp into codex mcp-server and codex app-server (#4471)
    This is a very large PR with some non-backwards-compatible changes.
    
    Historically, `codex mcp` (or `codex mcp serve`) started a JSON-RPC-ish
    server that had two overlapping responsibilities:
    
    - Running an MCP server, providing some basic tool calls.
    - Running the app server used to power experiences such as the VS Code
    extension.
    
    This PR aims to separate these into distinct concepts:
    
    - `codex mcp-server` for the MCP server
    - `codex app-server` for the "application server"
    
    Note `codex mcp` still exists because it already has its own subcommands
    for MCP management (`list`, `add`, etc.)
    
    The MCP logic continues to live in `codex-rs/mcp-server` whereas the
    refactored app server logic is in the new `codex-rs/app-server` folder.
    Note that most of the existing integration tests in
    `codex-rs/mcp-server/tests/suite` were actually for the app server, so
    all the tests have been moved with the exception of
    `codex-rs/mcp-server/tests/suite/mod.rs`.
    
    Because this is already a large diff, I tried not to change more than I
    had to, so `codex-rs/app-server/tests/common/mcp_process.rs` still uses
    the name `McpProcess` for now, but I will do some mechanical renamings
    to things like `AppServer` in subsequent PRs.
    
    While `mcp-server` and `app-server` share some overlapping functionality
    (like reading streams of JSONL and dispatching based on message types)
    and some differences (completely different message types), I ended up
    doing a bit of copypasta between the two crates, as both have somewhat
    similar `message_processor.rs` and `outgoing_message.rs` files for now,
    though I expect them to diverge more in the near future.
    
    One material change is that of the initialize handshake for `codex
    app-server`, as we no longer use the MCP types for that handshake.
    Instead, we update `codex-rs/protocol/src/mcp_protocol.rs` to add an
    `Initialize` variant to `ClientRequest`, which takes the `ClientInfo`
    object we need to update the `USER_AGENT_SUFFIX` in
    `codex-rs/app-server/src/message_processor.rs`.
    
    One other material change is in
    `codex-rs/app-server/src/codex_message_processor.rs` where I eliminated
    a use of the `send_event_as_notification()` method I am generally trying
    to deprecate (because it blindly maps an `EventMsg` into a
    `JSONNotification`) in favor of `send_server_notification()`, which
    takes a `ServerNotification`, as that is intended to be a custom enum of
    all notification types supported by the app server. So to make this
    update, I had to introduce a new variant of `ServerNotification`,
    `SessionConfigured`, which is a non-backwards compatible change with the
    old `codex mcp`, and clients will have to be updated after the next
    release that contains this PR. Note that
    `codex-rs/app-server/tests/suite/list_resume.rs` also had to be update
    to reflect this change.
    
    I introduced `codex-rs/utils/json-to-toml/src/lib.rs` as a small utility
    crate to avoid some of the copying between `mcp-server` and
    `app-server`.
  • chore: remove responses-api-proxy from the multitool (#4404)
    This removes the `codex responses-api-proxy` subcommand in favor of
    running it as a standalone CLI.
    
    As part of this change, we:
    
    - remove the dependency on `tokio`/`async/await` as well as `codex_arg0`
    - introduce the use of `pre_main_hardening()` so `CODEX_SECURE_MODE=1`
    is not required
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/4404).
    * #4406
    * __->__ #4404
    * #4403
  • feat: introduce responses-api-proxy (#4246)
    Details are in `responses-api-proxy/README.md`, but the key contribution
    of this PR is a new subcommand, `codex responses-api-proxy`, which reads
    the auth token for use with the OpenAI Responses API from `stdin` at
    startup and then proxies `POST` requests to `/v1/responses` over to
    `https://api.openai.com/v1/responses`, injecting the auth token as part
    of the `Authorization` header.
    
    The expectation is that `codex responses-api-proxy` is launched by a
    privileged user who has access to the auth token so that it can be used
    by unprivileged users of the Codex CLI on the same host.
    
    If the client only has one user account with `sudo`, one option is to:
    
    - run `sudo codex responses-api-proxy --http-shutdown --server-info
    /tmp/server-info.json` to start the server
    - record the port written to `/tmp/server-info.json`
    - relinquish their `sudo` privileges (which is irreversible!) like so:
    
    ```
    sudo deluser $USER sudo || sudo gpasswd -d $USER sudo || true
    ```
    
    - use `codex` with the proxy (see `README.md`)
    - when done, make a `GET` request to the server using the `PORT` from
    `server-info.json` to shut it down:
    
    ```shell
    curl --fail --silent --show-error "http://127.0.0.1:$PORT/shutdown"
    ```
    
    To protect the auth token, we:
    
    - allocate a 1024 byte buffer on the stack and write `"Bearer "` into it
    to start
    - we then read from `stdin`, copying to the contents into the buffer
    after the prefix
    - after verifying the input looks good, we create a `String` from that
    buffer (so the data is now on the heap)
    - we zero out the stack-allocated buffer using
    https://crates.io/crates/zeroize so it is not optimized away by the
    compiler
    - we invoke `.leak()` on the `String` so we can treat its contents as a
    `&'static str`, as it will live for the rest of the processs
    - on UNIX, we `mlock(2)` the memory backing the `&'static str`
    - when using the `&'static str` when building an HTTP request, we use
    `HeaderValue::from_static()` to avoid copying the `&str`
    - we also invoke `.set_sensitive(true)` on the `HeaderValue`, which in
    theory indicates to other parts of the HTTP stack that the header should
    be treated with "special care" to avoid leakage:
    
    
    https://github.com/hyperium/http/blob/439d1c50d71e3be3204b6c4a1bf2255ed78e1f93/src/header/value.rs#L346-L376
  • feat: add support for CODEX_SECURE_MODE=1 to restrict process observability (#4220)
    Because the `codex` process could contain sensitive information in
    memory, such as API keys, we add logic so that when
    `CODEX_SECURE_MODE=1` is specified, we avail ourselves of whatever the
    operating system provides to restrict observability/tampering, which
    includes:
    
    - disabling `ptrace(2)`, so it is not possible to attach to the process
    with a debugger, such as `gdb`
    - disabling core dumps
    
    Admittedly, a user with root privileges can defeat these safeguards.
    
    For now, we only add support for this in the `codex` multitool, but we
    may ultimately want to support this in some of the smaller CLIs that are
    buildable out of our Cargo workspace.
  • fix codex resume message at end of session (#3957)
    This was only being printed when running the codex-tui executable
    directly, not via the codex-cli wrapper.
  • chore: unify cargo versions (#4044)
    Unify cargo versions at root
  • chore(deps): bump tracing-subscriber from 0.3.19 to 0.3.20 in /codex-rs (#3620)
    Bumps [tracing-subscriber](https://github.com/tokio-rs/tracing) from
    0.3.19 to 0.3.20.
    <details>
    <summary>Release notes</summary>
    <p><em>Sourced from <a
    href="https://github.com/tokio-rs/tracing/releases">tracing-subscriber's
    releases</a>.</em></p>
    <blockquote>
    <h2>tracing-subscriber 0.3.20</h2>
    <p><strong>Security Fix</strong>: ANSI Escape Sequence Injection
    (CVE-TBD)</p>
    <h2>Impact</h2>
    <p>Previous versions of tracing-subscriber were vulnerable to ANSI
    escape sequence injection attacks. Untrusted user input containing ANSI
    escape sequences could be injected into terminal output when logged,
    potentially allowing attackers to:</p>
    <ul>
    <li>Manipulate terminal title bars</li>
    <li>Clear screens or modify terminal display</li>
    <li>Potentially mislead users through terminal manipulation</li>
    </ul>
    <p>In isolation, impact is minimal, however security issues have been
    found in terminal emulators that enabled an attacker to use ANSI escape
    sequences via logs to exploit vulnerabilities in the terminal
    emulator.</p>
    <h2>Solution</h2>
    <p>Version 0.3.20 fixes this vulnerability by escaping ANSI control
    characters in when writing events to destinations that may be printed to
    the terminal.</p>
    <h2>Affected Versions</h2>
    <p>All versions of tracing-subscriber prior to 0.3.20 are affected by
    this vulnerability.</p>
    <h2>Recommendations</h2>
    <p>Immediate Action Required: We recommend upgrading to
    tracing-subscriber 0.3.20 immediately, especially if your
    application:</p>
    <ul>
    <li>Logs user-provided input (form data, HTTP headers, query parameters,
    etc.)</li>
    <li>Runs in environments where terminal output is displayed to
    users</li>
    </ul>
    <h2>Migration</h2>
    <p>This is a patch release with no breaking API changes. Simply update
    your Cargo.toml:</p>
    <pre lang="toml"><code>[dependencies]
    tracing-subscriber = &quot;0.3.20&quot;
    </code></pre>
    <h2>Acknowledgments</h2>
    <p>We would like to thank <a href="http://github.com/zefr0x">zefr0x</a>
    who responsibly reported the issue at
    <code>security@tokio.rs</code>.</p>
    <p>If you believe you have found a security vulnerability in any
    tokio-rs project, please email us at <code>security@tokio.rs</code>.</p>
    </blockquote>
    </details>
    <details>
    <summary>Commits</summary>
    <ul>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/4c52ca5266a3920fc5dfeebda2accf15ee7fb278"><code>4c52ca5</code></a>
    fmt: fix ANSI escape sequence injection vulnerability (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3368">#3368</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/f71cebe41e4c12735b1d19ca804428d4ff7d905d"><code>f71cebe</code></a>
    subscriber: impl Clone for EnvFilter (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3360">#3360</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/3a1f571102b38bcdca13d59f3c454989d179055d"><code>3a1f571</code></a>
    Fix CI (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3361">#3361</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/e63ef57f3d686abe3727ddd586eb9af73d6715b7"><code>e63ef57</code></a>
    chore: prepare tracing-attributes 0.1.30 (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3316">#3316</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/6e59a13b1a7bcdd78b8b5a7cbcf70a0b2cdd76f0"><code>6e59a13</code></a>
    attributes: fix tracing::instrument regression around shadowing (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3311">#3311</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/e4df76127538aa8370d7dee32a6f84bbec6bbf10"><code>e4df761</code></a>
    tracing: update core to 0.1.34 and attributes to 0.1.29 (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3305">#3305</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/643f392ebb73c4fb856f56a78c066c82582dd22c"><code>643f392</code></a>
    chore: prepare tracing-attributes 0.1.29 (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3304">#3304</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/d08e7a6eea1833810ea527e18ea03b08cd402c9d"><code>d08e7a6</code></a>
    chore: prepare tracing-core 0.1.34 (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3302">#3302</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/6e70c571d319a033d5f37c885ccf99aa675a9eac"><code>6e70c57</code></a>
    tracing-subscriber: count numbers of enters in <code>Timings</code> (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/2944">#2944</a>)</li>
    <li><a
    href="https://github.com/tokio-rs/tracing/commit/c01d4fd9def2fb061669a310598095c789ca0a32"><code>c01d4fd</code></a>
    fix docs and enable CI on <code>main</code> branch (<a
    href="https://redirect.github.com/tokio-rs/tracing/issues/3295">#3295</a>)</li>
    <li>Additional commits viewable in <a
    href="https://github.com/tokio-rs/tracing/compare/tracing-subscriber-0.3.19...tracing-subscriber-0.3.20">compare
    view</a></li>
    </ul>
    </details>
    <br />
    
    
    [![Dependabot compatibility
    score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tracing-subscriber&package-manager=cargo&previous-version=0.3.19&new-version=0.3.20)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
    
    Dependabot will resolve any conflicts with this PR as long as you don't
    alter it yourself. You can also trigger a rebase manually by commenting
    `@dependabot rebase`.
    
    [//]: # (dependabot-automerge-start)
    [//]: # (dependabot-automerge-end)
    
    ---
    
    <details>
    <summary>Dependabot commands and options</summary>
    <br />
    
    You can trigger Dependabot actions by commenting on this PR:
    - `@dependabot rebase` will rebase this PR
    - `@dependabot recreate` will recreate this PR, overwriting any edits
    that have been made to it
    - `@dependabot merge` will merge this PR after your CI passes on it
    - `@dependabot squash and merge` will squash and merge this PR after
    your CI passes on it
    - `@dependabot cancel merge` will cancel a previously requested merge
    and block automerging
    - `@dependabot reopen` will reopen this PR if it is closed
    - `@dependabot close` will close this PR and stop Dependabot recreating
    it. You can achieve the same result by closing it manually
    - `@dependabot show <dependency name> ignore conditions` will show all
    of the ignore conditions of the specified dependency
    - `@dependabot ignore this major version` will close this PR and stop
    Dependabot creating any more for this major version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this minor version` will close this PR and stop
    Dependabot creating any more for this minor version (unless you reopen
    the PR or upgrade to it yourself)
    - `@dependabot ignore this dependency` will close this PR and stop
    Dependabot creating any more for this dependency (unless you reopen the
    PR or upgrade to it yourself)
    
    
    </details>
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • initial mcp add interface (#3543)
    Adds `codex mcp add`, `codex mcp list`, `codex mcp remove`. Currently writes to global config.
  • chore: move mcp-server/src/wire_format.rs to protocol/src/mcp_protocol.rs (#2423)
    The existing `wire_format.rs` should share more types with the
    `codex-protocol` crate (like `AskForApproval` instead of maintaining a
    parallel `CodexToolCallApprovalPolicy` enum), so this PR moves
    `wire_format.rs` into `codex-protocol`, renaming it as
    `mcp-protocol.rs`. We also de-dupe types, where appropriate.
    
    ---
    [//]: # (BEGIN SAPLING FOOTER)
    Stack created with [Sapling](https://sapling-scm.com). Best reviewed
    with [ReviewStack](https://reviewstack.dev/openai/codex/pull/2423).
    * #2424
    * __->__ #2423
  • Auto format toml (#1745)
    Add recommended extension and configure it to auto format prompt.
  • Add codex apply to apply a patch created from the Codex remote agent (#1528)
    In order to to this, I created a new `chatgpt` crate where we can put
    any code that interacts directly with ChatGPT as opposed to the OpenAI
    API. I added a disclaimer to the README for it that it should primarily
    be modified by OpenAI employees.
    
    
    https://github.com/user-attachments/assets/bb978e33-d2c9-4d8e-af28-c8c25b1988e8
  • 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.
  • feat: add mcp subcommand to CLI to run Codex as an MCP server (#934)
    Previously, running Codex as an MCP server required a standalone binary
    in our Cargo workspace, but this PR makes it available as a subcommand
    (`mcp`) of the main CLI.
    
    Ran this with:
    
    ```
    RUST_LOG=debug npx @modelcontextprotocol/inspector cargo run --bin codex -- mcp
    ```
    
    and verified it worked as expected in the inspector at
    `http://127.0.0.1:6274/`.
  • 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.
  • 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.
  • 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.