Skip to content

Commit f6dd9e3

Browse files
authored
tui: show non-file layer content in /debug-config (#11412)
The debug output listed non-file-backed layers such as session flags and MDM managed config, but it did not show their values. That made it difficult to explain unexpected effective settings because users could not inspect those layers on disk. Now `/debug-config` might include output like this: ``` Config layer stack (lowest precedence first): 1. system (/etc/codex/config.toml) (enabled) 2. user (/Users/mbolin/.codex/config.toml) (enabled) 3. legacy managed_config.toml (mdm) (enabled) MDM value: # Production Codex configuration file. [otel] log_user_prompt = true environment = "prod" exporter = { otlp-http = { endpoint = "https://example.com/otel", protocol = "binary" }} ```
1 parent fdd0cd1 commit f6dd9e3

File tree

6 files changed

+223
-20
lines changed

6 files changed

+223
-20
lines changed

codex-rs/core/src/config_loader/layer_io.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use super::LoaderOverrides;
22
use super::diagnostics::config_error_from_toml;
33
use super::diagnostics::io_error_from_config_error;
44
#[cfg(target_os = "macos")]
5+
use super::macos::ManagedAdminConfigLayer;
6+
#[cfg(target_os = "macos")]
57
use super::macos::load_managed_admin_config_layer;
68
use codex_utils_absolute_path::AbsolutePathBuf;
79
use std::io;
@@ -19,12 +21,18 @@ pub(super) struct MangedConfigFromFile {
1921
pub file: AbsolutePathBuf,
2022
}
2123

24+
#[derive(Debug, Clone)]
25+
pub(super) struct ManagedConfigFromMdm {
26+
pub managed_config: TomlValue,
27+
pub raw_toml: String,
28+
}
29+
2230
#[derive(Debug, Clone)]
2331
pub(super) struct LoadedConfigLayers {
2432
/// If present, data read from a file such as `/etc/codex/managed_config.toml`.
2533
pub managed_config: Option<MangedConfigFromFile>,
2634
/// If present, data read from managed preferences (macOS only).
27-
pub managed_config_from_mdm: Option<TomlValue>,
35+
pub managed_config_from_mdm: Option<ManagedConfigFromMdm>,
2836
}
2937

3038
pub(super) async fn load_config_layers_internal(
@@ -57,7 +65,9 @@ pub(super) async fn load_config_layers_internal(
5765

5866
#[cfg(target_os = "macos")]
5967
let managed_preferences =
60-
load_managed_admin_config_layer(managed_preferences_base64.as_deref()).await?;
68+
load_managed_admin_config_layer(managed_preferences_base64.as_deref())
69+
.await?
70+
.map(map_managed_admin_layer);
6171

6272
#[cfg(not(target_os = "macos"))]
6373
let managed_preferences = None;
@@ -68,6 +78,15 @@ pub(super) async fn load_config_layers_internal(
6878
})
6979
}
7080

81+
#[cfg(target_os = "macos")]
82+
fn map_managed_admin_layer(layer: ManagedAdminConfigLayer) -> ManagedConfigFromMdm {
83+
let ManagedAdminConfigLayer { config, raw_toml } = layer;
84+
ManagedConfigFromMdm {
85+
managed_config: config,
86+
raw_toml,
87+
}
88+
}
89+
7190
pub(super) async fn read_config_from_path(
7291
path: impl AsRef<Path>,
7392
log_missing_as_info: bool,

codex-rs/core/src/config_loader/macos.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ const MANAGED_PREFERENCES_APPLICATION_ID: &str = "com.openai.codex";
1515
const MANAGED_PREFERENCES_CONFIG_KEY: &str = "config_toml_base64";
1616
const MANAGED_PREFERENCES_REQUIREMENTS_KEY: &str = "requirements_toml_base64";
1717

18+
#[derive(Debug, Clone)]
19+
pub(super) struct ManagedAdminConfigLayer {
20+
pub config: TomlValue,
21+
pub raw_toml: String,
22+
}
23+
1824
pub(super) fn managed_preferences_requirements_source() -> RequirementSource {
1925
RequirementSource::MdmManagedPreferences {
2026
domain: MANAGED_PREFERENCES_APPLICATION_ID.to_string(),
@@ -24,7 +30,7 @@ pub(super) fn managed_preferences_requirements_source() -> RequirementSource {
2430

2531
pub(crate) async fn load_managed_admin_config_layer(
2632
override_base64: Option<&str>,
27-
) -> io::Result<Option<TomlValue>> {
33+
) -> io::Result<Option<ManagedAdminConfigLayer>> {
2834
if let Some(encoded) = override_base64 {
2935
let trimmed = encoded.trim();
3036
return if trimmed.is_empty() {
@@ -47,7 +53,7 @@ pub(crate) async fn load_managed_admin_config_layer(
4753
}
4854
}
4955

50-
fn load_managed_admin_config() -> io::Result<Option<TomlValue>> {
56+
fn load_managed_admin_config() -> io::Result<Option<ManagedAdminConfigLayer>> {
5157
load_managed_preference(MANAGED_PREFERENCES_CONFIG_KEY)?
5258
.as_deref()
5359
.map(str::trim)
@@ -122,9 +128,13 @@ fn load_managed_preference(key_name: &str) -> io::Result<Option<String>> {
122128
Ok(Some(value))
123129
}
124130

125-
fn parse_managed_config_base64(encoded: &str) -> io::Result<TomlValue> {
126-
match toml::from_str::<TomlValue>(&decode_managed_preferences_base64(encoded)?) {
127-
Ok(TomlValue::Table(parsed)) => Ok(TomlValue::Table(parsed)),
131+
fn parse_managed_config_base64(encoded: &str) -> io::Result<ManagedAdminConfigLayer> {
132+
let raw_toml = decode_managed_preferences_base64(encoded)?;
133+
match toml::from_str::<TomlValue>(&raw_toml) {
134+
Ok(TomlValue::Table(parsed)) => Ok(ManagedAdminConfigLayer {
135+
config: TomlValue::Table(parsed),
136+
raw_toml,
137+
}),
128138
Ok(other) => {
129139
tracing::error!("Managed config TOML must have a table at the root, found {other:?}",);
130140
Err(io::Error::new(

codex-rs/core/src/config_loader/mod.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,10 @@ pub async fn load_config_layers_state(
278278
));
279279
}
280280
if let Some(config) = managed_config_from_mdm {
281-
layers.push(ConfigLayerEntry::new(
281+
layers.push(ConfigLayerEntry::new_with_raw_toml(
282282
ConfigLayerSource::LegacyManagedConfigTomlFromMdm,
283-
config,
283+
config.managed_config,
284+
config.raw_toml,
284285
));
285286
}
286287

@@ -485,7 +486,12 @@ async fn load_requirements_from_legacy_scheme(
485486
} = loaded_config_layers;
486487

487488
for (source, config) in managed_config_from_mdm
488-
.map(|config| (RequirementSource::LegacyManagedConfigTomlFromMdm, config))
489+
.map(|config| {
490+
(
491+
RequirementSource::LegacyManagedConfigTomlFromMdm,
492+
config.managed_config,
493+
)
494+
})
489495
.into_iter()
490496
.chain(managed_config.map(|c| {
491497
(

codex-rs/core/src/config_loader/state.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub struct LoaderOverrides {
2727
pub struct ConfigLayerEntry {
2828
pub name: ConfigLayerSource,
2929
pub config: TomlValue,
30+
pub raw_toml: Option<String>,
3031
pub version: String,
3132
pub disabled_reason: Option<String>,
3233
}
@@ -37,6 +38,18 @@ impl ConfigLayerEntry {
3738
Self {
3839
name,
3940
config,
41+
raw_toml: None,
42+
version,
43+
disabled_reason: None,
44+
}
45+
}
46+
47+
pub fn new_with_raw_toml(name: ConfigLayerSource, config: TomlValue, raw_toml: String) -> Self {
48+
let version = version_for_toml(&config);
49+
Self {
50+
name,
51+
config,
52+
raw_toml: Some(raw_toml),
4053
version,
4154
disabled_reason: None,
4255
}
@@ -51,6 +64,7 @@ impl ConfigLayerEntry {
5164
Self {
5265
name,
5366
config,
67+
raw_toml: None,
5468
version,
5569
disabled_reason: Some(disabled_reason.into()),
5670
}
@@ -60,6 +74,10 @@ impl ConfigLayerEntry {
6074
self.disabled_reason.is_some()
6175
}
6276

77+
pub fn raw_toml(&self) -> Option<&str> {
78+
self.raw_toml.as_deref()
79+
}
80+
6381
pub fn metadata(&self) -> ConfigLayerMetadata {
6482
ConfigLayerMetadata {
6583
name: self.name.clone(),

codex-rs/core/src/config_loader/tests.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ async fn returns_empty_when_all_layers_missing() {
264264
.expect("resolve user config.toml path")
265265
},
266266
config: TomlValue::Table(toml::map::Map::new()),
267+
raw_toml: None,
267268
version: version_for_toml(&TomlValue::Table(toml::map::Map::new())),
268269
disabled_reason: None,
269270
},
@@ -325,18 +326,17 @@ flag = true
325326
"#,
326327
)
327328
.expect("write managed config");
329+
let raw_managed_preferences = r#"
330+
# managed profile
331+
[nested]
332+
value = "managed"
333+
flag = false
334+
"#;
328335

329336
let overrides = LoaderOverrides {
330337
managed_config_path: Some(managed_path),
331338
managed_preferences_base64: Some(
332-
base64::prelude::BASE64_STANDARD.encode(
333-
r#"
334-
[nested]
335-
value = "managed"
336-
flag = false
337-
"#
338-
.as_bytes(),
339-
),
339+
base64::prelude::BASE64_STANDARD.encode(raw_managed_preferences.as_bytes()),
340340
),
341341
macos_managed_config_requirements_base64: None,
342342
};
@@ -361,6 +361,19 @@ flag = false
361361
Some(&TomlValue::String("managed".to_string()))
362362
);
363363
assert_eq!(nested.get("flag"), Some(&TomlValue::Boolean(false)));
364+
let mdm_layer = state
365+
.layers_high_to_low()
366+
.into_iter()
367+
.find(|layer| {
368+
matches!(
369+
layer.name,
370+
super::ConfigLayerSource::LegacyManagedConfigTomlFromMdm
371+
)
372+
})
373+
.expect("mdm layer");
374+
let raw = mdm_layer.raw_toml().expect("preserved mdm toml");
375+
assert!(raw.contains("# managed profile"));
376+
assert!(raw.contains("value = \"managed\""));
364377
}
365378

366379
#[cfg(target_os = "macos")]
@@ -862,6 +875,7 @@ async fn project_layer_is_added_when_dot_codex_exists_without_config_toml() -> s
862875
dot_codex_folder: AbsolutePathBuf::from_absolute_path(project_root.join(".codex"))?,
863876
},
864877
config: TomlValue::Table(toml::map::Map::new()),
878+
raw_toml: None,
865879
version: version_for_toml(&TomlValue::Table(toml::map::Map::new())),
866880
disabled_reason: None,
867881
}],
@@ -955,6 +969,7 @@ async fn codex_home_within_project_tree_is_not_double_loaded() -> std::io::Resul
955969
dot_codex_folder: AbsolutePathBuf::from_absolute_path(&nested_dot_codex)?,
956970
},
957971
config: child_config.clone(),
972+
raw_toml: None,
958973
version: version_for_toml(&child_config),
959974
disabled_reason: None,
960975
}],

0 commit comments

Comments
 (0)