Skip to content

Commit 1a4cb18

Browse files
committed
codex 0.147.0
1 parent 5d42e15 commit 1a4cb18

17 files changed

Lines changed: 90 additions & 287 deletions

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ build/install/codex/bin/codex --version
6464
- The wrapper uses the official Solaris Rust standalone installer target
6565
`x86_64-pc-solaris`.
6666
- The pinned Codex source is the upstream `openai/codex` release tag
67-
`rust-v0.146.0`, built from its `codex-rs/` workspace.
67+
`rust-v0.147.0`, built from its `codex-rs/` workspace.
6868
- Set `SOLARIS_CODEX_PROXY_SETUP=/path/to/proxy.sh` if your host needs an
6969
environment hook before downloads.
7070
- The codex build clears inherited Solaris `LD_*` hardening variables because
@@ -91,9 +91,9 @@ patch series under `patches/codex/` before vendoring:
9191
startup.
9292
- `0007-app-server-daemon-use-fcntl-locks-on-solaris.patch` replaces
9393
unsupported `flock(2)` daemon lifecycle locks with Solaris `fcntl(2)` locks.
94-
- `0008-http-client-honor-no-proxy-before-system-proxy.patch` makes
95-
exec-server HTTP requests honor `NO_PROXY`/`no_proxy` hosts before reqwest's
96-
system proxy lookup.
94+
- `0008-http-client-honor-no-proxy-before-system-proxy.patch` enables Codex's
95+
route-aware system-proxy policy by default on Solaris so shared HTTP clients
96+
honor `NO_PROXY`/`no_proxy` without reqwest system proxy autodetection.
9797
- `0010-exec-server-drain-fs-helper-output-concurrently.patch` keeps large
9898
filesystem-helper responses from blocking on a full stdout pipe.
9999

common.sh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,15 +236,20 @@ apply_patch_series() {
236236
[[ -e "${patch}" ]] || continue
237237
if (
238238
cd "${root}"
239-
"${PATCH_TOOL}" --dry-run -p1 < "${patch}" >/dev/null 2>&1
239+
"${PATCH_TOOL}" --dry-run --batch --forward --fuzz=0 -p1 < "${patch}" >/dev/null 2>&1
240240
); then
241241
log "Applying $(basename "${patch}")"
242242
(
243243
cd "${root}"
244-
"${PATCH_TOOL}" -p1 < "${patch}"
244+
"${PATCH_TOOL}" --batch --forward --fuzz=0 -p1 < "${patch}"
245245
)
246+
elif (
247+
cd "${root}"
248+
"${PATCH_TOOL}" --dry-run --batch --forward --fuzz=0 -R -p1 < "${patch}" >/dev/null 2>&1
249+
); then
250+
log "Already applied $(basename "${patch}")"
246251
else
247-
log "Skipping $(basename "${patch}")"
252+
die "patch does not apply cleanly: ${patch}"
248253
fi
249254
done
250255
}

patches/codex/0002-tui-disable-unsupported-clipboard-backends-on-solaris.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ diff --git a/codex-rs/tui/Cargo.toml b/codex-rs/tui/Cargo.toml
1414
index 6c0b83a..c231dee 100644
1515
--- a/codex-rs/tui/Cargo.toml
1616
+++ b/codex-rs/tui/Cargo.toml
17-
@@ -141,9 +141,9 @@ windows-sys = { version = "0.52", features = [
17+
@@ -140,9 +140,9 @@ windows-sys = { version = "0.52", features = [
1818
] }
1919
winsplit = "0.1"
2020

patches/codex/0005-state-use-rollback-journal-on-solaris.patch

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ Why it is needed:
1313
`SQLITE_IOERR_SHMMAP` / SQLite extended error code 5386
1414

1515
diff --git a/codex-rs/state/src/sqlite.rs b/codex-rs/state/src/sqlite.rs
16-
index ca6f224..e141672 100644
16+
index 6c18670..62cf953 100644
1717
--- a/codex-rs/state/src/sqlite.rs
1818
+++ b/codex-rs/state/src/sqlite.rs
19-
@@ -3,16 +3,100 @@
19+
@@ -13,6 +13,8 @@ use crate::telemetry::DbKind;
2020
use codex_utils_absolute_path::AbsolutePathBuf;
2121
use log::LevelFilter;
2222
use sqlx::ConnectOptions;
2323
+#[cfg(target_os = "solaris")]
2424
+use sqlx::Connection;
2525
use sqlx::Error;
2626
use sqlx::SqlitePool;
27+
use sqlx::migrate::Migrator;
28+
@@ -19,8 +21,12 @@ use sqlx::migrate::Migrator;
2729
use sqlx::sqlite::SqliteAutoVacuum;
2830
use sqlx::sqlite::SqliteConnectOptions;
2931
use sqlx::sqlite::SqliteJournalMode;
@@ -34,9 +36,11 @@ index ca6f224..e141672 100644
3436
+#[cfg(target_os = "solaris")]
3537
+use std::ffi::OsString;
3638
use std::path::Path;
37-
+#[cfg(target_os = "solaris")]
38-
+use std::path::PathBuf;
39+
use std::path::PathBuf;
3940
use std::time::Duration;
41+
@@ -33,6 +39,82 @@ const MEMORIES_DB_FILENAME: &str = "memories_1.sqlite";
42+
const STATE_DB_FILENAME: &str = "state_5.sqlite";
43+
const THREAD_HISTORY_DB_FILENAME: &str = "thread_history_1.sqlite";
4044

4145
+fn default_sqlite_journal_mode() -> SqliteJournalMode {
4246
+ if cfg!(target_os = "solaris") {
@@ -114,10 +118,10 @@ index ca6f224..e141672 100644
114118
+ Ok(())
115119
+}
116120
+
117-
/// Resolved configuration shared by all Codex SQLite connections.
118-
#[derive(Clone, Debug, Eq, PartialEq)]
119-
pub struct SqliteConfig {
120-
@@ -35,9 +119,12 @@ impl SqliteConfig {
121+
#[derive(Clone, Copy)]
122+
struct RuntimeDbSpec {
123+
label: &'static str,
124+
@@ -277,9 +359,12 @@ impl SqliteConfig {
121125
/// Open a writable Codex SQLite database, creating it if necessary.
122126
pub async fn open_read_write_pool(&self, path: &Path) -> Result<SqlitePool, Error> {
123127
+ #[cfg(target_os = "solaris")]

patches/codex/0006-arg0-tolerate-solaris-stale-temp-cleanup.patch

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs
1313
index 1c28f81..501a172 100644
1414
--- a/codex-rs/arg0/src/lib.rs
1515
+++ b/codex-rs/arg0/src/lib.rs
16-
@@ -485,17 +485,41 @@ fn janitor_cleanup(temp_root: &Path) -> std::io::Result<()> {
16+
@@ -491,17 +491,41 @@ fn janitor_cleanup(temp_root: &Path) -> std::io::Result<()> {
1717
continue;
1818
};
1919

@@ -61,7 +61,7 @@ index 1c28f81..501a172 100644
6161
fn try_lock_dir(dir: &Path) -> std::io::Result<Option<File>> {
6262
let lock_path = dir.join(LOCK_FILENAME);
6363
let lock_file = match File::options().read(true).write(true).open(&lock_path) {
64-
@@ -717,6 +741,10 @@ mod tests {
64+
@@ -723,6 +747,10 @@ mod tests {
6565
}
6666

6767
#[test]

patches/codex/0007-app-server-daemon-use-fcntl-locks-on-solaris.patch

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ diff --git a/codex-rs/app-server-daemon/src/backend/pid.rs b/codex-rs/app-server
1414
index b525e61..68c85dc 100644
1515
--- a/codex-rs/app-server-daemon/src/backend/pid.rs
1616
+++ b/codex-rs/app-server-daemon/src/backend/pid.rs
17-
@@ -589,7 +589,7 @@ enum EmptyPidReservation {
17+
@@ -599,7 +599,7 @@ enum EmptyPidReservation {
1818
Record(PidRecord),
1919
}
2020

@@ -23,7 +23,7 @@ index b525e61..68c85dc 100644
2323
fn try_lock_file(file: &fs::File) -> Result<bool> {
2424
use std::os::fd::AsRawFd;
2525

26-
@@ -605,6 +605,35 @@ fn try_lock_file(file: &fs::File) -> Result<bool> {
26+
@@ -615,6 +615,35 @@ fn try_lock_file(file: &fs::File) -> Result<bool> {
2727
Err(err).context("failed to lock pid reservation")
2828
}
2929

@@ -63,7 +63,7 @@ diff --git a/codex-rs/app-server-daemon/src/lib.rs b/codex-rs/app-server-daemon/
6363
index 5870075..95ff316 100644
6464
--- a/codex-rs/app-server-daemon/src/lib.rs
6565
+++ b/codex-rs/app-server-daemon/src/lib.rs
66-
@@ -831,7 +831,7 @@ fn should_reexec_updater(
66+
@@ -826,7 +826,7 @@ fn should_reexec_updater(
6767
&& outcome == RestartIfRunningOutcome::Restarted
6868
}
6969

@@ -72,7 +72,7 @@ index 5870075..95ff316 100644
7272
fn try_lock_file(file: &tokio::fs::File) -> Result<bool> {
7373
use std::os::fd::AsRawFd;
7474

75-
@@ -847,6 +847,35 @@ fn try_lock_file(file: &tokio::fs::File) -> Result<bool> {
75+
@@ -842,6 +842,35 @@ fn try_lock_file(file: &tokio::fs::File) -> Result<bool> {
7676
Err(err).context("failed to lock daemon operation")
7777
}
7878

Lines changed: 22 additions & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,26 @@
1-
Some deployments need a general HTTPS proxy for public services while selected
2-
hosts must still be reached directly. On Solaris, the reqwest system proxy
3-
matcher can route an MCP HTTP request through the proxy even when NO_PROXY
4-
contains the destination host.
1+
Codex 0.147 moved outbound HTTP traffic to a shared route-aware client. Its
2+
RespectSystemProxy policy explicitly resolves environment proxies on Solaris
3+
and attaches NO_PROXY/no_proxy to the selected proxy, avoiding reqwest's
4+
system proxy autodetection for those requests. However, that policy is an
5+
under-development feature and remains disabled by default upstream.
56

6-
What it does:
7-
- evaluates NO_PROXY/no_proxy in Codex before constructing the per-request
8-
reqwest client
9-
- disables proxy autodetection only for destinations matching NO_PROXY/no_proxy
10-
- keeps existing proxy behavior for all other destinations
7+
Keep the upstream default on other platforms, but enable the shared policy by
8+
default on Solaris. This replaces the older exec-server-only workaround and
9+
also covers auth, API, WebSocket, and redirect routing through the shared HTTP
10+
client factory.
1111

12-
This was offered to upstream via:
12+
The original Solaris problem was reported upstream via:
1313
https://github.com/openai/codex/issues/4242
1414

15-
diff --git a/codex-rs/exec-server/src/client/reqwest_http_client.rs b/codex-rs/exec-server/src/client/reqwest_http_client.rs
16-
index d0cffa6..dd849de 100644
17-
--- a/codex-rs/exec-server/src/client/reqwest_http_client.rs
18-
+++ b/codex-rs/exec-server/src/client/reqwest_http_client.rs
19-
@@ -49,20 +49,25 @@ pub(crate) struct PendingReqwestHttpBodyStream {
20-
/// Validates `http/request` parameters and runs the actual `reqwest` call used
21-
/// by the exec-server route and the local [`HttpClient`] backend.
22-
pub(crate) struct ReqwestHttpRequestRunner {
23-
- client: reqwest::Client,
24-
+ timeout_ms: Option<u64>,
25-
+ redirect_policy: HttpRedirectPolicy,
26-
}
27-
28-
impl ReqwestHttpClient {
29-
fn build_client(
30-
timeout_ms: Option<u64>,
31-
redirect_policy: HttpRedirectPolicy,
32-
+ url: &Url,
33-
) -> Result<reqwest::Client, ExecServerError> {
34-
- let builder = match timeout_ms {
35-
+ let mut builder = match timeout_ms {
36-
None => reqwest::Client::builder(),
37-
Some(timeout_ms) => {
38-
reqwest::Client::builder().timeout(Duration::from_millis(timeout_ms))
39-
}
40-
};
41-
+ if request_matches_no_proxy(url) {
42-
+ builder = builder.no_proxy();
43-
+ }
44-
let builder = match redirect_policy {
45-
HttpRedirectPolicy::Follow => builder,
46-
HttpRedirectPolicy::Stop => builder.redirect(reqwest::redirect::Policy::none()),
47-
@@ -125,9 +130,10 @@ impl ReqwestHttpRequestRunner {
48-
timeout_ms: Option<u64>,
49-
redirect_policy: HttpRedirectPolicy,
50-
) -> Result<Self, JSONRPCErrorError> {
51-
- let client = ReqwestHttpClient::build_client(timeout_ms, redirect_policy)
52-
- .map_err(|error| internal_error(error.to_string()))?;
53-
- Ok(Self { client })
54-
+ Ok(Self {
55-
+ timeout_ms,
56-
+ redirect_policy,
57-
+ })
58-
}
59-
60-
pub(crate) async fn run(
61-
@@ -159,7 +165,9 @@ impl ReqwestHttpRequestRunner {
62-
);
63-
let mut headers = Self::build_headers(params.headers)?;
64-
codex_otel::inject_span_w3c_trace_headers(&request_span, &mut headers);
65-
- let mut request = self.client.request(method.clone(), url).headers(headers);
66-
+ let client = ReqwestHttpClient::build_client(self.timeout_ms, self.redirect_policy, &url)
67-
+ .map_err(|error| internal_error(error.to_string()))?;
68-
+ let mut request = client.request(method.clone(), url).headers(headers);
69-
if let Some(body) = params.body {
70-
request = request.body(body.into_inner());
71-
}
72-
@@ -320,3 +328,98 @@ fn error_source_chain(error: &reqwest::Error) -> Option<String> {
73-
}
74-
(!sources.is_empty()).then(|| sources.join(": "))
75-
}
76-
+
77-
+fn request_matches_no_proxy(url: &Url) -> bool {
78-
+ let Some(host) = url.host_str() else {
79-
+ return false;
80-
+ };
81-
+ let Some(no_proxy) = configured_no_proxy() else {
82-
+ return false;
83-
+ };
84-
+ no_proxy_list_matches_host(&no_proxy, host)
85-
+}
86-
+
87-
+fn configured_no_proxy() -> Option<String> {
88-
+ ["NO_PROXY", "no_proxy"].into_iter().find_map(|key| {
89-
+ let value = std::env::var(key).ok()?;
90-
+ if value.trim().is_empty() {
91-
+ return None;
92-
+ }
93-
+ Some(value)
94-
+ })
95-
+}
96-
+
97-
+fn no_proxy_list_matches_host(no_proxy: &str, host: &str) -> bool {
98-
+ let host = normalize_no_proxy_host(host);
99-
+ if host.is_empty() {
100-
+ return false;
101-
+ }
102-
+
103-
+ no_proxy.split(',').any(|entry| {
104-
+ let entry = normalize_no_proxy_host(strip_no_proxy_port(entry.trim()));
105-
+ if entry.is_empty() {
106-
+ return false;
107-
+ }
108-
+ if entry == "*" {
109-
+ return true;
110-
+ }
111-
+
112-
+ let entry = entry.strip_prefix('.').unwrap_or(&entry);
113-
+ host == entry || host.ends_with(&format!(".{entry}"))
114-
+ })
115-
+}
116-
+
117-
+fn normalize_no_proxy_host(host: &str) -> String {
118-
+ host.trim()
119-
+ .trim_matches(&['[', ']'][..])
120-
+ .trim_end_matches('.')
121-
+ .to_ascii_lowercase()
122-
+}
123-
+
124-
+fn strip_no_proxy_port(entry: &str) -> &str {
125-
+ let Some((host, port)) = entry.rsplit_once(':') else {
126-
+ return entry;
127-
+ };
128-
+ if host.contains(':') || port.is_empty() || !port.bytes().all(|byte| byte.is_ascii_digit()) {
129-
+ return entry;
130-
+ }
131-
+ host
132-
+}
133-
+
134-
+#[cfg(test)]
135-
+mod no_proxy_tests {
136-
+ use super::no_proxy_list_matches_host;
137-
+
138-
+ #[test]
139-
+ fn no_proxy_matches_exact_host() {
140-
+ assert!(no_proxy_list_matches_host(
141-
+ "service.example.com",
142-
+ "service.example.com"
143-
+ ));
144-
+ }
145-
+
146-
+ #[test]
147-
+ fn no_proxy_matches_parent_domain() {
148-
+ assert!(no_proxy_list_matches_host(
149-
+ ".example.com",
150-
+ "service.example.com"
151-
+ ));
152-
+ assert!(no_proxy_list_matches_host(
153-
+ "example.com",
154-
+ "service.example.com"
155-
+ ));
156-
+ }
157-
+
158-
+ #[test]
159-
+ fn no_proxy_rejects_partial_suffix() {
160-
+ assert!(!no_proxy_list_matches_host("example.com", "notexample.com"));
161-
+ }
162-
+
163-
+ #[test]
164-
+ fn no_proxy_ignores_optional_port() {
165-
+ assert!(no_proxy_list_matches_host(
166-
+ "service.example.com:443",
167-
+ "service.example.com"
168-
+ ));
169-
+ }
170-
+}
15+
diff --git a/codex-rs/features/src/lib.rs b/codex-rs/features/src/lib.rs
16+
--- a/codex-rs/features/src/lib.rs
17+
+++ b/codex-rs/features/src/lib.rs
18+
@@ -1100,7 +1100,7 @@ const FEATURES: &[FeatureSpec] = &[
19+
id: Feature::RespectSystemProxy,
20+
key: "respect_system_proxy",
21+
stage: Stage::UnderDevelopment,
22+
- default_enabled: false,
23+
+ default_enabled: cfg!(target_os = "solaris"),
24+
},
25+
FeatureSpec {
26+
id: Feature::Collab,

patches/codex/0010-exec-server-drain-fs-helper-output-concurrently.patch

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ index 812d767..1286e8f 100644
2121
use tokio::io::AsyncWriteExt;
2222
use tokio::process::Command;
2323

24-
@@ -302,6 +303,25 @@ async fn run_command(
24+
@@ -300,6 +301,25 @@ async fn run_command(
2525
request_json: Vec<u8>,
2626
) -> Result<FsHelperPayload, JSONRPCErrorError> {
2727
let mut child = spawn_command(command)?;
@@ -47,7 +47,7 @@ index 812d767..1286e8f 100644
4747
let mut stdin = child
4848
.stdin
4949
.take()
50-
@@ -310,15 +330,17 @@ async fn run_command(
50+
@@ -308,15 +328,17 @@ async fn run_command(
5151
stdin.shutdown().await.map_err(io_error)?;
5252
drop(stdin);
5353

@@ -70,7 +70,7 @@ index 812d767..1286e8f 100644
7070
match response {
7171
FsHelperResponse::Ok(payload) => Ok(payload),
7272
FsHelperResponse::Error(error) => Err(error),
73-
@@ -361,6 +383,10 @@ fn io_error(err: std::io::Error) -> JSONRPCErrorError {
73+
@@ -359,6 +381,10 @@ fn io_error(err: std::io::Error) -> JSONRPCErrorError {
7474
internal_error(err.to_string())
7575
}
7676

@@ -81,7 +81,7 @@ index 812d767..1286e8f 100644
8181
fn json_error(err: serde_json::Error) -> JSONRPCErrorError {
8282
internal_error(format!(
8383
"failed to encode or decode fs sandbox helper message: {err}"
84-
@@ -678,6 +704,58 @@ mod tests {
84+
@@ -682,6 +708,58 @@ mod tests {
8585
assert!(policy.can_read_path_with_cwd(alias_parent.as_path(), cwd.as_path()));
8686
}
8787

0 commit comments

Comments
 (0)