mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
config: add strict config parsing
This commit is contained in:
@@ -190,6 +190,7 @@ async fn run_command_under_sandbox(
|
||||
.map_err(anyhow::Error::msg)?,
|
||||
codex_linux_sandbox_exe,
|
||||
config_options,
|
||||
config_overrides.strict_config,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -629,12 +630,14 @@ async fn load_debug_sandbox_config(
|
||||
cli_overrides: Vec<(String, TomlValue)>,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
options: DebugSandboxConfigOptions,
|
||||
strict_config: bool,
|
||||
) -> anyhow::Result<Config> {
|
||||
load_debug_sandbox_config_with_codex_home(
|
||||
cli_overrides,
|
||||
codex_linux_sandbox_exe,
|
||||
options,
|
||||
/*codex_home*/ None,
|
||||
strict_config,
|
||||
)
|
||||
.await
|
||||
}
|
||||
@@ -644,6 +647,7 @@ async fn load_debug_sandbox_config_with_codex_home(
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
options: DebugSandboxConfigOptions,
|
||||
codex_home: Option<PathBuf>,
|
||||
strict_config: bool,
|
||||
) -> anyhow::Result<Config> {
|
||||
let DebugSandboxConfigOptions {
|
||||
permissions_profile,
|
||||
@@ -673,6 +677,7 @@ async fn load_debug_sandbox_config_with_codex_home(
|
||||
},
|
||||
codex_home.clone(),
|
||||
managed_requirements_mode,
|
||||
strict_config,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -690,6 +695,7 @@ async fn load_debug_sandbox_config_with_codex_home(
|
||||
},
|
||||
codex_home,
|
||||
managed_requirements_mode,
|
||||
strict_config,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
@@ -700,13 +706,18 @@ async fn build_debug_sandbox_config(
|
||||
harness_overrides: ConfigOverrides,
|
||||
codex_home: Option<PathBuf>,
|
||||
managed_requirements_mode: ManagedRequirementsMode,
|
||||
strict_config: bool,
|
||||
) -> std::io::Result<Config> {
|
||||
let mut builder = ConfigBuilder::default()
|
||||
.cli_overrides(cli_overrides)
|
||||
.harness_overrides(harness_overrides);
|
||||
if let ManagedRequirementsMode::Ignore = managed_requirements_mode {
|
||||
if strict_config || matches!(managed_requirements_mode, ManagedRequirementsMode::Ignore) {
|
||||
builder = builder.loader_overrides(LoaderOverrides {
|
||||
ignore_managed_requirements: true,
|
||||
strict_config,
|
||||
ignore_managed_requirements: matches!(
|
||||
managed_requirements_mode,
|
||||
ManagedRequirementsMode::Ignore
|
||||
),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
@@ -776,6 +787,7 @@ mod tests {
|
||||
ConfigOverrides::default(),
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
let legacy_config = build_debug_sandbox_config(
|
||||
@@ -786,6 +798,7 @@ mod tests {
|
||||
},
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -798,6 +811,7 @@ mod tests {
|
||||
managed_requirements_mode: ManagedRequirementsMode::Include,
|
||||
},
|
||||
Some(codex_home_path),
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -833,6 +847,7 @@ mod tests {
|
||||
ConfigOverrides::default(),
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
let read_only_config = build_debug_sandbox_config(
|
||||
@@ -843,6 +858,7 @@ mod tests {
|
||||
},
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -855,6 +871,7 @@ mod tests {
|
||||
managed_requirements_mode: ManagedRequirementsMode::Include,
|
||||
},
|
||||
Some(codex_home_path),
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -898,6 +915,7 @@ mod tests {
|
||||
},
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -910,6 +928,7 @@ mod tests {
|
||||
managed_requirements_mode: ManagedRequirementsMode::Include,
|
||||
},
|
||||
Some(codex_home_path),
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -935,6 +954,7 @@ mod tests {
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -968,6 +988,7 @@ mod tests {
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -997,6 +1018,7 @@ mod tests {
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1008,6 +1030,7 @@ mod tests {
|
||||
ConfigOverrides::default(),
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
ManagedRequirementsMode::Include,
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -1033,6 +1056,7 @@ mod tests {
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
/*strict_config*/ false,
|
||||
)
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -431,7 +431,13 @@ async fn load_config_or_exit(cli_config_overrides: CliConfigOverrides) -> Config
|
||||
}
|
||||
};
|
||||
|
||||
match Config::load_with_cli_overrides(cli_overrides).await {
|
||||
let loader_overrides = codex_config::LoaderOverrides {
|
||||
strict_config: cli_config_overrides.strict_config,
|
||||
..Default::default()
|
||||
};
|
||||
match Config::load_with_cli_overrides_and_loader_overrides(cli_overrides, loader_overrides)
|
||||
.await
|
||||
{
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
eprintln!("Error loading configuration: {e}");
|
||||
|
||||
+75
-12
@@ -53,6 +53,7 @@ use crate::mcp_cmd::McpCli;
|
||||
|
||||
use codex_core::build_models_manager;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
use codex_core::config::edit::ConfigEditsBuilder;
|
||||
use codex_core::config::find_codex_home;
|
||||
@@ -845,10 +846,11 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
None => {
|
||||
let transport = listen;
|
||||
let auth = auth.try_into_settings()?;
|
||||
let loader_overrides = loader_overrides_from_cli(&root_config_overrides);
|
||||
codex_app_server::run_main_with_transport(
|
||||
arg0_paths.clone(),
|
||||
root_config_overrides,
|
||||
codex_config::LoaderOverrides::default(),
|
||||
loader_overrides,
|
||||
analytics_default_enabled,
|
||||
transport,
|
||||
codex_protocol::protocol::SessionSource::VSCode,
|
||||
@@ -1209,11 +1211,12 @@ async fn cli_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let config = Config::load_with_cli_overrides_and_harness_overrides(
|
||||
cli_kv_overrides,
|
||||
overrides,
|
||||
)
|
||||
.await?;
|
||||
let config = ConfigBuilder::default()
|
||||
.cli_overrides(cli_kv_overrides)
|
||||
.harness_overrides(overrides)
|
||||
.loader_overrides(loader_overrides_from_cli(&root_config_overrides))
|
||||
.build()
|
||||
.await?;
|
||||
let mut rows = Vec::with_capacity(FEATURES.len());
|
||||
let mut name_width = 0;
|
||||
let mut stage_width = 0;
|
||||
@@ -1372,8 +1375,12 @@ async fn run_debug_prompt_input_command(
|
||||
additional_writable_roots: shared.add_dir,
|
||||
..Default::default()
|
||||
};
|
||||
let config =
|
||||
Config::load_with_cli_overrides_and_harness_overrides(cli_kv_overrides, overrides).await?;
|
||||
let config = ConfigBuilder::default()
|
||||
.cli_overrides(cli_kv_overrides)
|
||||
.harness_overrides(overrides)
|
||||
.loader_overrides(loader_overrides_from_cli(&root_config_overrides))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
let mut input = shared
|
||||
.images
|
||||
@@ -1404,7 +1411,11 @@ async fn run_debug_models_command(
|
||||
let cli_overrides = root_config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let config = Config::load_with_cli_overrides(cli_overrides).await?;
|
||||
let config = Config::load_with_cli_overrides_and_loader_overrides(
|
||||
cli_overrides,
|
||||
loader_overrides_from_cli(&root_config_overrides),
|
||||
)
|
||||
.await?;
|
||||
let auth_manager =
|
||||
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ true).await;
|
||||
let models_manager = build_models_manager(&config, auth_manager);
|
||||
@@ -1429,8 +1440,12 @@ async fn run_debug_clear_memories_command(
|
||||
config_profile: interactive.config_profile.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
let config =
|
||||
Config::load_with_cli_overrides_and_harness_overrides(cli_kv_overrides, overrides).await?;
|
||||
let config = ConfigBuilder::default()
|
||||
.cli_overrides(cli_kv_overrides)
|
||||
.harness_overrides(overrides)
|
||||
.loader_overrides(loader_overrides_from_cli(root_config_overrides))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
let state_path = state_db_path(config.sqlite_home.as_path());
|
||||
let mut cleared_state_db = false;
|
||||
@@ -1465,11 +1480,21 @@ fn prepend_config_flags(
|
||||
subcommand_config_overrides: &mut CliConfigOverrides,
|
||||
cli_config_overrides: CliConfigOverrides,
|
||||
) {
|
||||
subcommand_config_overrides.strict_config |= cli_config_overrides.strict_config;
|
||||
subcommand_config_overrides
|
||||
.raw_overrides
|
||||
.splice(0..0, cli_config_overrides.raw_overrides);
|
||||
}
|
||||
|
||||
fn loader_overrides_from_cli(
|
||||
config_overrides: &CliConfigOverrides,
|
||||
) -> codex_config::LoaderOverrides {
|
||||
codex_config::LoaderOverrides {
|
||||
strict_config: config_overrides.strict_config,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn reject_remote_mode_for_subcommand(
|
||||
remote: Option<&str>,
|
||||
remote_auth_token_env: Option<&str>,
|
||||
@@ -1569,10 +1594,11 @@ async fn run_interactive_tui(
|
||||
.map(read_remote_auth_token_from_env_var)
|
||||
.transpose()
|
||||
.map_err(std::io::Error::other)?;
|
||||
let loader_overrides = loader_overrides_from_cli(&interactive.config_overrides);
|
||||
codex_tui::run_main(
|
||||
interactive,
|
||||
arg0_paths,
|
||||
codex_config::LoaderOverrides::default(),
|
||||
loader_overrides,
|
||||
normalized_remote,
|
||||
remote_auth_token,
|
||||
)
|
||||
@@ -1672,6 +1698,7 @@ fn merge_interactive_cli_flags(interactive: &mut TuiCli, subcommand_cli: TuiCli)
|
||||
.config_overrides
|
||||
.raw_overrides
|
||||
.extend(config_overrides.raw_overrides);
|
||||
interactive.config_overrides.strict_config |= config_overrides.strict_config;
|
||||
}
|
||||
|
||||
fn print_completion(cmd: CompletionCommand) {
|
||||
@@ -2150,6 +2177,7 @@ mod tests {
|
||||
"my-profile",
|
||||
"-C",
|
||||
"/tmp",
|
||||
"--strict-check",
|
||||
"-i",
|
||||
"/tmp/a.png,/tmp/b.png",
|
||||
]
|
||||
@@ -2172,6 +2200,7 @@ mod tests {
|
||||
Some(std::path::Path::new("/tmp"))
|
||||
);
|
||||
assert!(interactive.web_search);
|
||||
assert!(interactive.config_overrides.strict_config);
|
||||
let has_a = interactive
|
||||
.images
|
||||
.iter()
|
||||
@@ -2605,4 +2634,38 @@ mod tests {
|
||||
.expect_err("feature should be rejected");
|
||||
assert_eq!(err.to_string(), "Unknown feature flag: does_not_exist");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strict_check_with_unknown_enable_errors() {
|
||||
let err = strict_check_feature_toggle_error(["--enable", "does_not_exist"].as_ref());
|
||||
assert_eq!(err.to_string(), "Unknown feature flag: does_not_exist");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strict_check_with_unknown_disable_errors() {
|
||||
let err = strict_check_feature_toggle_error(["--disable", "does_not_exist"].as_ref());
|
||||
assert_eq!(err.to_string(), "Unknown feature flag: does_not_exist");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn strict_check_with_compound_enable_errors() {
|
||||
let err = strict_check_feature_toggle_error(
|
||||
["--enable", "multi_agent_v2.subagent_usage_hint_text"].as_ref(),
|
||||
);
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
"Unknown feature flag: multi_agent_v2.subagent_usage_hint_text"
|
||||
);
|
||||
}
|
||||
|
||||
fn strict_check_feature_toggle_error(args: &[&str]) -> anyhow::Error {
|
||||
let cli_args = std::iter::once("codex")
|
||||
.chain(std::iter::once("--strict-check"))
|
||||
.chain(args.iter().copied());
|
||||
let cli = MultitoolCli::try_parse_from(cli_args).expect("parse should succeed");
|
||||
assert!(cli.config_overrides.strict_config);
|
||||
cli.feature_toggles
|
||||
.to_overrides()
|
||||
.expect_err("feature should be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,10 +70,16 @@ impl MarketplaceCli {
|
||||
let overrides = config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let loader_overrides = codex_config::LoaderOverrides {
|
||||
strict_config: config_overrides.strict_config,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
match subcommand {
|
||||
MarketplaceSubcommand::Add(args) => run_add(args).await?,
|
||||
MarketplaceSubcommand::Upgrade(args) => run_upgrade(overrides, args).await?,
|
||||
MarketplaceSubcommand::Upgrade(args) => {
|
||||
run_upgrade(overrides, loader_overrides, args).await?
|
||||
}
|
||||
MarketplaceSubcommand::Remove(args) => run_remove(args).await?,
|
||||
}
|
||||
|
||||
@@ -120,10 +126,11 @@ async fn run_add(args: AddMarketplaceArgs) -> Result<()> {
|
||||
|
||||
async fn run_upgrade(
|
||||
overrides: Vec<(String, toml::Value)>,
|
||||
loader_overrides: codex_config::LoaderOverrides,
|
||||
args: UpgradeMarketplaceArgs,
|
||||
) -> Result<()> {
|
||||
let UpgradeMarketplaceArgs { marketplace_name } = args;
|
||||
let config = Config::load_with_cli_overrides(overrides)
|
||||
let config = Config::load_with_cli_overrides_and_loader_overrides(overrides, loader_overrides)
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
let codex_home = find_codex_home().context("failed to resolve CODEX_HOME")?;
|
||||
|
||||
+18
-31
@@ -188,6 +188,19 @@ impl McpCli {
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_config(config_overrides: &CliConfigOverrides) -> Result<Config> {
|
||||
let overrides = config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let loader_overrides = codex_config::LoaderOverrides {
|
||||
strict_config: config_overrides.strict_config,
|
||||
..Default::default()
|
||||
};
|
||||
Config::load_with_cli_overrides_and_loader_overrides(overrides, loader_overrides)
|
||||
.await
|
||||
.context("failed to load configuration")
|
||||
}
|
||||
|
||||
/// Preserve compatibility with servers that still expect the legacy empty-scope
|
||||
/// OAuth request. If a discovered-scope request is rejected by the provider,
|
||||
/// retry the login flow once without scopes.
|
||||
@@ -237,13 +250,7 @@ async fn perform_oauth_login_retry_without_scopes(
|
||||
}
|
||||
|
||||
async fn run_add(config_overrides: &CliConfigOverrides, add_args: AddArgs) -> Result<()> {
|
||||
// Validate any provided overrides even though they are not currently applied.
|
||||
let overrides = config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let config = Config::load_with_cli_overrides(overrides)
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
let config = load_config(config_overrides).await?;
|
||||
|
||||
let AddArgs {
|
||||
name,
|
||||
@@ -388,12 +395,7 @@ async fn run_remove(config_overrides: &CliConfigOverrides, remove_args: RemoveAr
|
||||
}
|
||||
|
||||
async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs) -> Result<()> {
|
||||
let overrides = config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let config = Config::load_with_cli_overrides(overrides)
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
let config = load_config(config_overrides).await?;
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(
|
||||
config.codex_home.to_path_buf(),
|
||||
)));
|
||||
@@ -441,12 +443,7 @@ async fn run_login(config_overrides: &CliConfigOverrides, login_args: LoginArgs)
|
||||
}
|
||||
|
||||
async fn run_logout(config_overrides: &CliConfigOverrides, logout_args: LogoutArgs) -> Result<()> {
|
||||
let overrides = config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let config = Config::load_with_cli_overrides(overrides)
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
let config = load_config(config_overrides).await?;
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(
|
||||
config.codex_home.to_path_buf(),
|
||||
)));
|
||||
@@ -473,12 +470,7 @@ async fn run_logout(config_overrides: &CliConfigOverrides, logout_args: LogoutAr
|
||||
}
|
||||
|
||||
async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) -> Result<()> {
|
||||
let overrides = config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let config = Config::load_with_cli_overrides(overrides)
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
let config = load_config(config_overrides).await?;
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(
|
||||
config.codex_home.to_path_buf(),
|
||||
)));
|
||||
@@ -728,12 +720,7 @@ async fn run_list(config_overrides: &CliConfigOverrides, list_args: ListArgs) ->
|
||||
}
|
||||
|
||||
async fn run_get(config_overrides: &CliConfigOverrides, get_args: GetArgs) -> Result<()> {
|
||||
let overrides = config_overrides
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
let config = Config::load_with_cli_overrides(overrides)
|
||||
.await
|
||||
.context("failed to load configuration")?;
|
||||
let config = load_config(config_overrides).await?;
|
||||
let mcp_manager = McpManager::new(Arc::new(PluginsManager::new(
|
||||
config.codex_home.to_path_buf(),
|
||||
)));
|
||||
|
||||
Reference in New Issue
Block a user