mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Load configured environments from CODEX_HOME (#20667)
## Why The earlier PRs add stdio transport support and the config-backed environment provider, but the feature remains inert until normal Codex entrypoints construct `EnvironmentManager` with enough context to discover `CODEX_HOME/environments.toml`. This final stack PR activates the provider while preserving the legacy `CODEX_EXEC_SERVER_URL` fallback when no environments file exists. **Stack position:** this is PR 5 of 5. It is the product wiring PR that activates the configured environment provider added in PR 4. ## What Changed - Thread `codex_home` into `EnvironmentManagerArgs`. - Change `EnvironmentManager::new(...)` to load the provider from `CODEX_HOME`. - Preserve legacy behavior by falling back to `DefaultEnvironmentProvider::from_env()` when `environments.toml` is absent. - Make `environments.toml`-backed managers start new threads with all configured environments, default first, while keeping the legacy env-var path single-default. - Update the app-server, TUI, exec, MCP server, connector, prompt-debug, and thread-manager-sample callsites to pass `codex_home` and handle provider-loading errors. ## Self-Review Notes - The multi-environment startup path is intentionally tied to the `environments.toml` provider. Using `>1` configured environment as the only signal would also expand the legacy `CODEX_EXEC_SERVER_URL` provider because it keeps `local` addressable alongside `remote`. - The startup environment list is still derived inside `EnvironmentManager`; the provider only says whether its snapshot should start new threads with all configured environments. - The thread-manager sample was updated to pass the current `ThreadManager::new(...)` installation id argument so the stack compiles under Bazel. ## Stack - 1. https://github.com/openai/codex/pull/20663 - Add stdio exec-server listener - 2. https://github.com/openai/codex/pull/20664 - Add stdio exec-server client transport - 3. https://github.com/openai/codex/pull/20665 - Make environment providers own default selection - 4. https://github.com/openai/codex/pull/20666 - Add CODEX_HOME environments TOML provider - **5. This PR:** https://github.com/openai/codex/pull/20667 - Load configured environments from CODEX_HOME Split from original draft: https://github.com/openai/codex/pull/20508 ## Validation - `just fmt` - `git diff --check` - `bazel build --config=remote --strategy=remote --remote_download_toplevel //codex-rs/thread-manager-sample:codex-thread-manager-sample` - `bazel test --config=remote --strategy=remote --remote_download_toplevel //codex-rs/exec-server:exec-server-unit-tests` - `bazel test --config=remote --strategy=remote --remote_download_toplevel --test_sharding_strategy=disabled --test_arg=default_thread_environment_selections_use_manager_default_id //codex-rs/core:core-unit-tests` - `bazel test --config=remote --strategy=remote --remote_download_toplevel --test_sharding_strategy=disabled --test_arg=start_thread_uses_all_default_environments_from_codex_home //codex-rs/core:core-unit-tests` ## Documentation This activates `CODEX_HOME/environments.toml`; user-facing documentation should be added before this stack is treated as a documented public workflow. --------- Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -49,7 +49,6 @@ use codex_config::RemoteThreadConfigLoader;
|
||||
use codex_config::ThreadConfigLoader;
|
||||
use codex_core::config::Config;
|
||||
pub use codex_exec_server::EnvironmentManager;
|
||||
pub use codex_exec_server::EnvironmentManagerArgs;
|
||||
pub use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
|
||||
@@ -8,7 +8,6 @@ use codex_config::RemoteThreadConfigLoader;
|
||||
use codex_config::ThreadConfigLoader;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::resolve_installation_id;
|
||||
use codex_exec_server::EnvironmentManagerArgs;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
@@ -420,15 +419,6 @@ pub async fn run_main_with_transport_options(
|
||||
auth: AppServerWebsocketAuthSettings,
|
||||
runtime_options: AppServerRuntimeOptions,
|
||||
) -> IoResult<()> {
|
||||
let environment_manager = Arc::new(
|
||||
EnvironmentManager::new(EnvironmentManagerArgs::new(
|
||||
ExecServerRuntimePaths::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?,
|
||||
))
|
||||
.await,
|
||||
);
|
||||
let (transport_event_tx, mut transport_event_rx) =
|
||||
mpsc::channel::<TransportEvent>(CHANNEL_CAPACITY);
|
||||
let (outgoing_tx, mut outgoing_rx) = mpsc::channel::<OutgoingEnvelope>(CHANNEL_CAPACITY);
|
||||
@@ -444,6 +434,17 @@ pub async fn run_main_with_transport_options(
|
||||
)
|
||||
})?;
|
||||
let codex_home = find_codex_home()?;
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
let environment_manager = if loader_overrides.ignore_user_config {
|
||||
EnvironmentManager::from_env(local_runtime_paths).await
|
||||
} else {
|
||||
EnvironmentManager::from_codex_home(codex_home.clone(), local_runtime_paths).await
|
||||
}
|
||||
.map(Arc::new)
|
||||
.map_err(std::io::Error::other)?;
|
||||
let config_manager = ConfigManager::new(
|
||||
codex_home.to_path_buf(),
|
||||
cli_kv_overrides.clone(),
|
||||
|
||||
@@ -44,7 +44,6 @@ pub use codex_core::resolve_installation_id;
|
||||
pub use codex_core::skills::SkillsManager;
|
||||
pub use codex_core::thread_store_from_config;
|
||||
pub use codex_exec_server::EnvironmentManager;
|
||||
pub use codex_exec_server::EnvironmentManagerArgs;
|
||||
pub use codex_exec_server::ExecServerRuntimePaths;
|
||||
pub use codex_features::Feature;
|
||||
pub use codex_features::Features;
|
||||
|
||||
@@ -15,7 +15,6 @@ pub use codex_app_server_protocol::AppMetadata;
|
||||
use codex_connectors::AllConnectorsCacheKey;
|
||||
use codex_connectors::DirectoryListResponse;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::EnvironmentManagerArgs;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_tools::DiscoverableTool;
|
||||
@@ -202,7 +201,7 @@ pub async fn list_accessible_connectors_from_mcp_tools_with_options_and_status(
|
||||
config.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
let environment_manager =
|
||||
EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)).await;
|
||||
EnvironmentManager::from_codex_home(config.codex_home.clone(), local_runtime_paths).await?;
|
||||
list_accessible_connectors_from_mcp_tools_with_environment_manager(
|
||||
config,
|
||||
force_refetch,
|
||||
|
||||
@@ -15,12 +15,12 @@ pub(crate) fn default_thread_environment_selections(
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> Vec<TurnEnvironmentSelection> {
|
||||
environment_manager
|
||||
.default_environment_id()
|
||||
.default_environment_ids()
|
||||
.into_iter()
|
||||
.map(|environment_id| TurnEnvironmentSelection {
|
||||
environment_id: environment_id.to_string(),
|
||||
environment_id,
|
||||
cwd: cwd.clone(),
|
||||
})
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
@@ -111,10 +111,16 @@ mod tests {
|
||||
|
||||
assert_eq!(
|
||||
default_thread_environment_selections(&manager, &cwd),
|
||||
vec![TurnEnvironmentSelection {
|
||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
cwd,
|
||||
}]
|
||||
vec![
|
||||
TurnEnvironmentSelection {
|
||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
cwd: cwd.clone(),
|
||||
},
|
||||
TurnEnvironmentSelection {
|
||||
environment_id: "local".to_string(),
|
||||
cwd,
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::EnvironmentManagerArgs;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_login::AuthManager;
|
||||
use codex_protocol::error::CodexErr;
|
||||
use codex_protocol::error::Result as CodexResult;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -44,7 +44,11 @@ pub async fn build_prompt_input(
|
||||
&config,
|
||||
Arc::clone(&auth_manager),
|
||||
SessionSource::Exec,
|
||||
Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)).await),
|
||||
Arc::new(
|
||||
EnvironmentManager::from_codex_home(config.codex_home.clone(), local_runtime_paths)
|
||||
.await
|
||||
.map_err(|err| CodexErr::Fatal(err.to_string()))?,
|
||||
),
|
||||
/*analytics_events_client*/ None,
|
||||
thread_store,
|
||||
state_db.clone(),
|
||||
|
||||
@@ -21,6 +21,7 @@ use codex_protocol::protocol::SessionSource;
|
||||
use codex_protocol::protocol::ThreadSource;
|
||||
use codex_protocol::protocol::TurnStartedEvent;
|
||||
use codex_protocol::protocol::UserMessageEvent;
|
||||
use codex_protocol::user_input::UserInput;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::PathExt;
|
||||
use core_test_support::responses::mount_models_once;
|
||||
@@ -339,6 +340,99 @@ async fn start_thread_accepts_explicit_environment_when_default_environment_is_d
|
||||
assert_eq!(manager.list_thread_ids().await, vec![thread.thread_id]);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn start_thread_uses_all_default_environments_from_codex_home() {
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
let mut config = test_config().await;
|
||||
config.codex_home = temp_dir.path().join("codex-home").abs();
|
||||
config.cwd = config.codex_home.abs();
|
||||
std::fs::create_dir_all(&config.codex_home).expect("create codex home");
|
||||
std::fs::write(
|
||||
config.codex_home.join("environments.toml"),
|
||||
r#"
|
||||
default = "dev"
|
||||
|
||||
[[environments]]
|
||||
id = "dev"
|
||||
program = "ssh"
|
||||
args = ["dev", "cd /tmp && true"]
|
||||
"#,
|
||||
)
|
||||
.expect("write environments.toml");
|
||||
|
||||
let runtime_paths = codex_exec_server::ExecServerRuntimePaths::new(
|
||||
std::env::current_exe().expect("current exe path"),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
)
|
||||
.expect("runtime paths");
|
||||
let environment_manager = Arc::new(
|
||||
codex_exec_server::EnvironmentManager::from_codex_home(
|
||||
config.codex_home.clone(),
|
||||
runtime_paths,
|
||||
)
|
||||
.await
|
||||
.expect("environment manager"),
|
||||
);
|
||||
assert_eq!(
|
||||
environment_manager.default_environment_ids(),
|
||||
vec!["dev".to_string(), "local".to_string()]
|
||||
);
|
||||
|
||||
let manager = ThreadManager::with_models_provider_and_home_for_tests(
|
||||
CodexAuth::from_api_key("dummy"),
|
||||
config.model_provider.clone(),
|
||||
config.codex_home.to_path_buf(),
|
||||
environment_manager,
|
||||
);
|
||||
|
||||
let thread = manager
|
||||
.start_thread(config)
|
||||
.await
|
||||
.expect("thread should start");
|
||||
|
||||
let prompt_items = crate::prompt_debug::build_prompt_input_from_session(
|
||||
thread.thread.codex.session.as_ref(),
|
||||
Vec::<UserInput>::new(),
|
||||
)
|
||||
.await
|
||||
.expect("prompt input");
|
||||
let environment_context = prompt_items
|
||||
.iter()
|
||||
.filter_map(|item| match item {
|
||||
ResponseItem::Message { content, .. } => Some(content),
|
||||
_ => None,
|
||||
})
|
||||
.flatten()
|
||||
.find_map(|content| match content {
|
||||
ContentItem::InputText { text } if text.contains("<environment_context>") => {
|
||||
Some(text.as_str())
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.expect("environment context prompt item");
|
||||
assert!(environment_context.contains("<environments>"));
|
||||
let cwd = thread.session_configured.cwd.display().to_string();
|
||||
let dev_entry = format!(
|
||||
r#"<environment id="dev">
|
||||
<cwd>{cwd}</cwd>
|
||||
<shell>"#
|
||||
);
|
||||
let local_entry = format!(
|
||||
r#"<environment id="local">
|
||||
<cwd>{cwd}</cwd>
|
||||
<shell>"#
|
||||
);
|
||||
let dev_position = environment_context
|
||||
.find(&dev_entry)
|
||||
.expect("dev environment entry");
|
||||
let local_position = environment_context
|
||||
.find(&local_entry)
|
||||
.expect("local environment entry");
|
||||
assert!(dev_position < local_position);
|
||||
assert!(!environment_context.contains("\n <cwd>"));
|
||||
assert!(!environment_context.contains("\n <shell>"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn start_thread_keeps_internal_threads_hidden_from_normal_lookups() {
|
||||
let temp_dir = tempdir().expect("tempdir");
|
||||
|
||||
@@ -114,6 +114,15 @@ impl EnvironmentManager {
|
||||
Self::from_provider(provider.as_ref(), local_runtime_paths).await
|
||||
}
|
||||
|
||||
/// Builds a manager from the legacy environment-variable provider without
|
||||
/// reading user config files from `CODEX_HOME`.
|
||||
pub async fn from_env(
|
||||
local_runtime_paths: ExecServerRuntimePaths,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
let provider = DefaultEnvironmentProvider::from_env();
|
||||
Self::from_provider(&provider, local_runtime_paths).await
|
||||
}
|
||||
|
||||
async fn from_default_provider_url(
|
||||
exec_server_url: Option<String>,
|
||||
local_runtime_paths: ExecServerRuntimePaths,
|
||||
@@ -147,18 +156,26 @@ impl EnvironmentManager {
|
||||
environments,
|
||||
default,
|
||||
} = snapshot;
|
||||
for id in environments.keys() {
|
||||
let mut environment_map = HashMap::with_capacity(environments.len());
|
||||
for (id, environment) in environments {
|
||||
if id.is_empty() {
|
||||
return Err(ExecServerError::Protocol(
|
||||
"environment id cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
if environment_map
|
||||
.insert(id.clone(), Arc::new(environment))
|
||||
.is_some()
|
||||
{
|
||||
return Err(ExecServerError::Protocol(format!(
|
||||
"environment id `{id}` is duplicated"
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let default_environment = match default {
|
||||
EnvironmentDefault::Disabled => None,
|
||||
EnvironmentDefault::EnvironmentId(environment_id) => {
|
||||
if !environments.contains_key(&environment_id) {
|
||||
if !environment_map.contains_key(&environment_id) {
|
||||
return Err(ExecServerError::Protocol(format!(
|
||||
"default environment `{environment_id}` is not configured"
|
||||
)));
|
||||
@@ -167,14 +184,10 @@ impl EnvironmentManager {
|
||||
}
|
||||
};
|
||||
let local_environment = Arc::new(Environment::local(local_runtime_paths));
|
||||
let environments = environments
|
||||
.into_iter()
|
||||
.map(|(id, environment)| (id, Arc::new(environment)))
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
default_environment,
|
||||
environments,
|
||||
environments: environment_map,
|
||||
local_environment,
|
||||
})
|
||||
}
|
||||
@@ -191,6 +204,22 @@ impl EnvironmentManager {
|
||||
self.default_environment.as_deref()
|
||||
}
|
||||
|
||||
/// Returns the ordered environment ids used for new thread startup.
|
||||
pub fn default_environment_ids(&self) -> Vec<String> {
|
||||
let Some(default_environment_id) = self.default_environment.as_ref() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let mut environment_ids = Vec::with_capacity(self.environments.len());
|
||||
environment_ids.push(default_environment_id.clone());
|
||||
environment_ids.extend(
|
||||
self.environments
|
||||
.keys()
|
||||
.filter(|environment_id| *environment_id != default_environment_id)
|
||||
.cloned(),
|
||||
);
|
||||
environment_ids
|
||||
}
|
||||
|
||||
/// Returns the local environment instance used for internal runtime work.
|
||||
pub fn local_environment(&self) -> Arc<Environment> {
|
||||
Arc::clone(&self.local_environment)
|
||||
@@ -350,7 +379,6 @@ impl Environment {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::Environment;
|
||||
@@ -472,11 +500,11 @@ mod tests {
|
||||
async fn environment_manager_builds_from_provider() {
|
||||
let provider = TestEnvironmentProvider {
|
||||
snapshot: EnvironmentProviderSnapshot {
|
||||
environments: HashMap::from([(
|
||||
environments: vec![(
|
||||
REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
|
||||
.expect("remote environment"),
|
||||
)]),
|
||||
)],
|
||||
default: EnvironmentDefault::EnvironmentId(REMOTE_ENVIRONMENT_ID.to_string()),
|
||||
},
|
||||
};
|
||||
@@ -502,7 +530,7 @@ mod tests {
|
||||
async fn environment_manager_rejects_empty_environment_id() {
|
||||
let provider = TestEnvironmentProvider {
|
||||
snapshot: EnvironmentProviderSnapshot {
|
||||
environments: HashMap::from([("".to_string(), Environment::default_for_tests())]),
|
||||
environments: vec![("".to_string(), Environment::default_for_tests())],
|
||||
default: EnvironmentDefault::Disabled,
|
||||
},
|
||||
};
|
||||
@@ -520,7 +548,7 @@ mod tests {
|
||||
async fn environment_manager_uses_explicit_provider_default() {
|
||||
let provider = TestEnvironmentProvider {
|
||||
snapshot: EnvironmentProviderSnapshot {
|
||||
environments: HashMap::from([
|
||||
environments: vec![
|
||||
(
|
||||
LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
Environment::default_for_tests(),
|
||||
@@ -530,7 +558,7 @@ mod tests {
|
||||
Environment::create_for_tests(Some("ws://127.0.0.1:8765".to_string()))
|
||||
.expect("remote environment"),
|
||||
),
|
||||
]),
|
||||
],
|
||||
default: EnvironmentDefault::EnvironmentId("devbox".to_string()),
|
||||
},
|
||||
};
|
||||
@@ -539,6 +567,10 @@ mod tests {
|
||||
.expect("manager");
|
||||
|
||||
assert_eq!(manager.default_environment_id(), Some("devbox"));
|
||||
assert_eq!(
|
||||
manager.default_environment_ids(),
|
||||
vec!["devbox".to_string(), LOCAL_ENVIRONMENT_ID.to_string()]
|
||||
);
|
||||
assert!(manager.default_environment().expect("default").is_remote());
|
||||
}
|
||||
|
||||
@@ -546,10 +578,10 @@ mod tests {
|
||||
async fn environment_manager_disables_provider_default() {
|
||||
let provider = TestEnvironmentProvider {
|
||||
snapshot: EnvironmentProviderSnapshot {
|
||||
environments: HashMap::from([(
|
||||
environments: vec![(
|
||||
LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
Environment::default_for_tests(),
|
||||
)]),
|
||||
)],
|
||||
default: EnvironmentDefault::Disabled,
|
||||
},
|
||||
};
|
||||
@@ -566,10 +598,10 @@ mod tests {
|
||||
async fn environment_manager_rejects_unknown_provider_default() {
|
||||
let provider = TestEnvironmentProvider {
|
||||
snapshot: EnvironmentProviderSnapshot {
|
||||
environments: HashMap::from([(
|
||||
environments: vec![(
|
||||
LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
Environment::default_for_tests(),
|
||||
)]),
|
||||
)],
|
||||
default: EnvironmentDefault::EnvironmentId("missing".to_string()),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::Environment;
|
||||
@@ -12,9 +10,9 @@ use crate::environment::REMOTE_ENVIRONMENT_ID;
|
||||
/// Lists the concrete environments available to Codex.
|
||||
///
|
||||
/// Implementations own a startup snapshot containing both the available
|
||||
/// environment list and default environment selection. Providers that want the
|
||||
/// local environment to be addressable by id should include it explicitly in
|
||||
/// the returned map.
|
||||
/// environment list in configured order and the default environment
|
||||
/// selection. Providers that want the local environment to be addressable by
|
||||
/// id should include it explicitly in the returned list.
|
||||
#[async_trait]
|
||||
pub trait EnvironmentProvider: Send + Sync {
|
||||
/// Returns the provider-owned environment startup snapshot.
|
||||
@@ -26,7 +24,7 @@ pub trait EnvironmentProvider: Send + Sync {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct EnvironmentProviderSnapshot {
|
||||
pub environments: HashMap<String, Environment>,
|
||||
pub environments: Vec<(String, Environment)>,
|
||||
pub default: EnvironmentDefault,
|
||||
}
|
||||
|
||||
@@ -57,22 +55,25 @@ impl DefaultEnvironmentProvider {
|
||||
&self,
|
||||
local_runtime_paths: &ExecServerRuntimePaths,
|
||||
) -> EnvironmentProviderSnapshot {
|
||||
let mut environments = HashMap::from([(
|
||||
let mut environments = vec![(
|
||||
LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
Environment::local(local_runtime_paths.clone()),
|
||||
)]);
|
||||
)];
|
||||
let (exec_server_url, disabled) = normalize_exec_server_url(self.exec_server_url.clone());
|
||||
|
||||
if let Some(exec_server_url) = exec_server_url {
|
||||
environments.insert(
|
||||
environments.push((
|
||||
REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
Environment::remote_inner(exec_server_url, Some(local_runtime_paths.clone())),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
let has_remote = environments
|
||||
.iter()
|
||||
.any(|(id, _environment)| id == REMOTE_ENVIRONMENT_ID);
|
||||
let default = if disabled {
|
||||
EnvironmentDefault::Disabled
|
||||
} else if environments.contains_key(REMOTE_ENVIRONMENT_ID) {
|
||||
} else if has_remote {
|
||||
EnvironmentDefault::EnvironmentId(REMOTE_ENVIRONMENT_ID.to_string())
|
||||
} else {
|
||||
EnvironmentDefault::EnvironmentId(LOCAL_ENVIRONMENT_ID.to_string())
|
||||
@@ -105,6 +106,8 @@ pub(crate) fn normalize_exec_server_url(exec_server_url: Option<String>) -> (Opt
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use super::*;
|
||||
@@ -126,7 +129,11 @@ mod tests {
|
||||
.snapshot(&runtime_paths)
|
||||
.await
|
||||
.expect("environments");
|
||||
let environments = snapshot.environments;
|
||||
let EnvironmentProviderSnapshot {
|
||||
environments,
|
||||
default,
|
||||
} = snapshot;
|
||||
let environments: HashMap<_, _> = environments.into_iter().collect();
|
||||
|
||||
assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote());
|
||||
assert_eq!(
|
||||
@@ -135,7 +142,7 @@ mod tests {
|
||||
);
|
||||
assert!(!environments.contains_key(REMOTE_ENVIRONMENT_ID));
|
||||
assert_eq!(
|
||||
snapshot.default,
|
||||
default,
|
||||
EnvironmentDefault::EnvironmentId(LOCAL_ENVIRONMENT_ID.to_string())
|
||||
);
|
||||
}
|
||||
@@ -148,12 +155,16 @@ mod tests {
|
||||
.snapshot(&runtime_paths)
|
||||
.await
|
||||
.expect("environments");
|
||||
let environments = snapshot.environments;
|
||||
let EnvironmentProviderSnapshot {
|
||||
environments,
|
||||
default,
|
||||
} = snapshot;
|
||||
let environments: HashMap<_, _> = environments.into_iter().collect();
|
||||
|
||||
assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote());
|
||||
assert!(!environments.contains_key(REMOTE_ENVIRONMENT_ID));
|
||||
assert_eq!(
|
||||
snapshot.default,
|
||||
default,
|
||||
EnvironmentDefault::EnvironmentId(LOCAL_ENVIRONMENT_ID.to_string())
|
||||
);
|
||||
}
|
||||
@@ -166,11 +177,15 @@ mod tests {
|
||||
.snapshot(&runtime_paths)
|
||||
.await
|
||||
.expect("environments");
|
||||
let environments = snapshot.environments;
|
||||
let EnvironmentProviderSnapshot {
|
||||
environments,
|
||||
default,
|
||||
} = snapshot;
|
||||
let environments: HashMap<_, _> = environments.into_iter().collect();
|
||||
|
||||
assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote());
|
||||
assert!(!environments.contains_key(REMOTE_ENVIRONMENT_ID));
|
||||
assert_eq!(snapshot.default, EnvironmentDefault::Disabled);
|
||||
assert_eq!(default, EnvironmentDefault::Disabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -181,7 +196,11 @@ mod tests {
|
||||
.snapshot(&runtime_paths)
|
||||
.await
|
||||
.expect("environments");
|
||||
let environments = snapshot.environments;
|
||||
let EnvironmentProviderSnapshot {
|
||||
environments,
|
||||
default,
|
||||
} = snapshot;
|
||||
let environments: HashMap<_, _> = environments.into_iter().collect();
|
||||
|
||||
assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote());
|
||||
let remote_environment = &environments[REMOTE_ENVIRONMENT_ID];
|
||||
@@ -191,7 +210,7 @@ mod tests {
|
||||
Some("ws://127.0.0.1:8765")
|
||||
);
|
||||
assert_eq!(
|
||||
snapshot.default,
|
||||
default,
|
||||
EnvironmentDefault::EnvironmentId(REMOTE_ENVIRONMENT_ID.to_string())
|
||||
);
|
||||
}
|
||||
@@ -200,13 +219,14 @@ mod tests {
|
||||
async fn default_provider_normalizes_exec_server_url() {
|
||||
let provider = DefaultEnvironmentProvider::new(Some(" ws://127.0.0.1:8765 ".to_string()));
|
||||
let runtime_paths = test_runtime_paths();
|
||||
let environments = provider
|
||||
let snapshot = provider
|
||||
.snapshot(&runtime_paths)
|
||||
.await
|
||||
.expect("environments");
|
||||
let environments: HashMap<_, _> = snapshot.environments.into_iter().collect();
|
||||
|
||||
assert_eq!(
|
||||
environments.environments[REMOTE_ENVIRONMENT_ID].exec_server_url(),
|
||||
environments[REMOTE_ENVIRONMENT_ID].exec_server_url(),
|
||||
Some("ws://127.0.0.1:8765")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ struct EnvironmentToml {
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
struct TomlEnvironmentProvider {
|
||||
default: EnvironmentDefault,
|
||||
environments: HashMap<String, ExecServerTransportParams>,
|
||||
environments: Vec<(String, ExecServerTransportParams)>,
|
||||
}
|
||||
|
||||
impl TomlEnvironmentProvider {
|
||||
@@ -58,7 +58,7 @@ impl TomlEnvironmentProvider {
|
||||
config_dir: Option<&Path>,
|
||||
) -> Result<Self, ExecServerError> {
|
||||
let mut ids = HashSet::from([LOCAL_ENVIRONMENT_ID.to_string()]);
|
||||
let mut environments = HashMap::with_capacity(config.environments.len());
|
||||
let mut environments = Vec::with_capacity(config.environments.len());
|
||||
for item in config.environments {
|
||||
let (id, transport) = parse_environment_toml(item, config_dir)?;
|
||||
if !ids.insert(id.clone()) {
|
||||
@@ -66,7 +66,7 @@ impl TomlEnvironmentProvider {
|
||||
"environment id `{id}` is duplicated"
|
||||
)));
|
||||
}
|
||||
environments.insert(id, transport);
|
||||
environments.push((id, transport));
|
||||
}
|
||||
let default = normalize_default_environment_id(config.default.as_deref(), &ids)?;
|
||||
Ok(Self {
|
||||
@@ -82,19 +82,19 @@ impl EnvironmentProvider for TomlEnvironmentProvider {
|
||||
&self,
|
||||
local_runtime_paths: &ExecServerRuntimePaths,
|
||||
) -> Result<EnvironmentProviderSnapshot, ExecServerError> {
|
||||
let mut environments = HashMap::from([(
|
||||
let mut environments = Vec::with_capacity(self.environments.len() + 1);
|
||||
environments.push((
|
||||
LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
Environment::local(local_runtime_paths.clone()),
|
||||
)]);
|
||||
|
||||
));
|
||||
for (id, transport_params) in &self.environments {
|
||||
environments.insert(
|
||||
environments.push((
|
||||
id.clone(),
|
||||
Environment::remote_with_transport(
|
||||
transport_params.clone(),
|
||||
Some(local_runtime_paths.clone()),
|
||||
),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
Ok(EnvironmentProviderSnapshot {
|
||||
@@ -302,15 +302,6 @@ mod tests {
|
||||
|
||||
#[tokio::test]
|
||||
async fn toml_provider_adds_implicit_local_and_configured_environments() {
|
||||
let ssh_transport = ExecServerTransportParams::StdioCommand(StdioExecServerCommand {
|
||||
program: "ssh".to_string(),
|
||||
args: vec![
|
||||
"dev".to_string(),
|
||||
"codex exec-server --listen stdio".to_string(),
|
||||
],
|
||||
env: HashMap::from([("CODEX_LOG".to_string(), "debug".to_string())]),
|
||||
cwd: None,
|
||||
});
|
||||
let provider = TomlEnvironmentProvider::new(EnvironmentsToml {
|
||||
default: Some("ssh-dev".to_string()),
|
||||
environments: vec![
|
||||
@@ -345,13 +336,21 @@ mod tests {
|
||||
environments,
|
||||
default,
|
||||
} = snapshot;
|
||||
let environment_ids: Vec<_> = environments
|
||||
.iter()
|
||||
.map(|(id, _environment)| id.as_str())
|
||||
.collect();
|
||||
assert_eq!(
|
||||
environment_ids,
|
||||
vec![LOCAL_ENVIRONMENT_ID, "devbox", "ssh-dev"]
|
||||
);
|
||||
let environments: HashMap<_, _> = environments.into_iter().collect();
|
||||
|
||||
assert!(!environments[LOCAL_ENVIRONMENT_ID].is_remote());
|
||||
assert_eq!(
|
||||
environments["devbox"].exec_server_url(),
|
||||
Some("ws://127.0.0.1:8765")
|
||||
);
|
||||
assert_eq!(provider.environments["ssh-dev"], ssh_transport);
|
||||
assert!(environments["ssh-dev"].is_remote());
|
||||
assert_eq!(environments["ssh-dev"].exec_server_url(), None);
|
||||
assert_eq!(
|
||||
@@ -483,7 +482,7 @@ mod tests {
|
||||
.expect("provider");
|
||||
|
||||
assert_eq!(
|
||||
provider.environments["ssh-dev"],
|
||||
provider.environments[0].1,
|
||||
ExecServerTransportParams::StdioCommand(StdioExecServerCommand {
|
||||
program: "ssh".to_string(),
|
||||
args: Vec::new(),
|
||||
@@ -686,8 +685,13 @@ default = "none"
|
||||
.snapshot(&test_runtime_paths())
|
||||
.await
|
||||
.expect("environments");
|
||||
let environment_ids: Vec<_> = snapshot
|
||||
.environments
|
||||
.into_iter()
|
||||
.map(|(id, _environment)| id)
|
||||
.collect();
|
||||
|
||||
assert!(snapshot.environments.contains_key(LOCAL_ENVIRONMENT_ID));
|
||||
assert!(environment_ids.contains(&LOCAL_ENVIRONMENT_ID.to_string()));
|
||||
assert_eq!(snapshot.default, EnvironmentDefault::Disabled);
|
||||
}
|
||||
|
||||
@@ -702,7 +706,12 @@ default = "none"
|
||||
.snapshot(&test_runtime_paths())
|
||||
.await
|
||||
.expect("environments");
|
||||
let environment_ids: Vec<_> = snapshot
|
||||
.environments
|
||||
.into_iter()
|
||||
.map(|(id, _environment)| id)
|
||||
.collect();
|
||||
|
||||
assert!(snapshot.environments.contains_key(LOCAL_ENVIRONMENT_ID));
|
||||
assert!(environment_ids.contains(&LOCAL_ENVIRONMENT_ID.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ pub use codex_file_system::RemoveOptions;
|
||||
pub use environment::CODEX_EXEC_SERVER_URL_ENV_VAR;
|
||||
pub use environment::Environment;
|
||||
pub use environment::EnvironmentManager;
|
||||
pub use environment::EnvironmentManagerArgs;
|
||||
pub use environment::LOCAL_ENVIRONMENT_ID;
|
||||
pub use environment::REMOTE_ENVIRONMENT_ID;
|
||||
pub use environment_provider::DefaultEnvironmentProvider;
|
||||
|
||||
@@ -15,7 +15,6 @@ pub use cli::Command;
|
||||
pub use cli::ReviewArgs;
|
||||
use codex_app_server_client::DEFAULT_IN_PROCESS_CHANNEL_CAPACITY;
|
||||
use codex_app_server_client::EnvironmentManager;
|
||||
use codex_app_server_client::EnvironmentManagerArgs;
|
||||
use codex_app_server_client::ExecServerRuntimePaths;
|
||||
use codex_app_server_client::InProcessAppServerClient;
|
||||
use codex_app_server_client::InProcessClientStartArgs;
|
||||
@@ -509,6 +508,11 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
let state_db = codex_core::init_state_db(&config).await;
|
||||
let environment_manager = if run_loader_overrides.ignore_user_config {
|
||||
EnvironmentManager::from_env(local_runtime_paths).await?
|
||||
} else {
|
||||
EnvironmentManager::from_codex_home(config.codex_home.clone(), local_runtime_paths).await?
|
||||
};
|
||||
let in_process_start_args = InProcessClientStartArgs {
|
||||
arg0_paths,
|
||||
config: std::sync::Arc::new(config.clone()),
|
||||
@@ -518,9 +522,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result
|
||||
feedback: CodexFeedback::new(),
|
||||
log_db: None,
|
||||
state_db: state_db.clone(),
|
||||
environment_manager: std::sync::Arc::new(
|
||||
EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)).await,
|
||||
),
|
||||
environment_manager: std::sync::Arc::new(environment_manager),
|
||||
config_warnings,
|
||||
session_source: SessionSource::Exec,
|
||||
enable_codex_api_key_env: true,
|
||||
|
||||
@@ -9,7 +9,6 @@ use codex_arg0::Arg0DispatchPaths;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::resolve_installation_id;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::EnvironmentManagerArgs;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_login::default_client::set_default_client_residency_requirement;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
@@ -61,15 +60,6 @@ pub async fn run_main(
|
||||
arg0_paths: Arg0DispatchPaths,
|
||||
cli_config_overrides: CliConfigOverrides,
|
||||
) -> IoResult<()> {
|
||||
let environment_manager = Arc::new(
|
||||
EnvironmentManager::new(EnvironmentManagerArgs::new(
|
||||
ExecServerRuntimePaths::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?,
|
||||
))
|
||||
.await,
|
||||
);
|
||||
// Parse CLI overrides once and derive the base Config eagerly so later
|
||||
// components do not need to work with raw TOML values.
|
||||
let cli_kv_overrides = cli_config_overrides.parse_overrides().map_err(|e| {
|
||||
@@ -85,6 +75,17 @@ pub async fn run_main(
|
||||
})?;
|
||||
set_default_client_residency_requirement(config.enforce_residency.value());
|
||||
let state_db = codex_core::init_state_db(&config).await;
|
||||
let environment_manager = Arc::new(
|
||||
EnvironmentManager::from_codex_home(
|
||||
config.codex_home.clone(),
|
||||
ExecServerRuntimePaths::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?,
|
||||
)
|
||||
.await
|
||||
.map_err(std::io::Error::other)?,
|
||||
);
|
||||
|
||||
let otel = codex_core::otel_init::build_provider(
|
||||
&config,
|
||||
|
||||
@@ -20,7 +20,6 @@ use codex_core_api::Config;
|
||||
use codex_core_api::ConfigLayerStack;
|
||||
use codex_core_api::Constrained;
|
||||
use codex_core_api::EnvironmentManager;
|
||||
use codex_core_api::EnvironmentManagerArgs;
|
||||
use codex_core_api::EventMsg;
|
||||
use codex_core_api::ExecServerRuntimePaths;
|
||||
use codex_core_api::Features;
|
||||
@@ -114,8 +113,9 @@ async fn run_main(arg0_paths: Arg0DispatchPaths) -> anyhow::Result<()> {
|
||||
config.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
let thread_store = thread_store_from_config(&config, state_db.clone());
|
||||
let environment_manager =
|
||||
Arc::new(EnvironmentManager::new(EnvironmentManagerArgs::new(local_runtime_paths)).await);
|
||||
let environment_manager = Arc::new(
|
||||
EnvironmentManager::from_codex_home(config.codex_home.clone(), local_runtime_paths).await?,
|
||||
);
|
||||
let installation_id = resolve_installation_id(&config.codex_home).await?;
|
||||
let thread_manager = ThreadManager::new(
|
||||
&config,
|
||||
|
||||
@@ -575,6 +575,7 @@ impl App {
|
||||
) -> crate::chatwidget::ChatWidgetInit {
|
||||
crate::chatwidget::ChatWidgetInit {
|
||||
config: cfg,
|
||||
environment_manager: self.environment_manager.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: self.app_event_tx.clone(),
|
||||
workspace_command_runner: self.workspace_command_runner.clone(),
|
||||
@@ -739,6 +740,7 @@ impl App {
|
||||
.await;
|
||||
let init = crate::chatwidget::ChatWidgetInit {
|
||||
config: config.clone(),
|
||||
environment_manager: environment_manager.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: app_event_tx.clone(),
|
||||
workspace_command_runner: Some(workspace_command_runner.clone()),
|
||||
@@ -775,6 +777,7 @@ impl App {
|
||||
})?;
|
||||
let init = crate::chatwidget::ChatWidgetInit {
|
||||
config: config.clone(),
|
||||
environment_manager: environment_manager.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: app_event_tx.clone(),
|
||||
workspace_command_runner: Some(workspace_command_runner.clone()),
|
||||
@@ -816,6 +819,7 @@ impl App {
|
||||
})?;
|
||||
let init = crate::chatwidget::ChatWidgetInit {
|
||||
config: config.clone(),
|
||||
environment_manager: environment_manager.clone(),
|
||||
frame_requester: tui.frame_requester(),
|
||||
app_event_tx: app_event_tx.clone(),
|
||||
workspace_command_runner: Some(workspace_command_runner.clone()),
|
||||
|
||||
@@ -435,6 +435,7 @@ async fn enqueue_primary_thread_session_replays_turns_before_initial_prompt_subm
|
||||
let model = crate::legacy_core::test_support::get_model_offline(config.model.as_deref());
|
||||
app.chat_widget = ChatWidget::new_with_app_event(ChatWidgetInit {
|
||||
config,
|
||||
environment_manager: app.environment_manager.clone(),
|
||||
frame_requester: crate::tui::FrameRequester::test_dummy(),
|
||||
app_event_tx: app.app_event_tx.clone(),
|
||||
workspace_command_runner: None,
|
||||
@@ -4834,6 +4835,7 @@ async fn replace_chat_widget_reseeds_collab_agent_metadata_for_replay() {
|
||||
|
||||
let replacement = ChatWidget::new_with_app_event(ChatWidgetInit {
|
||||
config: app.config.clone(),
|
||||
environment_manager: app.environment_manager.clone(),
|
||||
frame_requester: crate::tui::FrameRequester::test_dummy(),
|
||||
app_event_tx: app.app_event_tx.clone(),
|
||||
workspace_command_runner: None,
|
||||
|
||||
@@ -129,6 +129,7 @@ use codex_config::types::ApprovalsReviewer;
|
||||
use codex_config::types::Notifications;
|
||||
use codex_config::types::WindowsSandboxModeToml;
|
||||
use codex_core_skills::model::SkillMetadata;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_features::FEATURES;
|
||||
use codex_features::Feature;
|
||||
#[cfg(test)]
|
||||
@@ -558,6 +559,7 @@ pub(crate) fn get_limits_duration(windows_minutes: i64) -> String {
|
||||
/// Common initialization parameters shared by all `ChatWidget` constructors.
|
||||
pub(crate) struct ChatWidgetInit {
|
||||
pub(crate) config: Config,
|
||||
pub(crate) environment_manager: Arc<EnvironmentManager>,
|
||||
pub(crate) frame_requester: FrameRequester,
|
||||
pub(crate) app_event_tx: AppEventSender,
|
||||
/// App-server-backed runner used by status surfaces for workspace metadata probes.
|
||||
@@ -759,6 +761,7 @@ pub(crate) struct ChatWidget {
|
||||
/// where the overlay may briefly treat new tail content as already cached.
|
||||
active_cell_revision: u64,
|
||||
config: Config,
|
||||
environment_manager: Arc<EnvironmentManager>,
|
||||
raw_output_mode: bool,
|
||||
/// Runtime value resolved by core. `config.service_tier` remains the explicit user choice.
|
||||
effective_service_tier: Option<String>,
|
||||
@@ -4839,6 +4842,7 @@ impl ChatWidget {
|
||||
fn new_with_op_target(common: ChatWidgetInit, codex_op_target: CodexOpTarget) -> Self {
|
||||
let ChatWidgetInit {
|
||||
config,
|
||||
environment_manager,
|
||||
frame_requester,
|
||||
app_event_tx,
|
||||
workspace_command_runner,
|
||||
@@ -4924,6 +4928,7 @@ impl ChatWidget {
|
||||
active_cell_revision: 0,
|
||||
raw_output_mode: config.tui_raw_output_mode,
|
||||
config,
|
||||
environment_manager,
|
||||
effective_service_tier,
|
||||
skills_all: Vec::new(),
|
||||
skills_initial_state: None,
|
||||
@@ -7148,12 +7153,14 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
let config = self.config.clone();
|
||||
let environment_manager = Arc::clone(&self.environment_manager);
|
||||
let app_event_tx = self.app_event_tx.clone();
|
||||
tokio::spawn(async move {
|
||||
let accessible_result =
|
||||
match connectors::list_accessible_connectors_from_mcp_tools_with_options_and_status(
|
||||
match connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager(
|
||||
&config,
|
||||
force_refetch,
|
||||
&environment_manager,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -192,6 +192,7 @@ pub(super) async fn make_chatwidget_manual(
|
||||
raw_output_mode: cfg.tui_raw_output_mode,
|
||||
config: cfg,
|
||||
effective_service_tier,
|
||||
environment_manager: Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
has_chatgpt_account: false,
|
||||
|
||||
@@ -1536,6 +1536,7 @@ async fn make_startup_chat_with_cli_overrides(
|
||||
let session_telemetry = test_session_telemetry(&cfg, resolved_model.as_str());
|
||||
let init = ChatWidgetInit {
|
||||
config: cfg.clone(),
|
||||
environment_manager: Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
|
||||
frame_requester: FrameRequester::test_dummy(),
|
||||
app_event_tx: AppEventSender::new(unbounded_channel::<AppEvent>().0),
|
||||
workspace_command_runner: None,
|
||||
|
||||
@@ -70,6 +70,7 @@ async fn experimental_mode_plan_is_ignored_on_startup() {
|
||||
let session_telemetry = test_session_telemetry(&cfg, resolved_model.as_str());
|
||||
let init = ChatWidgetInit {
|
||||
config: cfg.clone(),
|
||||
environment_manager: Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
|
||||
frame_requester: FrameRequester::test_dummy(),
|
||||
app_event_tx: AppEventSender::new(unbounded_channel::<AppEvent>().0),
|
||||
workspace_command_runner: None,
|
||||
|
||||
@@ -246,6 +246,7 @@ async fn helpers_are_available_and_do_not_panic() {
|
||||
let session_telemetry = test_session_telemetry(&cfg, resolved_model.as_str());
|
||||
let init = ChatWidgetInit {
|
||||
config: cfg.clone(),
|
||||
environment_manager: Arc::new(codex_exec_server::EnvironmentManager::default_for_tests()),
|
||||
frame_requester: FrameRequester::test_dummy(),
|
||||
app_event_tx: tx,
|
||||
workspace_command_runner: None,
|
||||
|
||||
+27
-16
@@ -8,7 +8,7 @@ use crate::legacy_core::config::Config;
|
||||
use crate::legacy_core::config::ConfigBuilder;
|
||||
use crate::legacy_core::config::ConfigOverrides;
|
||||
use crate::legacy_core::config::find_codex_home;
|
||||
use crate::legacy_core::config::load_config_as_toml_with_cli_overrides;
|
||||
use crate::legacy_core::config::load_config_as_toml_with_cli_and_loader_overrides;
|
||||
use crate::legacy_core::config::resolve_oss_provider;
|
||||
use crate::legacy_core::format_exec_policy_error_with_source;
|
||||
use crate::legacy_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
@@ -40,7 +40,6 @@ use codex_config::ConfigLoadError;
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_config::format_config_error_with_source;
|
||||
use codex_exec_server::EnvironmentManager;
|
||||
use codex_exec_server::EnvironmentManagerArgs;
|
||||
use codex_exec_server::ExecServerRuntimePaths;
|
||||
use codex_login::AuthConfig;
|
||||
use codex_login::default_client::set_default_client_residency_requirement;
|
||||
@@ -661,10 +660,10 @@ fn config_cwd_for_app_server_target(
|
||||
app_server_target: &AppServerTarget,
|
||||
environment_manager: &EnvironmentManager,
|
||||
) -> std::io::Result<Option<AbsolutePathBuf>> {
|
||||
if environment_manager
|
||||
.default_environment()
|
||||
.is_some_and(|environment| environment.is_remote())
|
||||
|| matches!(app_server_target, AppServerTarget::Remote { .. })
|
||||
if matches!(app_server_target, AppServerTarget::Remote { .. })
|
||||
|| environment_manager
|
||||
.default_environment()
|
||||
.is_some_and(|environment| environment.is_remote())
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
@@ -678,6 +677,14 @@ fn config_cwd_for_app_server_target(
|
||||
Ok(Some(cwd))
|
||||
}
|
||||
|
||||
fn should_load_configured_environments(
|
||||
loader_overrides: &LoaderOverrides,
|
||||
app_server_target: &AppServerTarget,
|
||||
) -> bool {
|
||||
!loader_overrides.ignore_user_config
|
||||
&& !matches!(app_server_target, AppServerTarget::Remote { .. })
|
||||
}
|
||||
|
||||
fn latest_session_cwd_filter<'a>(
|
||||
remote_mode: bool,
|
||||
remote_cwd_override: Option<&'a Path>,
|
||||
@@ -761,24 +768,28 @@ pub async fn run_main(
|
||||
}
|
||||
};
|
||||
|
||||
let environment_manager = Arc::new(
|
||||
EnvironmentManager::new(EnvironmentManagerArgs::new(
|
||||
ExecServerRuntimePaths::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?,
|
||||
))
|
||||
.await,
|
||||
);
|
||||
let local_runtime_paths = ExecServerRuntimePaths::from_optional_paths(
|
||||
arg0_paths.codex_self_exe.clone(),
|
||||
arg0_paths.codex_linux_sandbox_exe.clone(),
|
||||
)?;
|
||||
let environment_manager =
|
||||
if should_load_configured_environments(&loader_overrides, &app_server_target) {
|
||||
EnvironmentManager::from_codex_home(codex_home.clone(), local_runtime_paths).await
|
||||
} else {
|
||||
EnvironmentManager::from_env(local_runtime_paths).await
|
||||
}
|
||||
.map(Arc::new)
|
||||
.map_err(std::io::Error::other)?;
|
||||
let cwd = cli.cwd.clone();
|
||||
let config_cwd =
|
||||
config_cwd_for_app_server_target(cwd.as_deref(), &app_server_target, &environment_manager)?;
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
let config_toml = match load_config_as_toml_with_cli_overrides(
|
||||
let config_toml = match load_config_as_toml_with_cli_and_loader_overrides(
|
||||
&codex_home,
|
||||
config_cwd.as_ref(),
|
||||
cli_kv_overrides.clone(),
|
||||
loader_overrides.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user