Fix Termux/Android compatibility issues (DNS, locking, Android target support)#5
Fix Termux/Android compatibility issues (DNS, locking, Android target support)#5
Conversation
d61ca4b to
6c3d14b
Compare
|
Hi, do you know if this will continue to work with, and maybe improve, https://github.com/termux-user-repository/tur/blob/cf26feef42820d03b7061866ee5f456324657a7a/tur/codex/build.sh ? |
The current TUR provided codex is impacted by the issue that my PR resolves, so if I get an invite to PR upstream, this should fix at least the locking issues. I had some code change logic to handle the absence of /etc/resolve.conf on android, but found that simply adding an aarch64-linux-android target made all of that unnecessary.. but if this never gets merged upstream, I could provide a patch with that original conditional logic so that this can be fully solved via patches in the TUR. Are you a maintainer, @robertkirkman ? |
Yes, I have merge permission for TUR. If you want users of Codex on TUR to get this fix, you could also submit this to https://github.com/termux-user-repository/tur/pulls and I would be able to approve it there, and then whenever this version gets merged the patch could be removed from the TUR version when it gets updated. |
| #[cfg(target_os = "android")] | ||
| impl portable_pty::PtySystem for AndroidPtySystem { | ||
| fn openpty(&self, _size: PtySize) -> anyhow::Result<portable_pty::PtyPair> { | ||
| anyhow::bail!("PTY not supported on Android") |
There was a problem hiding this comment.
Could you describe what goes wrong if you do not disable PTY on Android? I was wondering about that.
There was a problem hiding this comment.
https://github.com/wallentx/codex/actions/runs/22244673318/job/64355979725#step:22:121
Compiling codex-cli v0.104.0-dev (/home/runner/work/codex/codex/codex-rs/cli)
error: linking with `/usr/local/lib/android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang` failed: exit status: 1
|
= note: "/usr/local/lib/android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android21-clang" "<1 object files omitted>" "-Wl,--as-needed" "-Wl,-Bstatic" "/tmp/rustc4CmmoS/{liblzma_sys-992b66fb89471db7,libbzip2_sys-ee2c4dea6113765e,liblibsqlite3_sys-74e3b2d982fb2832,libtree_sitter_bash-ba13aee0aa28265f,libtree_sitter-1ca4d45594f820fa,libzstd_sys-4dde45f3f9f13c1c,libaws_lc_sys-d9601faf5d845464,libring-7cc72dfc028a344e,libopenssl_sys-829833977529d351}.rlib" "<sysroot>/lib/rustlib/aarch64-linux-android/lib/libcompiler_builtins-*.rlib" "-Wl,-Bdynamic" "-lz" "-ldl" "-llog" "-lunwind" "-ldl" "-lm" "-lc" "-L" "/tmp/rustc4CmmoS/raw-dylibs" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/tree-sitter-6f935e6c94cea3b9/out" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/tree-sitter-bash-7043ce2866a501a9/out" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/aws-lc-sys-f78b418f69a41d9e/out" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/ring-478a869566eb754e/out" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/openssl-sys-50d764c58005cb2d/out/openssl-build/install/lib" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/zstd-sys-8fee5b13a7070f55/out" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/libsqlite3-sys-0c9f47d8c58ecbb3/out" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/bzip2-sys-18f29db9c9b4c5d5/out/lib" "-L" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/build/lzma-sys-e22fad6537c17296/out" "-o" "/home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/deps/codex-94cb809459dd56d4" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-Wl,--strip-all" "-nodefaultlibs"
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: ld.lld: error: undefined symbol: openpty
>>> referenced by codex.5ecef7b0ac420300-cgu.0
>>> /home/runner/work/codex/codex/codex-rs/target/aarch64-linux-android/release/deps/codex-94cb809459dd56d4.codex.5ecef7b0ac420300-cgu.0.rcgu.o:(_$LT$portable_pty..unix..UnixPtySystem$u20$as$u20$portable_pty..PtySystem$GT$::openpty::hf566b6e5dd6631e1)
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: could not compile `codex-cli` (bin "codex") due to 1 previous error
There was a problem hiding this comment.
Ohh I see, thanks for showing that.
The cause of the error ld.lld: error: undefined symbol: openpty is because the openpty function does not exist in Android 5, and this PR code is targeting Android 5.
Termux only supports Android 7 and newer, and in Android 6 and newer the openpty function does exist, so this error will not affect Termux.
Therefore, it is appropriate to keep this code which disables PTY for an upstream Codex version that can be used with Android 5, but for the Termux version the code which disables PTY will be possible to disable.
There was a problem hiding this comment.
Ahh thanks for this! I'll bump the API level to 24 and remove the PTY exclusion. I don't suspect this will be useful to anyone outside of termux.
There was a problem hiding this comment.
@robertkirkman I bumped the API target level to 24 and it failed with the same error.
https://github.com/wallentx/codex/actions/runs/22280259015/job/64449939914
Copilot informs me:
There’s an important difference between Termux and native Android development with the NDK. Termux provides openpty via its own libraries (such as libutil), so code compiled and run in Termux has access to that function. However, when building native Android apps with the NDK, the openpty function is not available in Android’s system libraries (bionic libc)—regardless of Android version. This means native binaries targeting Android, including Android 6 and newer, will encounter linker errors if they rely on openpty, unless they provide their own implementation.
So, while Termux users can use openpty, native Android apps built with the NDK cannot depend on it. That’s why this linker error matters for the CI job and NDK-based builds, even though Termux itself is unaffected.
I'll still keep the target level to 24, since that's what termux-packages uses, but it looks like I need to reapply the pty exclusion for android
There was a problem hiding this comment.
#!/bin/bash
echo "Instalando ca-certificates..."
pkg install ca-certificates -y
echo "Configurando .bashrc..."
Evita duplicados
if ! grep -q "SSL_CERT_FILE" ~/.bashrc; then
echo '' >> ~/.bashrc
echo '# Codex Termux fix' >> ~/.bashrc
echo 'export SSL_CERT_FILE=$PREFIX/etc/tls/cert.pem' >> ~/.bashrc
echo 'export SSL_CERT_DIR=$PREFIX/etc/tls/certs' >> ~/.bashrc
echo 'export RESOLV_CONF=$PREFIX/etc/resolv.conf' >> ~/.bashrc
echo "Listo! Variables agregadas al .bashrc"
else
echo "Las variables ya existen en .bashrc, no se duplicaron."
fi
source ~/.bashrc
echo "Todo listo! Ya puedes usar codex"
There was a problem hiding this comment.
@Jose2021H I tried that excited that it might work, but I'm getting:
file:///data/data/com.termux/files/usr/lib/node_modules/@openai/codex/bin/codex.js:100 throw new Error( ^
Error: Missing optional dependency @openai/codex-linux-arm64. Reinstall Codex: npm install -g @openai/codex@latest at file:///data/data/com.termux/files/usr/lib/node_modules/@openai/codex/bin/codex.js:100:11 at ModuleJob.run (node:internal/modules/esm/module_job:430:25) at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:639:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)
Node.js v25.8.2
There was a problem hiding this comment.
@licy183 someone tried to use codex from TUR, but because it doesn't work, they opened an issue, and because of that I sent them a link to this thread which they linked in their issue,
i assume that if wallentx is continuing with this, they will eventually be able to solve that issue
There was a problem hiding this comment.
@robertkirkman @tomiia @mevanlc
Things got pretty messy and broken for a bit, but I've done a lot of cleanup and have a fully working build here that is roughly equivalent to 0.122.0-alpha.5:
https://github.com/wallentx/codex/actions/runs/24577406145
I'm working on CI now that will mirror upstream pre-releases/releases as much as possible, then apply the Termux patch layer and produce an Android artifact from that result.
At this point the PR is less "one small Android target patch" and more a collection of compatibility fixes that will need to be applied to upstream code as it changes. My goal with the mirrored releases are to make this automated and maintainable, because I don't have high hopes that I'll be getting an invite to PR to OpenAI, and I don't suspect that any of this work is a high priority for them.
The current landscape of changes is roughly:
Android/Termux Runtime Fixes
-
Advisory file locks: Android/Bionic/Termux can return
ErrorKind::Unsupportedfor RustFile::lock()/try_lock()style APIs.
This was the cause of errors like:Error: thread/start failed during TUI bootstrapand:
Failed to initialize session: lock() not supportedI added a small shared helper crate for optional advisory file locking so call sites can distinguish "lock acquired", "would block", and "locking unsupported". There is also an audit script to help find future upstream lock usage that may need the same treatment.
-
File watcher behavior: The live filesystem watcher is disabled/nooped on Android. This avoids platform behavior that is not reliable in the Termux environment while still allowing the rest of the app to start normally.
-
Shell path resolution: Codex normally assumes standard Unix absolute paths like
/bin/sh. On Termux those paths are not the real userland. I added Android-specific shell lookup that prefers$PREFIX/bin/..., falling back to Termux's usual/data/data/com.termux/files/usr/bin/...location when$PREFIXis unavailable. -
Clipboard/image paste: Android builds exclude unsupported desktop clipboard/image-paste paths so the crate compiles cleanly and reports unsupported clipboard behavior instead of pulling in incompatible assumptions.
Login/Auth Fixes
-
Device-code auth: The branch includes the fixes needed for device-code auth to work on Termux. This currently does not work in upstream for this Android/Termux build target.
-
ChatGPT browser login duplicate callback: Device-code login worked first, but "Sign in with ChatGPT" failed on Android/Chrome after the browser hit the localhost callback more than once. The first callback successfully redeemed the OAuth code; the duplicate callback then tried to redeem the same code again and got a 400 from the token endpoint. The visible error was:
Token exchange failed: token endpoint returned status 400 Bad Request: Invalid request. Please try again later.I added callback state tracking so duplicate callbacks for the same code either reuse the success redirect or wait briefly for the first callback to finish.
-
TUI originator compatibility: The app-server client name from the TUI is normalized back to the expected default originator for login/client behavior, while preserving the user-agent suffix.
Android Binary/Build Artifact Fixes
-
TLS alignment: The produced binary needed post-processing/alignment fixes for ARM64 Bionic. Before that, Termux reported:
executable's TLS segment is underaligned: alignment is 64 (skew 16), needs to be at least 64 for ARM64 BionicThe workflow now runs the cleanup/alignment steps needed for the Android artifact.
-
Custom Android V8 artifact: For now I had to fork
denoland/rusty_v8, make changes there to produce an Android artifact, and publish that artifact in a release so this Codex fork can fetch and use it:
https://github.com/wallentx/rusty_v8/releases/tag/v146.9.0That is an external dependency I still need to chase upstream, because this should ideally come from the upstream
rusty_v8artifact pipeline rather than my fork.
Maintenance / Future-Proofing
-
Lock audit helper: Since upstream can add new
lock()usage at any time, I addedscripts/termux-lock-audit.sh/just termux-lock-auditto flag candidate file-lock call sites that may need Android/Termux handling. -
Release automation: I'm setting up a release-train workflow so when upstream publishes a new
rust-v*pre-release/release, my fork can create arelease/<version-train>branch, apply the Termux patch layer, build the Android artifact in CI, let me manually test that exact artifact, and then promote that same artifact into a mirrored Termux release once the PR is merged.
Some of these feel like reasonable upstream candidates, especially the "unsupported file locks should be handled gracefully" pieces, device-code auth, and the duplicate OAuth callback handling. Others are probably downstream-only for now, especially Termux path assumptions, Android artifact post-processing, and the temporary dependency on my fork's V8 artifact.
This might seem like a lot of effort for a niche group of users, but it's something I want for myself, and I'm currently unemployed (holla if hiring for Staff/Principal DevOps), so this is also keeping me sane and busy.
There was a problem hiding this comment.
Signed-off-by: wallentx <william.allentx@gmail.com>
Signed-off-by: wallentx <william.allentx@gmail.com>
4cccbca to
89ee71b
Compare
Signed-off-by: wallentx <william.allentx@gmail.com>
Signed-off-by: wallentx <william.allentx@gmail.com>
Signed-off-by: wallentx <william.allentx@gmail.com>
Signed-off-by: wallentx <william.allentx@gmail.com>
Signed-off-by: wallentx <william.allentx@gmail.com>
## Why `openai.gpt-5.4-cmb` is served through the Amazon Bedrock provider, whose request validator currently accepts `function` and `mcp` tool specs but rejects Responses `custom` tools. The CMB catalog entry reuses the bundled `gpt-5.4` metadata, which marks `apply_patch_tool_type` as `freeform`. That causes Codex to include an `apply_patch` tool with `type: "custom"`, so even heavily disabled sessions can fail before the model runs with: ```text Invalid tools: unknown variant `custom`, expected `function` or `mcp` ``` This is provider-specific: the model should still expose `apply_patch`, but for Bedrock it needs to use the JSON/function tool shape instead of the freeform/custom shape. ## What Changed - Override the `openai.gpt-5.4-cmb` static catalog entry to set `apply_patch_tool_type` to `function` after inheriting the rest of the `gpt-5.4` model metadata. - Update the catalog test expectation so the CMB entry continues to track `gpt-5.4` metadata except for this Bedrock-specific tool shape override. ## Verification - `cargo test -p codex-model-provider` - `just fix -p codex-model-provider`
…nt/wallentx_termux-target_from_release_0.125.0_efb7d2fe5730
…ermux into upstream/rust-v0.125.0 # Conflicts: # .github/termux-release.json # codex-rs/Cargo.toml # codex-rs/core/src/session/session.rs # codex-rs/core/src/tools/runtimes/shell/unix_escalation.rs # codex-rs/protocol/src/approvals.rs # codex-rs/rollout-trace/src/tool_dispatch.rs # codex-rs/tui/src/app/thread_session_state.rs # codex-rs/tui/src/app_server_session.rs # codex-rs/tui/src/chatwidget/tests/composer_submission.rs
…t_from_release_0.125.0_efb7d2fe5730 checkpoint: into wallentx/termux-target from release/0.125.0 @ efb7d2f
## Why `thread/fork` responses intentionally include copied history so the caller can render the fork immediately, but `thread/started` is a lifecycle notification. The v2 `Thread` contract says notifications should return `turns: []`, and the fork path was reusing the response thread directly, causing copied turns to be emitted through `thread/started` as well. ## What Changed - Route app-server `thread/started` notification construction through a helper that clears `thread.turns` before sending. - Keep `thread/fork` responses unchanged so callers still receive copied history. - Add persistent and ephemeral fork coverage that asserts `thread/started` emits an empty `turns` array while the response retains fork history. ## Testing - `just fmt` - `cargo test -p codex-app-server`
## Summary - Switch Unix socket app-server connections to perform the standard WebSocket HTTP Upgrade handshake - Update the Unix socket test to exercise a real upgrade over the Unix stream - Refresh the app-server README to describe the new Unix socket behavior ## Testing - `cargo test -p codex-app-server transport::unix_socket_tests` - `just fmt` - `git diff --check`
…nai#19170) Selection menus in the TUI currently let disabled rows interfere with numbering and default focus. This makes mixed menus harder to read and can land selection on rows that are not actionable. This change updates the shared selection-menu behavior in list_selection_view so disabled rows are not selected when these views open, and prevents them from being numbered like selectable rows. - Disabled rows no longer receive numeric labels - Digit shortcuts map to enabled rows only - Default selection moves to the first enabled row in mixed menus - Updated affected snapshot - Added snapshot coverage for a plugin detail error popup - Added a focused unit test for shared selection-view behavior --------- Co-authored-by: Codex <noreply@openai.com>
## Why The profile conversion path still required a `cwd` even when it was only translating a legacy `SandboxPolicy` into a `PermissionProfile`. That made profile producers invent an ambient `cwd`, which is exactly the anchoring we are trying to remove from permission-profile data. A legacy workspace-write policy can be represented symbolically instead: `:cwd = write` plus read-only `:project_roots` metadata subpaths. This PR creates that cwd-free base so the rest of the stack can stop threading cwd through profile construction. Callers that actually need a concrete runtime filesystem policy for a specific cwd still have an explicitly named cwd-bound conversion. ## What Changed - `PermissionProfile::from_legacy_sandbox_policy` now takes only `&SandboxPolicy`. - `FileSystemSandboxPolicy::from_legacy_sandbox_policy` is now the symbolic, cwd-free projection for profiles. - The old concrete projection is retained as `FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd` for runtime/boundary code that must materialize legacy cwd behavior. - Workspace-write profiles preserve `CurrentWorkingDirectory` and `ProjectRoots` special entries instead of materializing cwd into absolute paths. ## Verification - `cargo check -p codex-protocol -p codex-core -p codex-app-server-protocol -p codex-app-server -p codex-exec -p codex-exec-server -p codex-tui -p codex-sandboxing -p codex-linux-sandbox -p codex-analytics --tests` - `just fix -p codex-protocol -p codex-core -p codex-app-server-protocol -p codex-app-server -p codex-exec -p codex-exec-server -p codex-tui -p codex-sandboxing -p codex-linux-sandbox -p codex-analytics` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19414). * openai#19395 * openai#19394 * openai#19393 * openai#19392 * openai#19391 * __->__ openai#19414
- Route cold thread/resume and thread/fork source loading through ThreadStore reads instead of direct rollout path operations - Keep lookups that explicitly specify a rollout-path using the local thread store methods but return an invalid-request error for remote ThreadStore configurations - Add some additional unit tests for code path coverage
## Why We already prefer shipping the MUSL Linux builds, and the in-repo release consumers resolve Linux release assets through the MUSL targets. Keeping the GNU release jobs around adds release time and extra assets without serving the paths we actually publish and consume. This is also easier to reason about as a standalone change: future work can point back to this PR as the intentional decision to stop publishing `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu` release artifacts. ## What changed - Removed the `x86_64-unknown-linux-gnu` and `aarch64-unknown-linux-gnu` entries from the `build` matrix in `.github/workflows/rust-release.yml`. - Added a short comment in that matrix documenting that Linux release artifacts intentionally ship MUSL-linked binaries. ## Verification - Reviewed `.github/workflows/rust-release.yml` to confirm that the release workflow now only builds Linux release artifacts for `x86_64-unknown-linux-musl` and `aarch64-unknown-linux-musl`.
## Summary - Mirrors openai/skills#374 in the Codex bundled OpenAI Docs skill - Adds `gpt-image-2` as the best image generation/edit model - Updates `gpt-image-1.5` to less expensive image generation/edit quality ## Test plan - `git diff --check`
## Why The VS Code extension and desktop app do not need the full TUI binary, and `codex-app-server` is materially smaller than standalone `codex`. We still want to publish it as an official release artifact, but building it by tacking another `--bin` onto the existing release `cargo build` invocations would lengthen those jobs. This change keeps `codex-app-server` on its own release bundle so it can build in parallel with the existing `codex` and helper bundles. ## What changed - Made `.github/workflows/rust-release.yml` bundle-aware so each macOS and Linux MUSL target now builds either the existing `primary` bundle (`codex` and `codex-responses-api-proxy`) or a standalone `app-server` bundle (`codex-app-server`). - Preserved the historical artifact names for the primary macOS/Linux bundles so `scripts/stage_npm_packages.py` and `codex-cli/scripts/install_native_deps.py` continue to find release assets under the paths they already expect, while giving the new app-server artifacts distinct names. - Added a matching `app-server` bundle to `.github/workflows/rust-release-windows.yml`, and updated the final Windows packaging job to download, sign, stage, and archive `codex-app-server.exe` alongside the existing release binaries. - Generalized the shared signing actions in `.github/actions/linux-code-sign/action.yml`, `.github/actions/macos-code-sign/action.yml`, and `.github/actions/windows-code-sign/action.yml` so each workflow row declares its binaries once and reuses that list for build, signing, and staging. - Added `codex-app-server` to `.github/dotslash-config.json` so releases also publish a generated DotSlash manifest for the standalone app-server binary. - Kept the macOS DMG focused on the existing `primary` bundle; `codex-app-server` ships as the regular standalone archives and DotSlash manifest. ## Verification - Parsed the modified workflow and action YAML files locally with `python3` + `yaml.safe_load(...)`. - Parsed `.github/dotslash-config.json` locally with `python3` + `json.loads(...)`. - Reviewed the resulting release matrices, artifact names, and packaging paths to confirm that `codex-app-server` is built separately on macOS, Linux MUSL, and Windows, while the existing npm staging and Windows `codex` zip bundling contracts remain intact.
Termux rust-v0.125.0
…t_from_release_0.125.0_a2c0506d876d checkpoint: into wallentx/termux-target from release/0.125.0 @ a2c0506
Termux rust-v0.126.0-alpha.1
…ckpoint/wallentx_termux-target_from_release_0.126.0_d763bfd051ab # Conflicts: # codex-rs/Cargo.toml
…t_from_release_0.126.0_d763bfd051ab checkpoint: into wallentx/termux-target from release/0.126.0 @ d763bfd


Codex CLI currently fails on native Termux/Android due to DNS resolution and unsupported lock semantics in this environment. This PR adds compatibility adjustments that allow:
The changes preserve existing behavior on non-Android platforms.
Changes
DNS/Resolver fixes
$PREFIX/etc/resolv.conf,Graceful handling of unsupported locks
std::io::ErrorKind::Unsupportedfromtry_lock()without failing.Android build target support in CI
To produce and validate builds targeting Android (e.g. native Termux):
aarch64-linux-androidtarget in GitHub Actions.CC, andARfor the Android target.keyringtarget dependencies for Android.This enables CI to build Android artifacts that include the compatibility adjustments above, which aids testing and validation.
Validation
just fmtcargo test