Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 51 additions & 212 deletions codex-rs/exec-server/src/client.rs

Large diffs are not rendered by default.

200 changes: 0 additions & 200 deletions codex-rs/exec-server/src/client/local_backend.rs

This file was deleted.

10 changes: 0 additions & 10 deletions codex-rs/exec-server/src/client_api.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
use std::time::Duration;

use crate::protocol::ExecExitedNotification;
use crate::protocol::ExecOutputDeltaNotification;

/// Connection options for any exec-server client transport.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExecServerClientConnectOptions {
Expand All @@ -18,10 +15,3 @@ pub struct RemoteExecServerConnectArgs {
pub connect_timeout: Duration,
pub initialize_timeout: Duration,
}

/// Connection-level server events.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecServerEvent {
OutputDelta(ExecOutputDeltaNotification),
Exited(ExecExitedNotification),
}
114 changes: 92 additions & 22 deletions codex-rs/exec-server/src/environment.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
use std::sync::Arc;

use crate::ExecServerClient;
use crate::ExecServerError;
use crate::RemoteExecServerConnectArgs;
use crate::file_system::ExecutorFileSystem;
use crate::local_file_system::LocalFileSystem;
use crate::local_process::LocalProcess;
use crate::process::ExecProcess;
use crate::remote_file_system::RemoteFileSystem;
use std::sync::Arc;
use crate::remote_process::RemoteProcess;

#[derive(Clone, Default)]
pub trait ExecutorEnvironment: Send + Sync {
fn get_executor(&self) -> Arc<dyn ExecProcess>;
}

#[derive(Clone)]
pub struct Environment {
experimental_exec_server_url: Option<String>,
remote_exec_server_client: Option<ExecServerClient>,
executor: Arc<dyn ExecProcess>,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we Arc the Environment instead?

}

impl Default for Environment {
fn default() -> Self {
let local_process = LocalProcess::default();
if let Err(err) = local_process.initialize() {
panic!("default local process initialization should succeed: {err:?}");
}
if let Err(err) = local_process.initialized() {
panic!("default local process should accept initialized notification: {err}");
}

Self {
experimental_exec_server_url: None,
remote_exec_server_client: None,
executor: Arc::new(local_process),
}
}
}

impl std::fmt::Debug for Environment {
Expand All @@ -19,43 +46,55 @@ impl std::fmt::Debug for Environment {
"experimental_exec_server_url",
&self.experimental_exec_server_url,
)
.field(
"has_remote_exec_server_client",
&self.remote_exec_server_client.is_some(),
)
.finish()
.finish_non_exhaustive()
}
}

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 remote_exec_server_client = if let Some(url) = &experimental_exec_server_url {
Some(
ExecServerClient::connect_websocket(RemoteExecServerConnectArgs {
websocket_url: url.clone(),
client_name: "codex-environment".to_string(),
connect_timeout: std::time::Duration::from_secs(5),
initialize_timeout: std::time::Duration::from_secs(5),
})
.await?,
)
} else {
None
};

let executor: Arc<dyn ExecProcess> = if let Some(client) = remote_exec_server_client.clone()
{
Arc::new(RemoteProcess::new(client))
} else {
let local_process = LocalProcess::default();
local_process
.initialize()
.map_err(|err| ExecServerError::Protocol(err.message))?;
local_process
.initialized()
.map_err(ExecServerError::Protocol)?;
Arc::new(local_process)
};

Ok(Self {
experimental_exec_server_url,
remote_exec_server_client,
executor,
})
}

pub fn experimental_exec_server_url(&self) -> Option<&str> {
self.experimental_exec_server_url.as_deref()
}

pub fn remote_exec_server_client(&self) -> Option<&ExecServerClient> {
self.remote_exec_server_client.as_ref()
pub fn get_executor(&self) -> Arc<dyn ExecProcess> {
Arc::clone(&self.executor)
}

pub fn get_filesystem(&self) -> Arc<dyn ExecutorFileSystem> {
Expand All @@ -67,6 +106,12 @@ impl Environment {
}
}

impl ExecutorEnvironment for Environment {
fn get_executor(&self) -> Arc<dyn ExecProcess> {
Arc::clone(&self.executor)
}
}

#[cfg(test)]
mod tests {
use super::Environment;
Expand All @@ -77,6 +122,31 @@ mod tests {
let environment = Environment::create(None).await.expect("create environment");

assert_eq!(environment.experimental_exec_server_url(), None);
assert!(environment.remote_exec_server_client().is_none());
assert!(environment.remote_exec_server_client.is_none());
}

#[tokio::test]
async fn default_environment_has_ready_local_executor() {
let environment = Environment::default();

let response = environment
.get_executor()
.start(crate::ExecParams {
process_id: "default-env-proc".to_string(),
argv: vec!["true".to_string()],
cwd: std::env::current_dir().expect("read current dir"),
env: Default::default(),
tty: false,
arg0: None,
})
.await
.expect("start process");

assert_eq!(
response,
crate::ExecResponse {
process_id: "default-env-proc".to_string(),
}
);
}
}
Loading
Loading