Compare commits

..

4 Commits

Author SHA1 Message Date
Michael Bolin
a547507e48 fix: speed up test that uses PowerShell 2026-03-27 16:57:04 -07:00
Drew Hintz
f4f6eca871 [codex] Pin GitHub Actions workflow references (#15828)
Pin floating external GitHub Actions workflow refs to immutable SHAs.

Why are we doing this? Please see the rationale doc:
https://docs.google.com/document/d/1qOURCNx2zszQ0uWx7Fj5ERu4jpiYjxLVWBWgKa2wTsA/edit?tab=t.0

Did this break you? Please roll back and let hintz@ know
2026-03-27 23:00:05 +00:00
Eric Traut
d65deec617 Remove the legacy TUI split (#15922)
This is the part 1 of 2 PRs that will delete the `tui` /
`tui_app_server` split. This part simply deletes the existing `tui`
directory and marks the `tui_app_server` feature flag as removed. I left
the `tui_app_server` feature flag in place for now so its presence
doesn't result in an error. It is simply ignored.

Part 2 will rename the `tui_app_server` directory `tui`. I did this as
two parts to reduce visible code churn.
2026-03-27 22:56:44 +00:00
iceweasel-oai
307e427a9b don't include redundant write roots in apply_patch (#16030)
apply_patch sometimes provides additional parent dir as a writable root
when it is already writable. This is mostly a no-op on Mac/Linux but
causes actual ACL churn on Windows that is best avoided. We are also
seeing some actual failures with these ACLs in the wild, which I haven't
fully tracked down, but it's safe/best to avoid doing it altogether.
2026-03-27 15:41:51 -07:00
877 changed files with 485 additions and 131103 deletions

View File

@@ -48,7 +48,7 @@ jobs:
name: Local Bazel build on ${{ matrix.os }} for ${{ matrix.target }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel CI
id: setup_bazel
@@ -85,7 +85,7 @@ jobs:
- name: Save bazel repository cache
if: always() && !cancelled() && steps.setup_bazel.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@v5
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/bazel-repo-cache
@@ -106,7 +106,7 @@ jobs:
name: Bazel clippy on ${{ matrix.os }} for ${{ matrix.target }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel CI
id: setup_bazel
@@ -136,7 +136,7 @@ jobs:
- name: Save bazel repository cache
if: always() && !cancelled() && steps.setup_bazel.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@v5
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cache/bazel-repo-cache

View File

@@ -8,7 +8,7 @@ jobs:
name: Blob size policy
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0

View File

@@ -14,13 +14,13 @@ jobs:
working-directory: ./codex-rs
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
- name: Run cargo-deny
uses: EmbarkStudios/cargo-deny-action@v2
uses: EmbarkStudios/cargo-deny-action@82eb9f621fbc699dd0918f3ea06864c14cc84246 # v2
with:
rust-version: stable
manifest-path: ./codex-rs/Cargo.toml

View File

@@ -12,15 +12,15 @@ jobs:
NODE_OPTIONS: --max-old-space-size=4096
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup pnpm
uses: pnpm/action-setup@v5
uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with:
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: 22
@@ -28,7 +28,7 @@ jobs:
run: pnpm install --frozen-lockfile
# stage_npm_packages.py requires DotSlash when staging releases.
- uses: facebook/install-dotslash@v2
- uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Stage npm package
id: stage_npm_package
@@ -47,7 +47,7 @@ jobs:
echo "pack_output=$PACK_OUTPUT" >> "$GITHUB_OUTPUT"
- name: Upload staged npm package artifact
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: codex-npm-staging
path: ${{ steps.stage_npm_package.outputs.pack_output }}

View File

@@ -18,7 +18,7 @@ jobs:
if: ${{ github.repository_owner == 'openai' }}
runs-on: ubuntu-latest
steps:
- uses: contributor-assistant/github-action@v2.6.1
- uses: contributor-assistant/github-action@ca4a40a7d1004f18d9960b404b97e5f30a505a08 # v2.6.1
# Run on close only if the PR was merged. This will lock the PR to preserve
# the CLA agreement. We don't want to lock PRs that have been closed without
# merging because the contributor may want to respond with additional comments.

View File

@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Close inactive PRs from contributors
uses: actions/github-script@v8
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |

View File

@@ -18,7 +18,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Annotate locations with typos
uses: codespell-project/codespell-problem-matcher@b80729f885d32f78a716c2f107b4db1025001c42 # v1
- name: Codespell

View File

@@ -19,7 +19,7 @@ jobs:
reason: ${{ steps.normalize-all.outputs.reason }}
has_matches: ${{ steps.normalize-all.outputs.has_matches }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Prepare Codex inputs
env:
@@ -61,7 +61,7 @@ jobs:
# .github/prompts/issue-deduplicator.txt file is obsolete and removed.
- id: codex-all
name: Find duplicates (pass 1, all issues)
uses: openai/codex-action@main
uses: openai/codex-action@0b91f4a2703c23df3102c3f0967d3c6db34eedef # v1
with:
openai-api-key: ${{ secrets.CODEX_OPENAI_API_KEY }}
allow-users: "*"
@@ -155,7 +155,7 @@ jobs:
reason: ${{ steps.normalize-open.outputs.reason }}
has_matches: ${{ steps.normalize-open.outputs.has_matches }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Prepare Codex inputs
env:
@@ -195,7 +195,7 @@ jobs:
- id: codex-open
name: Find duplicates (pass 2, open issues)
uses: openai/codex-action@main
uses: openai/codex-action@0b91f4a2703c23df3102c3f0967d3c6db34eedef # v1
with:
openai-api-key: ${{ secrets.CODEX_OPENAI_API_KEY }}
allow-users: "*"
@@ -342,7 +342,7 @@ jobs:
issues: write
steps:
- name: Comment on issue
uses: actions/github-script@v8
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
env:
CODEX_OUTPUT: ${{ needs.select-final.outputs.codex_output }}
with:

View File

@@ -17,10 +17,10 @@ jobs:
outputs:
codex_output: ${{ steps.codex.outputs.final-message }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- id: codex
uses: openai/codex-action@main
uses: openai/codex-action@0b91f4a2703c23df3102c3f0967d3c6db34eedef # v1
with:
openai-api-key: ${{ secrets.CODEX_OPENAI_API_KEY }}
allow-users: "*"

View File

@@ -19,7 +19,7 @@ jobs:
codex: ${{ steps.detect.outputs.codex }}
workflows: ${{ steps.detect.outputs.workflows }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
- name: Detect changed paths (no external action)
@@ -66,8 +66,8 @@ jobs:
run:
working-directory: codex-rs
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@1.93.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
components: rustfmt
- name: cargo fmt
@@ -82,8 +82,8 @@ jobs:
run:
working-directory: codex-rs
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@1.93.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- uses: taiki-e/install-action@44c6d64aa62cd779e873306675c7a58e86d6d532 # v2
with:
tool: cargo-shear
@@ -97,14 +97,14 @@ jobs:
needs: changed
if: ${{ needs.changed.outputs.argument_comment_lint_package == 'true' || github.event_name == 'push' }}
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@1.93.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
toolchain: nightly-2025-09-18
components: llvm-tools-preview, rustc-dev, rust-src
- name: Cache cargo-dylint tooling
id: cargo_dylint_cache
uses: actions/cache@v5
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin/cargo-dylint
@@ -141,18 +141,18 @@ jobs:
group: codex-runners
labels: codex-windows-x64
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Linux sandbox build dependencies
if: ${{ runner.os == 'Linux' }}
shell: bash
run: |
sudo DEBIAN_FRONTEND=noninteractive apt-get update
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev
- uses: dtolnay/rust-toolchain@1.93.0
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
toolchain: nightly-2025-09-18
components: llvm-tools-preview, rustc-dev, rust-src
- uses: facebook/install-dotslash@v2
- uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Run argument comment lint on codex-rs
shell: bash
run: ./tools/argument-comment-lint/run-prebuilt-linter.sh
@@ -258,7 +258,7 @@ jobs:
labels: codex-windows-arm64
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Linux build dependencies
if: ${{ runner.os == 'Linux' }}
shell: bash
@@ -272,7 +272,7 @@ jobs:
fi
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${packages[@]}"
fi
- uses: dtolnay/rust-toolchain@1.93.0
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
targets: ${{ matrix.target }}
components: clippy
@@ -301,7 +301,7 @@ jobs:
# avoid caching the large target dir on the gnu-dev job.
- name: Restore cargo home cache
id: cache_cargo_home_restore
uses: actions/cache/restore@v5
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin/
@@ -346,7 +346,7 @@ jobs:
- name: Restore sccache cache (fallback)
if: ${{ env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' }}
id: cache_sccache_restore
uses: actions/cache/restore@v5
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ github.workspace }}/.sccache/
key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}
@@ -373,7 +373,7 @@ jobs:
- if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}}
name: Restore APT cache (musl)
id: cache_apt_restore
uses: actions/cache/restore@v5
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
/var/cache/apt
@@ -486,7 +486,7 @@ jobs:
- name: Upload Cargo timings (clippy)
if: always()
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: cargo-timings-rust-ci-clippy-${{ matrix.target }}-${{ matrix.profile }}
path: codex-rs/target/**/cargo-timings/cargo-timing.html
@@ -497,7 +497,7 @@ jobs:
- name: Save cargo home cache
if: always() && !cancelled() && steps.cache_cargo_home_restore.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@v5
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin/
@@ -513,7 +513,7 @@ jobs:
- name: Save sccache cache (fallback)
if: always() && !cancelled() && env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true'
continue-on-error: true
uses: actions/cache/save@v5
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ github.workspace }}/.sccache/
key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}
@@ -538,7 +538,7 @@ jobs:
- name: Save APT cache (musl)
if: always() && !cancelled() && (matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl') && steps.cache_apt_restore.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@v5
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
/var/cache/apt
@@ -598,9 +598,9 @@ jobs:
labels: codex-windows-arm64
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Node.js for js_repl tests
uses: actions/setup-node@v6
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version-file: codex-rs/node-version.txt
- name: Install Linux build dependencies
@@ -616,9 +616,9 @@ jobs:
# Some integration tests rely on DotSlash being installed.
# See https://github.com/openai/codex/pull/7617.
- name: Install DotSlash
uses: facebook/install-dotslash@v2
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- uses: dtolnay/rust-toolchain@1.93.0
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
targets: ${{ matrix.target }}
@@ -633,7 +633,7 @@ jobs:
- name: Restore cargo home cache
id: cache_cargo_home_restore
uses: actions/cache/restore@v5
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin/
@@ -673,7 +673,7 @@ jobs:
- name: Restore sccache cache (fallback)
if: ${{ env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true' }}
id: cache_sccache_restore
uses: actions/cache/restore@v5
uses: actions/cache/restore@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ github.workspace }}/.sccache/
key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}
@@ -715,7 +715,7 @@ jobs:
- name: Upload Cargo timings (nextest)
if: always()
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: cargo-timings-rust-ci-nextest-${{ matrix.target }}-${{ matrix.profile }}
path: codex-rs/target/**/cargo-timings/cargo-timing.html
@@ -724,7 +724,7 @@ jobs:
- name: Save cargo home cache
if: always() && !cancelled() && steps.cache_cargo_home_restore.outputs.cache-hit != 'true'
continue-on-error: true
uses: actions/cache/save@v5
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: |
~/.cargo/bin/
@@ -736,7 +736,7 @@ jobs:
- name: Save sccache cache (fallback)
if: always() && !cancelled() && env.USE_SCCACHE == 'true' && env.SCCACHE_GHA_ENABLED != 'true'
continue-on-error: true
uses: actions/cache/save@v5
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5
with:
path: ${{ github.workspace }}/.sccache/
key: sccache-${{ matrix.runner }}-${{ matrix.target }}-${{ matrix.profile }}-${{ steps.lockhash.outputs.hash }}-${{ github.run_id }}

View File

@@ -53,9 +53,9 @@ jobs:
labels: codex-windows-x64
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dtolnay/rust-toolchain@1.93.0
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
toolchain: nightly-2025-09-18
targets: ${{ matrix.target }}
@@ -97,7 +97,7 @@ jobs:
(cd "${RUNNER_TEMP}" && tar -czf "$GITHUB_WORKSPACE/$archive_path" argument-comment-lint)
fi
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: argument-comment-lint-${{ matrix.target }}
path: dist/argument-comment-lint/${{ matrix.target }}/*

View File

@@ -18,7 +18,7 @@ jobs:
if: github.repository == 'openai/codex'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: main
fetch-depth: 0
@@ -43,7 +43,7 @@ jobs:
curl --http1.1 --fail --show-error --location "${headers[@]}" "${url}" | jq '.' > codex-rs/core/models.json
- name: Open pull request (if changed)
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8
with:
commit-message: "Update models.json"
title: "Update models.json"

View File

@@ -67,7 +67,7 @@ jobs:
labels: codex-windows-arm64
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Print runner specs (Windows)
shell: powershell
run: |
@@ -82,7 +82,7 @@ jobs:
Write-Host "Total RAM: $ramGiB GiB"
Write-Host "Disk usage:"
Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize Name, @{Name='Size(GB)';Expression={[math]::Round(($_.Used + $_.Free) / 1GB, 1)}}, @{Name='Free(GB)';Expression={[math]::Round($_.Free / 1GB, 1)}}
- uses: dtolnay/rust-toolchain@1.93.0
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
targets: ${{ matrix.target }}
@@ -92,7 +92,7 @@ jobs:
cargo build --target ${{ matrix.target }} --release --timings ${{ matrix.build_args }}
- name: Upload Cargo timings
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: cargo-timings-rust-release-windows-${{ matrix.target }}-${{ matrix.bundle }}
path: codex-rs/target/**/cargo-timings/cargo-timing.html
@@ -112,7 +112,7 @@ jobs:
fi
- name: Upload Windows binaries
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: windows-binaries-${{ matrix.target }}-${{ matrix.bundle }}
path: |
@@ -147,16 +147,16 @@ jobs:
labels: codex-windows-arm64
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Download prebuilt Windows primary binaries
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: windows-binaries-${{ matrix.target }}-primary
path: codex-rs/target/${{ matrix.target }}/release
- name: Download prebuilt Windows helper binaries
uses: actions/download-artifact@v8
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: windows-binaries-${{ matrix.target }}-helpers
path: codex-rs/target/${{ matrix.target }}/release
@@ -193,7 +193,7 @@ jobs:
cp target/${{ matrix.target }}/release/codex-command-runner.exe "$dest/codex-command-runner-${{ matrix.target }}.exe"
- name: Install DotSlash
uses: facebook/install-dotslash@v2
uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Compress artifacts
shell: bash
@@ -257,7 +257,7 @@ jobs:
"${GITHUB_WORKSPACE}/.github/workflows/zstd" -T0 -19 "$dest/$base"
done
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: ${{ matrix.target }}
path: |

View File

@@ -45,7 +45,7 @@ jobs:
git \
libncursesw5-dev
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Build, smoke-test, and stage zsh artifact
shell: bash
@@ -53,7 +53,7 @@ jobs:
"${GITHUB_WORKSPACE}/.github/scripts/build-zsh-release-artifact.sh" \
"dist/zsh/${{ matrix.target }}/${{ matrix.archive_name }}"
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: codex-zsh-${{ matrix.target }}
path: dist/zsh/${{ matrix.target }}/*
@@ -81,7 +81,7 @@ jobs:
brew install autoconf
fi
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Build, smoke-test, and stage zsh artifact
shell: bash
@@ -89,7 +89,7 @@ jobs:
"${GITHUB_WORKSPACE}/.github/scripts/build-zsh-release-artifact.sh" \
"dist/zsh/${{ matrix.target }}/${{ matrix.archive_name }}"
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: codex-zsh-${{ matrix.target }}
path: dist/zsh/${{ matrix.target }}/*

View File

@@ -19,8 +19,8 @@ jobs:
tag-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@1.92
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: dtolnay/rust-toolchain@c2b55edffaf41a251c410bb32bed22afefa800f1 # 1.92
- name: Validate tag matches Cargo.toml version
shell: bash
run: |
@@ -79,7 +79,7 @@ jobs:
target: aarch64-unknown-linux-gnu
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Print runner specs (Linux)
if: ${{ runner.os == 'Linux' }}
shell: bash
@@ -125,7 +125,7 @@ jobs:
sudo apt-get update -y
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y libubsan1
fi
- uses: dtolnay/rust-toolchain@1.93.0
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
with:
targets: ${{ matrix.target }}
@@ -235,7 +235,7 @@ jobs:
cargo build --target ${{ matrix.target }} --release --timings --bin codex --bin codex-responses-api-proxy
- name: Upload Cargo timings
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: cargo-timings-rust-release-${{ matrix.target }}
path: codex-rs/target/**/cargo-timings/cargo-timing.html
@@ -374,7 +374,7 @@ jobs:
zstd -T0 -19 --rm "$dest/$base"
done
- uses: actions/upload-artifact@v7
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: ${{ matrix.target }}
# Upload the per-binary .zst files as well as the new .tar.gz
@@ -420,7 +420,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Generate release notes from tag commit message
id: release_notes
@@ -442,7 +442,7 @@ jobs:
echo "path=${notes_path}" >> "${GITHUB_OUTPUT}"
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
path: dist
@@ -492,12 +492,12 @@ jobs:
fi
- name: Setup pnpm
uses: pnpm/action-setup@v5
uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with:
run_install: false
- name: Setup Node.js for npm packaging
uses: actions/setup-node@v6
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: 22
@@ -505,7 +505,7 @@ jobs:
run: pnpm install --frozen-lockfile
# stage_npm_packages.py requires DotSlash when staging releases.
- uses: facebook/install-dotslash@v2
- uses: facebook/install-dotslash@1e4e7b3e07eaca387acb98f1d4720e0bee8dbb6a # v2
- name: Stage npm packages
env:
GH_TOKEN: ${{ github.token }}
@@ -523,7 +523,7 @@ jobs:
cp scripts/install/install.ps1 dist/install.ps1
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
with:
name: ${{ steps.release_name.outputs.name }}
tag_name: ${{ github.ref_name }}
@@ -533,21 +533,21 @@ jobs:
# (e.g. -alpha, -beta). Otherwise publish a normal release.
prerelease: ${{ contains(steps.release_name.outputs.name, '-') }}
- uses: facebook/dotslash-publish-release@v2
- uses: facebook/dotslash-publish-release@9c9ec027515c34db9282a09a25a9cab5880b2c52 # v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag: ${{ github.ref_name }}
config: .github/dotslash-config.json
- uses: facebook/dotslash-publish-release@v2
- uses: facebook/dotslash-publish-release@9c9ec027515c34db9282a09a25a9cab5880b2c52 # v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag: ${{ github.ref_name }}
config: .github/dotslash-zsh-config.json
- uses: facebook/dotslash-publish-release@v2
- uses: facebook/dotslash-publish-release@9c9ec027515c34db9282a09a25a9cab5880b2c52 # v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -582,7 +582,7 @@ jobs:
steps:
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: 22
registry-url: "https://registry.npmjs.org"

View File

@@ -25,10 +25,10 @@ jobs:
v8_version: ${{ steps.v8_version.outputs.version }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
@@ -75,13 +75,13 @@ jobs:
target: aarch64-unknown-linux-musl
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel
uses: bazelbuild/setup-bazelisk@v3
uses: bazelbuild/setup-bazelisk@6ecf4fd8b7d1f9721785f1dd656a689acf9add47 # v3
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
@@ -135,7 +135,7 @@ jobs:
--output-dir "dist/${TARGET}"
- name: Upload staged musl artifacts
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: rusty-v8-${{ needs.metadata.outputs.v8_version }}-${{ matrix.target }}
path: dist/${{ matrix.target }}/*
@@ -174,12 +174,12 @@ jobs:
exit 1
fi
- uses: actions/download-artifact@v8
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
path: dist
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
with:
tag_name: ${{ needs.metadata.outputs.release_tag }}
name: ${{ needs.metadata.outputs.release_tag }}

View File

@@ -13,7 +13,7 @@ jobs:
timeout-minutes: 10
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Install Linux bwrap build dependencies
shell: bash
@@ -23,17 +23,17 @@ jobs:
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev
- name: Setup pnpm
uses: pnpm/action-setup@v5
uses: pnpm/action-setup@a8198c4bff370c8506180b035930dea56dbd5288 # v5
with:
run_install: false
- name: Setup Node.js
uses: actions/setup-node@v6
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: 22
cache: pnpm
- uses: dtolnay/rust-toolchain@1.93.0
- uses: dtolnay/rust-toolchain@a0b273b48ed29de4470960879e8381ff45632f26 # 1.93.0
- name: build codex
run: cargo build --bin codex

View File

@@ -38,10 +38,10 @@ jobs:
v8_version: ${{ steps.v8_version.outputs.version }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
@@ -72,13 +72,13 @@ jobs:
target: aarch64-unknown-linux-musl
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Bazel
uses: bazelbuild/setup-bazelisk@v3
uses: bazelbuild/setup-bazelisk@6ecf4fd8b7d1f9721785f1dd656a689acf9add47 # v3
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.12"
@@ -126,7 +126,7 @@ jobs:
--output-dir "dist/${TARGET}"
- name: Upload staged musl artifacts
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
with:
name: v8-canary-${{ needs.metadata.outputs.v8_version }}-${{ matrix.target }}
path: dist/${{ matrix.target }}/*

View File

@@ -35,9 +35,9 @@ In the codex-rs folder where the rust code lives:
- If a file exceeds roughly 800 LoC, add new functionality in a new module instead of extending
the existing file unless there is a strong documented reason not to.
- This rule applies especially to high-touch files that already attract unrelated changes, such
as `codex-rs/tui/src/app.rs`, `codex-rs/tui/src/bottom_pane/chat_composer.rs`,
`codex-rs/tui/src/bottom_pane/footer.rs`, `codex-rs/tui/src/chatwidget.rs`,
`codex-rs/tui/src/bottom_pane/mod.rs`, and similarly central orchestration modules.
as `codex-rs/tui_app_server/src/app.rs`, `codex-rs/tui_app_server/src/bottom_pane/chat_composer.rs`,
`codex-rs/tui_app_server/src/bottom_pane/footer.rs`, `codex-rs/tui_app_server/src/chatwidget.rs`,
`codex-rs/tui_app_server/src/bottom_pane/mod.rs`, and similarly central orchestration modules.
- When extracting code from a large module, move the related tests and module/type docs toward
the new implementation so the invariants stay close to the code that owns them.
- When running Rust commands (e.g. `just fix` or `cargo test`) be patient with the command and never try to kill them using the PID. Rust lock can make the execution slow, this is expected.
@@ -66,7 +66,7 @@ Likewise, when reviewing code, do not hesitate to push back on PRs that would un
## TUI style conventions
See `codex-rs/tui/styles.md`.
See `codex-rs/tui_app_server/styles.md`.
## TUI code conventions

97
codex-rs/Cargo.lock generated
View File

@@ -1636,7 +1636,6 @@ dependencies = [
"codex-stdio-to-uds",
"codex-terminal-detection",
"codex-tui",
"codex-tui-app-server",
"codex-utils-cargo-bin",
"codex-utils-cli",
"codex-windows-sandbox",
@@ -2624,102 +2623,6 @@ dependencies = [
[[package]]
name = "codex-tui"
version = "0.0.0"
dependencies = [
"anyhow",
"arboard",
"assert_matches",
"base64 0.22.1",
"chrono",
"clap",
"codex-ansi-escape",
"codex-app-server-client",
"codex-app-server-protocol",
"codex-arg0",
"codex-backend-client",
"codex-chatgpt",
"codex-cli",
"codex-client",
"codex-cloud-requirements",
"codex-core",
"codex-exec-server",
"codex-features",
"codex-feedback",
"codex-file-search",
"codex-git-utils",
"codex-login",
"codex-otel",
"codex-protocol",
"codex-shell-command",
"codex-state",
"codex-terminal-detection",
"codex-tui-app-server",
"codex-utils-absolute-path",
"codex-utils-approval-presets",
"codex-utils-cargo-bin",
"codex-utils-cli",
"codex-utils-elapsed",
"codex-utils-fuzzy-match",
"codex-utils-oss",
"codex-utils-pty",
"codex-utils-sandbox-summary",
"codex-utils-sleep-inhibitor",
"codex-utils-string",
"codex-windows-sandbox",
"color-eyre",
"cpal",
"crossterm",
"derive_more 2.1.1",
"diffy",
"dirs",
"dunce",
"hound",
"image",
"insta",
"itertools 0.14.0",
"lazy_static",
"libc",
"pathdiff",
"pretty_assertions",
"pulldown-cmark",
"rand 0.9.2",
"ratatui",
"ratatui-macros",
"regex-lite",
"reqwest",
"rmcp",
"serde",
"serde_json",
"serial_test",
"shlex",
"strum 0.27.2",
"strum_macros 0.28.0",
"supports-color 3.0.2",
"syntect",
"tempfile",
"textwrap 0.16.2",
"thiserror 2.0.18",
"tokio",
"tokio-stream",
"tokio-util",
"toml 0.9.11+spec-1.1.0",
"tracing",
"tracing-appender",
"tracing-subscriber",
"two-face",
"unicode-segmentation",
"unicode-width 0.2.1",
"url",
"uuid",
"vt100",
"webbrowser",
"which 8.0.0",
"windows-sys 0.52.0",
"winsplit",
]
[[package]]
name = "codex-tui-app-server"
version = "0.0.0"
dependencies = [
"anyhow",
"arboard",

View File

@@ -49,7 +49,6 @@ members = [
"sandboxing",
"stdio-to-uds",
"otel",
"tui",
"tui_app_server",
"tools",
"v8-poc",
@@ -86,7 +85,7 @@ members = [
resolver = "2"
[workspace.package]
version = "0.118.0-alpha.3"
version = "0.0.0"
# Track the edition for all workspace crates in one place. Individual
# crates can still override this value, but keeping it here means new
# crates created with `cargo new -w ...` automatically inherit the 2024
@@ -150,8 +149,7 @@ codex-state = { path = "state" }
codex-stdio-to-uds = { path = "stdio-to-uds" }
codex-terminal-detection = { path = "terminal-detection" }
codex-tools = { path = "tools" }
codex-tui = { path = "tui" }
codex-tui-app-server = { path = "tui_app_server" }
codex-tui = { path = "tui_app_server" }
codex-v8-poc = { path = "v8-poc" }
codex-utils-absolute-path = { path = "utils/absolute-path" }
codex-utils-approval-presets = { path = "utils/approval-presets" }

View File

@@ -4,9 +4,8 @@ This module implements the websocket-backed app-server client transport.
It owns the remote connection lifecycle, including the initialize/initialized
handshake, JSON-RPC request/response routing, server-request resolution, and
notification streaming. The rest of the crate uses the same `AppServerEvent`
surface for both in-process and remote transports, so callers such as
`tui_app_server` can switch between them without changing their higher-level
session logic.
surface for both in-process and remote transports, so callers such as the TUI
can switch between them without changing their higher-level session logic.
*/
use std::collections::HashMap;

View File

@@ -88,7 +88,7 @@ use toml::Value as TomlValue;
use tracing::Instrument;
const EXTERNAL_AUTH_REFRESH_TIMEOUT: Duration = Duration::from_secs(10);
const TUI_APP_SERVER_CLIENT_NAME: &str = "codex-tui";
const TUI_CLIENT_NAME: &str = "codex-tui";
#[derive(Clone)]
struct ExternalAuthRefreshBridge {
@@ -569,9 +569,9 @@ impl MessageProcessor {
} = params.client_info;
session.app_server_client_name = Some(name.clone());
session.client_version = Some(version.clone());
let originator = if name == TUI_APP_SERVER_CLIENT_NAME {
// TODO: Remove this temporary workaround once app-server clients no longer
// need to retain the legacy TUI `codex_cli_rs` originator behavior.
let originator = if name == TUI_CLIENT_NAME {
// TODO: Remove this temporary workaround once the TUI no longer
// needs to retain the `codex_cli_rs` originator behavior.
DEFAULT_ORIGINATOR.to_string()
} else {
name.clone()

View File

@@ -41,7 +41,6 @@ codex-state = { workspace = true }
codex-stdio-to-uds = { workspace = true }
codex-terminal-detection = { workspace = true }
codex-tui = { workspace = true }
codex-tui-app-server = { workspace = true }
libc = { workspace = true }
owo-colors = { workspace = true }
regex-lite = { workspace = true }

View File

@@ -24,10 +24,10 @@ use codex_execpolicy::ExecPolicyCheckCommand;
use codex_responses_api_proxy::Args as ResponsesApiProxyArgs;
use codex_state::StateRuntime;
use codex_state::state_db_path;
use codex_tui::AppExitInfo;
use codex_tui::Cli as TuiCli;
use codex_tui::ExitReason;
use codex_tui::update_action::UpdateAction;
use codex_tui_app_server::AppExitInfo;
use codex_tui_app_server::Cli as TuiCli;
use codex_tui_app_server::ExitReason;
use codex_tui_app_server::update_action::UpdateAction;
use codex_utils_cli::CliConfigOverrides;
use owo_colors::OwoColorize;
use std::io::IsTerminal;
@@ -525,7 +525,7 @@ struct FeatureToggles {
#[derive(Debug, Default, Parser, Clone)]
struct InteractiveRemoteOptions {
/// Connect the app-server-backed TUI to a remote app server websocket endpoint.
/// Connect the TUI to a remote app server websocket endpoint.
///
/// Accepted forms: `ws://host:port` or `wss://host:port`.
#[arg(long = "remote", value_name = "ADDR")]
@@ -1226,7 +1226,6 @@ async fn run_interactive_tui(
}
}
let use_app_server_tui = codex_tui::should_use_app_server_tui(&interactive).await?;
let normalized_remote = remote
.as_deref()
.map(codex_tui_app_server::normalize_remote_addr)
@@ -1237,93 +1236,19 @@ async fn run_interactive_tui(
"`--remote-auth-token-env` requires `--remote`.",
));
}
if normalized_remote.is_some() && !use_app_server_tui {
return Ok(AppExitInfo::fatal(
"`--remote` requires the `tui_app_server` feature flag to be enabled.",
));
}
if use_app_server_tui {
let remote_auth_token = remote_auth_token_env
.as_deref()
.map(read_remote_auth_token_from_env_var)
.transpose()
.map_err(std::io::Error::other)?;
codex_tui_app_server::run_main(
into_app_server_tui_cli(interactive),
arg0_paths,
codex_core::config_loader::LoaderOverrides::default(),
normalized_remote,
remote_auth_token,
)
.await
.map(into_legacy_app_exit_info)
} else {
codex_tui::run_main(
interactive,
arg0_paths,
codex_core::config_loader::LoaderOverrides::default(),
)
.await
}
}
fn into_app_server_tui_cli(cli: TuiCli) -> codex_tui_app_server::Cli {
codex_tui_app_server::Cli {
prompt: cli.prompt,
images: cli.images,
resume_picker: cli.resume_picker,
resume_last: cli.resume_last,
resume_session_id: cli.resume_session_id,
resume_show_all: cli.resume_show_all,
fork_picker: cli.fork_picker,
fork_last: cli.fork_last,
fork_session_id: cli.fork_session_id,
fork_show_all: cli.fork_show_all,
model: cli.model,
oss: cli.oss,
oss_provider: cli.oss_provider,
config_profile: cli.config_profile,
sandbox_mode: cli.sandbox_mode,
approval_policy: cli.approval_policy,
full_auto: cli.full_auto,
dangerously_bypass_approvals_and_sandbox: cli.dangerously_bypass_approvals_and_sandbox,
cwd: cli.cwd,
web_search: cli.web_search,
add_dir: cli.add_dir,
no_alt_screen: cli.no_alt_screen,
config_overrides: cli.config_overrides,
}
}
fn into_legacy_update_action(
action: codex_tui_app_server::update_action::UpdateAction,
) -> UpdateAction {
match action {
codex_tui_app_server::update_action::UpdateAction::NpmGlobalLatest => {
UpdateAction::NpmGlobalLatest
}
codex_tui_app_server::update_action::UpdateAction::BunGlobalLatest => {
UpdateAction::BunGlobalLatest
}
codex_tui_app_server::update_action::UpdateAction::BrewUpgrade => UpdateAction::BrewUpgrade,
}
}
fn into_legacy_exit_reason(reason: codex_tui_app_server::ExitReason) -> ExitReason {
match reason {
codex_tui_app_server::ExitReason::UserRequested => ExitReason::UserRequested,
codex_tui_app_server::ExitReason::Fatal(message) => ExitReason::Fatal(message),
}
}
fn into_legacy_app_exit_info(exit_info: codex_tui_app_server::AppExitInfo) -> AppExitInfo {
AppExitInfo {
token_usage: exit_info.token_usage,
thread_id: exit_info.thread_id,
thread_name: exit_info.thread_name,
update_action: exit_info.update_action.map(into_legacy_update_action),
exit_reason: into_legacy_exit_reason(exit_info.exit_reason),
}
let remote_auth_token = remote_auth_token_env
.as_deref()
.map(read_remote_auth_token_from_env_var)
.transpose()
.map_err(std::io::Error::other)?;
codex_tui_app_server::run_main(
interactive,
arg0_paths,
codex_core::config_loader::LoaderOverrides::default(),
normalized_remote,
remote_auth_token,
)
.await
}
fn confirm(prompt: &str) -> std::io::Result<bool> {

View File

@@ -24,7 +24,7 @@ codex-client = { workspace = true }
codex-core = { path = "../core" }
codex-git-utils = { workspace = true }
codex-login = { path = "../login" }
codex-tui = { path = "../tui" }
codex-tui = { workspace = true }
codex-utils-cli = { workspace = true }
crossterm = { workspace = true, features = ["event-stream"] }
ratatui = { workspace = true }

View File

@@ -930,7 +930,8 @@ pub async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> an
if let Some(page) = app.new_task.as_mut() {
if page.composer.flush_paste_burst_if_due() { needs_redraw = true; }
if page.composer.is_in_paste_burst() {
let _ = frame_tx.send(Instant::now() + codex_tui::ComposerInput::recommended_flush_delay());
let _ = frame_tx
.send(Instant::now() + codex_tui_app_server::ComposerInput::recommended_flush_delay());
}
}
// Keep spinner pulsing only while loading.
@@ -1491,7 +1492,9 @@ pub async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> an
_ => {
if page.submitting {
// Ignore input while submitting
} else if let codex_tui::ComposerAction::Submitted(text) = page.composer.input(key) {
} else if let codex_tui_app_server::ComposerAction::Submitted(text) =
page.composer.input(key)
{
// Submit only if we have an env id
if let Some(env) = page.env_id.clone() {
append_error_log(format!(
@@ -1521,7 +1524,10 @@ pub async fn run_main(cli: Cli, _codex_linux_sandbox_exe: Option<PathBuf>) -> an
needs_redraw = true;
// If pasteburst is active, schedule a microflush frame.
if page.composer.is_in_paste_burst() {
let _ = frame_tx.send(Instant::now() + codex_tui::ComposerInput::recommended_flush_delay());
let _ = frame_tx.send(
Instant::now()
+ codex_tui_app_server::ComposerInput::recommended_flush_delay(),
);
}
// Always schedule an immediate redraw for key edits in the composer.
let _ = frame_tx.send(Instant::now());
@@ -2130,8 +2136,8 @@ mod tests {
use codex_cloud_tasks_client::TaskId;
use codex_cloud_tasks_client::TaskStatus;
use codex_cloud_tasks_client::TaskSummary;
use codex_tui::ComposerAction;
use codex_tui::ComposerInput;
use codex_tui_app_server::ComposerAction;
use codex_tui_app_server::ComposerInput;
use crossterm::event::KeyCode;
use crossterm::event::KeyEvent;
use crossterm::event::KeyModifiers;

View File

@@ -1,4 +1,4 @@
use codex_tui::ComposerInput;
use codex_tui_app_server::ComposerInput;
pub struct NewTaskPage {
pub composer: ComposerInput,

View File

@@ -23,7 +23,7 @@ use crate::app::AttemptView;
use crate::util::format_relative_time_now;
use codex_cloud_tasks_client::AttemptStatus;
use codex_cloud_tasks_client::TaskStatus;
use codex_tui::render_markdown_text;
use codex_tui_app_server::render_markdown_text;
pub fn draw(frame: &mut Frame, app: &mut App) {
let area = frame.area();

View File

@@ -3,7 +3,7 @@ use codex_app_server_protocol::AppInfo;
use serde::Deserialize;
use serde::Serialize;
const TUI_APP_SERVER_CLIENT_NAME: &str = "codex-tui";
const TUI_CLIENT_NAME: &str = "codex-tui";
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
@@ -96,7 +96,7 @@ pub(crate) fn filter_tool_suggest_discoverable_tools_for_client(
discoverable_tools: Vec<DiscoverableTool>,
app_server_client_name: Option<&str>,
) -> Vec<DiscoverableTool> {
if app_server_client_name != Some(TUI_APP_SERVER_CLIENT_NAME) {
if app_server_client_name != Some(TUI_CLIENT_NAME) {
return discoverable_tools;
}

View File

@@ -68,7 +68,11 @@ fn to_abs_path(cwd: &Path, path: &Path) -> Option<AbsolutePathBuf> {
AbsolutePathBuf::resolve_path_against_base(path, cwd).ok()
}
fn write_permissions_for_paths(file_paths: &[AbsolutePathBuf]) -> Option<PermissionProfile> {
fn write_permissions_for_paths(
file_paths: &[AbsolutePathBuf],
file_system_sandbox_policy: &codex_protocol::permissions::FileSystemSandboxPolicy,
cwd: &Path,
) -> Option<PermissionProfile> {
let write_paths = file_paths
.iter()
.map(|path| {
@@ -76,6 +80,7 @@ fn write_permissions_for_paths(file_paths: &[AbsolutePathBuf]) -> Option<Permiss
.unwrap_or_else(|| path.clone())
.into_path_buf()
})
.filter(|path| !file_system_sandbox_policy.can_write_path_with_cwd(path.as_path(), cwd))
.collect::<BTreeSet<_>>()
.into_iter()
.map(AbsolutePathBuf::from_absolute_path)
@@ -107,16 +112,16 @@ async fn effective_patch_permissions(
session.granted_session_permissions().await.as_ref(),
session.granted_turn_permissions().await.as_ref(),
);
let effective_additional_permissions = apply_granted_turn_permissions(
session,
crate::sandboxing::SandboxPermissions::UseDefault,
write_permissions_for_paths(&file_paths),
)
.await;
let file_system_sandbox_policy = effective_file_system_sandbox_policy(
&turn.file_system_sandbox_policy,
granted_permissions.as_ref(),
);
let effective_additional_permissions = apply_granted_turn_permissions(
session,
crate::sandboxing::SandboxPermissions::UseDefault,
write_permissions_for_paths(&file_paths, &file_system_sandbox_policy, turn.cwd.as_path()),
)
.await;
(
file_paths,

View File

@@ -1,5 +1,7 @@
use super::*;
use codex_apply_patch::MaybeApplyPatchVerified;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
use pretty_assertions::assert_eq;
use tempfile::TempDir;
@@ -26,3 +28,53 @@ fn approval_keys_include_move_destination() {
let keys = file_paths_for_action(&action);
assert_eq!(keys.len(), 2);
}
#[test]
fn write_permissions_for_paths_skip_dirs_already_writable_under_workspace_root() {
let tmp = TempDir::new().expect("tmp");
let cwd = tmp.path();
let nested = cwd.join("nested");
std::fs::create_dir_all(&nested).expect("create nested dir");
let file_path = AbsolutePathBuf::try_from(nested.join("file.txt"))
.expect("nested file path should be absolute");
let sandbox_policy = FileSystemSandboxPolicy::from(&SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
read_only_access: Default::default(),
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: false,
});
let permissions = write_permissions_for_paths(&[file_path], &sandbox_policy, cwd);
assert_eq!(permissions, None);
}
#[test]
fn write_permissions_for_paths_keep_dirs_outside_workspace_root() {
let tmp = TempDir::new().expect("tmp");
let cwd = tmp.path().join("workspace");
let outside = tmp.path().join("outside");
std::fs::create_dir_all(&cwd).expect("create cwd");
std::fs::create_dir_all(&outside).expect("create outside dir");
let file_path = AbsolutePathBuf::try_from(outside.join("file.txt"))
.expect("outside file path should be absolute");
let sandbox_policy = FileSystemSandboxPolicy::from(&SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
read_only_access: Default::default(),
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
});
let permissions = write_permissions_for_paths(&[file_path], &sandbox_policy, &cwd);
let expected_outside = AbsolutePathBuf::from_absolute_path(dunce::simplified(
&outside.canonicalize().expect("canonicalize outside dir"),
))
.expect("outside dir should be absolute");
assert_eq!(
permissions.and_then(|profile| profile.file_system.and_then(|fs| fs.write)),
Some(vec![expected_outside])
);
}

View File

@@ -176,7 +176,7 @@ pub enum Feature {
VoiceTranscription,
/// Enable experimental realtime voice conversation mode in the TUI.
RealtimeConversation,
/// Route interactive startup to the app-server-backed TUI implementation.
/// Removed compatibility flag. The TUI now always uses the app-server implementation.
TuiAppServer,
/// Prevent idle system sleep while a turn is actively running.
PreventIdleSleep,
@@ -371,10 +371,16 @@ impl Features {
Feature::WebSearchCached,
);
}
"tui_app_server" => {
continue;
}
_ => {}
}
match feature_for_key(k) {
Some(feat) => {
if matches!(feat, Feature::TuiAppServer) {
continue;
}
if k != feat.key() {
self.record_legacy_usage(k.as_str(), feat);
}
@@ -822,7 +828,7 @@ pub const FEATURES: &[FeatureSpec] = &[
FeatureSpec {
id: Feature::TuiAppServer,
key: "tui_app_server",
stage: Stage::Stable,
stage: Stage::Removed,
default_enabled: true,
},
FeatureSpec {

View File

@@ -340,6 +340,13 @@ mod tests {
args.iter().map(ToString::to_string).collect()
}
fn assert_unsafe_parsed_command(words: &[&str]) {
assert!(
!is_safe_powershell_command(&vec_str(words)),
"expected parsed command {words:?} to require approval",
);
}
#[test]
fn recognizes_safe_powershell_wrappers() {
assert!(is_safe_command_windows(&vec_str(&[
@@ -441,136 +448,69 @@ mod tests {
#[test]
fn rejects_git_global_override_options() {
let Some(pwsh) = try_find_pwsh_executable_blocking() else {
return;
};
let pwsh: String = pwsh.as_path().to_str().unwrap().into();
for script in [
"git -c core.pager=cat show HEAD:foo.rs",
"git --config-env core.pager=PAGER show HEAD:foo.rs",
"git --config-env=core.pager=PAGER show HEAD:foo.rs",
"git --git-dir .evil-git diff HEAD~1..HEAD",
"git --git-dir=.evil-git diff HEAD~1..HEAD",
"git --work-tree . status",
"git --work-tree=. status",
"git --exec-path .git/helpers show HEAD:foo.rs",
"git --exec-path=.git/helpers show HEAD:foo.rs",
"git --namespace attacker show HEAD:foo.rs",
"git --namespace=attacker show HEAD:foo.rs",
"git --super-prefix attacker/ show HEAD:foo.rs",
"git --super-prefix=attacker/ show HEAD:foo.rs",
for words in [
&["git", "-c", "core.pager=cat", "show", "HEAD:foo.rs"][..],
&[
"git",
"--config-env",
"core.pager=PAGER",
"show",
"HEAD:foo.rs",
][..],
&[
"git",
"--config-env=core.pager=PAGER",
"show",
"HEAD:foo.rs",
][..],
&["git", "--git-dir", ".evil-git", "diff", "HEAD~1..HEAD"][..],
&["git", "--git-dir=.evil-git", "diff", "HEAD~1..HEAD"][..],
&["git", "--work-tree", ".", "status"][..],
&["git", "--work-tree=.", "status"][..],
&["git", "--exec-path", ".git/helpers", "show", "HEAD:foo.rs"][..],
&["git", "--exec-path=.git/helpers", "show", "HEAD:foo.rs"][..],
&["git", "--namespace", "attacker", "show", "HEAD:foo.rs"][..],
&["git", "--namespace=attacker", "show", "HEAD:foo.rs"][..],
&["git", "--super-prefix", "attacker/", "show", "HEAD:foo.rs"][..],
&["git", "--super-prefix=attacker/", "show", "HEAD:foo.rs"][..],
] {
assert!(
!is_safe_command_windows(&[
pwsh.clone(),
"-NoLogo".to_string(),
"-NoProfile".to_string(),
"-Command".to_string(),
script.to_string(),
]),
"expected {script:?} to require approval due to unsafe git global option",
);
assert_unsafe_parsed_command(words);
}
}
#[test]
fn rejects_powershell_commands_with_side_effects() {
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-NoLogo",
"-Command",
"Remove-Item foo.txt",
])));
fn rejects_parsed_powershell_commands_with_side_effects() {
for words in [
&["Remove-Item", "foo.txt"][..],
&["rg", "--pre", "cat"][..],
&["Set-Content", "foo.txt", "hello"][..],
&["Out-File", "y"][..],
&["Write-Output", "(Set-Content", "foo6.txt", "abc)"][..],
&["Write-Host", "(Remove-Item", "foo.txt)"][..],
&["Get-Content", "(New-Item", "bar.txt)"][..],
] {
assert_unsafe_parsed_command(words);
}
}
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-NoProfile",
"-Command",
"rg --pre cat",
])));
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Set-Content foo.txt 'hello'",
])));
// Redirections are blocked
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
#[test]
fn rejects_parser_only_powershell_side_effect_patterns() {
for script in [
"echo hi > out.txt",
])));
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Get-Content x | Out-File y",
])));
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Write-Output foo 2> err.txt",
])));
// Call operator is blocked
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"& Remove-Item foo",
])));
// Chained safe + unsafe must fail
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Get-ChildItem; Remove-Item foo",
])));
// Nested unsafe cmdlet inside safe command must fail
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Write-Output (Set-Content foo6.txt 'abc')",
])));
// Additional nested unsafe cmdlet examples must fail
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Write-Host (Remove-Item foo.txt)",
])));
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Get-Content (New-Item bar.txt)",
])));
// Unsafe @ expansion.
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"ls @(calc.exe)"
])));
// Unsupported constructs that the AST parser refuses (no fallback to manual splitting).
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"ls && pwd"
])));
// Sub-expressions are rejected even if they contain otherwise safe commands.
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"Write-Output $(Get-Content foo)"
])));
// Empty words from the parser (e.g. '') are rejected.
assert!(!is_safe_command_windows(&vec_str(&[
"powershell.exe",
"-Command",
"''"
])));
"ls @(calc.exe)",
"ls && pwd",
"Write-Output $(Get-Content foo)",
"''",
] {
assert!(
!is_safe_command_windows(&vec_str(&["powershell.exe", "-Command", script,])),
"expected {script:?} to require approval",
);
}
}
#[test]

View File

@@ -1,20 +0,0 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "tui",
crate_name = "codex_tui",
compile_data = glob(
include = ["**"],
exclude = [
"**/* *",
"BUILD.bazel",
"Cargo.toml",
],
allow_empty = True,
),
test_data_extra = glob(["src/**/snapshots/**"]) + ["//codex-rs/core:model_availability_nux_fixtures"],
integration_compile_data_extra = ["src/test_backend.rs"],
extra_binaries = [
"//codex-rs/cli:codex",
],
)

View File

@@ -1,150 +0,0 @@
[package]
name = "codex-tui"
version.workspace = true
edition.workspace = true
license.workspace = true
[[bin]]
name = "codex-tui"
path = "src/main.rs"
[lib]
name = "codex_tui"
path = "src/lib.rs"
[features]
default = ["voice-input"]
# Enable vt100-based tests (emulator) when running with `--features vt100-tests`.
vt100-tests = []
# Gate verbose debug logging inside the TUI implementation.
debug-logs = []
voice-input = ["dep:cpal", "dep:hound"]
[lints]
workspace = true
[dependencies]
anyhow = { workspace = true }
base64 = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
clap = { workspace = true, features = ["derive"] }
codex-ansi-escape = { workspace = true }
codex-app-server-client = { workspace = true }
codex-app-server-protocol = { workspace = true }
codex-arg0 = { workspace = true }
codex-backend-client = { workspace = true }
codex-chatgpt = { workspace = true }
codex-client = { workspace = true }
codex-cloud-requirements = { workspace = true }
codex-core = { workspace = true }
codex-exec-server = { workspace = true }
codex-features = { workspace = true }
codex-feedback = { workspace = true }
codex-file-search = { workspace = true }
codex-git-utils = { workspace = true }
codex-login = { workspace = true }
codex-otel = { workspace = true }
codex-protocol = { workspace = true }
codex-shell-command = { workspace = true }
codex-state = { workspace = true }
codex-terminal-detection = { workspace = true }
codex-tui-app-server = { workspace = true }
codex-utils-approval-presets = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-cli = { workspace = true }
codex-utils-elapsed = { workspace = true }
codex-utils-fuzzy-match = { workspace = true }
codex-utils-oss = { workspace = true }
codex-utils-sandbox-summary = { workspace = true }
codex-utils-sleep-inhibitor = { workspace = true }
codex-utils-string = { workspace = true }
color-eyre = { workspace = true }
crossterm = { workspace = true, features = ["bracketed-paste", "event-stream"] }
derive_more = { workspace = true, features = ["is_variant"] }
diffy = { workspace = true }
dirs = { workspace = true }
dunce = { workspace = true }
image = { workspace = true, features = ["jpeg", "png", "gif", "webp"] }
itertools = { workspace = true }
lazy_static = { workspace = true }
pathdiff = { workspace = true }
pulldown-cmark = { workspace = true }
rand = { workspace = true }
ratatui = { workspace = true, features = [
"scrolling-regions",
"unstable-backend-writer",
"unstable-rendered-line-info",
"unstable-widget-ref",
] }
ratatui-macros = { workspace = true }
regex-lite = { workspace = true }
reqwest = { workspace = true, features = ["json", "multipart"] }
rmcp = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["preserve_order"] }
shlex = { workspace = true }
strum = { workspace = true }
strum_macros = { workspace = true }
supports-color = { workspace = true }
tempfile = { workspace = true }
textwrap = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true, features = [
"io-std",
"macros",
"process",
"rt-multi-thread",
"signal",
"test-util",
"time",
] }
tokio-stream = { workspace = true, features = ["sync"] }
toml = { workspace = true }
tracing = { workspace = true, features = ["log"] }
tracing-appender = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
syntect = "5"
two-face = { version = "0.5", default-features = false, features = ["syntect-default-onig"] }
unicode-segmentation = { workspace = true }
unicode-width = { workspace = true }
url = { workspace = true }
webbrowser = { workspace = true }
uuid = { workspace = true }
codex-windows-sandbox = { workspace = true }
tokio-util = { workspace = true, features = ["time"] }
[target.'cfg(not(target_os = "linux"))'.dependencies]
cpal = { version = "0.15", optional = true }
hound = { version = "3.5", optional = true }
[target.'cfg(unix)'.dependencies]
libc = { workspace = true }
[target.'cfg(windows)'.dependencies]
which = { workspace = true }
windows-sys = { version = "0.52", features = [
"Win32_Foundation",
"Win32_System_Console",
] }
winsplit = "0.1"
# Clipboard support via `arboard` is not available on Android/Termux.
# Only include it for non-Android targets so the crate builds on Android.
[target.'cfg(not(target_os = "android"))'.dependencies]
arboard = { workspace = true }
[dev-dependencies]
codex-cli = { workspace = true }
codex-core = { workspace = true }
codex-utils-cargo-bin = { workspace = true }
codex-utils-pty = { workspace = true }
assert_matches = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
insta = { workspace = true }
pretty_assertions = { workspace = true }
rand = { workspace = true }
serial_test = { workspace = true }
vt100 = { workspace = true }
uuid = { workspace = true }

View File

@@ -1,17 +0,0 @@
▒▓▒▓▒██▒▒██▒
▒▒█▓█▒█▓█▒▒░░▒▒ ▒ █▒
█░█░███ ▒░ ░ █░ ░▒░░░█
▓█▒▒████▒ ▓█░▓░█
▒▒▓▓█▒░▒░▒▒ ▓░▒▒█
░█ █░ ░█▓▓░░█ █▓▒░░█
█▒ ▓█ █▒░█▓ ░▒ ░▓░
░░▒░░ █▓▓░▓░█ ░░
░▒░█░ ▓░░▒▒░ ▓░██████▒██ ▒ ░
▒░▓█ ▒▓█░ ▓█ ░ ░▒▒▒▓▓███░▓█▓█░
▒▒▒ ▒ ▒▒█▓▓░ ░▒████ ▒█ ▓█▓▒▓
█▒█ █ ░ ██▓█▒░
▒▒█░▒█▒ ▒▒▒█░▒█
▒██▒▒ ██▓▓▒▓▓▓▒██▒█░█
░█ █░░░▒▒▒█▒▓██

View File

@@ -1,17 +0,0 @@
▒████▒██▒
██░███▒░▓▒██
▒▒█░░▓░░▓░█▒██
░▒▒▓▒░▓▒▓▒███▒▒█
▓ ▓░░ ░▒ ██▓▒▓░▓
░░ █░█░▓▓▒ ░▒ ░
▒ ░█ █░░░░█ ░▓█
░░▒█▓█░░▓▒░▓▒░░
░▒ ▒▒░▓░░█▒█▓░░
░ █░▒█░▒▓▒█▒▒▒░█░
█ ░░░░░ ▒█ ▒░░
▒░██▒██ ▒░ █▓▓
░█ ░░░░██▓█▓░▓░
▓░██▓░█▓▒ ▓▓█
██ ▒█▒▒█▓█

View File

@@ -1,17 +0,0 @@
███████▒
▓ ▓░░░▒▒█
▓ ▒▒░░▓▒█▓▒█
░▒▒░░▒▓█▒▒▓▓
▒ ▓▓▒░█▒█▓▒░░█
░█░░░█▒▓▓░▒▓░░
██ █░░░░░░▒░▒▒
░ ░░▓░░▒▓ ░ ░
▓ █░▓░░█▓█░▒░
██ ▒░▓▒█ ▓░▒░▒
█░▓ ░░░░▒▓░▒▒░
▒▒▓▓░▒█▓██▓░░
▒ █░▒▒▒▒░▓
▒█ █░░█▒▓█░
▒▒ ███▒█░

View File

@@ -1,17 +0,0 @@
█████▓
█▒░▒▓░█▒
░▓▒██
▓█░░░▒▒ ░
░ █░░░░▓▓░
░█▓▓█▒ ▒░
░ ░▓▒░░▒
░ ▓█▒░░
██ ░▓░░█░░
░ ▓░█▓█▒
░▓ ░ ▒██▓
█ █░ ▒█░
▓ ██░██▒░
█▒▓ █░▒░░
▒ █░▒▓▓

View File

@@ -1,17 +0,0 @@
▓████
░▒▒░░
░░▒░
░██░▒
█ ░░
▓▓░░
█ ░░
█ ░
▓█ ▒░▓
░ █▒░
█░▓▓ ░░
░▒▒▒░
░██░▒
█▒▒░▒
█ ▓ ▒

View File

@@ -1,17 +0,0 @@
████▓
█▓▒▒▓▒
░▒░░▓ ░
░░▓░ ▒░█
░░░▒ ░
░█░░ █░
░░░░ ▓ █
░░▒░░ ▒
░░░░
▒▓▓ ▓▓
▒░ █▓█░
░█░░▒▒▒░
▓ ░▒▒▒░
░▒▓█▒▒▓
▒█ █▒▓

View File

@@ -1,17 +0,0 @@
█████░▒
░█▒░░▒▓██
▓▓░█▒▒░ █░
░▓░ ▓▓█▓▒▒░
░░▒ ▒▒░░▓ ▒░
▒░░▓░░▓▓░
░░ ░░░░░░█░
░░▓░░█░░░ █▓░
░░████░░░▒▓▓░
░▒░▓▓░▒░█▓ ▓░
░▓░░░░▒░ ░ ▓
░██▓▒░░▒▓ ▒
█░▒█ ▓▓▓░ ▓░
░▒░░▒▒▓█▒▓
▒▒█▒▒▒▒▓
░░

View File

@@ -1,17 +0,0 @@
▒▒█ ███░▒
▓▒░░█░░▒░▒▒
░▓▓ ▒▓▒▒░░ █▒
▓▓▓ ▓█▒▒░▒░░██░
░░▓▒▓██▒░░█▓░░▒
░░░█░█ ░▒▒ ░ ░▓░
▒▒░ ▓░█░░░░▓█ █ ░
░▓▓ ░░░░▓░░░ ▓ ░░
▒▒░░░█░▓▒░░ ██ ▓
█ ▒▒█▒▒▒█░▓▒░ █▒░
░░░█ ▓█▒░▓ ▓▓░░░
░░█ ░░ ░▓▓█ ▓
▒░█ ░ ▓█▓▒█░
▒░░ ▒█░▓▓█▒░
█▓▓▒▒▓▒▒▓█

View File

@@ -1,17 +0,0 @@
█▒███▓▓░█▒
▒▓██░░░█▒█░█ ▒█
██▒▓▒▒▒░██ ░░░▒ ▒
▓░▓▒▓░ ▒░ █░▓▒░░░▒▒
░▓▒ ░ ░ ▓▒▒▒▓▓ █
░▒██▓░ █▓▓░ ▓█▒▓░▓▓
█ ▓▓░ █▓▓░▒ █ ░░▓▒░
▓ ▒░ ▓▓░░▓░█░░▒▓█
█▓█▓▒▒▒█░▒▒░▒▒▓▒░░░ ░
░ ▒▓▒▒░▓█▒▓░░▒ ▒███▒
▒▒▒▓ ████▒▒░█▓▓▒ ▒█
▒░░▒█ ░▓░░░ ▓
▒▒▒ █▒▒ ███▓▒▒▓
█ ░██▒▒█░▒▓█▓░█
░█▓▓▒██░█▒██

View File

@@ -1,17 +0,0 @@
▒▒▒█▒▒█▓░█▒
▒█ ▒▓███░▒▒█ █▓▓▒
▒▓▓░█ █▒ █ ▓▒ █▓▓▒ █
█░░█▓█▒ █ █▒░▒▓▒░▒▓▒▒▒█
▒▒▓▓ ▓░ ▒ █▒▒▓░▓░▒▒▓▒▒▒
▓▒░ ██░▓▒▒▒▓███░█▓▓▒▓░▓░
░░▒▓▓ █▓█▓░ ▒▓ █░▒░▒█
▒▓░░ ▒▒ ░░▓▒ ░▓░
▒ █▒▒▒▓▒▓█░░█░█▓▒█ ░█░░
▒▒▒░█▒█ ░░▓▒▒▒▒░░░▒▓░░▒ █
░▓░▒░ █████░ ▒▒▒▓░▓█▓░▓░
▒▒ █▒█ ░░█ ▓█▒█
▒▒██▒▒▓ ▒█▒▒▓▒█░
█░▓████▒▒▒▒██▒▓▒██
░░▒▓▒▒█▓█ ▓█

View File

@@ -1,17 +0,0 @@
▒▒▒▒█░█▒▒░▓▒
▒█░░░▒▓▒▒▒▒█▒█░███
██▓▓▓ ░██░ ░█▓█░█▓▒
▓▓░██▒░ ▒▒▒██▒░██
░░▓░▓░ █░▒ ▓ ░▒ ░▒█
░▒▓██ ▒░█░▓ ▓▓ █▓█░
▒▒░░█ ▓█▒▓░██░ ▓▓▓█░
░░░░ ░▓ ▒░ █ ░ ░░░
░█░▒█▒▓▓▒▒▒░░░░██▓█░▓ ▒ ░░
▒▓▓█░▒█▓▒██▒█░█ ▒▒ ▓▒▒▒█▓▓░▒
█▒ ▓█░ ██ ▒▒▒▓░▓▓ ▓▓█
▒▒▒█▒▒ ░▓▓▒▓▓█
█ ▒▒░░██ █▓▒▓▓░▓░
█ ▓░█▓░█▒▒▒▓▓█ ▓█░█
░▓▒▓▓█▒█▓▒█▓▒

View File

@@ -1,17 +0,0 @@
▒▓▒▓▒█▒▒▒██▒
▒██▓█▓█░░░▒░░▒▒█░██▒
█░█░▒██░█░░ ░ █▒█▓░░▓░█
▒░▓▒▓████▒ ▓█▒░▓░█
█▒ ▓█▒░▒▒▒▒▒ ▒█░▒░█
█▓█ ░ ░█▒█▓▒█ ▒▒░█░
█░██░ ▒▓░▓░▒░█ ▓ ░ ░
░ ▒░ █░█░░▓█ ░█▓▓░
█ ▒░ ▓░▒▒▒░ ▓░█████████░▒░░█
▒▒█░ ▓░░█ ▓█ ░▒▒▒▒▒▒▓▓▒▒░█▓ ░
▒▒▒ █ █▒▓▓░█ ░ ███████ ░██░░
█▒▒▓▓█ ░ ██▓▓██
▓▒▒▒░██ █▒▒█ ▒░
░░▒▓▒▒ ██▓▓▒▓▓▓▒█░▒░░█
░████░░▒▒▒▒░▓▓█

View File

@@ -1,17 +0,0 @@
▒▒█▒░░▒█▒█▒▒
█▓▒ ▓█▒█▒▒▒░░▒▒█▒██
██ ▒██ ░█ ░ ▒ ▒██░█▒
▒░ ▒█░█ ▒██░▒▓█▒▒
▒░ █░█ ▒▓ ▒░░▒█▒░░▒
▓░█░█ ███▓░ ▓ █▒░░▒
▓░▓█░ ██ ▓██▒ █▒░▓
░▒▒▓░ ▓▓░ █ ░░ ░
░▓░░▓█▒▓▒▒▒▒▒▒▒██▓▒▒▒▒█ ▓ ░▒
█░▒░▒ ▓░░▒▒▒▒░▒ █▒▒ ░▒▒ █▓ ░░
▒█▒▒█ █ ▒█▒░░█░ ▓▒
█ ▒█▓█ ▒▓█▓░▓
▒▒▒██░▒ █▓█░▓██
▒█▓▓ ░█▒▓▓█▓ ░ ░█▓██
░██░▒ ▒▒▒▒▒░█

View File

@@ -1,17 +0,0 @@
▒▒█▒█▒▒█▒██▒▒
███░░▒▒█░▒░█▓▒░▓██▒
▓█▒▒██▒ ░ ░▒░██▒░██
██░▓ █ ▒█▓██▓██
▓█▓█░ █░▓▒▒ ▒▒▒▒█
▓ ▓░ ███▒▓▓ ▒▒▒█
░█░░ ▒ ▓░█▓█ ▒▓▒
░▒ ▒▓ ░█ ░ ░
░ ░ ██▓▓▓▓▓███ ▒░█ ░█ ▓▓ ░
░ ░▒ ░▒ ▒█░ ▒ ░█░█ ▓ ▓▓
▓ ▓ ░░ █░ ██▒█▓ ▓░ █
██ ▓▓▒ ▒█ ▓
█▒ ▒▓▒ ▒▓▓██ █░
█▒▒ █ ██▓░░▓▓▒█ ▓░
███▓█▒▒▒▒█▒▓██░

View File

@@ -1,17 +0,0 @@
▒██▒█▒▒█▒██▒
▒█▓█░▓▒▓░▓▒░░▓░█▓██▒
█▓█▓░▒██░ ░ █▒███▒▒██
▓█░██ ██░░░▒█▒
▒░░▓█ █▒▓░▒░▓▓▓█░
▒░█▓░ █░▓░▓▒▓░ ▒░▒▒░
░██▒▓ ░█░▒█▓█ ░░▓░
░░▒░░ ░▒░░▒▒ ░▒░ ░
░░█ █ █░▒▒▓▓▓▒██▒▒█░▒ ▒█ ▒░▓
▒░▒ █▒▒▒█ ▓█ ░▓▓░ ▒█▓▒ ░██ ▓▒▒
▒▒▒▒░ ██ ░ ░▓██▒▓▓▓ █░
▒█▒▒▒█ ▒██ ░██
█ █▓ ██▒ ▒▓██ █▒▓
█▓███ █░▓▒█▓▓▓▒█ ███
░ ░▒▓▒▒▒▓▒▒▓▒█░

View File

@@ -1,17 +0,0 @@
▒██▒▒████▒█▒▒
▒▒░█░▒▒█▒▒▒█░▒░█░█▒
█ █░██▓█░ ░▓█░▒▓░░█
▓▓░█▓▓░ ▒▓▓▒░░▓▒
▓▓░░▓█ █▓████▓█▒░▒
█▒░ ▓░ ▒█████▓██░░▒░█
░░░ ░ ▓▓▓▓ ▒░░ ░██
░▓░ ░ ░ ░█▒▒█ ░ █▓░
▒ ▒ ░█░▓▒▒▒▒▒▓▒░▒█░▒ ▒▒ ░ ░░░
░▒▒▒░ ▒ ▓░▒ ▒░▒▒█░ ▒▒░
▓█░ ░ ░ █░▓▓▒░▒▓▒▓░
█░░▒░▓ █▓░▒▒▓░
▒ ░██▓▒▒ ▒▓ ▓█▓█▓
▒▒▒█▓██▒░▒▒▒██ ▓▒██░
░ █▒▒░▒▒█▒▒██░

View File

@@ -1,17 +0,0 @@
▒░▒▓███▒▒█▒
█ ▒▓ ░▒▒░▒▒██▒██
█ █▓▒▓█ ░ ▓░▓█░███ ▒
██▓▓█▓░▒█▒░░▓░ ▒█▒░▒▒█
█ ▓▓▒▓█ ░ ▓▒▒░░░▒░██
░█▒█▒░ ███▓ ▓░▓ ▓ ▒
░ ░░ █▓▒█▓ ▓▒▒░▒▒░▒
░ ▒░░ ░█▒▓▒▒░░▒▓▓░░░
░▓ ░▓▓▓▓██░░░██▒██▒░ ░ ░░
▒ ▓ █░▓██▓▓██░▓▒▒██░ ░█░
▒ █▒░▒█ ░ ▒█▓█▒░▒▓█░
▒ ▒██▒ ░ ▓▓▓
▒▓█▒░░▓ ▒▒ ▒▓▓▒█
▓▓██▒▒ ░░▓▒▒▓░▒▒▓░
█▓▒██▓▒▒▒▒▒██

View File

@@ -1,17 +0,0 @@
▒█▒█▓████▒
█ ███░▒▓▒░█░░█
▓░▓▓██ ▓░█▒▒▒░░░▒
░██░ ▓ ▒░ ▒░██▒▓
█▒▒▒█▓█▒▓▓▒░ ░▓▓▒▓█
▒█░░░▒██▓▒░▓ ▓░█░▓▓░█
░▓░█░ ░▒▒▓▒▒▓░▒▓▒ ░▒░
░░░▓░▓ ░▒▒▒▓░▒▒░▒░░▒
▒█▒░ ░▒▒▒▒▒▒█░░▒▒░██░▒
▓▓ ░▓░█░▒░░▓█▒░▒█▒▓▒░
▒░█▓▒░░ ██▓░▒░▓░░
░▒ ░▓█▓▒▓██▓▒▓█▓▓░▓
▒░▒░▒▒▒█▓▓█▒▓▒░░▓
▒▓▓▒▒▒█▒░██ █░█
░█ █▒██▒█░█

View File

@@ -1,17 +0,0 @@
▒▓███ ██
▓█░▓▓▒░█▓░█
▓█ ░▓▒░▒ ▒█
▓█ █░░░▒░░▒█▓▒
░▒█▒░▓░ █▒▓▓░▒▓
▒ ░▓▓▓ █▒▒ ▒▒▓
░ ██▒░░▓░░▓▓ █
▓▓ ▒░░░▒▒▒░░▓░░
░ ▓▒█▓█░█▒▒▓▒░░
▓▒░▓█░▒▒██▒▒█░
░░ ▓░█ ▒█▓░█▒░░
▒▒░░▓▒ ▓▓ ░░░
█ █░▒ ▒░▓░▓█
░ █▒▒ █▒██▓
▒▓▓▒█░▒▒█

View File

@@ -1,17 +0,0 @@
▓█████
░▓▓▓░▓▒
▓█░ █░▓█░
░░░▒░░▓░░
░ ░░▒▓█▒
░▒▓▒ ░░░░░
▒ ░░▒█░░
░ ░░░░▒ ░░
░▓ ▓ ░█░░░░
█▒ ▓ ▒░▒█░░
░▓ ▒▒███▓█
░░██░░▒▓░
░▒▒█▒█▓░▒
▒▒▒░▒▒▓▓
█▒ ▒▒▓

View File

@@ -1,17 +0,0 @@
▓██▓
░█▒░░
▒ ▓░░
░▓░█░
░ ░░
░ ▓ ░
▒░░ ▒░
░▓ ░
▓▒ ▒░
░░▓▓░░
░ ▒░
░▒█▒░
░▒█░░
█▒▒▓░
░ ▓█░

View File

@@ -1,17 +0,0 @@
██████
█░█▓ █▒
▒█░░ █ ░
░░░░▒▒█▓
▒ ░ ░ ░
░█░░░ ▒▒
░▒▒░░░ ▒
░░▒░░
░░░█░ ░
▒░▒░░ ░
█░░▓░▒ ▒
░▓░░░ ▒░
░░░░░░▒░
░▒░█▓ ░█
░░█ ▓█

View File

@@ -1,17 +0,0 @@
▒▓▒▓██▒▒▒▒█▒
▒██▓▒░░█░ ▒▒░▓▒▒░██▒
█▓▓█▓░█ ░ ░ ░ ███▓▒░█
▓█▓▒░▓██▒ ░▒█ ░░▒
█▓█░▓▒▓░░█▒▒ ▒▒▒░░▒
▓░▒▒▓ ▓█░▒▓▒▒ ░ ▒▒░
▒█ ░ ██▒░▒ ░█ ▓█▓░█
█▓░█░ █▓░ ▓▒░ ░▒░▒░
▓ █░ ▓░██░░█▓░▒██▒▒▒██▒░▒ ▓░
█▒▓▒█ ▓▓█▓▓▓░ ░█░▒▒█ ▒▓█▓▒░░▒░░
█▒░ ░ ░░██ ███ ███▓▓▓█▓
██░ ▒█ ░ ▓▒█▒▓▓
▒▒▓▓█▒█ ██▓▓ █░█
▒▒██▒██▒▒▓▒▓█▓▒█▓░▒█
░███▒▓░▒▒▒▒░▓▓▒

View File

@@ -1,17 +0,0 @@
▒▓ ████
▒▓▓░░▒██▒▒
█▒░█▒▒░██▒
░░▒░▓░▒▒░▒ ▒█
▒█░░░▒░█░█ ░
░█░▒█ █░░░░▓░
▒▓░░░▒▒ ▒▓▒░ ▒░
░ ██▒░█░ ░▓ ░
░▒ ▒░▒░▒▓░█ ░
░░▒░▒▒░░ ██ ░
▒░░▓▒▒█░░░█░░
░█▓▓█▓█▒░░ ░
▒░▒░░▓█░░█░▓
█▒██▒▒▓░█▓█
▒▓▓░▒▒▒▓█
░░░░

View File

@@ -1,17 +0,0 @@
▒▓▓████▒█
▒██▓██▒ █▒███
█░▒▓▓█▒▒░▓ ░▒█▒
█░▓█▒▒█▓▒█▒▒░▒░░▒
▒░░░░█▓█▒▒█ ▒░▓▒▒
▓░▒░░▒░█ ▒▓██▓▓░█ ░
▓░░ ░▒█░▒▓▒▓▓█░█░▓░
▒▒█ ░░ ░▒ ░▒ ░░▒▓░
░▒█▒░█▒░░░▓█░░░▒ ░
░░░▓▓░░▒▒▒▒▒░▒░░ █
▒█▒▓█░█ ▓███░▓░█░▒
░░░▒▒▒█ ▒▒█ ░
▓░█▒▒ █ ▓ ░█░▓░
▓░▒░▓▒░░█░ █░░
█ ▒░▒██▓▓▓█
░░░░

View File

@@ -1,17 +0,0 @@
█████▓▓░█▒
▓█░██░▒░░██░░█
▓▒█▒▒██▒▓▓░█░█▒███
█▓▓░▒█░▓▓ ▓ █▒▒░██ █
▓▓░█░█▒██░▓ █░█░▒▓▒█▒█
▒▓▒▒█▒█░░▓░░█▒ ░█▓ █
█░ ▓█░█▒░░██░█▒░▓▒▓▓░█▒
░░░█▒ ▒░░ ▓█░▓▓▒ ▒░ ░
▒░░▓▒ █▒░ ▒▒░███░░░▒░ ▒░
█ ▒░░█▒█▒▒▒▒▒▒░░█░▓░▓▒
█▒█░░▓ ░█ ███▒▓▓▓▓▓▓
▒█░▒▒▒ █▒░▓█░
███░░░█▒ ▒▓▒░▓ █
▒▓▒ ░█░▓▒█░▒█ ▒▓
░▓▒▒▒██▓█▒
░░░

View File

@@ -1,17 +0,0 @@
▒██▒█▒█▒░▓▒
▒██░░▒█▒░▓░▓░█░█▓
▒▓▒░████▒ ░ █▓░░█ █
█▒▓░▓▒░█▒ █░░▒▒█
▒▓░▓░░░▓▒▒▒ ░█▒▒▒
▓▓█ ▒▒▒▒░▒█ ▓▒▓▒▒
░░█ ▒██░▒░▒ ░█░░
█░██ ███▒▓▒█ ▒ ░█
░░░ ░ █░ ▓████▓▒▒█░░█▓▒░▒░
▒▓░█ ▓▓█▓░░░▒▒▒▒▒░░█▒▒▒░░▓
▒▒▒█ ░▓░▓ ▓ ███ ░░█▓▒░
▒█▒██ █ ▓▓▓▓▒▓
█▒ ███▓█ ▒█░█▓█▒█
▒░ █▒█░█▓█▒ ▓█▒█░█
▒▒██▒▒▒▒██▓▓
░░░░

View File

@@ -1,17 +0,0 @@
▒█▒████▒░█▒
▒███▓▒▓░ ░██▒██▓█▒▒
▒▓▓█░█ ▓░█░ ░▒▒▒█ ███
█▓▒░█▒▓█▒ █░██▒▒
▓▓░▒▓▓░ ░ █ ▒▒█▒▒
█▓▒░░▓ ▒▒ ░▒█▒ ▒█▒░▒
░█▒░▒ █▒▒█░▒▒ ░▓░▒
▒░▒ ▓ ░█▒░▓ ░ ▓ ▒▒
██▓▓ ▓▒▓▓ ▒▒▒██████░▒▒ ░▒░
░░▒█▓██▒ ▓▓█░░░▒░▓▒▒▒█▓▒░░░░▒
▓▒▒█ ░▒░█▒ ██░░░░▒ █▓█▒░█
▓█▒▓▒▒▒ ▓▓▓░▓█
▒█░░█▒▓█ ▒█▒ ▒▓█░
▓▒▓░ ░██▓██▒█▒█░██▓█
░▒▓▒▒▒▒▒▒▓▒█▒▒
░░░

View File

@@ -1,17 +0,0 @@
▒██▓▒███▒██▒
██▒█▓░███ ░█░▓ ░█▒▒
▒▓▓░▓██░▒█ ░ ░ █▒█▓ ░██
█▓▓█▓█▓█▒ ██▒▒░▒
▓▓░░▓▓▒ ▒██ ░▒█░█
▓▓▓▓█░ █░▒ ▓▓█▒ ░▒▒░
▒ ▓▓ ▒▒ ██▒▓ ░▒▒▒
░░░▓ ▓▒▒▓▓█ ▓ ▓
▓ █▒ █░░▓▓ ▓░▒▒▒▓▒▒█░░ ░░▒█
░█▒▓█ ▓▓▓ ██▓░▓ ▒█▒▒▒▒▓ ░▓█ ░█
▓░▒██▓▒▒░▓▒░ ░ ▒▒▒▒█▒▒█▓▓▒█░
▓▒▒▓░ ▒▓█ █▒
▒▓░▒▓█▓█ █▓▓▒███
▒▒ ░█░▓▓░░█░▓▓█ ▒▓▓
▒░▓▒▒▒▓▒▒███ ▒

View File

@@ -1,17 +0,0 @@
▒▒█▒████▒██▒
▒▒ ▒█▓▓▓█▒█▓██ ███▒
█▒█▒███▓█ ░░ ░ █░██░██░█
▒░ ██▒▒▒▒ ██░▒ ░
█▓▒▓▒█░▒░▒█▓ ▒▒▓█
▓ █▓░ █▒ ░▓█ ▒▒█
░ ▓ ░ ▒ ▒▒ ░▒░█
░░▒░ ▒▒ ▒▓▓ ▒░ ░
░█ ░ ▓▓ ██ ████▒█████▒ ░▒░░
▒█░▒ █░▒▒▓░▓ ░░▒▒▒▒▒▒▒░░ ▒▓█░
█ █░▒ █▒█▓▒ ██▒▒▒▒▒ ░█ ▓
██ ▒▓▓ █▓░ ▓
▒▓░░█░█ ███ ▓█░
██▒ ██▒▒▓░▒█░▓ ▓ █▓██
░██▓░▒██▒██████

View File

@@ -1,17 +0,0 @@
▒▓▒▓█▒▒█▒██▒
▒▓ ██░▓ ░▒▒▓█▓░ ▓██▒
██▒░░░██ ░ ░▒░▒▒█░▓▒▒▒
▓▓░█░ ▓██ ░██▒█▒
▓░▓▒░▒░▒▓▒░█ ▒ ▒░█▒
▓░░▒░ █▒░░▓█▒ █ ▒▒░█
▒░▓░ ███▒█ ░█ █ ▓░
░▓▒ █░▓█▒░░ ░░░
▒░ ░▒ ▓░▓ ▒▓▓█░███▒▒▒▒██ ░░█
░▒▓ ░ █▓▓▓█▒░░▒▒░█▓▒█▓▓▒▓░▓▓ ░
░░▓█▒█▒▒█▒▓ ████████▒▓░░░░
█░▒ ░▒░ █▒▓▓███
▒▒█▓▒ █▒ ▒▓▒██▓░▓
░░░▒▒██▒▓▓▒▓██▒██▒░█░
█▒▒░▓░▒▒▒▒▒▓▓█░

View File

@@ -1,17 +0,0 @@
▒▓▒▓▓█▒▒▒██▒
▒█ █▓█▓░░█░▒█▓▒░ ██
█▒▓▒█░█ ░ ▒▒░█▒ ███
█░▓░▓░▓▒█ ▓▒░░░░▒
█▒▓█▓▒▒█░▒▒█ ░ ▒░▒░▒
░░░░▓ ▒▒░▒▓▓░▒ █▓░░
░▓░ █ ░▒▒░▒ ░█ ██░█░█
░▓░▒ █▒▒░▓▒░ █░▒░
░█░▒█ ▓▒░ █░█▒▒░█░▒▒▒██▒ ░▓░
▒▒░▒██▓██ ░ ▓▓▒▒▒█▒▓█▓░▓█░░
▒█░░█░█▒▒▓█░ ██ █░▓░▒▓
▒▒█▓▒▒ ░ ▓▒▓██▒
▒▓█▒░▒█▒ ▒▒████▓█
▒░█░███▒▓░▒▒██▒█▒░▓█
▒▓█▒█ ▒▒▒▓▒███░

View File

@@ -1,17 +0,0 @@
▒▓▒▓▓█▒▒██▒▒
█▒▓▓█░▒██░██▓▒███▒
███░░░█ ░ ░▓▒███▓▒▒
▓█░█░█▒▒█ ▒█░░░░█
█▒░░░█▒▒██▒ ▓▒▒░▒█
▓▓▓░▓░▒█▓░▒▒░█ ▓▒▒▓░
▒ █░░ ▒▒░▓▒▒ ▒█░▒░
░ ░░░ ▒░▒░▓░░ ░█▒░░
▒▓░▓░ ▓█░░█▓▓█▒░█░▒▒██▒▓▒▓░
░░▒█▓▒▒▒▓█ ░▓▒██░░█▓▒▒▒░█░▒
▓ ░ ▓░░░▓▓ █ ██ ░▒▒▓░
█ ▓ ▓█░ █▓▒▓▓░░
▓░▒▒███ ▒█▒▒▓███
░ ░██ █ ▓░▒▒████ ▓▓█
▒▓▓███▒▒▒░▒███

View File

@@ -1,17 +0,0 @@
▒▓░▓██▒▒██▒
██░█▒░███▒▒▒▓ ░██
█ █░░░█░ ░▒░░ █▓▒██
▒▒░░░░▓█ ▒░▒█░▓█
░█░█░░▒░▓▒█ ▓ █░░▒
░ ▓░░ ░█▒▓░▒ █▓░░░
░▒ ░ ▒▒░▒░▒░ ██▒░░
▒ ▓░░ ▒█▓░█░░ █ ░░░
▓ ░█ █ ▒▓░▒▓░░▓▓▒░░▒▓█▒░░
░██░░▒▓░░▓█░▓▒░░▒▒█▒█▓▒░▒░
▒ ▒▒▓█░█▒▓ ██████ ▒▓░░
█▒ ▓▒▓▒░ █ ▓▓▓▓█
█▓██▒▒▒▒ █▒░██▓██
▒▒█▒░█▒▓░▒▒▒██░██▓
░█ ░▓░▒▒█▒▓██

View File

@@ -1,17 +0,0 @@
▒▒█▒▓██▒██▒
█ █▓░░░█▒▒ ░ █
▒░▒█░▓▓█ █ ░▓░█▒█▒█
▒█▒█▓░██░ █ ▒▒░░▒
█ ▓░▓█▒░▓▒ ▓█▒░░█
░██░▒▒▒▒▒░▒█ ▒█░░░
░█░░░ █▒▓▒░░░ ░▒░▓░█
▒█░░▓ ░█▒▓░██▓ ▓░▓░░
▒ ▒░░▒▒ ▓█▒░░▓█████▒░░░
▒█▓▒▒░ █░█░░▓░▒▒▒░░▒█
▓▓▒▒░▒░░░▓█▒█▒█ ▒█ ▓▒░
██ ░▒░░░ ▓█▓▓▓█
█▒▒█▒▒▒▒ ▒▓▒▒░█▓█
▓▓█░██ ▓▓██▓▓▒█░░
░░▒██▒░▒██▓▒░
░░

View File

@@ -1,17 +0,0 @@
▓▒▒█▓██▒█
▓█▒▓░░█ ▒ ▒▓▒▒
▓ █░░▓█▒▒▒▓ ▒▒░█
░░▓▓▒▒ ▒▒█░▒▒░██
▓█ ▓▒█ ░██ █▓██▓█░░
░ ░░░ ▒░▒▓▒▒ ░█░█░░░
░ ░█▒░██░▒▒█ ▓█▓ ░░░
░ ░▓▒█▒░░░▒▓▒▒▒░ ░░
█░ ▓░ ░░░░█░░█░░░
░▒░░░▒█░▒░▒░░░░▒▒░░░
░▒▓▒▒░▓ ████░░ ▓▒░
▒░░░▒█░ █▓ ▒▓░░
▒█▒░▒▒ ▓▓▒▓░▓█
▒▓ ▒▒░█▓█▒▓▓█░░
█▓▒ █▒▒░▓█▓

View File

@@ -1,17 +0,0 @@
eoeddccddcoe
edoocecocedxxde ecce
oxcxccccee eccecxdxxxc
dceeccooe ocxdxo
eedocexeeee coxeeo
xc ce xcodxxo coexxo
cecoc cexcocxe xox
xxexe oooxdxc cex
xdxce dxxeexcoxcccccceco dc x
exdc edce oc xcxeeeodoooxoooox
eeece eeoooe eecccc eccoodeo
ceo co e ococex
eeoeece edecxecc
ecoee ccdddddodcceoxc
ecccxxxeeeoedccc

View File

@@ -1,17 +0,0 @@
eccccecce
ccecccexoeco
eeoxxoxxoxceoo
xeeoexdeoeocceeo
o dxxcxe cooeoxo
xe cxcxooe eecx
e xcccxxxxc xoo
c xxecocxxoeeoexx
c xe eexdxxcecdxx
x oxeoxeoeceeexce
o cxxxxxcc eocexe
eecoeocc exccooo
xc xxxxcodooxoe
deccoxcde ooc
co eceeodc

View File

@@ -1,17 +0,0 @@
occcccce
oc dxxxeeo
oceexxdecoeo
xeexxddoedoo
ecodexcecdexxo
xcexxceddxeoxx
cc oxxxxxxexde
x xxoxxeo xcx
o cxoxxcocxex
cc exodocoxexe
ceo xxxxdoxeex
eeooxecoccdxe
e cxeeeexdc
ec cxxoeoce
ee cccece

View File

@@ -1,17 +0,0 @@
ccccco
odeeoxoe
c xoeco
ocxxxddcx
x cxxxxoox
xcoocecexc
x xoexxe
x ocexxc
co xoxxcxx
x oxcdce
xo xcdcco
o cx eox
o ccxocex
ceocoxexe
e cxeoo

View File

@@ -1,17 +0,0 @@
occco
xeexx
xeexc
xccxe
c xx
cdoxx
o xx
c cx
oc exo
xc cdx
ceoo xe
xeeex
xcoxe
ceexd
o ocd

View File

@@ -1,17 +0,0 @@
ccccd
ooeeoe
xexxo x
xxoxcexo
xxxe x
xcxx cx
xxxx o c
xxexe e
xxxx c
ceoo do
exccooox
xcxxeeex
o cxddde
xeoceeo
ec cdo

View File

@@ -1,17 +0,0 @@
cccccxe
eodxxedco
ooxcdexccx
xoe ooooeex
xxdcdexxocex
exxoxxoox c
xx xxxxxxox
xxoxxcxxx cox
xxcoocxxxeodx
xexdoxexco ox
xoxxxxex e d
xccoexxeo d
cxeo oooe de
xexxeeoceo
eeceeeeo
ee

View File

@@ -1,17 +0,0 @@
edcccccxe
oexxcxxexde
xooceodexx ce
ooo dceexexxccx
xxdeoccdxxcoxee
xxxcxc xed x xox
eex oeoxxxxocco x
xod xexxoxxxcd ex
eexxxcxoexxccc o
cceeoddecxoex oex
xxxcccocexdcdoxxe
xxc xe eooo o
exc x oooeox
exxcecxoocex
cdoeddeedc

View File

@@ -1,17 +0,0 @@
odcccddxoe
edccxxxcdcxoceo
oceoeddecocxxxece
oxoeoxcee cxdexxxde
xoe x xcoedeoo o
edcooe odox oodoxoo
c dox oooxe ccxxodx
ocdx ooxxoxoxxddc
oocoeddcxeexeedexxx x
xcedeexoceoxxe eccce
eeeoccccccceexcooe ec
exxec eoxxe d
eee cee ocooeeo
o xccdeceedcdxc
ecdoeocxcecc
e

View File

@@ -1,17 +0,0 @@
eddcddcdxoe
eccedoccxeeoccdde
eodxcccdcocoeccooe c
oxxcooecc ceeeodxedeeeo
eeoo ox ecceeoxoxeedeee
oex ooxoeeeoocoxcooeoeox
xxedo cocoxceoccxdxdo
ceoxx eecxxde xdxc
ecc oedddddcxxoxcoeo xcxe
eeexcec xxoeeeexxxedxee o
xoxeeccccccce eeeoxocoeoe
ee oeo eeccocec
eecceeo eceeoeoe
cxoccccdddecceoeoc
cxxeoeeooccdcc
e

View File

@@ -1,17 +0,0 @@
eeddcxcddxoe
ecxxxeodddeceoxcoo
ocddocxcce ecdoecde
odxcoee eddcoexco
xxoeoe oxecocxe xeo
xeocc excxo oo cocx
edxxc oceoxcoe odocx
xxxx xdcexco x xxx
xcxeoddddddxxxxccdcxd e cxx
edooxdcoecceoeo ee deeeoooxe
cecocxcccccccc eeeoxoo ooc
eeecee eooeooc
c eexxco oddooxde
ccoxcoxceeddocc dcxc
cxoedoceooecoe

View File

@@ -1,17 +0,0 @@
eoeddcdddcoe
ecoocdcxxxdxxdecxcce
oxcxeccxcee eccdcoxxdxo
exoeoccooe ooexoxo
oecocexeeeee eoxexo
cocce xcecoec eexcx
oxccx eoxdxexo ocxcx
xc ee oxcxxdc xcoox
cccdx dxeeexcoxccccccccoxexxc
edcx oxxc oc xdeeeeeooeexco x
eee c ceooxc ecccccccccxocxx
ceeooo e ocdooc
oeeexco odec exc
exedeecccdddddodceexxc
eccccxxeeeexdocc

View File

@@ -1,17 +0,0 @@
eecdxxdcdoee
oddcdoeodddxxeececo
oocecccxcc ecececcxce
excecxc eocxeocee
ex oxc eo exxecexxe
oeoxc cccdxco cexxe
dxdcx oc occe oexo
xeeoe ccddxco xxcx
xoxxdoddddddddeocdeeeec o xe
cxexec oeeeeeexe ceecxde oo xx
eoeecccccccccc eodxxox oe
c ecoo eocoxo
eeecoxe odcedcc
eooocxceddodcxceoocc
eccxe deeeexccc

View File

@@ -1,17 +0,0 @@
eeodcddcdcoee
occeeeecxdxcdeeocce
dceeccece eexcceeco
ocxdcc eodcodco
oooce oxoee eeeeo
ocox occeoo eeeo
xcxe e oeooc edec
ee ed cxo x x
x x ocdddddccc exocxo do x
x xe xe eox ececxo ocoo
d co eeccc ce cceod oe o
cc dde ecc o
ce eoe eodcc oe
cde ccccdxxdddccc oe
cccdceeeeoedcce

View File

@@ -1,17 +0,0 @@
eocdcddcdcoe
ecocxoeoxoexxdxcocce
odcdxecce ecceccceeco
dcxccc ooxxxece
exxoc oeoxdxoodcx
excoe oxoxdeoe exedx
xcceo xcxecoc xxox
xxdxe xexxee xexcx
xxoco cxddddddcceecxe eo exdc
exd ceeeo oocxoox ecdecxoo oed
eeeex cccccccce edcceooocoe
eceeeo ecocxoc
cccd cce eococceo
cdccoccxddcddodccccoc
cxcxedeeeodeodce

View File

@@ -1,17 +0,0 @@
eocedccccdcee
edxcxeeoeddoxexcxce
occxcodce cxdcxedxxo
odxcdoe eddexxde
ooxeoc ooooccocexe
oexcoe ecccccoccxxexo
exxcx odoo exe c xcc
xox x xcxoeeo x cox
ece xcxddddddddxecxecee x xxx
xeeexcdc oee exeeox eex
ocx x eccccccc ceoddxeoeoe
oxxexo ooxeeoe
e xocoee eocdcoco
edecdccexddecccoecce
cx cdexeeceecce

View File

@@ -1,17 +0,0 @@
exedcccddoe
oceocxeexddcoecc
occdeoccx oedcxcco e
ocooooxdoeexoe ecexeec
o ooeoo eccoeexeeexoc
xoecee cooo oxd oce
x xx ooeoocoeexeexe
x exx xodoeexxeooexx
xo xddddccxxxccecoex x xx
e o cxoooddooxoeeccx xcx
e cexeccccccce eoocexdooe
e eoce x codo
eoceexo edceodec
oocoeecxxddddxeeoe
cdeccdeeeddcc

View File

@@ -1,17 +0,0 @@
ecdcdcccce
o coceedexcxxo
oxoooocoxcedexxxe
xccx o dx cexoceo
oeeeoocedoexc xooeoc
eoxxxeccoexd oxoxooxo
xoxcx xeeoeeoxeoecxdx
xxxoxoc xedeoxeexdxxe
ecexcxeeddddcxxeexccxe
oocxoxoxexxdcexecdoex
excoexecccccccoxexoxe
xecxdcdeoocdeooooxo
eeexeeecdooeoexxo
eodeeecdxcc cxc
xoccecoecxc

View File

@@ -1,17 +0,0 @@
edccccco
ocxdoexcdxo
occcxdexecceo
dccoxxxexxecoe
xeoexoxcceodxed
e cxodocceeceeo
x ccdxxoxxddcc
oo exxxeedxxoxx
x oecdcxcddoexx
oexooxeeoceecx
xecoxcceooecexx
eexxoe oocxxe
c cxe eeoxoo
xcceecceccd
eodecxeec

View File

@@ -1,17 +0,0 @@
dcccco
xddoxoe
dce cxocx
xxxexxdxx
x exeocd
xeoecexxxe
d cxxecxx
x exxxdcxx
xo o xcxxxx
cd ocexecxx
xo eecccoc
xxccxxeox
xddcdooxe
eeexedoo
cec eeo

View File

@@ -1,17 +0,0 @@
occd
xcexe
d dxe
xoecx
x xx
x ocx
exx ex
xoccx
oe ex
xxodxx
x ex
xdcdx
xdcxx
ceeox
x ocx

View File

@@ -1,17 +0,0 @@
ccccco
oxco ce
eoxx ccx
xxxxeeoo
e xcx x
xoxxx ee
xeexxx e
xxdxx
xxxcx e
exdxx e
cxxoxe d
xoxxx ex
xxxxexex
xdxcocxc
xxc oo

View File

@@ -1,17 +0,0 @@
eoddccddddoe
ecooexxcxcddxdeexcce
odocdxccce ecx cccoexo
ocoexdoce edc xxe
cocxoeoxxcee eeexxe
oxeeo ooxedee x eex
dc x ccexecxo ocoxo
ooxox ooxcoex xexdx
occx dxccxxcoxdcceeeccexecdx
oedeo oocoddx xcxeeo doodeexexe
cex x cxxcoc cccccccccoooooo
ccx ec e oeceoo
deooceo ocdocoxc
decoecceddddoddcdeecc
ecccedxeeeexdoec

View File

@@ -1,17 +0,0 @@
edcccco
eodxxeccde
ccexoeexcoe
xxexoxeexe eo
dcxxeexoxo x
xcxec cxxxxox
eoxxxee eoex de
cx ccdxoxcxo e
cxecexdxeoxo e
cxxexeexx co e
exxdeecxxxcxx
xcoooocexxc x
exexxocxxoxo
oeocdeoxooc
eooxeeedc
eeee

View File

@@ -1,17 +0,0 @@
eodccccdo
eccdcoeccecco
oxeooodeeocxece
oxoceecdeoeexexxe
exxxxcoceeocexoee
dxeexexccedcoooxocx
oxx xecxeododcxcxox
eeo xxcxe xeccxxeox
xeoexcexxxocxxxe x
cxxxooxxeeeeexexx c
eceocxo occceoxcxe
xxxeeeo edc x
dxcde o o xceoe
dxexoexeoxcoxe
ccdxeccoodc
eeee

View File

@@ -1,17 +0,0 @@
occccddxoe
dcxccxexxccxxo
oecdeocedoecxcecco
cooxeoedo o oeexco o
ooxoxceccxd ceoxeoeceo
eoeeoecxedxxce xco c
cxcdoecexxooxodeoeooxce
xxxoe cexxcocxdoecexcce
exxoe cexceexcccxxxdxcde
ccceexceceeeeeexxcxdxoe
oecxxo xccccccedooooo
eoxeee oexocx
cccxxxce eoexo o
eoecxcxddceecceo
xddeeococecc
eee

View File

@@ -1,17 +0,0 @@
eocdcdcdxoe
eccxxecdxdxoxcxco
eoexcccodce ccoxxcco
oeoxoexoe cxxeec
eoxoexxoeee xceee
ooo eeeeeeo oeoee
xxc eocxexe xcxx
cxoo occeodo ecxc
xxx x oe ocooodddcxxcoexex
eoxccodooexxeeeeexxceeexxo
edeo xoxo o ccccccc xxooee
ececoco oododo
ceccocdo ecxoocec
exccecxodcecdoecxc
cddcoeeeeccdoc
eeee

View File

@@ -1,17 +0,0 @@
eodccccdxoe
ecccoedxcxccdccdode
eoooxccdxce eeeeoccco
ooexceooe cxocee
ooxeoox xc o eecee
ooexeo eecxece eoexe
xcexe ceecxee xdxe
exdcd xcexocx o ee
ocooc oeooceddccccccxeec xee
xxeooooecoocxxxexoeeeooexxexe
oeeo xexce ccceeeee oooexc
ooeddee odoxoc
ecexcedo ecdceooe
oeoxcxcodocdcdceccdc
cxeddeeeeeddcde
eee

Some files were not shown because too many files have changed in this diff Show More