mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
Add executor-local config reads to the exec server (#37408)
## What changed - Add the `environmentConfig/read` RPC for selecting literal TOML paths from executor-local config and requirements layers. - Return layer precedence, cloud insertion points, source and base-directory metadata, and executor home and hostname context without normalizing path-bearing values. - Expose the operation through both remote clients and local `Environment` instances, with invalid selectors reported as invalid parameters. ## Testing - Cover projected remote config reads and rejection of empty selectors. GitOrigin-RevId: fee15bf833de6bcb2058d405ed5b1d5d928d7218
This commit is contained in:
@@ -45,6 +45,7 @@ use crate::process::ExecProcessEventReceiver;
|
||||
use crate::protocol::CAPABILITY_ROOTS_DISCOVER_METHOD;
|
||||
use crate::protocol::CapabilityRootsDiscoverParams;
|
||||
use crate::protocol::CapabilityRootsDiscoverResponse;
|
||||
use crate::protocol::ENVIRONMENT_CONFIG_READ_METHOD;
|
||||
use crate::protocol::ENVIRONMENT_INFO_METHOD;
|
||||
use crate::protocol::ENVIRONMENT_STATUS_METHOD;
|
||||
use crate::protocol::EXEC_CLOSED_METHOD;
|
||||
@@ -55,6 +56,8 @@ use crate::protocol::EXEC_READ_METHOD;
|
||||
use crate::protocol::EXEC_SIGNAL_METHOD;
|
||||
use crate::protocol::EXEC_TERMINATE_METHOD;
|
||||
use crate::protocol::EXEC_WRITE_METHOD;
|
||||
use crate::protocol::EnvironmentConfigReadParams;
|
||||
use crate::protocol::EnvironmentConfigReadResponse;
|
||||
use crate::protocol::EnvironmentInfo;
|
||||
use crate::protocol::EnvironmentStatus;
|
||||
use crate::protocol::ExecClosedNotification;
|
||||
@@ -736,6 +739,13 @@ impl ExecServerClient {
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn read_environment_config(
|
||||
&self,
|
||||
params: EnvironmentConfigReadParams,
|
||||
) -> Result<EnvironmentConfigReadResponse, ExecServerError> {
|
||||
self.call(ENVIRONMENT_CONFIG_READ_METHOD, ¶ms).await
|
||||
}
|
||||
|
||||
pub async fn environment_status(&self) -> Result<EnvironmentStatus, ExecServerError> {
|
||||
// Health checks only reuse an existing RPC connection and never initiate recovery.
|
||||
let rpc_client = self.rpc_client_without_recovery()?;
|
||||
|
||||
@@ -12,6 +12,8 @@ use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
|
||||
use crate::CapabilityRootsDiscoverParams;
|
||||
use crate::CapabilityRootsDiscoverResponse;
|
||||
use crate::EnvironmentConfigReadParams;
|
||||
use crate::EnvironmentConfigReadResponse;
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecutorFileSystem;
|
||||
@@ -24,6 +26,7 @@ use crate::client_api::DEFAULT_REMOTE_EXEC_SERVER_CONNECT_TIMEOUT;
|
||||
use crate::client_api::ExecServerTransportParams;
|
||||
use crate::environment_bootstrap::PreparedEnvironmentManager;
|
||||
use crate::environment_bootstrap::PreparedEnvironmentSource;
|
||||
use crate::environment_config::read_environment_config;
|
||||
use crate::environment_provider::DefaultEnvironmentProvider;
|
||||
use crate::environment_provider::EnvironmentDefault;
|
||||
use crate::environment_provider::EnvironmentProvider;
|
||||
@@ -881,6 +884,19 @@ impl Environment {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads selected executor-local configuration fields for this environment.
|
||||
pub async fn read_environment_config(
|
||||
&self,
|
||||
params: EnvironmentConfigReadParams,
|
||||
) -> Result<EnvironmentConfigReadResponse, ExecServerError> {
|
||||
match &self.remote_client {
|
||||
Some(client) => client.get().await?.read_environment_config(params).await,
|
||||
None => read_environment_config(self.filesystem.as_ref(), params)
|
||||
.await
|
||||
.map_err(|error| ExecServerError::Protocol(error.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Discovers plugin and skill manifests through the environment's high-level discovery API.
|
||||
pub async fn discover_capability_roots(
|
||||
&self,
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
use codex_config::CONFIG_TOML_FILE;
|
||||
use codex_config::format_config_layer_source;
|
||||
use codex_config::host_name;
|
||||
use codex_config::loader::LocalTomlLayerStack;
|
||||
use codex_config::loader::load_local_config_layers;
|
||||
use codex_exec_server_protocol::EnvironmentConfigLayer;
|
||||
use codex_exec_server_protocol::EnvironmentConfigLayerStack;
|
||||
use codex_exec_server_protocol::EnvironmentConfigReadParams;
|
||||
use codex_exec_server_protocol::EnvironmentConfigReadResponse;
|
||||
use codex_file_system::ExecutorFileSystem;
|
||||
use codex_utils_home_dir::find_codex_home;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub(crate) enum ReadEnvironmentConfigError {
|
||||
#[error("{0}")]
|
||||
InvalidParams(String),
|
||||
#[error("{0}")]
|
||||
Internal(String),
|
||||
}
|
||||
|
||||
pub(crate) async fn read_environment_config(
|
||||
file_system: &dyn ExecutorFileSystem,
|
||||
params: EnvironmentConfigReadParams,
|
||||
) -> Result<EnvironmentConfigReadResponse, ReadEnvironmentConfigError> {
|
||||
validate_paths(¶ms)?;
|
||||
let cwd = params
|
||||
.cwd
|
||||
.to_abs_path()
|
||||
.map_err(|error| ReadEnvironmentConfigError::InvalidParams(error.to_string()))?;
|
||||
let codex_home = find_codex_home().map_err(|error| {
|
||||
ReadEnvironmentConfigError::Internal(format!("failed to find Codex home: {error}"))
|
||||
})?;
|
||||
let layers = load_local_config_layers(file_system, codex_home.as_path(), &cwd)
|
||||
.await
|
||||
.map_err(|error| {
|
||||
ReadEnvironmentConfigError::Internal(format!(
|
||||
"failed to load executor-local config: {error}"
|
||||
))
|
||||
})?
|
||||
.project(¶ms.config_paths, ¶ms.requirements_paths);
|
||||
|
||||
Ok(EnvironmentConfigReadResponse {
|
||||
user_home_dir: dirs::home_dir()
|
||||
.and_then(|home_dir| PathUri::from_host_native_path(home_dir).ok()),
|
||||
codex_home_dir: PathUri::from_abs_path(&codex_home),
|
||||
hostname: host_name(),
|
||||
config: serialize_layer_stack(layers.config, |source| {
|
||||
format_config_layer_source(source, CONFIG_TOML_FILE)
|
||||
})?,
|
||||
requirements: serialize_layer_stack(layers.requirements, ToString::to_string)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn validate_paths(params: &EnvironmentConfigReadParams) -> Result<(), ReadEnvironmentConfigError> {
|
||||
if params.config_paths.is_empty() && params.requirements_paths.is_empty() {
|
||||
return Err(ReadEnvironmentConfigError::InvalidParams(
|
||||
"at least one config or requirements path is required".to_string(),
|
||||
));
|
||||
}
|
||||
if params
|
||||
.config_paths
|
||||
.iter()
|
||||
.chain(¶ms.requirements_paths)
|
||||
.any(Vec::is_empty)
|
||||
{
|
||||
return Err(ReadEnvironmentConfigError::InvalidParams(
|
||||
"TOML paths must contain at least one key segment".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_layer_stack<S>(
|
||||
stack: LocalTomlLayerStack<S>,
|
||||
source_name: impl Fn(&S) -> String,
|
||||
) -> Result<EnvironmentConfigLayerStack, ReadEnvironmentConfigError> {
|
||||
let layers = stack
|
||||
.layers
|
||||
.into_iter()
|
||||
.map(|layer| {
|
||||
let toml = toml::to_string(&layer.toml).map_err(|error| {
|
||||
ReadEnvironmentConfigError::Internal(format!(
|
||||
"failed to serialize executor-local config: {error}"
|
||||
))
|
||||
})?;
|
||||
Ok(EnvironmentConfigLayer {
|
||||
source: source_name(&layer.source),
|
||||
base_dir: PathUri::from_abs_path(&layer.base_dir),
|
||||
toml,
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, ReadEnvironmentConfigError>>()?;
|
||||
Ok(EnvironmentConfigLayerStack {
|
||||
layers,
|
||||
cloud_insertion_index: stack.cloud_insertion_index,
|
||||
})
|
||||
}
|
||||
@@ -7,6 +7,7 @@ mod client_transport;
|
||||
mod connection;
|
||||
mod environment;
|
||||
mod environment_bootstrap;
|
||||
mod environment_config;
|
||||
mod environment_provider;
|
||||
mod environment_registry;
|
||||
mod environment_toml;
|
||||
@@ -123,6 +124,10 @@ pub use protocol::CapabilityTextFile;
|
||||
pub use protocol::DiscoveredPluginFiles;
|
||||
pub use protocol::DiscoveredSkillFiles;
|
||||
pub use protocol::EnvironmentCapabilities;
|
||||
pub use protocol::EnvironmentConfigLayer;
|
||||
pub use protocol::EnvironmentConfigLayerStack;
|
||||
pub use protocol::EnvironmentConfigReadParams;
|
||||
pub use protocol::EnvironmentConfigReadResponse;
|
||||
pub use protocol::EnvironmentInfo;
|
||||
pub use protocol::EnvironmentStatus;
|
||||
pub use protocol::EnvironmentStatusKind;
|
||||
|
||||
@@ -16,8 +16,12 @@ use crate::ExecServerRuntimePaths;
|
||||
use crate::client::http_client::PendingRouteAwareHttpBodyStream;
|
||||
use crate::client::http_client::RouteAwareHttpClient;
|
||||
use crate::client::http_client::RouteAwareHttpRequestRunner;
|
||||
use crate::environment_config::ReadEnvironmentConfigError;
|
||||
use crate::environment_config::read_environment_config;
|
||||
use crate::protocol::CapabilityRootsDiscoverParams;
|
||||
use crate::protocol::CapabilityRootsDiscoverResponse;
|
||||
use crate::protocol::EnvironmentConfigReadParams;
|
||||
use crate::protocol::EnvironmentConfigReadResponse;
|
||||
use crate::protocol::EnvironmentInfo;
|
||||
use crate::protocol::EnvironmentStatus;
|
||||
use crate::protocol::EnvironmentStatusKind;
|
||||
@@ -175,6 +179,19 @@ impl ExecServerHandler {
|
||||
Ok(EnvironmentInfo::local())
|
||||
}
|
||||
|
||||
pub(crate) async fn environment_config_read(
|
||||
&self,
|
||||
params: EnvironmentConfigReadParams,
|
||||
) -> Result<EnvironmentConfigReadResponse, JSONRPCErrorError> {
|
||||
self.require_initialized_for("environment config")?;
|
||||
read_environment_config(crate::LOCAL_FS.as_ref(), params)
|
||||
.await
|
||||
.map_err(|error| match error {
|
||||
ReadEnvironmentConfigError::InvalidParams(message) => invalid_params(message),
|
||||
ReadEnvironmentConfigError::Internal(message) => internal_error(message),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn environment_status(&self) -> Result<EnvironmentStatus, JSONRPCErrorError> {
|
||||
self.require_initialized_for("environment status")?;
|
||||
Ok(EnvironmentStatus {
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use crate::protocol::CAPABILITY_ROOTS_DISCOVER_METHOD;
|
||||
use crate::protocol::CapabilityRootsDiscoverParams;
|
||||
use crate::protocol::ENVIRONMENT_CONFIG_READ_METHOD;
|
||||
use crate::protocol::ENVIRONMENT_INFO_METHOD;
|
||||
use crate::protocol::ENVIRONMENT_STATUS_METHOD;
|
||||
use crate::protocol::EXEC_METHOD;
|
||||
@@ -9,6 +10,7 @@ use crate::protocol::EXEC_READ_METHOD;
|
||||
use crate::protocol::EXEC_SIGNAL_METHOD;
|
||||
use crate::protocol::EXEC_TERMINATE_METHOD;
|
||||
use crate::protocol::EXEC_WRITE_METHOD;
|
||||
use crate::protocol::EnvironmentConfigReadParams;
|
||||
use crate::protocol::ExecParams;
|
||||
use crate::protocol::FS_CANONICALIZE_METHOD;
|
||||
use crate::protocol::FS_CLOSE_METHOD;
|
||||
@@ -74,6 +76,12 @@ pub(crate) fn build_router() -> RpcRouter<ExecServerHandler> {
|
||||
ENVIRONMENT_INFO_METHOD,
|
||||
|handler: Arc<ExecServerHandler>, _params: ()| async move { handler.environment_info() },
|
||||
);
|
||||
router.request(
|
||||
ENVIRONMENT_CONFIG_READ_METHOD,
|
||||
|handler: Arc<ExecServerHandler>, params: EnvironmentConfigReadParams| async move {
|
||||
handler.environment_config_read(params).await
|
||||
},
|
||||
);
|
||||
router.request(
|
||||
ENVIRONMENT_STATUS_METHOD,
|
||||
|handler: Arc<ExecServerHandler>, _params: ()| async move { handler.environment_status() },
|
||||
|
||||
Reference in New Issue
Block a user