mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
changes
This commit is contained in:
@@ -6,8 +6,13 @@ use crate::config::edit::apply_blocking;
|
||||
use assert_matches::assert_matches;
|
||||
use codex_config::CONFIG_TOML_FILE;
|
||||
use codex_config::ConfigLayerEntry;
|
||||
use codex_config::ConfigLayerStack;
|
||||
use codex_config::ConfigRequirements;
|
||||
use codex_config::ConfigRequirementsToml;
|
||||
use codex_config::FeatureRequirementsToml;
|
||||
use codex_config::ProfileV2Name;
|
||||
use codex_config::RequirementSource;
|
||||
use codex_config::Sourced;
|
||||
use codex_config::config_toml::AgentRoleToml;
|
||||
use codex_config::config_toml::AgentsToml;
|
||||
use codex_config::config_toml::AutoReviewToml;
|
||||
@@ -5108,7 +5113,8 @@ async fn feature_table_overrides_legacy_flags() -> std::io::Result<()> {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auth_keyring_backend_kind_from_config_toml_uses_secret_auth_storage_feature() {
|
||||
fn resolve_bootstrap_auth_keyring_backend_kind_uses_secret_auth_storage_feature()
|
||||
-> std::io::Result<()> {
|
||||
let config_toml = ConfigToml {
|
||||
features: Some(FeaturesToml::from(BTreeMap::from([(
|
||||
"secret_auth_storage".to_string(),
|
||||
@@ -5117,7 +5123,10 @@ fn auth_keyring_backend_kind_from_config_toml_uses_secret_auth_storage_feature()
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
auth_keyring_backend_kind_from_config_toml(&config_toml),
|
||||
resolve_bootstrap_auth_keyring_backend_kind(&config_toml_load_result(
|
||||
config_toml,
|
||||
/*feature_requirements*/ None,
|
||||
)?)?,
|
||||
AuthKeyringBackendKind::Secrets
|
||||
);
|
||||
|
||||
@@ -5129,9 +5138,44 @@ fn auth_keyring_backend_kind_from_config_toml_uses_secret_auth_storage_feature()
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
auth_keyring_backend_kind_from_config_toml(&config_toml),
|
||||
resolve_bootstrap_auth_keyring_backend_kind(&config_toml_load_result(
|
||||
config_toml.clone(),
|
||||
/*feature_requirements*/ None,
|
||||
)?)?,
|
||||
AuthKeyringBackendKind::Direct
|
||||
);
|
||||
|
||||
let requirements = Sourced::new(
|
||||
FeatureRequirementsToml {
|
||||
entries: BTreeMap::from([("secret_auth_storage".to_string(), true)]),
|
||||
},
|
||||
RequirementSource::Unknown,
|
||||
);
|
||||
assert_eq!(
|
||||
resolve_bootstrap_auth_keyring_backend_kind(&config_toml_load_result(
|
||||
config_toml,
|
||||
Some(requirements),
|
||||
)?)?,
|
||||
AuthKeyringBackendKind::Secrets
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn config_toml_load_result(
|
||||
config_toml: ConfigToml,
|
||||
feature_requirements: Option<Sourced<FeatureRequirementsToml>>,
|
||||
) -> std::io::Result<ConfigTomlLoadResult> {
|
||||
let mut requirements = ConfigRequirements::default();
|
||||
requirements.feature_requirements = feature_requirements;
|
||||
Ok(ConfigTomlLoadResult {
|
||||
config_toml,
|
||||
config_layer_stack: ConfigLayerStack::new(
|
||||
Vec::new(),
|
||||
requirements,
|
||||
ConfigRequirementsToml::default(),
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -1624,20 +1624,31 @@ pub async fn load_config_as_toml_with_cli_and_loader_overrides(
|
||||
cli_overrides: Vec<(String, TomlValue)>,
|
||||
loader_overrides: LoaderOverrides,
|
||||
) -> std::io::Result<ConfigToml> {
|
||||
load_config_as_toml_with_cli_and_load_options(codex_home, cwd, cli_overrides, loader_overrides)
|
||||
load_config_toml_with_layer_stack(codex_home, cwd, cli_overrides, loader_overrides)
|
||||
.await
|
||||
.map(|result| result.config_toml)
|
||||
}
|
||||
|
||||
/// Partially loaded config plus the layer stack used to derive it.
|
||||
///
|
||||
/// This is intended for startup paths that must inspect raw config before a
|
||||
/// full [`Config`] can be constructed, but still need access to managed
|
||||
/// requirements loaded with the config layers.
|
||||
pub struct ConfigTomlLoadResult {
|
||||
pub config_toml: ConfigToml,
|
||||
pub config_layer_stack: ConfigLayerStack,
|
||||
}
|
||||
|
||||
/// DEPRECATED for most callers: prefer [Config::load_with_cli_overrides()] or
|
||||
/// [ConfigBuilder] because working with [ConfigToml] directly means
|
||||
/// [ConfigRequirements] have not been applied yet, which risks skipping
|
||||
/// required constraints.
|
||||
pub async fn load_config_as_toml_with_cli_and_load_options(
|
||||
pub async fn load_config_toml_with_layer_stack(
|
||||
codex_home: &Path,
|
||||
cwd: Option<&AbsolutePathBuf>,
|
||||
cli_overrides: Vec<(String, TomlValue)>,
|
||||
options: impl Into<ConfigLoadOptions>,
|
||||
) -> std::io::Result<ConfigToml> {
|
||||
) -> std::io::Result<ConfigTomlLoadResult> {
|
||||
let config_layer_stack = load_config_layers_state(
|
||||
LOCAL_FS.as_ref(),
|
||||
codex_home,
|
||||
@@ -1654,16 +1665,20 @@ pub async fn load_config_as_toml_with_cli_and_load_options(
|
||||
e
|
||||
})?;
|
||||
|
||||
Ok(cfg)
|
||||
Ok(ConfigTomlLoadResult {
|
||||
config_toml: cfg,
|
||||
config_layer_stack,
|
||||
})
|
||||
}
|
||||
|
||||
/// Resolve the auth keyring backend from a partially loaded config.
|
||||
/// Resolve the auth keyring backend from a partially loaded bootstrap config.
|
||||
///
|
||||
/// This is intended for startup paths that must read auth before managed cloud
|
||||
/// requirements can be loaded and before a full [`Config`] exists.
|
||||
pub fn auth_keyring_backend_kind_from_config_toml(
|
||||
config_toml: &ConfigToml,
|
||||
) -> AuthKeyringBackendKind {
|
||||
pub fn resolve_bootstrap_auth_keyring_backend_kind(
|
||||
bootstrap_config: &ConfigTomlLoadResult,
|
||||
) -> std::io::Result<AuthKeyringBackendKind> {
|
||||
let config_toml = &bootstrap_config.config_toml;
|
||||
let features = Features::from_sources(
|
||||
FeatureConfigSource {
|
||||
features: config_toml.features.as_ref(),
|
||||
@@ -1672,7 +1687,17 @@ pub fn auth_keyring_backend_kind_from_config_toml(
|
||||
FeatureConfigSource::default(),
|
||||
FeatureOverrides::default(),
|
||||
);
|
||||
auth_keyring_backend_kind_from_secret_auth_storage(features.enabled(Feature::SecretAuthStorage))
|
||||
let managed_features = ManagedFeatures::from_configured(
|
||||
features,
|
||||
bootstrap_config
|
||||
.config_layer_stack
|
||||
.requirements()
|
||||
.feature_requirements
|
||||
.clone(),
|
||||
)?;
|
||||
Ok(auth_keyring_backend_kind_from_secret_auth_storage(
|
||||
managed_features.enabled(Feature::SecretAuthStorage),
|
||||
))
|
||||
}
|
||||
|
||||
fn auth_keyring_backend_kind_from_secret_auth_storage(
|
||||
|
||||
+13
-11
@@ -62,9 +62,10 @@ use codex_core::check_execpolicy_for_warnings;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
use codex_core::config::auth_keyring_backend_kind_from_config_toml;
|
||||
use codex_core::config::ConfigTomlLoadResult;
|
||||
use codex_core::config::find_codex_home;
|
||||
use codex_core::config::load_config_as_toml_with_cli_and_load_options;
|
||||
use codex_core::config::load_config_toml_with_layer_stack;
|
||||
use codex_core::config::resolve_bootstrap_auth_keyring_backend_kind;
|
||||
use codex_core::config::resolve_oss_provider;
|
||||
use codex_core::config::resolve_profile_v2_config_path;
|
||||
use codex_core::find_thread_meta_by_name_str;
|
||||
@@ -332,7 +333,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let bootstrap_config_toml = load_config_toml_or_exit(
|
||||
let bootstrap_config = load_bootstrap_config_or_exit(
|
||||
&codex_home,
|
||||
Some(&config_cwd),
|
||||
cli_kv_overrides.clone(),
|
||||
@@ -341,6 +342,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
CloudConfigBundleLoader::default(),
|
||||
)
|
||||
.await;
|
||||
let bootstrap_config_toml = &bootstrap_config.config_toml;
|
||||
|
||||
let chatgpt_base_url = bootstrap_config_toml
|
||||
.chatgpt_base_url
|
||||
@@ -352,7 +354,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
bootstrap_config_toml
|
||||
.cli_auth_credentials_store
|
||||
.unwrap_or_default(),
|
||||
auth_keyring_backend_kind_from_config_toml(&bootstrap_config_toml),
|
||||
resolve_bootstrap_auth_keyring_backend_kind(&bootstrap_config)?,
|
||||
chatgpt_base_url,
|
||||
)
|
||||
.await;
|
||||
@@ -361,12 +363,12 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
let run_cloud_config_bundle = cloud_config_bundle.clone();
|
||||
|
||||
let model_provider = if oss {
|
||||
let config_toml_with_cloud_config;
|
||||
let bootstrap_config_with_cloud_config;
|
||||
let config_toml_for_oss = if oss_provider.is_none() {
|
||||
// The first load intentionally skips cloud config so we can read
|
||||
// auth/base-url settings needed to fetch the bundle. If OSS mode
|
||||
// needs a default provider from config, reload with the bundle.
|
||||
config_toml_with_cloud_config = load_config_toml_or_exit(
|
||||
bootstrap_config_with_cloud_config = load_bootstrap_config_or_exit(
|
||||
&codex_home,
|
||||
Some(&config_cwd),
|
||||
cli_kv_overrides.clone(),
|
||||
@@ -375,9 +377,9 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
cloud_config_bundle.clone(),
|
||||
)
|
||||
.await;
|
||||
&config_toml_with_cloud_config
|
||||
&bootstrap_config_with_cloud_config.config_toml
|
||||
} else {
|
||||
&bootstrap_config_toml
|
||||
bootstrap_config_toml
|
||||
};
|
||||
|
||||
let resolved = resolve_oss_provider(oss_provider.as_deref(), config_toml_for_oss);
|
||||
@@ -612,15 +614,15 @@ where
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
async fn load_config_toml_or_exit(
|
||||
async fn load_bootstrap_config_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(
|
||||
) -> ConfigTomlLoadResult {
|
||||
match load_config_toml_with_layer_stack(
|
||||
codex_home,
|
||||
cwd,
|
||||
cli_kv_overrides,
|
||||
|
||||
@@ -158,7 +158,12 @@ async fn persisted_credentials_auth_status_child() -> anyhow::Result<()> {
|
||||
token_response: WrappedOAuthTokenResponse(response),
|
||||
expires_at: Some(0),
|
||||
};
|
||||
save_oauth_tokens(SERVER_NAME, &tokens, OAuthCredentialsStoreMode::File)?;
|
||||
save_oauth_tokens(
|
||||
SERVER_NAME,
|
||||
&tokens,
|
||||
OAuthCredentialsStoreMode::File,
|
||||
AuthKeyringBackendKind::default(),
|
||||
)?;
|
||||
|
||||
let status = auth_status(UNREFRESHABLE_SERVER_URL).await?;
|
||||
assert_eq!(status, McpAuthStatus::NotLoggedIn);
|
||||
@@ -179,7 +184,12 @@ async fn persisted_credentials_auth_status_child() -> anyhow::Result<()> {
|
||||
token_response: WrappedOAuthTokenResponse(response),
|
||||
expires_at: Some(now.saturating_add(/*rhs*/ 60_000)),
|
||||
};
|
||||
save_oauth_tokens(SERVER_NAME, &tokens, OAuthCredentialsStoreMode::File)?;
|
||||
save_oauth_tokens(
|
||||
SERVER_NAME,
|
||||
&tokens,
|
||||
OAuthCredentialsStoreMode::File,
|
||||
AuthKeyringBackendKind::default(),
|
||||
)?;
|
||||
|
||||
let status = auth_status(UNEXPIRED_SERVER_URL).await?;
|
||||
assert_eq!(status, McpAuthStatus::OAuth);
|
||||
@@ -197,7 +207,12 @@ async fn persisted_credentials_auth_status_child() -> anyhow::Result<()> {
|
||||
token_response: WrappedOAuthTokenResponse(response),
|
||||
expires_at: Some(0),
|
||||
};
|
||||
save_oauth_tokens(SERVER_NAME, &tokens, OAuthCredentialsStoreMode::File)?;
|
||||
save_oauth_tokens(
|
||||
SERVER_NAME,
|
||||
&tokens,
|
||||
OAuthCredentialsStoreMode::File,
|
||||
AuthKeyringBackendKind::default(),
|
||||
)?;
|
||||
|
||||
let status = auth_status(REFRESHABLE_SERVER_URL).await?;
|
||||
assert_eq!(status, McpAuthStatus::OAuth);
|
||||
@@ -212,6 +227,7 @@ async fn auth_status(server_url: &str) -> anyhow::Result<McpAuthStatus> {
|
||||
/*http_headers*/ None,
|
||||
/*env_http_headers*/ None,
|
||||
OAuthCredentialsStoreMode::File,
|
||||
AuthKeyringBackendKind::default(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
+13
-11
@@ -7,8 +7,9 @@ use crate::legacy_core::check_execpolicy_for_warnings;
|
||||
use crate::legacy_core::config::Config;
|
||||
use crate::legacy_core::config::ConfigBuilder;
|
||||
use crate::legacy_core::config::ConfigOverrides;
|
||||
use crate::legacy_core::config::auth_keyring_backend_kind_from_config_toml;
|
||||
use crate::legacy_core::config::load_config_as_toml_with_cli_and_load_options;
|
||||
use crate::legacy_core::config::ConfigTomlLoadResult;
|
||||
use crate::legacy_core::config::load_config_toml_with_layer_stack;
|
||||
use crate::legacy_core::config::resolve_bootstrap_auth_keyring_backend_kind;
|
||||
use crate::legacy_core::config::resolve_oss_provider;
|
||||
use crate::legacy_core::config::resolve_profile_v2_config_path;
|
||||
use crate::legacy_core::format_exec_policy_error_with_source;
|
||||
@@ -1009,7 +1010,7 @@ pub async fn run_main(
|
||||
loader_overrides.user_config_profile = Some(profile_v2.clone());
|
||||
}
|
||||
|
||||
let bootstrap_config_toml = load_config_toml_or_exit(
|
||||
let bootstrap_config = load_bootstrap_config_or_exit(
|
||||
&codex_home,
|
||||
config_cwd.as_ref(),
|
||||
cli_kv_overrides.clone(),
|
||||
@@ -1018,6 +1019,7 @@ pub async fn run_main(
|
||||
CloudConfigBundleLoader::default(),
|
||||
)
|
||||
.await;
|
||||
let bootstrap_config_toml = &bootstrap_config.config_toml;
|
||||
|
||||
let chatgpt_base_url = bootstrap_config_toml
|
||||
.chatgpt_base_url
|
||||
@@ -1029,7 +1031,7 @@ pub async fn run_main(
|
||||
bootstrap_config_toml
|
||||
.cli_auth_credentials_store
|
||||
.unwrap_or_default(),
|
||||
auth_keyring_backend_kind_from_config_toml(&bootstrap_config_toml),
|
||||
resolve_bootstrap_auth_keyring_backend_kind(&bootstrap_config)?,
|
||||
chatgpt_base_url,
|
||||
)
|
||||
.await;
|
||||
@@ -1042,12 +1044,12 @@ pub async fn run_main(
|
||||
|
||||
let mut manually_selected_oss_provider = None;
|
||||
let model_provider_override = if cli.oss {
|
||||
let config_toml_with_cloud_config;
|
||||
let bootstrap_config_with_cloud_config;
|
||||
let config_toml_for_oss = if cli.oss_provider.is_none() {
|
||||
// The first load intentionally skips cloud config so we can read
|
||||
// auth/base-url settings needed to fetch the bundle. If OSS mode
|
||||
// needs a default provider from config, reload with the bundle.
|
||||
config_toml_with_cloud_config = load_config_toml_or_exit(
|
||||
bootstrap_config_with_cloud_config = load_bootstrap_config_or_exit(
|
||||
&codex_home,
|
||||
config_cwd.as_ref(),
|
||||
cli_kv_overrides.clone(),
|
||||
@@ -1056,9 +1058,9 @@ pub async fn run_main(
|
||||
cloud_config_bundle.clone(),
|
||||
)
|
||||
.await;
|
||||
&config_toml_with_cloud_config
|
||||
&bootstrap_config_with_cloud_config.config_toml
|
||||
} else {
|
||||
&bootstrap_config_toml
|
||||
bootstrap_config_toml
|
||||
};
|
||||
|
||||
let resolved = resolve_oss_provider(cli.oss_provider.as_deref(), config_toml_for_oss);
|
||||
@@ -1999,15 +2001,15 @@ async fn load_config_or_exit_with_fallback_cwd(
|
||||
}
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
async fn load_config_toml_or_exit(
|
||||
async fn load_bootstrap_config_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(
|
||||
) -> ConfigTomlLoadResult {
|
||||
match load_config_toml_with_layer_stack(
|
||||
codex_home,
|
||||
cwd,
|
||||
cli_kv_overrides,
|
||||
|
||||
@@ -9,8 +9,8 @@ use crate::Cli;
|
||||
use crate::app_server_session::AppServerSession;
|
||||
use crate::legacy_core::config::ConfigBuilder;
|
||||
use crate::legacy_core::config::ConfigOverrides;
|
||||
use crate::legacy_core::config::auth_keyring_backend_kind_from_config_toml;
|
||||
use crate::legacy_core::config::load_config_as_toml_with_cli_and_load_options;
|
||||
use crate::legacy_core::config::load_config_toml_with_layer_stack;
|
||||
use crate::legacy_core::config::resolve_bootstrap_auth_keyring_backend_kind;
|
||||
use crate::legacy_core::config::resolve_oss_provider;
|
||||
use crate::legacy_core::config::resolve_profile_v2_config_path;
|
||||
use codex_app_server_protocol::Thread as AppServerThread;
|
||||
@@ -246,7 +246,7 @@ async fn start_app_server_for_archive_command(
|
||||
loader_overrides.user_config_profile = Some(profile_v2.clone());
|
||||
}
|
||||
|
||||
let config_toml = load_config_as_toml_with_cli_and_load_options(
|
||||
let bootstrap_config = load_config_toml_with_layer_stack(
|
||||
codex_home.as_path(),
|
||||
config_cwd.as_ref(),
|
||||
cli_kv_overrides.clone(),
|
||||
@@ -258,6 +258,7 @@ async fn start_app_server_for_archive_command(
|
||||
)
|
||||
.await
|
||||
.wrap_err("failed to load config.toml")?;
|
||||
let config_toml = &bootstrap_config.config_toml;
|
||||
let chatgpt_base_url = config_toml
|
||||
.chatgpt_base_url
|
||||
.clone()
|
||||
@@ -266,7 +267,7 @@ async fn start_app_server_for_archive_command(
|
||||
codex_home.to_path_buf(),
|
||||
/*enable_codex_api_key_env*/ false,
|
||||
config_toml.cli_auth_credentials_store.unwrap_or_default(),
|
||||
auth_keyring_backend_kind_from_config_toml(&config_toml),
|
||||
resolve_bootstrap_auth_keyring_backend_kind(&bootstrap_config)?,
|
||||
chatgpt_base_url,
|
||||
)
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user