mirror of
https://github.com/openai/codex.git
synced 2026-05-30 07:50:17 +00:00
Refine cloud config bundle runtime integration
Move CloudConfigBundleLoader into ConfigLoadOptions so lower-level config loading does not need a separate bundle parameter or a bundle-specific TOML helper. Keep exec and TUI on the straightforward local helper path: bootstrap from local config, build the shared cloud bundle loader, and reload merged TOML with that loader only when OSS provider inference needs cloud-delivered config. Preserve strict validation and path-base behavior for cloud config and requirements fragments, including diagnostics that name the cloud layer and requirements paths resolved against CODEX_HOME. Validation: just fmt; just test -p codex-core -E 'test(load_config_layers_resolves_relative_cloud_requirements_paths_against_codex_home) | test(strict_config_rejects_unknown_cloud_config_key)'; just test -p codex-config; just test -p codex-exec -E 'test(top_cli_parses_resume_prompt_after_config_flag)'; just test -p codex-tui -E 'test(app_server_target_for_launch_prefers_explicit_remote_endpoint)'; just fix -p codex-config -p codex-core -p codex-exec -p codex-tui -p codex-app-server -p codex-core-plugins; git diff --check.
This commit is contained in:
@@ -274,8 +274,8 @@ impl ConfigManager {
|
||||
codex_config::ConfigLoadOptions {
|
||||
loader_overrides: self.loader_overrides.clone(),
|
||||
strict_config: self.strict_config,
|
||||
cloud_config_bundle: self.current_cloud_config_bundle(),
|
||||
},
|
||||
self.current_cloud_config_bundle(),
|
||||
thread_config_loader.as_ref(),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -10,7 +10,7 @@ This module is the canonical place to **load and describe Codex configuration la
|
||||
|
||||
Exported from `codex_config::loader`:
|
||||
|
||||
- `load_config_layers_state(fs, codex_home, cwd_opt, cli_overrides, options, cloud_config_bundle, thread_config_loader) -> ConfigLayerStack`
|
||||
- `load_config_layers_state(fs, codex_home, cwd_opt, cli_overrides, options, thread_config_loader) -> ConfigLayerStack`
|
||||
- `ConfigLayerStack`
|
||||
- `effective_config() -> toml::Value`
|
||||
- `origins() -> HashMap<String, ConfigLayerMetadata>`
|
||||
@@ -48,7 +48,6 @@ computing the effective config and origins metadata. This is what
|
||||
Most callers want the effective config plus metadata:
|
||||
|
||||
```rust
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_config::NoopThreadConfigLoader;
|
||||
use codex_config::loader::load_config_layers_state;
|
||||
@@ -64,7 +63,6 @@ let layers = load_config_layers_state(
|
||||
Some(cwd),
|
||||
&cli_overrides,
|
||||
LoaderOverrides::default(),
|
||||
CloudConfigBundleLoader::default(),
|
||||
&NoopThreadConfigLoader,
|
||||
).await?;
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@ model = "gpt-work"
|
||||
/*cwd*/ None,
|
||||
&[],
|
||||
overrides,
|
||||
CloudConfigBundleLoader::default(),
|
||||
&crate::NoopThreadConfigLoader,
|
||||
)
|
||||
.await
|
||||
@@ -166,7 +165,6 @@ model = "gpt-main"
|
||||
/*cwd*/ None,
|
||||
&[],
|
||||
overrides,
|
||||
CloudConfigBundleLoader::default(),
|
||||
&crate::NoopThreadConfigLoader,
|
||||
)
|
||||
.await
|
||||
@@ -223,7 +221,6 @@ model = "gpt-dev"
|
||||
/*cwd*/ None,
|
||||
&[],
|
||||
overrides,
|
||||
CloudConfigBundleLoader::default(),
|
||||
&crate::NoopThreadConfigLoader,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::path::Path;
|
||||
|
||||
use crate::OPENAI_CURATED_MARKETPLACE_NAME;
|
||||
use crate::PluginsConfigInput;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_config::NoopThreadConfigLoader;
|
||||
use codex_config::loader::load_config_layers_state;
|
||||
@@ -106,7 +105,6 @@ pub(crate) async fn load_plugins_config(codex_home: &Path, cwd: &Path) -> Plugin
|
||||
Some(cwd),
|
||||
&[],
|
||||
LoaderOverrides::without_managed_config_for_tests(),
|
||||
CloudConfigBundleLoader::default(),
|
||||
&NoopThreadConfigLoader,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -1152,8 +1152,8 @@ impl ConfigBuilder {
|
||||
ConfigLoadOptions {
|
||||
loader_overrides,
|
||||
strict_config,
|
||||
cloud_config_bundle,
|
||||
},
|
||||
cloud_config_bundle,
|
||||
thread_config_loader
|
||||
.as_deref()
|
||||
.unwrap_or(&codex_config::NoopThreadConfigLoader),
|
||||
@@ -1543,7 +1543,6 @@ pub async fn load_config_as_toml_with_cli_and_load_options(
|
||||
cwd.cloned(),
|
||||
&cli_overrides,
|
||||
options,
|
||||
CloudConfigBundleLoader::default(),
|
||||
&codex_config::NoopThreadConfigLoader,
|
||||
)
|
||||
.await?;
|
||||
@@ -1754,7 +1753,6 @@ pub async fn load_global_mcp_servers(
|
||||
cwd,
|
||||
&cli_overrides,
|
||||
LoaderOverrides::default(),
|
||||
CloudConfigBundleLoader::default(),
|
||||
&codex_config::NoopThreadConfigLoader,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -10,7 +10,6 @@ use anyhow::Result;
|
||||
use async_trait::async_trait;
|
||||
use codex_app_server_protocol::ConfigLayerSource;
|
||||
use codex_config::CONFIG_TOML_FILE;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLayerStack;
|
||||
use codex_config::ConfigLayerStackOrdering;
|
||||
use codex_config::LoaderOverrides;
|
||||
@@ -60,7 +59,6 @@ async fn build_config_state_with_mtimes() -> Result<(ConfigState, Vec<LayerMtime
|
||||
/*cwd*/ None,
|
||||
&cli_overrides,
|
||||
overrides,
|
||||
CloudConfigBundleLoader::default(),
|
||||
&codex_config::NoopThreadConfigLoader,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -52,6 +52,7 @@ use codex_app_server_protocol::TurnStartResponse;
|
||||
use codex_app_server_protocol::TurnStartedNotification;
|
||||
use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLoadError;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_config::LoaderOverrides;
|
||||
@@ -328,43 +329,26 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let config_toml = match load_config_as_toml_with_cli_and_load_options(
|
||||
let bootstrap_config_toml = load_config_toml_or_exit(
|
||||
&codex_home,
|
||||
Some(&config_cwd),
|
||||
cli_kv_overrides.clone(),
|
||||
ConfigLoadOptions {
|
||||
loader_overrides: loader_overrides.clone(),
|
||||
strict_config,
|
||||
},
|
||||
loader_overrides.clone(),
|
||||
strict_config,
|
||||
CloudConfigBundleLoader::default(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(config_toml) => config_toml,
|
||||
Err(err) => {
|
||||
let config_error = err
|
||||
.get_ref()
|
||||
.and_then(|err| err.downcast_ref::<ConfigLoadError>())
|
||||
.map(ConfigLoadError::config_error);
|
||||
if let Some(config_error) = config_error {
|
||||
eprintln!(
|
||||
"Error loading config.toml:\n{}",
|
||||
format_config_error_with_source(config_error)
|
||||
);
|
||||
} else {
|
||||
eprintln!("Error loading config.toml: {err}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
.await;
|
||||
|
||||
let chatgpt_base_url = config_toml
|
||||
let chatgpt_base_url = bootstrap_config_toml
|
||||
.chatgpt_base_url
|
||||
.clone()
|
||||
.unwrap_or_else(|| "https://chatgpt.com/backend-api/".to_string());
|
||||
let cloud_config_bundle = cloud_config_bundle_loader_for_storage(
|
||||
codex_home.to_path_buf(),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
config_toml.cli_auth_credentials_store.unwrap_or_default(),
|
||||
bootstrap_config_toml
|
||||
.cli_auth_credentials_store
|
||||
.unwrap_or_default(),
|
||||
chatgpt_base_url,
|
||||
)
|
||||
.await;
|
||||
@@ -372,8 +356,24 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
let run_loader_overrides = loader_overrides.clone();
|
||||
let run_cloud_config_bundle = cloud_config_bundle.clone();
|
||||
|
||||
let config_toml_for_oss;
|
||||
let config_toml_for_oss = if oss && oss_provider.is_none() {
|
||||
config_toml_for_oss = load_config_toml_or_exit(
|
||||
&codex_home,
|
||||
Some(&config_cwd),
|
||||
cli_kv_overrides.clone(),
|
||||
loader_overrides.clone(),
|
||||
strict_config,
|
||||
cloud_config_bundle.clone(),
|
||||
)
|
||||
.await;
|
||||
&config_toml_for_oss
|
||||
} else {
|
||||
&bootstrap_config_toml
|
||||
};
|
||||
|
||||
let model_provider = if oss {
|
||||
let resolved = resolve_oss_provider(oss_provider.as_deref(), &config_toml);
|
||||
let resolved = resolve_oss_provider(oss_provider.as_deref(), config_toml_for_oss);
|
||||
|
||||
if let Some(provider) = resolved {
|
||||
Some(provider)
|
||||
@@ -560,6 +560,46 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
async fn load_config_toml_or_exit(
|
||||
codex_home: &Path,
|
||||
cwd: Option<&AbsolutePathBuf>,
|
||||
cli_kv_overrides: Vec<(String, codex_config::TomlValue)>,
|
||||
loader_overrides: LoaderOverrides,
|
||||
strict_config: bool,
|
||||
cloud_config_bundle: CloudConfigBundleLoader,
|
||||
) -> codex_config::config_toml::ConfigToml {
|
||||
match load_config_as_toml_with_cli_and_load_options(
|
||||
codex_home,
|
||||
cwd,
|
||||
cli_kv_overrides,
|
||||
ConfigLoadOptions {
|
||||
loader_overrides,
|
||||
strict_config,
|
||||
cloud_config_bundle,
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(config_toml) => config_toml,
|
||||
Err(err) => {
|
||||
let config_error = err
|
||||
.get_ref()
|
||||
.and_then(|err| err.downcast_ref::<ConfigLoadError>())
|
||||
.map(ConfigLoadError::config_error);
|
||||
if let Some(config_error) = config_error {
|
||||
eprintln!(
|
||||
"Error loading config.toml:\n{}",
|
||||
format_config_error_with_source(config_error)
|
||||
);
|
||||
} else {
|
||||
eprintln!("Error loading config.toml: {err}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_exec_session(args: ExecRunArgs) -> anyhow::Result<()> {
|
||||
let ExecRunArgs {
|
||||
in_process_start_args,
|
||||
|
||||
@@ -971,51 +971,55 @@ pub async fn run_main(
|
||||
loader_overrides.user_config_profile = Some(profile_v2.clone());
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
let config_toml = match load_config_as_toml_with_cli_and_load_options(
|
||||
let bootstrap_config_toml = load_config_toml_or_exit(
|
||||
&codex_home,
|
||||
config_cwd.as_ref(),
|
||||
cli_kv_overrides.clone(),
|
||||
codex_config::ConfigLoadOptions {
|
||||
loader_overrides: loader_overrides.clone(),
|
||||
strict_config,
|
||||
},
|
||||
loader_overrides.clone(),
|
||||
strict_config,
|
||||
CloudConfigBundleLoader::default(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(config_toml) => config_toml,
|
||||
Err(err) => {
|
||||
let config_error = err
|
||||
.get_ref()
|
||||
.and_then(|err| err.downcast_ref::<ConfigLoadError>())
|
||||
.map(ConfigLoadError::config_error);
|
||||
if let Some(config_error) = config_error {
|
||||
eprintln!(
|
||||
"Error loading config.toml:\n{}",
|
||||
format_config_error_with_source(config_error)
|
||||
);
|
||||
} else {
|
||||
eprintln!("Error loading config.toml: {err}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
.await;
|
||||
|
||||
let chatgpt_base_url = config_toml
|
||||
let chatgpt_base_url = bootstrap_config_toml
|
||||
.chatgpt_base_url
|
||||
.clone()
|
||||
.unwrap_or_else(|| "https://chatgpt.com/backend-api/".to_string());
|
||||
let cloud_config_bundle = cloud_config_bundle_loader_for_storage(
|
||||
codex_home.to_path_buf(),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
config_toml.cli_auth_credentials_store.unwrap_or_default(),
|
||||
bootstrap_config_toml
|
||||
.cli_auth_credentials_store
|
||||
.unwrap_or_default(),
|
||||
chatgpt_base_url,
|
||||
)
|
||||
.await;
|
||||
|
||||
let cwd_override = if app_server_target.uses_remote_workspace() {
|
||||
None
|
||||
} else {
|
||||
cwd.clone()
|
||||
};
|
||||
|
||||
let config_toml_for_oss;
|
||||
let config_toml_for_oss = if cli.oss && cli.oss_provider.is_none() {
|
||||
config_toml_for_oss = load_config_toml_or_exit(
|
||||
&codex_home,
|
||||
config_cwd.as_ref(),
|
||||
cli_kv_overrides.clone(),
|
||||
loader_overrides.clone(),
|
||||
strict_config,
|
||||
cloud_config_bundle.clone(),
|
||||
)
|
||||
.await;
|
||||
&config_toml_for_oss
|
||||
} else {
|
||||
&bootstrap_config_toml
|
||||
};
|
||||
|
||||
let mut manually_selected_oss_provider = None;
|
||||
let model_provider_override = if cli.oss {
|
||||
let resolved = resolve_oss_provider(cli.oss_provider.as_deref(), &config_toml);
|
||||
let resolved = resolve_oss_provider(cli.oss_provider.as_deref(), config_toml_for_oss);
|
||||
|
||||
if let Some(provider) = resolved {
|
||||
Some(provider)
|
||||
@@ -1056,11 +1060,7 @@ pub async fn run_main(
|
||||
model,
|
||||
approval_policy,
|
||||
sandbox_mode,
|
||||
cwd: if app_server_target.uses_remote_workspace() {
|
||||
None
|
||||
} else {
|
||||
cwd
|
||||
},
|
||||
cwd: cwd_override,
|
||||
model_provider: model_provider_override.clone(),
|
||||
codex_self_exe: arg0_paths.codex_self_exe.clone(),
|
||||
codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
@@ -1150,6 +1150,11 @@ pub async fn run_main(
|
||||
tracing::warn!(error = %err, "failed to deserialize config for personality migration");
|
||||
}
|
||||
}
|
||||
let config_toml_log_dir_configured = config
|
||||
.config_layer_stack
|
||||
.effective_config()
|
||||
.as_table()
|
||||
.is_some_and(|table| table.contains_key("log_dir"));
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
match check_execpolicy_for_warnings(&config.config_layer_stack).await {
|
||||
@@ -1193,7 +1198,7 @@ pub async fn run_main(
|
||||
}
|
||||
}
|
||||
|
||||
let (tui_file_layer, _tui_file_log_guard) = if config_toml.log_dir.is_some() {
|
||||
let (tui_file_layer, _tui_file_log_guard) = if config_toml_log_dir_configured {
|
||||
let log_dir = config.log_dir.clone();
|
||||
std::fs::create_dir_all(&log_dir)?;
|
||||
let mut log_file_opts = OpenOptions::new();
|
||||
@@ -1916,6 +1921,46 @@ async fn load_config_or_exit_with_fallback_cwd(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
async fn load_config_toml_or_exit(
|
||||
codex_home: &Path,
|
||||
cwd: Option<&AbsolutePathBuf>,
|
||||
cli_kv_overrides: Vec<(String, codex_config::TomlValue)>,
|
||||
loader_overrides: LoaderOverrides,
|
||||
strict_config: bool,
|
||||
cloud_config_bundle: CloudConfigBundleLoader,
|
||||
) -> codex_config::config_toml::ConfigToml {
|
||||
match load_config_as_toml_with_cli_and_load_options(
|
||||
codex_home,
|
||||
cwd,
|
||||
cli_kv_overrides,
|
||||
codex_config::ConfigLoadOptions {
|
||||
loader_overrides,
|
||||
strict_config,
|
||||
cloud_config_bundle,
|
||||
},
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(config_toml) => config_toml,
|
||||
Err(err) => {
|
||||
let config_error = err
|
||||
.get_ref()
|
||||
.and_then(|err| err.downcast_ref::<ConfigLoadError>())
|
||||
.map(ConfigLoadError::config_error);
|
||||
if let Some(config_error) = config_error {
|
||||
eprintln!(
|
||||
"Error loading config.toml:\n{}",
|
||||
format_config_error_with_source(config_error)
|
||||
);
|
||||
} else {
|
||||
eprintln!("Error loading config.toml: {err}");
|
||||
}
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if the user has decided whether to trust the current directory.
|
||||
fn should_show_trust_screen(config: &Config) -> bool {
|
||||
config.active_project.trust_level.is_none()
|
||||
|
||||
Reference in New Issue
Block a user