diff --git a/.github/actions/setup-rusty-v8/action.yml b/.github/actions/setup-rusty-v8-musl/action.yml similarity index 71% rename from .github/actions/setup-rusty-v8/action.yml rename to .github/actions/setup-rusty-v8-musl/action.yml index d9c4484657c6..fbec1feb4636 100644 --- a/.github/actions/setup-rusty-v8/action.yml +++ b/.github/actions/setup-rusty-v8-musl/action.yml @@ -1,20 +1,29 @@ -name: setup-rusty-v8 -description: Download and verify Codex-built rusty_v8 artifacts for Cargo builds. +name: setup-rusty-v8-musl +description: Download and verify musl rusty_v8 artifacts for Cargo builds. inputs: target: - description: Rust target triple with Codex-built V8 release artifacts. + description: Rust musl target triple. required: true runs: using: composite steps: - - name: Configure rusty_v8 artifact overrides and verify checksums + - name: Configure musl rusty_v8 artifact overrides and verify checksums shell: bash env: TARGET: ${{ inputs.target }} run: | set -euo pipefail + case "${TARGET}" in + x86_64-unknown-linux-musl|aarch64-unknown-linux-musl) + ;; + *) + echo "Unsupported musl rusty_v8 target: ${TARGET}" >&2 + exit 1 + ;; + esac + version="$(python3 "${GITHUB_WORKSPACE}/.github/scripts/rusty_v8_bazel.py" resolved-v8-crate-version)" release_tag="rusty-v8-v${version}" base_url="https://github.com/openai/codex/releases/download/${release_tag}" @@ -33,10 +42,6 @@ runs: exit 1 fi - if command -v sha256sum >/dev/null 2>&1; then - (cd "${binding_dir}" && sha256sum -c "${checksums_path}") - else - (cd "${binding_dir}" && shasum -a 256 -c "${checksums_path}") - fi + (cd "${binding_dir}" && sha256sum -c "${checksums_path}") echo "RUSTY_V8_ARCHIVE=${archive_path}" >> "${GITHUB_ENV}" echo "RUSTY_V8_SRC_BINDING_PATH=${binding_path}" >> "${GITHUB_ENV}" diff --git a/.github/dotslash-config.json b/.github/dotslash-config.json index 78ea6b37e6fe..a0297c269a87 100644 --- a/.github/dotslash-config.json +++ b/.github/dotslash-config.json @@ -3,56 +3,56 @@ "codex": { "platforms": { "macos-aarch64": { - "regex": "^codex-package-aarch64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex" + "regex": "^codex-aarch64-apple-darwin\\.zst$", + "path": "codex" }, "macos-x86_64": { - "regex": "^codex-package-x86_64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex" + "regex": "^codex-x86_64-apple-darwin\\.zst$", + "path": "codex" }, "linux-x86_64": { - "regex": "^codex-package-x86_64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex" + "regex": "^codex-x86_64-unknown-linux-musl-bundle\\.tar\\.zst$", + "path": "codex" }, "linux-aarch64": { - "regex": "^codex-package-aarch64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex" + "regex": "^codex-aarch64-unknown-linux-musl-bundle\\.tar\\.zst$", + "path": "codex" }, "windows-x86_64": { - "regex": "^codex-package-x86_64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex.exe" + "regex": "^codex-x86_64-pc-windows-msvc\\.exe\\.zst$", + "path": "codex.exe" }, "windows-aarch64": { - "regex": "^codex-package-aarch64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex.exe" + "regex": "^codex-aarch64-pc-windows-msvc\\.exe\\.zst$", + "path": "codex.exe" } } }, "codex-app-server": { "platforms": { "macos-aarch64": { - "regex": "^codex-app-server-package-aarch64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex-app-server" + "regex": "^codex-app-server-aarch64-apple-darwin\\.zst$", + "path": "codex-app-server" }, "macos-x86_64": { - "regex": "^codex-app-server-package-x86_64-apple-darwin\\.tar\\.zst$", - "path": "bin/codex-app-server" + "regex": "^codex-app-server-x86_64-apple-darwin\\.zst$", + "path": "codex-app-server" }, "linux-x86_64": { - "regex": "^codex-app-server-package-x86_64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex-app-server" + "regex": "^codex-app-server-x86_64-unknown-linux-musl\\.zst$", + "path": "codex-app-server" }, "linux-aarch64": { - "regex": "^codex-app-server-package-aarch64-unknown-linux-musl\\.tar\\.zst$", - "path": "bin/codex-app-server" + "regex": "^codex-app-server-aarch64-unknown-linux-musl\\.zst$", + "path": "codex-app-server" }, "windows-x86_64": { - "regex": "^codex-app-server-package-x86_64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex-app-server.exe" + "regex": "^codex-app-server-x86_64-pc-windows-msvc\\.exe\\.zst$", + "path": "codex-app-server.exe" }, "windows-aarch64": { - "regex": "^codex-app-server-package-aarch64-pc-windows-msvc\\.tar\\.zst$", - "path": "bin/codex-app-server.exe" + "regex": "^codex-app-server-aarch64-pc-windows-msvc\\.exe\\.zst$", + "path": "codex-app-server.exe" } } }, diff --git a/.github/dotslash-zsh-config.json b/.github/dotslash-zsh-config.json index 37285f19e24c..db2c41640154 100644 --- a/.github/dotslash-zsh-config.json +++ b/.github/dotslash-zsh-config.json @@ -7,11 +7,6 @@ "format": "tar.gz", "path": "codex-zsh/bin/zsh" }, - "macos-x86_64": { - "name": "codex-zsh-x86_64-apple-darwin.tar.gz", - "format": "tar.gz", - "path": "codex-zsh/bin/zsh" - }, "linux-x86_64": { "name": "codex-zsh-x86_64-unknown-linux-musl.tar.gz", "format": "tar.gz", diff --git a/.github/scripts/build-codex-package-archive.sh b/.github/scripts/build-codex-package-archive.sh index 80da4cf20c91..90eae12ef074 100644 --- a/.github/scripts/build-codex-package-archive.sh +++ b/.github/scripts/build-codex-package-archive.sh @@ -8,9 +8,6 @@ Usage: build-codex-package-archive.sh \ --bundle \ --entrypoint-dir \ --archive-dir \ - [--bwrap-bin ] \ - [--codex-command-runner-bin ] \ - [--codex-windows-sandbox-setup-bin ] \ [--target-suffixed-entrypoint] EOF } @@ -20,10 +17,6 @@ bundle="" entrypoint_dir="" archive_dir="" target_suffixed_entrypoint="false" -resource_args=() -bwrap_bin_provided="false" -command_runner_bin_provided="false" -sandbox_setup_bin_provided="false" while [[ $# -gt 0 ]]; do case "$1" in @@ -43,27 +36,6 @@ while [[ $# -gt 0 ]]; do archive_dir="${2:?--archive-dir requires a value}" shift 2 ;; - --bwrap-bin) - resource_args+=(--bwrap-bin "${2:?--bwrap-bin requires a value}") - bwrap_bin_provided="true" - shift 2 - ;; - --codex-command-runner-bin) - resource_args+=( - --codex-command-runner-bin - "${2:?--codex-command-runner-bin requires a value}" - ) - command_runner_bin_provided="true" - shift 2 - ;; - --codex-windows-sandbox-setup-bin) - resource_args+=( - --codex-windows-sandbox-setup-bin - "${2:?--codex-windows-sandbox-setup-bin requires a value}" - ) - sandbox_setup_bin_provided="true" - shift 2 - ;; --target-suffixed-entrypoint) target_suffixed_entrypoint="true" shift @@ -114,25 +86,6 @@ if [[ "$target_suffixed_entrypoint" == "true" ]]; then entrypoint_name="${entrypoint_name}-${target}" fi -case "$target" in - *linux*) - bwrap_bin="${entrypoint_dir%/}/bwrap" - if [[ "$bwrap_bin_provided" == "false" && -f "$bwrap_bin" ]]; then - resource_args+=(--bwrap-bin "$bwrap_bin") - fi - ;; - *windows*) - command_runner_bin="${entrypoint_dir%/}/codex-command-runner.exe" - sandbox_setup_bin="${entrypoint_dir%/}/codex-windows-sandbox-setup.exe" - if [[ "$command_runner_bin_provided" == "false" && -f "$command_runner_bin" ]]; then - resource_args+=(--codex-command-runner-bin "$command_runner_bin") - fi - if [[ "$sandbox_setup_bin_provided" == "false" && -f "$sandbox_setup_bin" ]]; then - resource_args+=(--codex-windows-sandbox-setup-bin "$sandbox_setup_bin") - fi - ;; -esac - repo_root="${GITHUB_WORKSPACE:-}" if [[ -z "$repo_root" ]]; then repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" @@ -144,29 +97,16 @@ else python_bin="python" fi -if ! command -v zstd >/dev/null 2>&1 && [[ -x "${repo_root}/.github/workflows/zstd" ]]; then - export PATH="${repo_root}/.github/workflows:${PATH}" -fi - mkdir -p "$archive_dir" package_dir="${RUNNER_TEMP:-/tmp}/${archive_stem}-${target}" -gzip_archive_path="${archive_dir}/${archive_stem}-${target}.tar.gz" -zstd_archive_path="${archive_dir}/${archive_stem}-${target}.tar.zst" +archive_path="${archive_dir}/${archive_stem}-${target}.tar.gz" rm -rf "$package_dir" -python_args=( - "${repo_root}/scripts/build_codex_package.py" - --target "$target" - --variant "$variant" - --entrypoint-bin "${entrypoint_dir%/}/${entrypoint_name}${exe_suffix}" - --cargo-profile release - --package-dir "$package_dir" - --archive-output "$gzip_archive_path" - --archive-output "$zstd_archive_path" -) -if ((${#resource_args[@]} > 0)); then - python_args+=("${resource_args[@]}") -fi -python_args+=(--force) - -"$python_bin" "${python_args[@]}" +"$python_bin" "${repo_root}/scripts/build_codex_package.py" \ + --target "$target" \ + --variant "$variant" \ + --entrypoint-bin "${entrypoint_dir%/}/${entrypoint_name}${exe_suffix}" \ + --cargo-profile release \ + --package-dir "$package_dir" \ + --archive-output "$archive_path" \ + --force diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b1ee1395e104..a1c60acc26d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,9 +26,6 @@ jobs: - name: Verify Bazel clippy flags match Cargo workspace lints run: python3 .github/scripts/verify_bazel_clippy_lints.py - - name: Test Codex package builder - run: python3 -m unittest discover -s scripts/codex_package -p 'test_*.py' - - name: Setup pnpm uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5 with: @@ -42,6 +39,9 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile + # stage_npm_packages.py requires DotSlash when staging releases. + - uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 + - name: Stage npm package id: stage_npm_package env: @@ -52,13 +52,15 @@ jobs: # cross-platform native payload required by the npm package layout. # Passing the workflow URL directly avoids relying on old rust-v* # branches remaining discoverable via `gh run list --branch ...`. - CODEX_VERSION=0.133.0-alpha.4 - WORKFLOW_URL="https://github.com/openai/codex/actions/runs/26201494185" + CODEX_VERSION=0.125.0 + WORKFLOW_URL="https://github.com/openai/codex/actions/runs/24901475298" OUTPUT_DIR="${RUNNER_TEMP}" + # This reused workflow predates the standalone bwrap artifact. python3 ./scripts/stage_npm_packages.py \ --release-version "$CODEX_VERSION" \ --workflow-url "$WORKFLOW_URL" \ --package codex \ + --allow-missing-native-component bwrap \ --output-dir "$OUTPUT_DIR" PACK_OUTPUT="${OUTPUT_DIR}/codex-npm-${CODEX_VERSION}.tgz" echo "pack_output=$PACK_OUTPUT" >> "$GITHUB_OUTPUT" diff --git a/.github/workflows/rust-ci-full.yml b/.github/workflows/rust-ci-full.yml index c4a9329c0035..08e0709e1702 100644 --- a/.github/workflows/rust-ci-full.yml +++ b/.github/workflows/rust-ci-full.yml @@ -28,13 +28,8 @@ jobs: - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 with: components: rustfmt - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: just - name: cargo fmt run: cargo fmt -- --config imports_granularity=Item --check - - name: Rust benchmark smoke test - run: just bench-smoke cargo_shear: name: cargo shear @@ -441,9 +436,9 @@ jobs: echo "CFLAGS=${cflags}" >> "$GITHUB_ENV" echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV" - - if: ${{ !contains(matrix.target, 'windows') }} - name: Configure rusty_v8 artifact overrides and verify checksums - uses: ./.github/actions/setup-rusty-v8 + - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }} + name: Configure musl rusty_v8 artifact overrides and verify checksums + uses: ./.github/actions/setup-rusty-v8-musl with: target: ${{ matrix.target }} diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 029b6f2c7ee4..75c5c3360123 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -70,13 +70,8 @@ jobs: - uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0 with: components: rustfmt - - uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2.62.49 - with: - tool: just - name: cargo fmt run: cargo fmt -- --config imports_granularity=Item --check - - name: Rust benchmark smoke test - run: just bench-smoke cargo_shear: name: cargo shear diff --git a/.github/workflows/rust-release-windows.yml b/.github/workflows/rust-release-windows.yml index 51412be0e02a..ac28b7855a17 100644 --- a/.github/workflows/rust-release-windows.yml +++ b/.github/workflows/rust-release-windows.yml @@ -220,9 +220,6 @@ jobs: "$dest/${binary}-${{ matrix.target }}.exe" done - - name: Install DotSlash - uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 - - name: Build Codex package archives shell: bash run: | @@ -258,12 +255,16 @@ jobs: stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${{ matrix.target }}" wheel_dir="${GITHUB_WORKSPACE}/python-runtime-dist/${{ matrix.target }}" + # Keep the helpers next to codex.exe in the runtime wheel so Windows + # sandbox/elevation lookup matches the standalone release zip. python "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" \ stage-runtime \ "$stage_dir" \ - "dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" \ + "${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex.exe" \ --codex-version "${GITHUB_REF_NAME}" \ - --platform-tag "$platform_tag" + --platform-tag "$platform_tag" \ + --resource-binary "${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex-command-runner.exe" \ + --resource-binary "${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe" "${RUNNER_TEMP}/python-runtime-build-venv/Scripts/python.exe" -m build --wheel --outdir "$wheel_dir" "$stage_dir" - name: Upload Python runtime wheel @@ -273,6 +274,9 @@ jobs: path: python-runtime-dist/${{ matrix.target }}/*.whl if-no-files-found: error + - name: Install DotSlash + uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 + - name: Compress artifacts shell: bash run: | @@ -291,7 +295,7 @@ jobs: base="$(basename "$f")" # Skip files that are already archives (shouldn't happen, but be # safe). - if [[ "$base" == *.tar.gz || "$base" == *.tar.zst || "$base" == *.zip || "$base" == *.dmg ]]; then + if [[ "$base" == *.tar.gz || "$base" == *.zip || "$base" == *.dmg ]]; then continue fi diff --git a/.github/workflows/rust-release-zsh.yml b/.github/workflows/rust-release-zsh.yml index b55d2e714bcf..492b8dc5e75d 100644 --- a/.github/workflows/rust-release-zsh.yml +++ b/.github/workflows/rust-release-zsh.yml @@ -69,10 +69,6 @@ jobs: fail-fast: false matrix: include: - - runner: macos-15-large - target: x86_64-apple-darwin - variant: macos-15 - archive_name: codex-zsh-x86_64-apple-darwin.tar.gz - runner: macos-15-xlarge target: aarch64-apple-darwin variant: macos-15 diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 2cd11e66fe0f..c55337ecfe6e 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -180,25 +180,25 @@ jobs: binaries: "codex-app-server" build_dmg: "false" # Release artifacts intentionally ship MUSL-linked Linux binaries. - - runner: codex-linux-x64-xl + - runner: ubuntu-24.04 target: x86_64-unknown-linux-musl bundle: primary artifact_name: x86_64-unknown-linux-musl binaries: "codex codex-responses-api-proxy bwrap" build_dmg: "false" - - runner: codex-linux-x64-xl + - runner: ubuntu-24.04 target: x86_64-unknown-linux-musl bundle: app-server artifact_name: x86_64-unknown-linux-musl-app-server binaries: "codex-app-server" build_dmg: "false" - - runner: codex-linux-arm64 + - runner: ubuntu-24.04-arm target: aarch64-unknown-linux-musl bundle: primary artifact_name: aarch64-unknown-linux-musl binaries: "codex codex-responses-api-proxy bwrap" build_dmg: "false" - - runner: codex-linux-arm64 + - runner: ubuntu-24.04-arm target: aarch64-unknown-linux-musl bundle: app-server artifact_name: aarch64-unknown-linux-musl-app-server @@ -340,12 +340,13 @@ jobs: echo "CFLAGS=${cflags}" >> "$GITHUB_ENV" echo "CXXFLAGS=${cxxflags}" >> "$GITHUB_ENV" - - name: Configure rusty_v8 artifact overrides and verify checksums - uses: ./.github/actions/setup-rusty-v8 + - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl' }} + name: Configure musl rusty_v8 artifact overrides and verify checksums + uses: ./.github/actions/setup-rusty-v8-musl with: target: ${{ matrix.target }} - - if: ${{ contains(matrix.target, 'linux') }} + - if: ${{ contains(matrix.target, 'linux') && matrix.bundle == 'primary' }} name: Build bwrap and export digest shell: bash run: | @@ -568,10 +569,18 @@ jobs: "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" stage-runtime "$stage_dir" - "dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" + "${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/codex" --codex-version "${GITHUB_REF_NAME}" --platform-tag "$platform_tag" ) + if [[ "${{ matrix.target }}" == *linux* ]]; then + # Keep bwrap in the runtime wheel so Linux sandbox fallback behavior + # matches the standalone release bundle on hosts without system bwrap. + stage_runtime_args+=( + --resource-binary + "${GITHUB_WORKSPACE}/codex-rs/target/${{ matrix.target }}/release/bwrap" + ) + fi python3 "${stage_runtime_args[@]}" "${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build --wheel --outdir "$wheel_dir" "$stage_dir" @@ -791,20 +800,6 @@ jobs: cp "$dmg_source" "$dest/$dmg_name" fi - - name: Build Codex package archive - shell: bash - env: - TARGET: ${{ matrix.target }} - BUNDLE: ${{ matrix.bundle }} - run: | - set -euo pipefail - bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \ - --target "$TARGET" \ - --bundle "$BUNDLE" \ - --entrypoint-dir "dist/${TARGET}" \ - --archive-dir "dist/${TARGET}" \ - --target-suffixed-entrypoint - - name: Build Python runtime wheel if: ${{ matrix.bundle == 'primary' }} shell: bash @@ -833,11 +828,25 @@ jobs: "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" \ stage-runtime \ "$stage_dir" \ - "dist/${{ matrix.target }}/codex-package-${{ matrix.target }}.tar.gz" \ + "${GITHUB_WORKSPACE}/codex-rs/dist/${{ matrix.target }}/codex-${{ matrix.target }}" \ --codex-version "${GITHUB_REF_NAME}" \ --platform-tag "$platform_tag" "${RUNNER_TEMP}/python-runtime-build-venv/bin/python" -m build --wheel --outdir "$wheel_dir" "$stage_dir" + - name: Build Codex package archive + shell: bash + env: + TARGET: ${{ matrix.target }} + BUNDLE: ${{ matrix.bundle }} + run: | + set -euo pipefail + bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \ + --target "$TARGET" \ + --bundle "$BUNDLE" \ + --entrypoint-dir "dist/${TARGET}" \ + --archive-dir "dist/${TARGET}" \ + --target-suffixed-entrypoint + - name: Upload Python runtime wheel if: ${{ matrix.bundle == 'primary' }} uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 @@ -1098,33 +1107,15 @@ jobs: # If included in files: dist/**, release upload races on duplicate # asset names and can fail with 404s. find dist -type f -name 'cargo-timing.html' -delete + # Keep package-builder sidecar archives as workflow artifacts only + # until distribution channels are ready to consume them. + find dist -type f \ + \( -name 'codex-package-*' -o -name 'codex-app-server-package-*' \) \ + -delete find dist -type d -empty -delete ls -R dist/ - - name: Add Codex package checksum manifest - run: | - set -euo pipefail - - manifest="dist/codex-package_SHA256SUMS" - tmp_manifest="$(mktemp)" - find dist -type f \ - \( -name 'codex-package-*.tar.gz' -o -name 'codex-app-server-package-*.tar.gz' \) \ - -print | - sort | - while IFS= read -r archive; do - sha256sum "$archive" | - awk -v name="$(basename "$archive")" '{ print $1 " " name }' - done > "$tmp_manifest" - - if [[ ! -s "$tmp_manifest" ]]; then - echo "No Codex package archives found for checksum manifest" - exit 1 - fi - - mv "$tmp_manifest" "$manifest" - cat "$manifest" - - name: Add config schema release asset run: | cp codex-rs/core/config.schema.json dist/config-schema.json @@ -1199,6 +1190,8 @@ jobs: if: ${{ env.SIGN_MACOS == 'true' }} run: pnpm install --frozen-lockfile + # stage_npm_packages.py requires DotSlash when staging releases. + - uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2 - name: Stage npm packages if: ${{ env.SIGN_MACOS == 'true' }} env: diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index b843fcae6bf7..bf018b43a06f 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -402,7 +402,7 @@ checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" [[package]] name = "app_test_support" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "base64 0.22.1", @@ -1778,7 +1778,7 @@ dependencies = [ [[package]] name = "codex-agent-graph-store" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "codex-protocol", @@ -1793,7 +1793,7 @@ dependencies = [ [[package]] name = "codex-agent-identity" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "base64 0.22.1", @@ -1812,7 +1812,7 @@ dependencies = [ [[package]] name = "codex-analytics" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-app-server-protocol", "codex-git-utils", @@ -1832,7 +1832,7 @@ dependencies = [ [[package]] name = "codex-ansi-escape" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "ansi-to-tui", "ratatui", @@ -1841,7 +1841,7 @@ dependencies = [ [[package]] name = "codex-api" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "assert_matches", @@ -1875,7 +1875,7 @@ dependencies = [ [[package]] name = "codex-app-server" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "app_test_support", @@ -1960,7 +1960,7 @@ dependencies = [ [[package]] name = "codex-app-server-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-app-server", "codex-app-server-protocol", @@ -1987,7 +1987,7 @@ dependencies = [ [[package]] name = "codex-app-server-daemon" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-app-server-protocol", @@ -2008,7 +2008,7 @@ dependencies = [ [[package]] name = "codex-app-server-protocol" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", @@ -2035,7 +2035,7 @@ dependencies = [ [[package]] name = "codex-app-server-test-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", @@ -2056,7 +2056,7 @@ dependencies = [ [[package]] name = "codex-app-server-transport" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "axum", @@ -2095,7 +2095,7 @@ dependencies = [ [[package]] name = "codex-apply-patch" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "assert_cmd", @@ -2114,7 +2114,7 @@ dependencies = [ [[package]] name = "codex-arg0" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-apply-patch", @@ -2123,6 +2123,7 @@ dependencies = [ "codex-sandboxing", "codex-shell-escalation", "codex-utils-absolute-path", + "codex-utils-file-lock", "codex-utils-home-dir", "dotenvy", "tempfile", @@ -2131,7 +2132,7 @@ dependencies = [ [[package]] name = "codex-async-utils" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "pretty_assertions", @@ -2141,7 +2142,7 @@ dependencies = [ [[package]] name = "codex-aws-auth" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "aws-config", "aws-credential-types", @@ -2156,7 +2157,7 @@ dependencies = [ [[package]] name = "codex-backend-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-api", @@ -2173,7 +2174,7 @@ dependencies = [ [[package]] name = "codex-backend-openapi-models" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "serde", "serde_json", @@ -2182,7 +2183,7 @@ dependencies = [ [[package]] name = "codex-bwrap" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "cc", "libc", @@ -2191,7 +2192,7 @@ dependencies = [ [[package]] name = "codex-chatgpt" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", @@ -2214,7 +2215,7 @@ dependencies = [ [[package]] name = "codex-cli" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "assert_cmd", @@ -2279,7 +2280,7 @@ dependencies = [ [[package]] name = "codex-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "bytes", @@ -2310,7 +2311,7 @@ dependencies = [ [[package]] name = "codex-cloud-requirements" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "base64 0.22.1", @@ -2336,7 +2337,7 @@ dependencies = [ [[package]] name = "codex-cloud-tasks" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -2368,7 +2369,7 @@ dependencies = [ [[package]] name = "codex-cloud-tasks-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -2383,7 +2384,7 @@ dependencies = [ [[package]] name = "codex-cloud-tasks-mock-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "chrono", @@ -2393,7 +2394,7 @@ dependencies = [ [[package]] name = "codex-code-mode" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-channel", "async-trait", @@ -2410,11 +2411,11 @@ dependencies = [ [[package]] name = "codex-collaboration-mode-templates" -version = "0.0.0" +version = "0.132.0-alpha.1" [[package]] name = "codex-config" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -2462,7 +2463,7 @@ dependencies = [ [[package]] name = "codex-connectors" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-app-server-protocol", @@ -2478,7 +2479,7 @@ dependencies = [ [[package]] name = "codex-core" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "arc-swap", @@ -2532,6 +2533,7 @@ dependencies = [ "codex-utils-absolute-path", "codex-utils-cache", "codex-utils-cargo-bin", + "codex-utils-file-lock", "codex-utils-home-dir", "codex-utils-image", "codex-utils-output-truncation", @@ -2596,7 +2598,7 @@ dependencies = [ [[package]] name = "codex-core-api" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-analytics", "codex-app-server-protocol", @@ -2615,7 +2617,7 @@ dependencies = [ [[package]] name = "codex-core-plugins" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "chrono", @@ -2654,7 +2656,7 @@ dependencies = [ [[package]] name = "codex-core-skills" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-analytics", @@ -2685,7 +2687,7 @@ dependencies = [ [[package]] name = "codex-debug-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", @@ -2697,7 +2699,7 @@ dependencies = [ [[package]] name = "codex-exec" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "assert_cmd", @@ -2743,7 +2745,7 @@ dependencies = [ [[package]] name = "codex-exec-server" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "arc-swap", @@ -2784,11 +2786,12 @@ dependencies = [ [[package]] name = "codex-execpolicy" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", "codex-utils-absolute-path", + "codex-utils-file-lock", "multimap", "pretty_assertions", "serde", @@ -2801,7 +2804,7 @@ dependencies = [ [[package]] name = "codex-execpolicy-legacy" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "allocative", "anyhow", @@ -2821,7 +2824,7 @@ dependencies = [ [[package]] name = "codex-experimental-api-macros" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "proc-macro2", "quote", @@ -2830,7 +2833,7 @@ dependencies = [ [[package]] name = "codex-extension-api" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "codex-protocol", @@ -2839,7 +2842,7 @@ dependencies = [ [[package]] name = "codex-external-agent-migration" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-hooks", "pretty_assertions", @@ -2851,7 +2854,7 @@ dependencies = [ [[package]] name = "codex-external-agent-sessions" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "chrono", "codex-app-server-protocol", @@ -2865,7 +2868,7 @@ dependencies = [ [[package]] name = "codex-features" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-otel", "codex-protocol", @@ -2878,7 +2881,7 @@ dependencies = [ [[package]] name = "codex-feedback" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-login", @@ -2891,7 +2894,7 @@ dependencies = [ [[package]] name = "codex-file-search" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", @@ -2907,7 +2910,7 @@ dependencies = [ [[package]] name = "codex-file-system" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "codex-protocol", @@ -2917,7 +2920,7 @@ dependencies = [ [[package]] name = "codex-file-watcher" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "notify", "pretty_assertions", @@ -2928,7 +2931,7 @@ dependencies = [ [[package]] name = "codex-git-utils" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "chrono", @@ -2952,7 +2955,7 @@ dependencies = [ [[package]] name = "codex-goal-extension" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -2972,7 +2975,7 @@ dependencies = [ [[package]] name = "codex-guardian" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "codex-core", @@ -2982,7 +2985,7 @@ dependencies = [ [[package]] name = "codex-hooks" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "chrono", @@ -3005,7 +3008,7 @@ dependencies = [ [[package]] name = "codex-install-context" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-utils-absolute-path", "codex-utils-home-dir", @@ -3015,7 +3018,7 @@ dependencies = [ [[package]] name = "codex-keyring-store" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "keyring", "tracing", @@ -3023,7 +3026,7 @@ dependencies = [ [[package]] name = "codex-linux-sandbox" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "clap", "codex-core", @@ -3047,7 +3050,7 @@ dependencies = [ [[package]] name = "codex-lmstudio" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-core", "codex-model-provider-info", @@ -3061,7 +3064,7 @@ dependencies = [ [[package]] name = "codex-login" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -3103,7 +3106,7 @@ dependencies = [ [[package]] name = "codex-mcp" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-channel", @@ -3135,7 +3138,7 @@ dependencies = [ [[package]] name = "codex-mcp-server" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-arg0", @@ -3167,7 +3170,7 @@ dependencies = [ [[package]] name = "codex-memories-extension" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "codex-core", @@ -3188,7 +3191,7 @@ dependencies = [ [[package]] name = "codex-memories-mcp" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-utils-absolute-path", @@ -3205,7 +3208,7 @@ dependencies = [ [[package]] name = "codex-memories-read" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-protocol", "codex-shell-command", @@ -3219,7 +3222,7 @@ dependencies = [ [[package]] name = "codex-memories-write" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "chrono", @@ -3254,7 +3257,7 @@ dependencies = [ [[package]] name = "codex-message-history" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-config", "pretty_assertions", @@ -3267,7 +3270,7 @@ dependencies = [ [[package]] name = "codex-model-provider" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "codex-agent-identity", @@ -3291,7 +3294,7 @@ dependencies = [ [[package]] name = "codex-model-provider-info" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-api", "codex-app-server-protocol", @@ -3308,7 +3311,7 @@ dependencies = [ [[package]] name = "codex-models-manager" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "chrono", @@ -3329,7 +3332,7 @@ dependencies = [ [[package]] name = "codex-network-proxy" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -3360,7 +3363,7 @@ dependencies = [ [[package]] name = "codex-ollama" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "assert_matches", "async-stream", @@ -3379,7 +3382,7 @@ dependencies = [ [[package]] name = "codex-otel" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "chrono", "codex-api", @@ -3411,7 +3414,7 @@ dependencies = [ [[package]] name = "codex-plugin" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-config", "codex-utils-absolute-path", @@ -3421,7 +3424,7 @@ dependencies = [ [[package]] name = "codex-process-hardening" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "libc", "pretty_assertions", @@ -3429,7 +3432,7 @@ dependencies = [ [[package]] name = "codex-protocol" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "chardetng", @@ -3469,7 +3472,7 @@ dependencies = [ [[package]] name = "codex-realtime-webrtc" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "libwebrtc", "thiserror 2.0.18", @@ -3478,7 +3481,7 @@ dependencies = [ [[package]] name = "codex-response-debug-context" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "base64 0.22.1", "codex-api", @@ -3489,7 +3492,7 @@ dependencies = [ [[package]] name = "codex-responses-api-proxy" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", @@ -3506,7 +3509,7 @@ dependencies = [ [[package]] name = "codex-rmcp-client" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "axum", @@ -3544,7 +3547,7 @@ dependencies = [ [[package]] name = "codex-rollout" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -3570,7 +3573,7 @@ dependencies = [ [[package]] name = "codex-rollout-trace" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-code-mode", @@ -3586,7 +3589,7 @@ dependencies = [ [[package]] name = "codex-sandboxing" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -3607,7 +3610,7 @@ dependencies = [ [[package]] name = "codex-secrets" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "age", "anyhow", @@ -3628,7 +3631,7 @@ dependencies = [ [[package]] name = "codex-shell-command" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "base64 0.22.1", @@ -3648,7 +3651,7 @@ dependencies = [ [[package]] name = "codex-shell-escalation" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "async-trait", @@ -3669,7 +3672,7 @@ dependencies = [ [[package]] name = "codex-skills" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-utils-absolute-path", "include_dir", @@ -3678,7 +3681,7 @@ dependencies = [ [[package]] name = "codex-state" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "chrono", @@ -3701,7 +3704,7 @@ dependencies = [ [[package]] name = "codex-stdio-to-uds" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-uds", @@ -3713,7 +3716,7 @@ dependencies = [ [[package]] name = "codex-terminal-detection" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "pretty_assertions", "tracing", @@ -3721,7 +3724,7 @@ dependencies = [ [[package]] name = "codex-test-binary-support" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-arg0", "tempfile", @@ -3729,7 +3732,7 @@ dependencies = [ [[package]] name = "codex-thread-manager-sample" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "clap", @@ -3740,7 +3743,7 @@ dependencies = [ [[package]] name = "codex-thread-store" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "chrono", @@ -3762,7 +3765,7 @@ dependencies = [ [[package]] name = "codex-tools" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-trait", "codex-app-server-protocol", @@ -3786,7 +3789,7 @@ dependencies = [ [[package]] name = "codex-tui" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "arboard", @@ -3897,7 +3900,7 @@ dependencies = [ [[package]] name = "codex-uds" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "async-io", "pretty_assertions", @@ -3909,7 +3912,7 @@ dependencies = [ [[package]] name = "codex-utils-absolute-path" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "dirs", "dunce", @@ -3923,14 +3926,14 @@ dependencies = [ [[package]] name = "codex-utils-approval-presets" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-protocol", ] [[package]] name = "codex-utils-cache" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "lru 0.16.3", "sha1", @@ -3939,7 +3942,7 @@ dependencies = [ [[package]] name = "codex-utils-cargo-bin" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "assert_cmd", "runfiles", @@ -3948,7 +3951,7 @@ dependencies = [ [[package]] name = "codex-utils-cli" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "clap", "codex-protocol", @@ -3960,15 +3963,19 @@ dependencies = [ [[package]] name = "codex-utils-elapsed" -version = "0.0.0" +version = "0.132.0-alpha.1" + +[[package]] +name = "codex-utils-file-lock" +version = "0.132.0-alpha.1" [[package]] name = "codex-utils-fuzzy-match" -version = "0.0.0" +version = "0.132.0-alpha.1" [[package]] name = "codex-utils-home-dir" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-utils-absolute-path", "dirs", @@ -3978,7 +3985,7 @@ dependencies = [ [[package]] name = "codex-utils-image" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "base64 0.22.1", "codex-utils-cache", @@ -3991,7 +3998,7 @@ dependencies = [ [[package]] name = "codex-utils-json-to-toml" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "pretty_assertions", "serde_json", @@ -4000,7 +4007,7 @@ dependencies = [ [[package]] name = "codex-utils-oss" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-core", "codex-lmstudio", @@ -4010,7 +4017,7 @@ dependencies = [ [[package]] name = "codex-utils-output-truncation" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-protocol", "codex-utils-string", @@ -4019,7 +4026,7 @@ dependencies = [ [[package]] name = "codex-utils-path" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-utils-absolute-path", "dunce", @@ -4029,7 +4036,7 @@ dependencies = [ [[package]] name = "codex-utils-plugins" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-exec-server", "codex-login", @@ -4042,7 +4049,7 @@ dependencies = [ [[package]] name = "codex-utils-pty" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "filedescriptor", @@ -4058,7 +4065,7 @@ dependencies = [ [[package]] name = "codex-utils-readiness" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "assert_matches", "async-trait", @@ -4069,14 +4076,14 @@ dependencies = [ [[package]] name = "codex-utils-rustls-provider" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "rustls", ] [[package]] name = "codex-utils-sandbox-summary" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "codex-core", "codex-model-provider-info", @@ -4087,7 +4094,7 @@ dependencies = [ [[package]] name = "codex-utils-sleep-inhibitor" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "core-foundation 0.9.4", "libc", @@ -4097,14 +4104,14 @@ dependencies = [ [[package]] name = "codex-utils-stream-parser" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "pretty_assertions", ] [[package]] name = "codex-utils-string" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "pretty_assertions", "regex-lite", @@ -4114,14 +4121,14 @@ dependencies = [ [[package]] name = "codex-utils-template" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "pretty_assertions", ] [[package]] name = "codex-v8-poc" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "pretty_assertions", "v8", @@ -4129,7 +4136,7 @@ dependencies = [ [[package]] name = "codex-windows-sandbox" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "base64 0.22.1", @@ -4386,7 +4393,7 @@ dependencies = [ [[package]] name = "core_test_support" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "assert_cmd", @@ -8616,7 +8623,7 @@ dependencies = [ [[package]] name = "mcp_test_support" -version = "0.0.0" +version = "0.132.0-alpha.1" dependencies = [ "anyhow", "codex-login", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 95c065c74bdf..da7fa5299a4a 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -81,6 +81,7 @@ members = [ "v8-poc", "utils/absolute-path", "utils/cargo-bin", + "utils/file-lock", "git-utils", "utils/cache", "utils/image", @@ -117,7 +118,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.0.0" +version = "0.134.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 @@ -219,6 +220,7 @@ codex-utils-cache = { path = "utils/cache" } codex-utils-cargo-bin = { path = "utils/cargo-bin" } codex-utils-cli = { path = "utils/cli" } codex-utils-elapsed = { path = "utils/elapsed" } +codex-utils-file-lock = { path = "utils/file-lock" } codex-utils-fuzzy-match = { path = "utils/fuzzy-match" } codex-utils-home-dir = { path = "utils/home-dir" } codex-utils-image = { path = "utils/image" } diff --git a/codex-rs/arg0/Cargo.toml b/codex-rs/arg0/Cargo.toml index 7ee21a770e49..7c2c17692020 100644 --- a/codex-rs/arg0/Cargo.toml +++ b/codex-rs/arg0/Cargo.toml @@ -20,6 +20,7 @@ codex-linux-sandbox = { workspace = true } codex-sandboxing = { workspace = true } codex-shell-escalation = { workspace = true } codex-utils-absolute-path = { workspace = true } +codex-utils-file-lock = { workspace = true } codex-utils-home-dir = { workspace = true } dotenvy = { workspace = true } tempfile = { workspace = true } diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs index 2f6ae4653c65..5a558769a7e5 100644 --- a/codex-rs/arg0/src/lib.rs +++ b/codex-rs/arg0/src/lib.rs @@ -6,6 +6,8 @@ use std::path::PathBuf; use codex_apply_patch::CODEX_CORE_APPLY_PATCH_ARG1; use codex_exec_server::CODEX_FS_HELPER_ARG1; use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0; +use codex_utils_file_lock::TryFileLockOutcome; +use codex_utils_file_lock::try_lock_exclusive_optional; use codex_utils_home_dir::find_codex_home; #[cfg(unix)] use std::os::unix::fs::symlink; @@ -33,12 +35,12 @@ pub struct Arg0DispatchPaths { /// Keeps the per-session PATH entry alive and locked for the process lifetime. pub struct Arg0PathEntryGuard { _temp_dir: TempDir, - _lock_file: File, + _lock_file: Option, paths: Arg0DispatchPaths, } impl Arg0PathEntryGuard { - fn new(temp_dir: TempDir, lock_file: File, paths: Arg0DispatchPaths) -> Self { + fn new(temp_dir: TempDir, lock_file: Option, paths: Arg0DispatchPaths) -> Self { Self { _temp_dir: temp_dir, _lock_file: lock_file, @@ -326,7 +328,13 @@ pub fn prepend_path_entry_for_codex_aliases() -> std::io::Result Some(lock_file), + TryFileLockOutcome::Unsupported => None, + TryFileLockOutcome::WouldBlock => { + return Err(std::io::Error::from(std::io::ErrorKind::WouldBlock).into()); + } + }; for filename in &[ APPLY_PATCH_ARG0, @@ -445,10 +453,9 @@ fn try_lock_dir(dir: &Path) -> std::io::Result> { Err(err) => return Err(err), }; - match lock_file.try_lock() { - Ok(()) => Ok(Some(lock_file)), - Err(std::fs::TryLockError::WouldBlock) => Ok(None), - Err(err) => Err(err.into()), + match try_lock_exclusive_optional(&lock_file)? { + TryFileLockOutcome::Acquired => Ok(Some(lock_file)), + TryFileLockOutcome::WouldBlock | TryFileLockOutcome::Unsupported => Ok(None), } } @@ -562,7 +569,13 @@ mod tests { let dir = root.path().join("locked"); fs::create_dir(&dir)?; let lock_file = create_lock(&dir)?; - lock_file.try_lock()?; + match try_lock_exclusive_optional(&lock_file)? { + TryFileLockOutcome::Acquired => {} + TryFileLockOutcome::Unsupported => return Ok(()), + TryFileLockOutcome::WouldBlock => { + panic!("newly created lock file should not be locked"); + } + } janitor_cleanup(root.path())?; diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index 8472918dcae9..fdf1d4b18a9e 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -63,6 +63,7 @@ codex-thread-store = { workspace = true } codex-tools = { workspace = true } codex-utils-absolute-path = { workspace = true } codex-utils-cache = { workspace = true } +codex-utils-file-lock = { workspace = true } codex-utils-image = { workspace = true } codex-utils-home-dir = { workspace = true } codex-utils-output-truncation = { workspace = true } @@ -125,6 +126,10 @@ openssl-sys = { workspace = true, features = ["vendored"] } [target.aarch64-unknown-linux-musl.dependencies] openssl-sys = { workspace = true, features = ["vendored"] } +# Build OpenSSL from source for Android builds. +[target.aarch64-linux-android.dependencies] +openssl-sys = { workspace = true, features = ["vendored"] } + [target.'cfg(unix)'.dependencies] codex-shell-escalation = { workspace = true } diff --git a/codex-rs/core/src/installation_id.rs b/codex-rs/core/src/installation_id.rs index a42e6b6d8353..a87c660f6132 100644 --- a/codex-rs/core/src/installation_id.rs +++ b/codex-rs/core/src/installation_id.rs @@ -11,6 +11,9 @@ use std::os::unix::fs::OpenOptionsExt; use std::os::unix::fs::PermissionsExt; use codex_utils_absolute_path::AbsolutePathBuf; +use codex_utils_file_lock::FileLockOutcome; +use codex_utils_file_lock::acquire_sibling_lock_dir; +use codex_utils_file_lock::lock_exclusive_optional; use tokio::fs; use uuid::Uuid; @@ -29,7 +32,10 @@ pub async fn resolve_installation_id(codex_home: &AbsolutePathBuf) -> Result None, + FileLockOutcome::Unsupported => Some(acquire_sibling_lock_dir(&path)?), + }; #[cfg(unix)] { diff --git a/codex-rs/execpolicy/Cargo.toml b/codex-rs/execpolicy/Cargo.toml index b22226a79e4b..b87af084d7d1 100644 --- a/codex-rs/execpolicy/Cargo.toml +++ b/codex-rs/execpolicy/Cargo.toml @@ -21,6 +21,7 @@ workspace = true anyhow = { workspace = true } clap = { workspace = true, features = ["derive"] } codex-utils-absolute-path = { workspace = true } +codex-utils-file-lock = { workspace = true } multimap = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/codex-rs/execpolicy/src/amend.rs b/codex-rs/execpolicy/src/amend.rs index e25fd1bd9144..1ef36b1ff5d8 100644 --- a/codex-rs/execpolicy/src/amend.rs +++ b/codex-rs/execpolicy/src/amend.rs @@ -6,6 +6,10 @@ use std::io::Write; use std::path::Path; use std::path::PathBuf; +use codex_utils_file_lock::FileLockOutcome; +use codex_utils_file_lock::acquire_sibling_lock_dir; +use codex_utils_file_lock::lock_exclusive_optional; + use crate::decision::Decision; use crate::rule::NetworkRuleProtocol; use crate::rule::normalize_network_rule_host; @@ -154,10 +158,21 @@ fn append_locked_line(policy_path: &Path, line: &str) -> Result<(), AmendError> path: policy_path.to_path_buf(), source, })?; - file.lock().map_err(|source| AmendError::LockPolicyFile { - path: policy_path.to_path_buf(), - source, - })?; + let _lock_dir_guard = + match lock_exclusive_optional(&file).map_err(|source| AmendError::LockPolicyFile { + path: policy_path.to_path_buf(), + source, + })? { + FileLockOutcome::Acquired => None, + FileLockOutcome::Unsupported => { + Some(acquire_sibling_lock_dir(policy_path).map_err(|source| { + AmendError::LockPolicyFile { + path: policy_path.to_path_buf(), + source, + } + })?) + } + }; file.seek(SeekFrom::Start(0)) .map_err(|source| AmendError::SeekPolicyFile { diff --git a/codex-rs/tui/Cargo.toml b/codex-rs/tui/Cargo.toml index c213e92bae41..40624baba0d0 100644 --- a/codex-rs/tui/Cargo.toml +++ b/codex-rs/tui/Cargo.toml @@ -126,7 +126,7 @@ uuid = { workspace = true } codex-windows-sandbox = { workspace = true } tokio-util = { workspace = true, features = ["time"] } -[target.'cfg(not(target_os = "linux"))'.dependencies] +[target.'cfg(all(not(target_os = "linux"), not(target_os = "android")))'.dependencies] cpal = "0.15" [target.'cfg(unix)'.dependencies] diff --git a/codex-rs/tui/src/lib.rs b/codex-rs/tui/src/lib.rs index 7348e3b452ff..c0522fee49a0 100644 --- a/codex-rs/tui/src/lib.rs +++ b/codex-rs/tui/src/lib.rs @@ -89,9 +89,9 @@ mod app_server_approval_conversions; mod app_server_session; mod approval_events; mod ascii_animation; -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "linux", target_os = "android")))] mod audio_device; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[allow(dead_code)] mod audio_device { use crate::app_event::RealtimeAudioDeviceKind; @@ -196,11 +196,11 @@ mod update_prompt; mod update_versions; mod updates; mod version; -#[cfg(not(target_os = "linux"))] +#[cfg(not(any(target_os = "linux", target_os = "android")))] mod voice; mod width; mod workspace_command; -#[cfg(target_os = "linux")] +#[cfg(any(target_os = "linux", target_os = "android"))] #[allow(dead_code)] mod voice { use crate::app_event_sender::AppEventSender; diff --git a/codex-rs/utils/file-lock/BUILD.bazel b/codex-rs/utils/file-lock/BUILD.bazel new file mode 100644 index 000000000000..face70c53518 --- /dev/null +++ b/codex-rs/utils/file-lock/BUILD.bazel @@ -0,0 +1,6 @@ +load("//:defs.bzl", "codex_rust_crate") + +codex_rust_crate( + name = "file-lock", + crate_name = "codex_utils_file_lock", +) diff --git a/codex-rs/utils/file-lock/Cargo.toml b/codex-rs/utils/file-lock/Cargo.toml new file mode 100644 index 000000000000..5e3877fc1630 --- /dev/null +++ b/codex-rs/utils/file-lock/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "codex-utils-file-lock" +version.workspace = true +edition.workspace = true +license.workspace = true + +[lints] +workspace = true + +[dependencies] diff --git a/codex-rs/utils/file-lock/src/lib.rs b/codex-rs/utils/file-lock/src/lib.rs new file mode 100644 index 000000000000..fdcfbdb81e75 --- /dev/null +++ b/codex-rs/utils/file-lock/src/lib.rs @@ -0,0 +1,168 @@ +use std::fs::File; +use std::fs::create_dir; +use std::fs::remove_dir; +use std::io; +use std::path::Path; +use std::path::PathBuf; +use std::thread; +use std::time::Duration; + +const LOCK_DIR_RETRY_SLEEP: Duration = Duration::from_millis(100); + +/// Result of acquiring a blocking advisory file lock. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FileLockOutcome { + Acquired, + Unsupported, +} + +/// Result of attempting to acquire a non-blocking advisory file lock. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TryFileLockOutcome { + Acquired, + WouldBlock, + Unsupported, +} + +/// Result of attempting to acquire a non-blocking lock directory. +#[derive(Debug, PartialEq, Eq)] +pub enum TryLockDirOutcome { + Acquired(LockDirGuard), + WouldBlock, +} + +/// Guard for a sibling lock directory created with an atomic `mkdir`. +#[derive(Debug, PartialEq, Eq)] +pub struct LockDirGuard { + path: PathBuf, +} + +impl Drop for LockDirGuard { + fn drop(&mut self) { + let _ = remove_dir(&self.path); + } +} + +/// Acquires an exclusive advisory file lock, treating unsupported file locking +/// as a distinct outcome for platforms such as Termux. +pub fn lock_exclusive_optional(file: &File) -> io::Result { + match file.lock() { + Ok(()) => Ok(FileLockOutcome::Acquired), + Err(err) if err.kind() == io::ErrorKind::Unsupported => Ok(FileLockOutcome::Unsupported), + Err(err) => Err(err), + } +} + +/// Attempts to acquire an exclusive advisory file lock without blocking, +/// preserving `WouldBlock` and unsupported file locking as distinct outcomes. +pub fn try_lock_exclusive_optional(file: &File) -> io::Result { + match file.try_lock() { + Ok(()) => Ok(TryFileLockOutcome::Acquired), + Err(std::fs::TryLockError::WouldBlock) => Ok(TryFileLockOutcome::WouldBlock), + Err(std::fs::TryLockError::Error(err)) if err.kind() == io::ErrorKind::Unsupported => { + Ok(TryFileLockOutcome::Unsupported) + } + Err(std::fs::TryLockError::Error(err)) => Err(err), + } +} + +/// Returns the sibling directory path used as a fallback lock for `path`. +pub fn sibling_lock_dir(path: &Path) -> PathBuf { + let Some(file_name) = path.file_name() else { + return path.with_file_name(".lock"); + }; + + let mut lock_name = file_name.to_os_string(); + lock_name.push(".lock"); + path.with_file_name(lock_name) +} + +/// Acquires a sibling lock directory, blocking until it is available. +pub fn acquire_sibling_lock_dir(path: &Path) -> io::Result { + loop { + match try_acquire_sibling_lock_dir(path)? { + TryLockDirOutcome::Acquired(guard) => return Ok(guard), + TryLockDirOutcome::WouldBlock => thread::sleep(LOCK_DIR_RETRY_SLEEP), + } + } +} + +/// Attempts to acquire a sibling lock directory without blocking. +pub fn try_acquire_sibling_lock_dir(path: &Path) -> io::Result { + let lock_dir = sibling_lock_dir(path); + match create_dir(&lock_dir) { + Ok(()) => Ok(TryLockDirOutcome::Acquired(LockDirGuard { path: lock_dir })), + Err(err) if err.kind() == io::ErrorKind::AlreadyExists => Ok(TryLockDirOutcome::WouldBlock), + Err(err) => Err(err), + } +} + +/// Attempts to acquire a shared advisory file lock without blocking, +/// preserving `WouldBlock` and unsupported file locking as distinct outcomes. +pub fn try_lock_shared_optional(file: &File) -> io::Result { + match file.try_lock_shared() { + Ok(()) => Ok(TryFileLockOutcome::Acquired), + Err(std::fs::TryLockError::WouldBlock) => Ok(TryFileLockOutcome::WouldBlock), + Err(std::fs::TryLockError::Error(err)) if err.kind() == io::ErrorKind::Unsupported => { + Ok(TryFileLockOutcome::Unsupported) + } + Err(std::fs::TryLockError::Error(err)) => Err(err), + } +} + +#[cfg(test)] +mod tests { + use super::TryLockDirOutcome; + use super::sibling_lock_dir; + use super::try_acquire_sibling_lock_dir; + use std::fs; + use std::path::PathBuf; + use std::time::SystemTime; + + fn unique_temp_file_path(name: &str) -> PathBuf { + let nanos = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .expect("system clock should be after Unix epoch") + .as_nanos(); + std::env::temp_dir().join(format!( + "codex-file-lock-{name}-{}-{nanos}", + std::process::id() + )) + } + + #[test] + fn sibling_lock_dir_appends_lock_suffix() { + let path = PathBuf::from("/tmp/history.jsonl"); + + assert_eq!( + sibling_lock_dir(&path), + PathBuf::from("/tmp/history.jsonl.lock") + ); + } + + #[test] + fn try_acquire_sibling_lock_dir_is_exclusive_until_drop() { + let path = unique_temp_file_path("exclusive"); + let lock_dir = sibling_lock_dir(&path); + let _ = fs::remove_dir_all(&lock_dir); + + let guard = match try_acquire_sibling_lock_dir(&path).expect("acquire lock dir") { + TryLockDirOutcome::Acquired(guard) => guard, + TryLockDirOutcome::WouldBlock => panic!("first lock attempt should acquire"), + }; + assert!(lock_dir.is_dir()); + + assert!(matches!( + try_acquire_sibling_lock_dir(&path).expect("try acquire held lock dir"), + TryLockDirOutcome::WouldBlock + )); + + drop(guard); + assert!(!lock_dir.exists()); + + let reacquired = try_acquire_sibling_lock_dir(&path).expect("reacquire lock dir"); + assert!(matches!(reacquired, TryLockDirOutcome::Acquired(_))); + + let _ = fs::remove_dir_all(lock_dir); + } +} diff --git a/justfile b/justfile index d5e9fc3a36e1..85324d02cd2b 100644 --- a/justfile +++ b/justfile @@ -124,6 +124,11 @@ argument-comment-lint *args: argument-comment-lint-from-source *args: {{ justfile_directory() }}/tools/argument-comment-lint/run.py "$@" +# Audit advisory file locks that may need Termux Unsupported handling. +[no-cd] +termux-lock-audit *args: + {{ justfile_directory() }}/scripts/termux-lock-audit.sh "$@" + # Tail logs from the state SQLite database log *args: if [ "${1:-}" = "--" ]; then shift; fi; cargo run -p codex-state --bin logs_client -- "$@" diff --git a/scripts/termux-lock-audit.sh b/scripts/termux-lock-audit.sh new file mode 100755 index 000000000000..ac40f8e57246 --- /dev/null +++ b/scripts/termux-lock-audit.sh @@ -0,0 +1,126 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat <<'USAGE' +Usage: scripts/termux-lock-audit.sh [--strict] + +Audits Rust advisory file-lock usage that may need Termux compatibility handling. + +By default this script prints findings and exits successfully. With --strict, it +exits non-zero when candidate file-lock calls are found in files that do not also +mention Unsupported/TryLockError handling. +USAGE +} + +strict=false +for arg in "$@"; do + case "$arg" in + --strict) + strict=true + ;; + -h | --help) + usage + exit 0 + ;; + *) + echo "unknown argument: $arg" >&2 + usage >&2 + exit 2 + ;; + esac +done + +if ! command -v rg >/dev/null 2>&1; then + echo "error: rg is required for this audit" >&2 + exit 1 +fi + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$repo_root" + +unsupported_pattern='std::fs::TryLockError|fs::TryLockError|TryLockError::|ErrorKind::Unsupported|std::io::ErrorKind::Unsupported' +lock_context_pattern='(\.lock\(\)|\.try_lock\(|\.try_lock_shared\(|TryLockError|ErrorKind::Unsupported)' +candidate_file_lock_pattern='\b([A-Za-z_][A-Za-z0-9_]*_file|file)\.(lock|try_lock|try_lock_shared)\(' +candidate_false_positive_pattern='poisoned' +helper_pattern='codex_utils_file_lock|FileLockOutcome|TryFileLockOutcome|lock_exclusive_optional|try_lock_exclusive_optional|try_lock_shared_optional' + +print_section() { + printf '\n== %s ==\n' "$1" +} + +print_section "Unsupported-aware lock handling" +echo "These files already mention Unsupported/TryLockError and are likely patched or intentionally reviewed:" + +unsupported_count=0 +while IFS= read -r file; do + if rg -q "$lock_context_pattern" "$file"; then + rg -n -H "$lock_context_pattern" "$file" + unsupported_count=$((unsupported_count + 1)) + fi +done < <(rg -l "$unsupported_pattern" codex-rs -g '*.rs' || true) + +if [ "$unsupported_count" -eq 0 ]; then + echo "No unsupported-aware lock handling found." +fi + +print_section "Optional file-lock helper usage" +echo "These files use the shared optional advisory file-lock helper:" + +helper_count=0 +while IFS= read -r file; do + rg -n -H "$helper_pattern" "$file" + helper_count=$((helper_count + 1)) +done < <(rg -l "$helper_pattern" codex-rs -g '*.rs' || true) + +if [ "$helper_count" -eq 0 ]; then + echo "No optional file-lock helper usage found." +fi + +print_section "Candidate file-lock calls for manual review" +echo "These receiver names may be std::fs::File advisory locks, but the file does not mention Unsupported/TryLockError handling." +echo "This section can include false positives, such as mutexes wrapping a file." + +review_count=0 +while IFS= read -r file; do + if ! rg -q "$unsupported_pattern" "$file"; then + matches="$(rg -n -H "$candidate_file_lock_pattern" "$file" | rg -v "$candidate_false_positive_pattern" || true)" + if [ -n "$matches" ]; then + echo "$matches" + review_count=$((review_count + 1)) + fi + fi +done < <(rg -l "$candidate_file_lock_pattern" codex-rs -g '*.rs' || true) + +if [ "$review_count" -eq 0 ]; then + echo "No unpatched candidate file-lock calls found." +fi + +print_section "Candidate file-lock calls already in unsupported-aware files" +echo "These are candidate file-lock calls in files that already mention Unsupported/TryLockError handling:" + +aware_candidate_count=0 +while IFS= read -r file; do + if rg -q "$unsupported_pattern" "$file"; then + matches="$(rg -n -H "$candidate_file_lock_pattern" "$file" | rg -v "$candidate_false_positive_pattern" || true)" + if [ -n "$matches" ]; then + echo "$matches" + aware_candidate_count=$((aware_candidate_count + 1)) + fi + fi +done < <(rg -l "$candidate_file_lock_pattern" codex-rs -g '*.rs' || true) + +if [ "$aware_candidate_count" -eq 0 ]; then + echo "No unsupported-aware candidate file-lock calls found." +fi + +print_section "Summary" +echo "unsupported-aware files: $unsupported_count" +echo "optional helper files: $helper_count" +echo "review candidate files: $review_count" +echo "unsupported-aware candidate files: $aware_candidate_count" + +if [ "$strict" = true ] && [ "$review_count" -gt 0 ]; then + echo "strict mode: manual-review candidate files were found" >&2 + exit 1 +fi