fix(tui/input): normalize KeyEvent kind on non-enhanced terminals to restore typing on Git Bash/mintty (Windows)

This maps Release-only key events to Press when keyboard enhancement flags are unsupported, fixing unresponsive input and Ctrl+C on some Windows terminals.

Closes #18. Closes #14.
This commit is contained in:
James Peter
2025-09-03 21:44:29 +10:00
parent 3e1348da0e
commit 5b722e0732
10 changed files with 169 additions and 9 deletions
+5 -1
View File
@@ -25,9 +25,13 @@ Help us improve by filing issues or submitting PRs (see the section below for ho
Install globally:
```shell
npm install -g @openai/codex
npm install -g @just-every/code
```
Note: The old package name `@just-every/coder` is deprecated and no longer
receives updates. If you installed that in the past, please switch to
`@just-every/code` to get the latest releases.
Next, set your OpenAI API key as an environment variable:
```shell
+2 -3
View File
@@ -27,13 +27,12 @@
"publishConfig": {
"access": "public"
},
"dependencies": {
"@vscode/ripgrep": "^1.15.14"
},
"dependencies": {},
"devDependencies": {
"prettier": "^3.3.3"
},
"optionalDependencies": {
"@vscode/ripgrep": "^1.15.14",
"@just-every/code-darwin-arm64": "0.2.56",
"@just-every/code-darwin-x64": "0.2.56",
"@just-every/code-linux-x64-musl": "0.2.56",
+1 -1
View File
@@ -16,7 +16,7 @@ pub(crate) async fn chatgpt_get_request<T: DeserializeOwned>(
init_chatgpt_token_from_auth(&config.codex_home).await?;
// Make direct HTTP request to ChatGPT backend API with the token
let client = reqwest::Client::new();
let client = codex_core::http_client::build_http_client();
let url = format!("{chatgpt_base_url}{path}");
let token =
+1 -1
View File
@@ -96,7 +96,7 @@ impl ModelClient {
Self {
config,
auth_manager,
client: reqwest::Client::new(),
client: crate::http_client::build_http_client(),
provider,
session_id,
effort,
+51
View File
@@ -0,0 +1,51 @@
use std::fs;
use std::path::PathBuf;
/// Build a reqwest Client with optional extra root certificates loaded from
/// common environment variables (SSL_CERT_FILE, REQUESTS_CA_BUNDLE,
/// NODE_EXTRA_CA_CERTS). This helps environments using corporate/mitm proxies
/// whose CAs are distributed via system config or user-provided files.
pub fn build_http_client() -> reqwest::Client {
let mut builder = reqwest::Client::builder();
// Helper to load a PEM or DER certificate file if it exists and is readable.
fn load_cert(path: PathBuf) -> Option<reqwest::Certificate> {
if !path.exists() || !path.is_file() {
return None;
}
let bytes = fs::read(&path).ok()?;
reqwest::Certificate::from_pem(&bytes)
.or_else(|_| reqwest::Certificate::from_der(&bytes))
.ok()
}
// Single-file variables (common across ecosystems)
for var in ["SSL_CERT_FILE", "REQUESTS_CA_BUNDLE", "NODE_EXTRA_CA_CERTS"] {
if let Ok(val) = std::env::var(var) {
if !val.trim().is_empty() {
if let Some(cert) = load_cert(PathBuf::from(val)) {
builder = builder.add_root_certificate(cert);
}
}
}
}
// Directory of certs (PEM/CRT). Iterate best-effort.
if let Ok(dir) = std::env::var("SSL_CERT_DIR") {
let path = PathBuf::from(dir);
if path.is_dir() {
if let Ok(rd) = fs::read_dir(path) {
for entry in rd.flatten() {
let p = entry.path();
if p.extension().and_then(|s| s.to_str()).map(|s| matches!(s, "crt" | "pem" | "der")).unwrap_or(false) {
if let Some(cert) = load_cert(p) {
builder = builder.add_root_certificate(cert);
}
}
}
}
}
}
builder.build().unwrap_or_else(|_| reqwest::Client::new())
}
+1
View File
@@ -69,6 +69,7 @@ pub mod user_agent;
pub use rollout::list::ConversationsPage;
mod user_notification;
pub mod util;
pub mod http_client;
pub use apply_patch::CODEX_APPLY_PATCH_ARG1;
pub use safety::get_platform_sandbox;
// Use our local protocol definitions to preserve custom events and input items.
+2 -2
View File
@@ -325,7 +325,7 @@ async fn exchange_code_for_tokens(
refresh_token: String,
}
let client = reqwest::Client::new();
let client = codex_core::http_client::build_http_client();
let resp = client
.post(format!("{issuer}/oauth/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
@@ -494,7 +494,7 @@ async fn obtain_api_key(issuer: &str, client_id: &str, id_token: &str) -> io::Re
struct ExchangeResp {
access_token: String,
}
let client = reqwest::Client::new();
let client = codex_core::http_client::build_http_client();
let resp = client
.post(format!("{issuer}/oauth/token"))
.header("Content-Type", "application/x-www-form-urlencoded")
+8 -1
View File
@@ -363,7 +363,14 @@ impl App<'_> {
widget.on_commit_tick();
}
}
AppEvent::KeyEvent(key_event) => {
AppEvent::KeyEvent(mut key_event) => {
// On terminals that do not support keyboard enhancement flags
// (notably some Windows Git Bash/mintty setups), crossterm may
// report only Release events. Normalize such events to Press so
// keys register consistently.
if !self.enhanced_keys_supported {
key_event = KeyEvent::new(key_event.code, key_event.modifiers);
}
// Reset double‑Esc timer on any non‑Esc key
if !matches!(key_event.code, KeyCode::Esc) {
self.last_esc_time = None;
+30
View File
@@ -0,0 +1,30 @@
Homebrew (macOS)
This repository now includes a helper script to generate a Homebrew formula
from the latest GitHub release artifacts. Publishing to Homebrew requires a
tap repository (for example, `just-every/homebrew-tap`). Once a tap exists,
you can generate and publish the formula like so:
1) Generate the formula for the latest version:
```
scripts/generate-homebrew-formula.sh
```
2) Copy the generated `Code.rb` into your tap repo under `Formula/Code.rb`
and update the `url`/`sha256` if needed.
3) Users can then install with:
```
brew tap just-every/tap
brew install code
```
Notes
- The formula expects release assets named like:
- `code-aarch64-apple-darwin.tar.gz`
- `code-x86_64-apple-darwin.tar.gz`
- The CLI is installed as `code` and `coder` shims for compatibility.
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
set -euo pipefail
# Generate a minimal Homebrew formula from the latest GitHub release.
# Writes Formula/Code.rb into the repo root (not a tap); you can copy it
# into a tap repo to publish.
owner_repo="just-every/code"
version="${1:-$(jq -r .version codex-cli/package.json)}"
assets=(
"code-aarch64-apple-darwin.tar.gz"
"code-x86_64-apple-darwin.tar.gz"
)
mkdir -p Formula
cat > Formula/Code.rb <<'RUBY'
class Code < Formula
desc "Terminal coding agent"
homepage "https://github.com/just-every/code"
RUBY
echo " version \"v${version}\"" >> Formula/Code.rb
cat >> Formula/Code.rb <<'RUBY'
on_macos do
if Hardware::CPU.arm?
url "__URL_ARM64__"
sha256 "__SHA_ARM64__"
else
url "__URL_X64__"
sha256 "__SHA_X64__"
end
end
def install
bin.install Dir["code-*"].first => "code"
# Provide a compatibility shim
(bin/"coder").write <<~EOS
#!/bin/bash
exec "#{bin}/code" "$@"
EOS
end
test do
system "#{bin}/code", "--help"
end
end
RUBY
for a in "${assets[@]}"; do
url="https://github.com/${owner_repo}/releases/download/v${version}/${a}"
tmp="/tmp/${a}"
echo "Downloading ${url}..." >&2
curl -fsSL "${url}" -o "${tmp}"
sha=$(shasum -a 256 "${tmp}" | awk '{print $1}')
if [[ "${a}" == *"aarch64-apple-darwin"* ]]; then
sed -i.bak "s#__URL_ARM64__#${url}#" Formula/Code.rb
sed -i.bak "s#__SHA_ARM64__#${sha}#" Formula/Code.rb
else
sed -i.bak "s#__URL_X64__#${url}#" Formula/Code.rb
sed -i.bak "s#__SHA_X64__#${sha}#" Formula/Code.rb
fi
done
rm -f Formula/Code.rb.bak
echo "Wrote Formula/Code.rb for v${version}" >&2