use crate::ConfigRequirementsToml;use crate::ManagedHooksRequirementsToml;use crate::RequirementSource;use crate::RequirementsExecPolicyToml;use codex_utils_absolute_path::AbsolutePathBuf;use codex_utils_absolute_path::AbsolutePathBufGuard;use toml::Value as TomlValue;use super::stack::RequirementsCompositionError;#[derive(Clone, Debug)]pub struct RequirementsLayerEntry { pub(super) source: RequirementSource, toml: RequirementsLayerToml, base_dir: Option<AbsolutePathBuf>,}impl RequirementsLayerEntry { pub fn from_toml(source: RequirementSource, contents: impl Into<String>) -> Self { Self { source, toml: RequirementsLayerToml::String(contents.into()), base_dir: None, } } pub fn from_toml_value(source: RequirementSource, value: TomlValue) -> Self { Self { source, toml: RequirementsLayerToml::Value(value), base_dir: None, } } pub fn with_base_dir(mut self, base_dir: AbsolutePathBuf) -> Self { self.base_dir = Some(base_dir); self }}#[derive(Clone, Debug)]enum RequirementsLayerToml { String(String), Value(TomlValue),}#[derive(Clone, Debug)]pub(super) struct ComposableRequirementsLayer { pub(super) source: RequirementSource, pub(super) regular_toml: TomlValue, pub(super) domain_fields: DomainMergedRequirementsFields,}impl ComposableRequirementsLayer { pub(super) fn from_entry( layer: RequirementsLayerEntry, hostname_resolver: &dyn Fn() -> Option<String>, ) -> Result<Self, RequirementsCompositionError> { let RequirementsLayerEntry { source, toml, base_dir, } = layer; let (mut regular_toml, mut requirements) = { let _guard = base_dir .as_ref() .map(|base_dir| AbsolutePathBufGuard::new(base_dir.as_path())); let regular_toml = parse_layer_toml(&toml, &source)?; let requirements = parse_layer_requirements(&toml, &source)?; (regular_toml, requirements) }; // Hostname lookup is configuration-driven and may block on DNS, so only // resolve it when this layer contains hostname-based sandbox selectors. let hostname = requirements .remote_sandbox_config .as_ref() .and_then(|_| hostname_resolver()); requirements.apply_remote_sandbox_config(hostname.as_deref()); materialize_remote_sandbox_config(&mut regular_toml, &requirements)?; strip_special_fields(&mut regular_toml); Ok(Self { source, regular_toml, domain_fields: DomainMergedRequirementsFields { rules: requirements.rules, hooks: requirements.hooks, permissions: requirements.permissions, }, }) }}#[derive(Clone, Debug)]pub(super) struct DomainMergedRequirementsFields { pub(super) rules: Option<RequirementsExecPolicyToml>, pub(super) hooks: Option<ManagedHooksRequirementsToml>, pub(super) permissions: Option<crate::config_requirements::PermissionsRequirementsToml>,}fn parse_layer_toml( toml: &RequirementsLayerToml, source: &RequirementSource,) -> Result<TomlValue, RequirementsCompositionError> { match toml { RequirementsLayerToml::String(contents) => { toml::from_str(contents).map_err(|err: toml::de::Error| { RequirementsCompositionError::Parse { layer_source: source.clone(), message: err.to_string(), } }) } RequirementsLayerToml::Value(value) => Ok(value.clone()), }}fn parse_layer_requirements( toml: &RequirementsLayerToml, source: &RequirementSource,) -> Result<ConfigRequirementsToml, RequirementsCompositionError> { match toml { RequirementsLayerToml::String(contents) => { toml::from_str(contents).map_err(|err: toml::de::Error| { RequirementsCompositionError::Parse { layer_source: source.clone(), message: err.to_string(), } }) } RequirementsLayerToml::Value(value) => { value.clone().try_into().map_err(|err: toml::de::Error| { RequirementsCompositionError::Parse { layer_source: source.clone(), message: err.to_string(), } }) } }}fn materialize_remote_sandbox_config( layer_toml: &mut TomlValue, requirements: &ConfigRequirementsToml,) -> Result<(), RequirementsCompositionError> { remove_top_level_field(layer_toml, "remote_sandbox_config"); let Some(allowed_sandbox_modes) = requirements.allowed_sandbox_modes.as_ref() else { return Ok(()); }; let Some(table) = layer_toml.as_table_mut() else { return Ok(()); }; table.insert( "allowed_sandbox_modes".to_string(), toml_value_from_serializable(allowed_sandbox_modes)?, ); Ok(())}fn toml_value_from_serializable<T: serde::Serialize>( value: T,) -> Result<TomlValue, RequirementsCompositionError> { TomlValue::try_from(value).map_err(|err| RequirementsCompositionError::ComposedParse { message: err.to_string(), })}fn strip_special_fields(layer_toml: &mut TomlValue) { remove_top_level_field(layer_toml, "rules"); remove_top_level_field(layer_toml, "hooks"); remove_nested_field_and_prune_empty(layer_toml, &["permissions", "filesystem", "deny_read"]);}fn remove_top_level_field(value: &mut TomlValue, key: &str) -> Option<TomlValue> { value.as_table_mut()?.remove(key)}fn remove_nested_field_and_prune_empty(value: &mut TomlValue, path: &[&str]) -> Option<TomlValue> { let (key, remaining) = path.split_first()?; let table = value.as_table_mut()?; if remaining.is_empty() { return table.remove(*key); } let removed = table .get_mut(*key) .and_then(|child| remove_nested_field_and_prune_empty(child, remaining)); if table .get(*key) .and_then(TomlValue::as_table) .is_some_and(toml::map::Map::is_empty) { table.remove(*key); } removed}