Make Environment construction async-only for process setup

Remove sync Default from Environment, construct the local process client
with connect_in_process() in create(None), and switch the fs-only app
server default to a local filesystem constructor.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
starr-openai
2026-03-19 14:56:20 -07:00
parent bdfd5abcca
commit e60eeec4bf
2 changed files with 24 additions and 98 deletions

View File

@@ -34,7 +34,7 @@ pub(crate) struct FsApi {
impl Default for FsApi {
fn default() -> Self {
Self {
file_system: Arc::new(Environment::default().get_filesystem()),
file_system: Arc::new(Environment::local_filesystem()),
}
}
}

View File

@@ -1,51 +1,16 @@
use crate::ExecServerClient;
use crate::ExecServerClientConnectOptions;
use crate::ExecServerError;
use crate::ExecServerEvent;
use crate::ExecProcess;
use crate::ExecutorEnvironment;
use crate::RemoteExecServerConnectArgs;
use crate::protocol::ExecParams;
use crate::protocol::ExecResponse;
use crate::protocol::ReadParams;
use crate::protocol::ReadResponse;
use crate::protocol::TerminateResponse;
use crate::protocol::WriteResponse;
use crate::fs;
use crate::fs::ExecutorFileSystem;
use async_trait::async_trait;
use std::sync::Arc;
use tokio::sync::broadcast;
use tokio::sync::OnceCell;
#[derive(Clone)]
struct LocalExecProcess {
client: Arc<OnceCell<ExecServerClient>>,
}
impl Default for LocalExecProcess {
fn default() -> Self {
Self {
client: Arc::new(OnceCell::new()),
}
}
}
#[derive(Clone)]
pub struct Environment {
experimental_exec_server_url: Option<String>,
remote_exec_server_client: Option<ExecServerClient>,
local_exec_process: LocalExecProcess,
}
impl Default for Environment {
fn default() -> Self {
Self {
experimental_exec_server_url: None,
remote_exec_server_client: None,
local_exec_process: LocalExecProcess::default(),
}
}
exec_server_client: ExecServerClient,
}
impl std::fmt::Debug for Environment {
@@ -57,7 +22,7 @@ impl std::fmt::Debug for Environment {
)
.field(
"has_remote_exec_server_client",
&self.remote_exec_server_client.is_some(),
&self.experimental_exec_server_url.is_some(),
)
.finish()
}
@@ -67,23 +32,20 @@ impl Environment {
pub async fn create(
experimental_exec_server_url: Option<String>,
) -> Result<Self, ExecServerError> {
let remote_exec_server_client =
if let Some(websocket_url) = experimental_exec_server_url.as_deref() {
Some(
ExecServerClient::connect_websocket(RemoteExecServerConnectArgs::new(
websocket_url.to_string(),
"codex-core".to_string(),
))
.await?,
)
} else {
None
};
let exec_server_client = if let Some(websocket_url) = experimental_exec_server_url.as_deref()
{
ExecServerClient::connect_websocket(RemoteExecServerConnectArgs::new(
websocket_url.to_string(),
"codex-core".to_string(),
))
.await?
} else {
ExecServerClient::connect_in_process(ExecServerClientConnectOptions::default()).await?
};
Ok(Self {
experimental_exec_server_url,
remote_exec_server_client,
local_exec_process: LocalExecProcess::default(),
exec_server_client,
})
}
@@ -92,18 +54,24 @@ impl Environment {
}
pub fn remote_exec_server_client(&self) -> Option<&ExecServerClient> {
self.remote_exec_server_client.as_ref()
if self.experimental_exec_server_url.is_some() {
Some(&self.exec_server_client)
} else {
None
}
}
pub fn process(&self) -> &(dyn ExecProcess + '_) {
self.remote_exec_server_client
.as_ref()
.map_or(&self.local_exec_process as &dyn ExecProcess, |client| client)
&self.exec_server_client
}
pub fn get_filesystem(&self) -> impl ExecutorFileSystem + use<> {
fs::LocalFileSystem
}
pub fn local_filesystem() -> impl ExecutorFileSystem + use<> {
fs::LocalFileSystem
}
}
impl ExecutorEnvironment for Environment {
@@ -112,48 +80,6 @@ impl ExecutorEnvironment for Environment {
}
}
#[async_trait]
impl ExecProcess for LocalExecProcess {
async fn start(&self, params: ExecParams) -> Result<ExecResponse, ExecServerError> {
self.client().await?.start(params).await
}
async fn read(&self, params: ReadParams) -> Result<ReadResponse, ExecServerError> {
self.client().await?.read(params).await
}
async fn write(
&self,
process_id: &str,
chunk: Vec<u8>,
) -> Result<WriteResponse, ExecServerError> {
self.client().await?.write(process_id, chunk).await
}
async fn terminate(&self, process_id: &str) -> Result<TerminateResponse, ExecServerError> {
self.client().await?.terminate(process_id).await
}
fn subscribe_events(&self) -> broadcast::Receiver<ExecServerEvent> {
if let Some(client) = self.client.get() {
client.event_receiver()
} else {
let (_tx, rx) = broadcast::channel(1);
rx
}
}
}
impl LocalExecProcess {
async fn client(&self) -> Result<&ExecServerClient, ExecServerError> {
self.client
.get_or_try_init(|| async {
ExecServerClient::connect_in_process(ExecServerClientConnectOptions::default()).await
})
.await;
}
}
#[cfg(test)]
mod tests {
use super::Environment;