From 8793624db22321bdc5776fe0edf70f2659ea141c Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Wed, 11 Feb 2026 08:35:05 -0800 Subject: [PATCH] Extract `codex-config` from `codex-core` ## Why `codex-core` has accumulated config loading, requirements parsing, constraint logic, and config-layer state handling in one large crate. This refactor pulls that cohesive subsystem into a dedicated crate so we can reduce the compile/test surface area of `codex-core` and make future config work more isolated. This is part of the broader goal of right-sizing crates to reduce monolithic rebuild cost and improve incremental development speed. ## What Changed ### New crate - Added a new workspace crate: `codex-rs/config` (`codex-config`) - Added workspace wiring in `codex-rs/Cargo.toml` - Added dependency from `codex-core` to `codex-config` ### Moved config internals from `core` to `config` Moved these modules into `codex-config`: - `core/src/config/constraint.rs` -> `config/src/constraint.rs` - `core/src/config_loader/cloud_requirements.rs` -> `config/src/config_loader/cloud_requirements.rs` - `core/src/config_loader/config_requirements.rs` -> `config/src/config_loader/config_requirements.rs` - `core/src/config_loader/fingerprint.rs` -> `config/src/config_loader/fingerprint.rs` - `core/src/config_loader/merge.rs` -> `config/src/config_loader/merge.rs` - `core/src/config_loader/overrides.rs` -> `config/src/config_loader/overrides.rs` - `core/src/config_loader/requirements_exec_policy.rs` -> `config/src/config_loader/requirements_exec_policy.rs` - `core/src/config_loader/state.rs` -> `config/src/config_loader/state.rs` ### Removed shim modules in `core` After the move, the temporary one-line `pub use` shim files under `core/src/config_loader/` were deleted so history is a clean move/delete rather than introducing extra permanent forwarding modules. `core/src/config_loader/mod.rs` now imports/re-exports directly from `codex_config`, including direct use of `build_cli_overrides_layer` and test-only access to `version_for_toml`. ### Follow-on fixes for direct imports - Updated `core/src/config_loader/macos.rs` to use `super::{ConfigRequirementsToml, ConfigRequirementsWithSources, RequirementSource}`. - Updated `core/src/config_loader/tests.rs` imports to reference `crate::config_loader` re-exports and `codex_config` exec-policy TOML types. ## Behavior and API Notes - Config behavior is intended to be unchanged. - `codex-core` continues to expose the same config-loader-facing API surface to its internal callers via `core/src/config_loader/mod.rs` re-exports. - The main functional change is crate ownership and dependency direction, not config semantics. ## Validation Ran: - `cargo test -p codex-config` - `cargo test -p codex-core --no-run` - `cargo test -p codex-core config_loader::tests::load_requirements_toml_produces_expected_constraints` - `cargo test -p codex-core config_loader::tests::requirements_exec_policy_tests::parses_single_prefix_rule_from_raw_toml` - `just fmt` - `just fix -p codex-config -p codex-core` - `just fix -p codex-core` All listed commands completed successfully in this workspace environment. --- codex-rs/Cargo.lock | 22 ++++- codex-rs/Cargo.toml | 68 ++++++++++----- codex-rs/config/BUILD.bazel | 6 ++ codex-rs/config/Cargo.toml | 26 ++++++ .../src/config_loader/cloud_requirements.rs | 0 .../src/config_loader/config_requirements.rs | 17 ++-- .../src/config_loader/fingerprint.rs | 2 +- .../src/config_loader/merge.rs | 0 codex-rs/config/src/config_loader/mod.rs | 29 +++++++ .../src/config_loader/overrides.rs | 2 +- .../config_loader/requirements_exec_policy.rs | 2 +- .../src/config_loader/state.rs | 0 .../src/config => config/src}/constraint.rs | 37 -------- codex-rs/config/src/lib.rs | 33 +++++++ codex-rs/core/Cargo.toml | 2 +- codex-rs/core/src/config/mod.rs | 7 +- codex-rs/core/src/config_loader/macos.rs | 6 +- codex-rs/core/src/config_loader/mod.rs | 51 +++++------ codex-rs/core/src/config_loader/tests.rs | 18 ++-- codex-rs/tui/src/debug_config.rs | 86 ++++++++++--------- 20 files changed, 258 insertions(+), 156 deletions(-) create mode 100644 codex-rs/config/BUILD.bazel create mode 100644 codex-rs/config/Cargo.toml rename codex-rs/{core => config}/src/config_loader/cloud_requirements.rs (100%) rename codex-rs/{core => config}/src/config_loader/config_requirements.rs (99%) rename codex-rs/{core => config}/src/config_loader/fingerprint.rs (97%) rename codex-rs/{core => config}/src/config_loader/merge.rs (100%) create mode 100644 codex-rs/config/src/config_loader/mod.rs rename codex-rs/{core => config}/src/config_loader/overrides.rs (94%) rename codex-rs/{core => config}/src/config_loader/requirements_exec_policy.rs (99%) rename codex-rs/{core => config}/src/config_loader/state.rs (100%) rename codex-rs/{core/src/config => config/src}/constraint.rs (85%) create mode 100644 codex-rs/config/src/lib.rs diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 9481950faf..41a263314f 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1628,6 +1628,26 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "codex-config" +version = "0.0.0" +dependencies = [ + "anyhow", + "codex-app-server-protocol", + "codex-execpolicy", + "codex-protocol", + "codex-utils-absolute-path", + "futures", + "multimap", + "pretty_assertions", + "serde", + "serde_json", + "sha2", + "thiserror 2.0.18", + "tokio", + "toml 0.9.12+spec-1.1.0", +] + [[package]] name = "codex-core" version = "0.0.0" @@ -1650,6 +1670,7 @@ dependencies = [ "codex-arg0", "codex-async-utils", "codex-client", + "codex-config", "codex-execpolicy", "codex-file-search", "codex-git", @@ -1686,7 +1707,6 @@ dependencies = [ "landlock", "libc", "maplit", - "multimap", "notify", "once_cell", "openssl-sys", diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index 3b57f1a175..a615cdac2a 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -15,6 +15,7 @@ members = [ "cloud-tasks", "cloud-tasks-client", "cli", + "config", "shell-command", "core", "hooks", @@ -83,20 +84,19 @@ codex-apply-patch = { path = "apply-patch" } codex-arg0 = { path = "arg0" } codex-async-utils = { path = "async-utils" } codex-backend-client = { path = "backend-client" } -codex-cloud-requirements = { path = "cloud-requirements" } codex-chatgpt = { path = "chatgpt" } -codex-cli = { path = "cli"} +codex-cli = { path = "cli" } codex-client = { path = "codex-client" } -codex-shell-command = { path = "shell-command" } +codex-cloud-requirements = { path = "cloud-requirements" } +codex-config = { path = "config" } codex-core = { path = "core" } -codex-hooks = { path = "hooks" } -codex-secrets = { path = "secrets" } codex-exec = { path = "exec" } codex-execpolicy = { path = "execpolicy" } codex-experimental-api-macros = { path = "codex-experimental-api-macros" } codex-feedback = { path = "feedback" } codex-file-search = { path = "file-search" } codex-git = { path = "utils/git" } +codex-hooks = { path = "hooks" } codex-keyring-store = { path = "keyring-store" } codex-linux-sandbox = { path = "linux-sandbox" } codex-lmstudio = { path = "lmstudio" } @@ -109,25 +109,27 @@ codex-process-hardening = { path = "process-hardening" } codex-protocol = { path = "protocol" } codex-responses-api-proxy = { path = "responses-api-proxy" } codex-rmcp-client = { path = "rmcp-client" } +codex-secrets = { path = "secrets" } +codex-shell-command = { path = "shell-command" } codex-state = { path = "state" } codex-stdio-to-uds = { path = "stdio-to-uds" } codex-tui = { path = "tui" } codex-utils-absolute-path = { path = "utils/absolute-path" } +codex-utils-approval-presets = { path = "utils/approval-presets" } codex-utils-cache = { path = "utils/cache" } codex-utils-cargo-bin = { path = "utils/cargo-bin" } +codex-utils-cli = { path = "utils/cli" } +codex-utils-elapsed = { path = "utils/elapsed" } +codex-utils-fuzzy-match = { path = "utils/fuzzy-match" } +codex-utils-home-dir = { path = "utils/home-dir" } codex-utils-image = { path = "utils/image" } codex-utils-json-to-toml = { path = "utils/json-to-toml" } -codex-utils-home-dir = { path = "utils/home-dir" } +codex-utils-oss = { path = "utils/oss" } codex-utils-pty = { path = "utils/pty" } codex-utils-readiness = { path = "utils/readiness" } codex-utils-rustls-provider = { path = "utils/rustls-provider" } -codex-utils-string = { path = "utils/string" } -codex-utils-cli = { path = "utils/cli" } -codex-utils-elapsed = { path = "utils/elapsed" } codex-utils-sandbox-summary = { path = "utils/sandbox-summary" } -codex-utils-approval-presets = { path = "utils/approval-presets" } -codex-utils-oss = { path = "utils/oss" } -codex-utils-fuzzy-match = { path = "utils/fuzzy-match" } +codex-utils-string = { path = "utils/string" } codex-windows-sandbox = { path = "windows-sandbox-rs" } core_test_support = { path = "core/tests/common" } exec_server_test_support = { path = "exec-server/tests/common" } @@ -139,12 +141,12 @@ allocative = "0.3.3" ansi-to-tui = "7.0.0" anyhow = "1" arboard = { version = "3", features = ["wayland-data-control"] } +askama = "0.15.4" assert_cmd = "2" assert_matches = "1.5.0" async-channel = "2.3.1" async-stream = "0.3.6" async-trait = "0.1.89" -askama = "0.15.4" axum = { version = "0.8", default-features = false } base64 = "0.22.1" bm25 = "2.3.2" @@ -154,8 +156,8 @@ chrono = "0.4.43" clap = "4" clap_complete = "4" color-eyre = "0.6.3" -crossterm = "0.28.1" crossbeam-channel = "0.5.15" +crossterm = "0.28.1" ctor = "0.6.3" derive_more = "2" diffy = "0.4.2" @@ -173,10 +175,10 @@ icu_decimal = "2.1" icu_locale_core = "2.1" icu_provider = { version = "2.1", features = ["sync"] } ignore = "0.4.23" -indoc = "2.0" image = { version = "^0.25.9", default-features = false } include_dir = "0.7.4" indexmap = "2.12.0" +indoc = "2.0" insta = "1.46.3" inventory = "0.3.19" itertools = "0.14.0" @@ -198,7 +200,6 @@ opentelemetry-appender-tracing = "0.31.0" opentelemetry-otlp = "0.31.0" opentelemetry-semantic-conventions = "0.31.0" opentelemetry_sdk = "0.31.0" -tracing-opentelemetry = "0.32.0" os_info = "3.12.0" owo-colors = "4.2.0" path-absolutize = "3.1.1" @@ -214,10 +215,14 @@ regex = "1.12.3" regex-lite = "0.1.8" reqwest = "0.12" rmcp = { version = "0.14.0", default-features = false } -rustls = { version = "0.23", default-features = false, features = ["ring", "std"] } runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" } +rustls = { version = "0.23", default-features = false, features = [ + "ring", + "std", +] } schemars = "0.8.22" seccompiler = "0.5.0" +semver = "1.0" sentry = "0.46.0" serde = "1" serde_json = "1" @@ -227,11 +232,19 @@ serde_yaml = "0.9" serial_test = "3.2.0" sha1 = "0.10.6" sha2 = "0.10" -semver = "1.0" shlex = "1.3.0" similar = "2.7.0" socket2 = "0.6.1" -sqlx = { version = "0.8.6", default-features = false, features = ["chrono", "json", "macros", "migrate", "runtime-tokio-rustls", "sqlite", "time", "uuid"] } +sqlx = { version = "0.8.6", default-features = false, features = [ + "chrono", + "json", + "macros", + "migrate", + "runtime-tokio-rustls", + "sqlite", + "time", + "uuid", +] } starlark = "0.13.0" strum = "0.27.2" strum_macros = "0.27.2" @@ -246,20 +259,23 @@ tiny_http = "0.12" tokio = "1" tokio-stream = "0.1.18" tokio-test = "0.4" -tokio-tungstenite = { version = "0.28.0", features = ["proxy", "rustls-tls-native-roots"] } -tungstenite = { version = "0.27.0", features = ["deflate", "proxy"] } +tokio-tungstenite = { version = "0.28.0", features = [ + "proxy", + "rustls-tls-native-roots", +] } tokio-util = "0.7.18" toml = "0.9.5" toml_edit = "0.24.0" tracing = "0.1.44" tracing-appender = "0.2.3" +tracing-opentelemetry = "0.32.0" tracing-subscriber = "0.3.22" tracing-test = "0.2.5" tree-sitter = "0.25.10" tree-sitter-bash = "0.25" -zstd = "0.13" tree-sitter-highlight = "0.25.10" ts-rs = "11" +tungstenite = { version = "0.27.0", features = ["deflate", "proxy"] } uds_windows = "1.1.0" unicode-segmentation = "1.12.0" unicode-width = "0.2" @@ -272,6 +288,7 @@ webbrowser = "1.0" which = "8" wildmatch = "2.6.1" zip = "2.4.2" +zstd = "0.13" wiremock = "0.6" zeroize = "1.8.2" @@ -317,7 +334,12 @@ unwrap_used = "deny" # cargo-shear cannot see the platform-specific openssl-sys usage, so we # silence the false positive here instead of deleting a real dependency. [workspace.metadata.cargo-shear] -ignored = ["icu_provider", "openssl-sys", "codex-utils-readiness", "codex-secrets"] +ignored = [ + "icu_provider", + "openssl-sys", + "codex-utils-readiness", + "codex-secrets", +] [profile.release] lto = "fat" diff --git a/codex-rs/config/BUILD.bazel b/codex-rs/config/BUILD.bazel new file mode 100644 index 0000000000..2b832782b0 --- /dev/null +++ b/codex-rs/config/BUILD.bazel @@ -0,0 +1,6 @@ +load("//:defs.bzl", "codex_rust_crate") + +codex_rust_crate( + name = "config", + crate_name = "codex_config", +) diff --git a/codex-rs/config/Cargo.toml b/codex-rs/config/Cargo.toml new file mode 100644 index 0000000000..1c804fb8db --- /dev/null +++ b/codex-rs/config/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "codex-config" +version.workspace = true +edition.workspace = true +license.workspace = true + +[lints] +workspace = true + +[dependencies] +codex-app-server-protocol = { workspace = true } +codex-execpolicy = { workspace = true } +codex-protocol = { workspace = true } +codex-utils-absolute-path = { workspace = true } +futures = { workspace = true, features = ["alloc", "std"] } +multimap = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +sha2 = { workspace = true } +thiserror = { workspace = true } +toml = { workspace = true } + +[dev-dependencies] +anyhow = { workspace = true } +pretty_assertions = { workspace = true } +tokio = { workspace = true, features = ["full"] } diff --git a/codex-rs/core/src/config_loader/cloud_requirements.rs b/codex-rs/config/src/config_loader/cloud_requirements.rs similarity index 100% rename from codex-rs/core/src/config_loader/cloud_requirements.rs rename to codex-rs/config/src/config_loader/cloud_requirements.rs diff --git a/codex-rs/core/src/config_loader/config_requirements.rs b/codex-rs/config/src/config_loader/config_requirements.rs similarity index 99% rename from codex-rs/core/src/config_loader/config_requirements.rs rename to codex-rs/config/src/config_loader/config_requirements.rs index 6ba68afebe..76b8812c25 100644 --- a/codex-rs/core/src/config_loader/config_requirements.rs +++ b/codex-rs/config/src/config_loader/config_requirements.rs @@ -10,8 +10,8 @@ use std::fmt; use super::requirements_exec_policy::RequirementsExecPolicy; use super::requirements_exec_policy::RequirementsExecPolicyToml; -use crate::config::Constrained; -use crate::config::ConstraintError; +use crate::Constrained; +use crate::ConstraintError; #[derive(Debug, Clone, PartialEq, Eq)] pub enum RequirementSource { @@ -80,7 +80,7 @@ pub struct ConfigRequirements { pub sandbox_policy: ConstrainedWithSource, pub web_search_mode: ConstrainedWithSource, pub mcp_servers: Option>>, - pub(crate) exec_policy: Option>, + pub exec_policy: Option>, pub enforce_residency: ConstrainedWithSource>, /// Managed network constraints derived from requirements. pub network: Option>, @@ -560,7 +560,6 @@ impl TryFrom for ConfigRequirements { #[cfg(test)] mod tests { use super::*; - use crate::config_loader::system_requirements_toml_file; use anyhow::Result; use codex_execpolicy::Decision; use codex_execpolicy::Evaluation; @@ -574,6 +573,12 @@ mod tests { cmd.iter().map(std::string::ToString::to_string).collect() } + fn requirements_toml_file() -> Result { + Ok(AbsolutePathBuf::try_from( + std::env::temp_dir().join("requirements.toml"), + )?) + } + fn with_unknown_source(toml: ConfigRequirementsToml) -> ConfigRequirementsWithSources { let ConfigRequirementsToml { allowed_approval_policies, @@ -732,7 +737,7 @@ mod tests { "#, )?; - let requirements_toml_file = system_requirements_toml_file()?; + let requirements_toml_file = requirements_toml_file()?; let source_location = RequirementSource::SystemRequirementsToml { file: requirements_toml_file, }; @@ -1149,7 +1154,7 @@ mod tests { ] "#; let config: ConfigRequirementsToml = from_str(toml_str)?; - let requirements_toml_file = system_requirements_toml_file()?; + let requirements_toml_file = requirements_toml_file()?; let source_location = RequirementSource::SystemRequirementsToml { file: requirements_toml_file, }; diff --git a/codex-rs/core/src/config_loader/fingerprint.rs b/codex-rs/config/src/config_loader/fingerprint.rs similarity index 97% rename from codex-rs/core/src/config_loader/fingerprint.rs rename to codex-rs/config/src/config_loader/fingerprint.rs index c072134fde..d8e0263389 100644 --- a/codex-rs/core/src/config_loader/fingerprint.rs +++ b/codex-rs/config/src/config_loader/fingerprint.rs @@ -34,7 +34,7 @@ pub(super) fn record_origins( } } -pub(super) fn version_for_toml(value: &TomlValue) -> String { +pub fn version_for_toml(value: &TomlValue) -> String { let json = serde_json::to_value(value).unwrap_or(JsonValue::Null); let canonical = canonical_json(&json); let serialized = serde_json::to_vec(&canonical).unwrap_or_default(); diff --git a/codex-rs/core/src/config_loader/merge.rs b/codex-rs/config/src/config_loader/merge.rs similarity index 100% rename from codex-rs/core/src/config_loader/merge.rs rename to codex-rs/config/src/config_loader/merge.rs diff --git a/codex-rs/config/src/config_loader/mod.rs b/codex-rs/config/src/config_loader/mod.rs new file mode 100644 index 0000000000..8a1d6cdfdd --- /dev/null +++ b/codex-rs/config/src/config_loader/mod.rs @@ -0,0 +1,29 @@ +pub mod cloud_requirements; +pub mod config_requirements; +pub mod fingerprint; +pub mod merge; +pub mod overrides; +pub mod requirements_exec_policy; +pub mod state; + +pub use cloud_requirements::CloudRequirementsLoader; +pub use config_requirements::ConfigRequirements; +pub use config_requirements::ConfigRequirementsToml; +pub use config_requirements::ConfigRequirementsWithSources; +pub use config_requirements::ConstrainedWithSource; +pub use config_requirements::McpServerIdentity; +pub use config_requirements::McpServerRequirement; +pub use config_requirements::NetworkConstraints; +pub use config_requirements::NetworkRequirementsToml; +pub use config_requirements::RequirementSource; +pub use config_requirements::ResidencyRequirement; +pub use config_requirements::SandboxModeRequirement; +pub use config_requirements::Sourced; +pub use config_requirements::WebSearchModeRequirement; +pub use fingerprint::version_for_toml; +pub use merge::merge_toml_values; +pub use overrides::build_cli_overrides_layer; +pub use state::ConfigLayerEntry; +pub use state::ConfigLayerStack; +pub use state::ConfigLayerStackOrdering; +pub use state::LoaderOverrides; diff --git a/codex-rs/core/src/config_loader/overrides.rs b/codex-rs/config/src/config_loader/overrides.rs similarity index 94% rename from codex-rs/core/src/config_loader/overrides.rs rename to codex-rs/config/src/config_loader/overrides.rs index a9fe8eff9e..a27caedb9a 100644 --- a/codex-rs/core/src/config_loader/overrides.rs +++ b/codex-rs/config/src/config_loader/overrides.rs @@ -4,7 +4,7 @@ pub(crate) fn default_empty_table() -> TomlValue { TomlValue::Table(Default::default()) } -pub(crate) fn build_cli_overrides_layer(cli_overrides: &[(String, TomlValue)]) -> TomlValue { +pub fn build_cli_overrides_layer(cli_overrides: &[(String, TomlValue)]) -> TomlValue { let mut root = default_empty_table(); for (path, value) in cli_overrides { apply_toml_override(&mut root, path, value.clone()); diff --git a/codex-rs/core/src/config_loader/requirements_exec_policy.rs b/codex-rs/config/src/config_loader/requirements_exec_policy.rs similarity index 99% rename from codex-rs/core/src/config_loader/requirements_exec_policy.rs rename to codex-rs/config/src/config_loader/requirements_exec_policy.rs index 74546fc426..64d60f8814 100644 --- a/codex-rs/core/src/config_loader/requirements_exec_policy.rs +++ b/codex-rs/config/src/config_loader/requirements_exec_policy.rs @@ -10,7 +10,7 @@ use std::sync::Arc; use thiserror::Error; #[derive(Debug, Clone)] -pub(crate) struct RequirementsExecPolicy { +pub struct RequirementsExecPolicy { policy: Policy, } diff --git a/codex-rs/core/src/config_loader/state.rs b/codex-rs/config/src/config_loader/state.rs similarity index 100% rename from codex-rs/core/src/config_loader/state.rs rename to codex-rs/config/src/config_loader/state.rs diff --git a/codex-rs/core/src/config/constraint.rs b/codex-rs/config/src/constraint.rs similarity index 85% rename from codex-rs/core/src/config/constraint.rs rename to codex-rs/config/src/constraint.rs index d095794e4f..23b6c57c74 100644 --- a/codex-rs/core/src/config/constraint.rs +++ b/codex-rs/config/src/constraint.rs @@ -92,29 +92,6 @@ impl Constrained { } } - pub fn allow_only(only_value: T) -> Self - where - T: Clone + fmt::Debug + PartialEq + 'static, - { - let allowed_value = only_value.clone(); - Self { - value: only_value, - validator: Arc::new(move |candidate| { - if candidate == &allowed_value { - Ok(()) - } else { - Err(ConstraintError::InvalidValue { - field_name: "", - candidate: format!("{candidate:?}"), - allowed: format!("[{allowed_value:?}]"), - requirement_source: RequirementSource::Unknown, - }) - } - }), - normalizer: None, - } - } - /// Allow any value of T, using T's Default as the initial value. pub fn allow_any_from_default() -> Self where @@ -199,20 +176,6 @@ mod tests { assert_eq!(constrained.value(), 0); } - #[test] - fn constrained_allow_only_rejects_different_values() { - let mut constrained = Constrained::allow_only(5); - constrained - .set(5) - .expect("allowed value should be accepted"); - - let err = constrained - .set(6) - .expect_err("different value should be rejected"); - assert_eq!(err, invalid_value("6", "[5]")); - assert_eq!(constrained.value(), 5); - } - #[test] fn constrained_normalizer_applies_on_init_and_set() -> anyhow::Result<()> { let mut constrained = Constrained::normalized(-1, |value| value.max(0))?; diff --git a/codex-rs/config/src/lib.rs b/codex-rs/config/src/lib.rs new file mode 100644 index 0000000000..0ab4e1b199 --- /dev/null +++ b/codex-rs/config/src/lib.rs @@ -0,0 +1,33 @@ +pub mod config_loader; +mod constraint; + +pub use config_loader::CloudRequirementsLoader; +pub use config_loader::ConfigLayerEntry; +pub use config_loader::ConfigLayerStack; +pub use config_loader::ConfigLayerStackOrdering; +pub use config_loader::ConfigRequirements; +pub use config_loader::ConfigRequirementsToml; +pub use config_loader::ConfigRequirementsWithSources; +pub use config_loader::ConstrainedWithSource; +pub use config_loader::LoaderOverrides; +pub use config_loader::McpServerIdentity; +pub use config_loader::McpServerRequirement; +pub use config_loader::NetworkConstraints; +pub use config_loader::NetworkRequirementsToml; +pub use config_loader::RequirementSource; +pub use config_loader::ResidencyRequirement; +pub use config_loader::SandboxModeRequirement; +pub use config_loader::Sourced; +pub use config_loader::WebSearchModeRequirement; +pub use config_loader::build_cli_overrides_layer; +pub use config_loader::merge_toml_values; +pub use config_loader::requirements_exec_policy::RequirementsExecPolicy; +pub use config_loader::requirements_exec_policy::RequirementsExecPolicyDecisionToml; +pub use config_loader::requirements_exec_policy::RequirementsExecPolicyParseError; +pub use config_loader::requirements_exec_policy::RequirementsExecPolicyPatternTokenToml; +pub use config_loader::requirements_exec_policy::RequirementsExecPolicyPrefixRuleToml; +pub use config_loader::requirements_exec_policy::RequirementsExecPolicyToml; +pub use config_loader::version_for_toml; +pub use constraint::Constrained; +pub use constraint::ConstraintError; +pub use constraint::ConstraintResult; diff --git a/codex-rs/core/Cargo.toml b/codex-rs/core/Cargo.toml index e759072fac..7fa128399b 100644 --- a/codex-rs/core/Cargo.toml +++ b/codex-rs/core/Cargo.toml @@ -33,6 +33,7 @@ codex-app-server-protocol = { workspace = true } codex-apply-patch = { workspace = true } codex-async-utils = { workspace = true } codex-client = { workspace = true } +codex-config = { workspace = true } codex-shell-command = { workspace = true } codex-execpolicy = { workspace = true } codex-file-search = { workspace = true } @@ -62,7 +63,6 @@ indexmap = { workspace = true } indoc = { workspace = true } keyring = { workspace = true, features = ["crypto-rust"] } libc = { workspace = true } -multimap = { workspace = true } notify = { workspace = true } once_cell = { workspace = true } os_info = { workspace = true } diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index a923b0ef78..6ad363a614 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -77,16 +77,15 @@ use crate::config::profile::ConfigProfile; use toml::Value as TomlValue; use toml_edit::DocumentMut; -mod constraint; pub mod edit; mod network_proxy_spec; pub mod profile; pub mod schema; pub mod service; pub mod types; -pub use constraint::Constrained; -pub use constraint::ConstraintError; -pub use constraint::ConstraintResult; +pub use codex_config::Constrained; +pub use codex_config::ConstraintError; +pub use codex_config::ConstraintResult; pub use network_proxy_spec::NetworkProxySpec; pub use network_proxy_spec::StartedNetworkProxy; diff --git a/codex-rs/core/src/config_loader/macos.rs b/codex-rs/core/src/config_loader/macos.rs index b474e81eaa..d94b1930b7 100644 --- a/codex-rs/core/src/config_loader/macos.rs +++ b/codex-rs/core/src/config_loader/macos.rs @@ -1,6 +1,6 @@ -use super::config_requirements::ConfigRequirementsToml; -use super::config_requirements::ConfigRequirementsWithSources; -use super::config_requirements::RequirementSource; +use super::ConfigRequirementsToml; +use super::ConfigRequirementsWithSources; +use super::RequirementSource; use base64::Engine; use base64::prelude::BASE64_STANDARD; use core_foundation::base::TCFType; diff --git a/codex-rs/core/src/config_loader/mod.rs b/codex-rs/core/src/config_loader/mod.rs index 68973718fe..6a1c97b29d 100644 --- a/codex-rs/core/src/config_loader/mod.rs +++ b/codex-rs/core/src/config_loader/mod.rs @@ -1,14 +1,7 @@ -mod cloud_requirements; -mod config_requirements; mod diagnostics; -mod fingerprint; mod layer_io; #[cfg(target_os = "macos")] mod macos; -mod merge; -mod overrides; -mod requirements_exec_policy; -mod state; #[cfg(test)] mod tests; @@ -16,10 +9,10 @@ mod tests; use crate::config::CONFIG_TOML_FILE; use crate::config::ConfigToml; use crate::config::deserialize_config_toml_with_base; -use crate::config_loader::config_requirements::ConfigRequirementsWithSources; use crate::config_loader::layer_io::LoadedConfigLayers; use crate::git_info::resolve_root_git_project_for_trust; use codex_app_server_protocol::ConfigLayerSource; +use codex_config::config_loader::ConfigRequirementsWithSources; use codex_protocol::config_types::SandboxMode; use codex_protocol::config_types::TrustLevel; use codex_protocol::protocol::AskForApproval; @@ -33,19 +26,27 @@ use std::path::Path; use std::path::PathBuf; use toml::Value as TomlValue; -pub use cloud_requirements::CloudRequirementsLoader; -pub use config_requirements::ConfigRequirements; -pub use config_requirements::ConfigRequirementsToml; -pub use config_requirements::ConstrainedWithSource; -pub use config_requirements::McpServerIdentity; -pub use config_requirements::McpServerRequirement; -pub use config_requirements::NetworkConstraints; -pub use config_requirements::NetworkRequirementsToml; -pub use config_requirements::RequirementSource; -pub use config_requirements::ResidencyRequirement; -pub use config_requirements::SandboxModeRequirement; -pub use config_requirements::Sourced; -pub use config_requirements::WebSearchModeRequirement; +pub use codex_config::config_loader::CloudRequirementsLoader; +pub use codex_config::config_loader::ConfigLayerEntry; +pub use codex_config::config_loader::ConfigLayerStack; +pub use codex_config::config_loader::ConfigLayerStackOrdering; +pub use codex_config::config_loader::ConfigRequirements; +pub use codex_config::config_loader::ConfigRequirementsToml; +pub use codex_config::config_loader::ConstrainedWithSource; +pub use codex_config::config_loader::LoaderOverrides; +pub use codex_config::config_loader::McpServerIdentity; +pub use codex_config::config_loader::McpServerRequirement; +pub use codex_config::config_loader::NetworkConstraints; +pub use codex_config::config_loader::NetworkRequirementsToml; +pub use codex_config::config_loader::RequirementSource; +pub use codex_config::config_loader::ResidencyRequirement; +pub use codex_config::config_loader::SandboxModeRequirement; +pub use codex_config::config_loader::Sourced; +pub use codex_config::config_loader::WebSearchModeRequirement; +#[cfg(test)] +pub(crate) use codex_config::config_loader::fingerprint::version_for_toml; +pub use codex_config::config_loader::merge_toml_values; +pub(crate) use codex_config::config_loader::overrides::build_cli_overrides_layer; pub use diagnostics::ConfigError; pub use diagnostics::ConfigLoadError; pub use diagnostics::TextPosition; @@ -56,12 +57,6 @@ pub(crate) use diagnostics::first_layer_config_error_from_entries; pub use diagnostics::format_config_error; pub use diagnostics::format_config_error_with_source; pub(crate) use diagnostics::io_error_from_config_error; -pub use merge::merge_toml_values; -pub(crate) use overrides::build_cli_overrides_layer; -pub use state::ConfigLayerEntry; -pub use state::ConfigLayerStack; -pub use state::ConfigLayerStackOrdering; -pub use state::LoaderOverrides; /// On Unix systems, load default settings from this file path, if present. /// Note that /etc/codex/ is treated as a "config folder," so subfolders such @@ -145,7 +140,7 @@ pub async fn load_config_layers_state( let cli_overrides_layer = if cli_overrides.is_empty() { None } else { - let cli_overrides_layer = overrides::build_cli_overrides_layer(cli_overrides); + let cli_overrides_layer = build_cli_overrides_layer(cli_overrides); let base_dir = cwd .as_ref() .map(AbsolutePathBuf::as_path) diff --git a/codex-rs/core/src/config_loader/tests.rs b/codex-rs/core/src/config_loader/tests.rs index 79c657ad96..d90ba182e2 100644 --- a/codex-rs/core/src/config_loader/tests.rs +++ b/codex-rs/core/src/config_loader/tests.rs @@ -11,10 +11,10 @@ use crate::config_loader::ConfigLayerEntry; use crate::config_loader::ConfigLoadError; use crate::config_loader::ConfigRequirements; use crate::config_loader::ConfigRequirementsToml; -use crate::config_loader::config_requirements::ConfigRequirementsWithSources; -use crate::config_loader::config_requirements::RequirementSource; -use crate::config_loader::fingerprint::version_for_toml; +use crate::config_loader::ConfigRequirementsWithSources; +use crate::config_loader::RequirementSource; use crate::config_loader::load_requirements_toml; +use crate::config_loader::version_for_toml; use codex_protocol::config_types::TrustLevel; use codex_protocol::config_types::WebSearchMode; use codex_protocol::protocol::AskForApproval; @@ -1246,19 +1246,19 @@ async fn project_root_markers_supports_alternate_markers() -> std::io::Result<() } mod requirements_exec_policy_tests { - use super::super::config_requirements::ConfigRequirementsWithSources; - use super::super::requirements_exec_policy::RequirementsExecPolicyDecisionToml; - use super::super::requirements_exec_policy::RequirementsExecPolicyParseError; - use super::super::requirements_exec_policy::RequirementsExecPolicyPatternTokenToml; - use super::super::requirements_exec_policy::RequirementsExecPolicyPrefixRuleToml; - use super::super::requirements_exec_policy::RequirementsExecPolicyToml; use crate::config_loader::ConfigLayerEntry; use crate::config_loader::ConfigLayerStack; use crate::config_loader::ConfigRequirements; use crate::config_loader::ConfigRequirementsToml; + use crate::config_loader::ConfigRequirementsWithSources; use crate::config_loader::RequirementSource; use crate::exec_policy::load_exec_policy; use codex_app_server_protocol::ConfigLayerSource; + use codex_config::config_loader::requirements_exec_policy::RequirementsExecPolicyDecisionToml; + use codex_config::config_loader::requirements_exec_policy::RequirementsExecPolicyParseError; + use codex_config::config_loader::requirements_exec_policy::RequirementsExecPolicyPatternTokenToml; + use codex_config::config_loader::requirements_exec_policy::RequirementsExecPolicyPrefixRuleToml; + use codex_config::config_loader::requirements_exec_policy::RequirementsExecPolicyToml; use codex_execpolicy::Decision; use codex_execpolicy::Evaluation; use codex_execpolicy::RuleMatch; diff --git a/codex-rs/tui/src/debug_config.rs b/codex-rs/tui/src/debug_config.rs index 01f5cf6cac..ddc155da0c 100644 --- a/codex-rs/tui/src/debug_config.rs +++ b/codex-rs/tui/src/debug_config.rs @@ -474,44 +474,46 @@ mod tests { } else { absolute_path("/etc/codex/requirements.toml") }; - let mut requirements = ConfigRequirements::default(); - requirements.approval_policy = ConstrainedWithSource::new( - Constrained::allow_any(AskForApproval::OnRequest), - Some(RequirementSource::CloudRequirements), - ); - requirements.sandbox_policy = ConstrainedWithSource::new( - Constrained::allow_any(SandboxPolicy::ReadOnly), - Some(RequirementSource::SystemRequirementsToml { - file: requirements_file.clone(), - }), - ); - requirements.mcp_servers = Some(Sourced::new( - BTreeMap::from([( - "docs".to_string(), - McpServerRequirement { - identity: McpServerIdentity::Command { - command: "codex-mcp".to_string(), + let requirements = ConfigRequirements { + approval_policy: ConstrainedWithSource::new( + Constrained::allow_any(AskForApproval::OnRequest), + Some(RequirementSource::CloudRequirements), + ), + sandbox_policy: ConstrainedWithSource::new( + Constrained::allow_any(SandboxPolicy::ReadOnly), + Some(RequirementSource::SystemRequirementsToml { + file: requirements_file.clone(), + }), + ), + mcp_servers: Some(Sourced::new( + BTreeMap::from([( + "docs".to_string(), + McpServerRequirement { + identity: McpServerIdentity::Command { + command: "codex-mcp".to_string(), + }, }, + )]), + RequirementSource::LegacyManagedConfigTomlFromMdm, + )), + enforce_residency: ConstrainedWithSource::new( + Constrained::allow_any(Some(ResidencyRequirement::Us)), + Some(RequirementSource::CloudRequirements), + ), + web_search_mode: ConstrainedWithSource::new( + Constrained::allow_any(WebSearchMode::Cached), + Some(RequirementSource::CloudRequirements), + ), + network: Some(Sourced::new( + NetworkConstraints { + enabled: Some(true), + allowed_domains: Some(vec!["example.com".to_string()]), + ..Default::default() }, - )]), - RequirementSource::LegacyManagedConfigTomlFromMdm, - )); - requirements.enforce_residency = ConstrainedWithSource::new( - Constrained::allow_any(Some(ResidencyRequirement::Us)), - Some(RequirementSource::CloudRequirements), - ); - requirements.web_search_mode = ConstrainedWithSource::new( - Constrained::allow_any(WebSearchMode::Cached), - Some(RequirementSource::CloudRequirements), - ); - requirements.network = Some(Sourced::new( - NetworkConstraints { - enabled: Some(true), - allowed_domains: Some(vec!["example.com".to_string()]), - ..Default::default() - }, - RequirementSource::CloudRequirements, - )); + RequirementSource::CloudRequirements, + )), + ..ConfigRequirements::default() + }; let requirements_toml = ConfigRequirementsToml { allowed_approval_policies: Some(vec![AskForApproval::OnRequest]), @@ -631,11 +633,13 @@ approval_policy = "never" #[test] fn debug_config_output_normalizes_empty_web_search_mode_list() { - let mut requirements = ConfigRequirements::default(); - requirements.web_search_mode = ConstrainedWithSource::new( - Constrained::allow_any(WebSearchMode::Disabled), - Some(RequirementSource::CloudRequirements), - ); + let requirements = ConfigRequirements { + web_search_mode: ConstrainedWithSource::new( + Constrained::allow_any(WebSearchMode::Disabled), + Some(RequirementSource::CloudRequirements), + ), + ..ConfigRequirements::default() + }; let requirements_toml = ConfigRequirementsToml { allowed_approval_policies: None,