mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
feat(app-server) install runtime
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -26,6 +26,8 @@ futures = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["json", "rustls-tls", "stream"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
toml = { workspace = true }
|
||||
tokio = { workspace = true, features = [
|
||||
@@ -43,6 +45,7 @@ tokio-util = { workspace = true, features = ["rt"] }
|
||||
tokio-tungstenite = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
zip = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
@@ -50,6 +53,5 @@ codex-test-binary-support = { workspace = true }
|
||||
ctor = { workspace = true }
|
||||
pretty_assertions = { workspace = true }
|
||||
serial_test = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
test-case = "3.3.1"
|
||||
wiremock = { workspace = true }
|
||||
|
||||
@@ -8,6 +8,8 @@ use std::time::Duration;
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use codex_app_server_protocol::JSONRPCNotification;
|
||||
use codex_app_server_protocol::RuntimeInstallParams;
|
||||
use codex_app_server_protocol::RuntimeInstallResponse;
|
||||
use futures::FutureExt;
|
||||
use futures::future::BoxFuture;
|
||||
use serde_json::Value;
|
||||
@@ -69,6 +71,7 @@ use crate::protocol::INITIALIZED_METHOD;
|
||||
use crate::protocol::InitializeParams;
|
||||
use crate::protocol::InitializeResponse;
|
||||
use crate::protocol::ProcessOutputChunk;
|
||||
use crate::protocol::RUNTIME_INSTALL_METHOD;
|
||||
use crate::protocol::ReadParams;
|
||||
use crate::protocol::ReadResponse;
|
||||
use crate::protocol::TerminateParams;
|
||||
@@ -397,6 +400,13 @@ impl ExecServerClient {
|
||||
self.call(FS_COPY_METHOD, ¶ms).await
|
||||
}
|
||||
|
||||
pub async fn runtime_install(
|
||||
&self,
|
||||
params: RuntimeInstallParams,
|
||||
) -> Result<RuntimeInstallResponse, ExecServerError> {
|
||||
self.call(RUNTIME_INSTALL_METHOD, ¶ms).await
|
||||
}
|
||||
|
||||
pub(crate) async fn register_session(
|
||||
&self,
|
||||
process_id: &ProcessId,
|
||||
|
||||
@@ -2,6 +2,10 @@ use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::RuntimeInstallParams;
|
||||
use codex_app_server_protocol::RuntimeInstallResponse;
|
||||
|
||||
use crate::ExecServerError;
|
||||
use crate::ExecServerRuntimePaths;
|
||||
use crate::ExecutorFileSystem;
|
||||
@@ -20,6 +24,7 @@ use crate::local_process::LocalProcess;
|
||||
use crate::process::ExecBackend;
|
||||
use crate::remote_file_system::RemoteFileSystem;
|
||||
use crate::remote_process::RemoteProcess;
|
||||
use crate::rpc::internal_error;
|
||||
|
||||
pub const CODEX_EXEC_SERVER_URL_ENV_VAR: &str = "CODEX_EXEC_SERVER_URL";
|
||||
|
||||
@@ -294,9 +299,45 @@ pub struct Environment {
|
||||
exec_backend: Arc<dyn ExecBackend>,
|
||||
filesystem: Arc<dyn ExecutorFileSystem>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
runtime_installer: RuntimeInstaller,
|
||||
local_runtime_paths: Option<ExecServerRuntimePaths>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum RuntimeInstaller {
|
||||
Local,
|
||||
Remote(LazyRemoteExecServerClient),
|
||||
}
|
||||
|
||||
impl RuntimeInstaller {
|
||||
async fn install_runtime(
|
||||
&self,
|
||||
params: RuntimeInstallParams,
|
||||
) -> Result<RuntimeInstallResponse, JSONRPCErrorError> {
|
||||
match self {
|
||||
RuntimeInstaller::Local => crate::runtime_install::install_runtime(params).await,
|
||||
RuntimeInstaller::Remote(client) => {
|
||||
let client = client.get().await.map_err(exec_server_error_to_jsonrpc)?;
|
||||
client
|
||||
.runtime_install(params)
|
||||
.await
|
||||
.map_err(exec_server_error_to_jsonrpc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_server_error_to_jsonrpc(err: ExecServerError) -> JSONRPCErrorError {
|
||||
match err {
|
||||
ExecServerError::Server { code, message } => JSONRPCErrorError {
|
||||
code,
|
||||
data: None,
|
||||
message,
|
||||
},
|
||||
_ => internal_error(err.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
impl Environment {
|
||||
/// Builds a test-only local environment without configured sandbox helper paths.
|
||||
pub fn default_for_tests() -> Self {
|
||||
@@ -306,6 +347,7 @@ impl Environment {
|
||||
exec_backend: Arc::new(LocalProcess::default()),
|
||||
filesystem: Arc::new(LocalFileSystem::unsandboxed()),
|
||||
http_client: Arc::new(ReqwestHttpClient),
|
||||
runtime_installer: RuntimeInstaller::Local,
|
||||
local_runtime_paths: None,
|
||||
}
|
||||
}
|
||||
@@ -364,6 +406,7 @@ impl Environment {
|
||||
local_runtime_paths.clone(),
|
||||
)),
|
||||
http_client: Arc::new(ReqwestHttpClient),
|
||||
runtime_installer: RuntimeInstaller::Local,
|
||||
local_runtime_paths: Some(local_runtime_paths),
|
||||
}
|
||||
}
|
||||
@@ -393,13 +436,15 @@ impl Environment {
|
||||
let exec_backend: Arc<dyn ExecBackend> = Arc::new(RemoteProcess::new(client.clone()));
|
||||
let filesystem: Arc<dyn ExecutorFileSystem> =
|
||||
Arc::new(RemoteFileSystem::new(client.clone()));
|
||||
let http_client = client.clone();
|
||||
|
||||
Self {
|
||||
exec_server_url,
|
||||
remote_transport: Some(remote_transport),
|
||||
exec_backend,
|
||||
filesystem,
|
||||
http_client: Arc::new(client),
|
||||
http_client: Arc::new(http_client),
|
||||
runtime_installer: RuntimeInstaller::Remote(client),
|
||||
local_runtime_paths,
|
||||
}
|
||||
}
|
||||
@@ -428,6 +473,13 @@ impl Environment {
|
||||
pub fn get_filesystem(&self) -> Arc<dyn ExecutorFileSystem> {
|
||||
Arc::clone(&self.filesystem)
|
||||
}
|
||||
|
||||
pub async fn install_runtime(
|
||||
&self,
|
||||
params: RuntimeInstallParams,
|
||||
) -> Result<RuntimeInstallResponse, JSONRPCErrorError> {
|
||||
self.runtime_installer.install_runtime(params).await
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -17,6 +17,7 @@ mod remote;
|
||||
mod remote_file_system;
|
||||
mod remote_process;
|
||||
mod rpc;
|
||||
mod runtime_install;
|
||||
mod runtime_paths;
|
||||
mod sandboxed_file_system;
|
||||
mod server;
|
||||
@@ -82,6 +83,7 @@ pub use protocol::HttpRequestResponse;
|
||||
pub use protocol::InitializeParams;
|
||||
pub use protocol::InitializeResponse;
|
||||
pub use protocol::ProcessOutputChunk;
|
||||
pub use protocol::RUNTIME_INSTALL_METHOD;
|
||||
pub use protocol::ReadParams;
|
||||
pub use protocol::ReadResponse;
|
||||
pub use protocol::TerminateParams;
|
||||
|
||||
@@ -30,6 +30,7 @@ pub const FS_COPY_METHOD: &str = "fs/copy";
|
||||
pub const HTTP_REQUEST_METHOD: &str = "http/request";
|
||||
/// JSON-RPC notification method for streamed executor HTTP response bodies.
|
||||
pub const HTTP_REQUEST_BODY_DELTA_METHOD: &str = "http/request/bodyDelta";
|
||||
pub const RUNTIME_INSTALL_METHOD: &str = "runtime/install";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
|
||||
@@ -80,7 +80,7 @@ impl RpcNotificationSender {
|
||||
self.outgoing_tx
|
||||
.send(RpcServerOutboundMessage::Response { request_id, result })
|
||||
.await
|
||||
.map_err(|_| internal_error("RPC connection closed while sending response".into()))
|
||||
.map_err(|_| internal_error("RPC connection closed while sending response"))
|
||||
}
|
||||
|
||||
pub(crate) async fn notify<P: Serialize>(
|
||||
@@ -97,7 +97,7 @@ impl RpcNotificationSender {
|
||||
},
|
||||
))
|
||||
.await
|
||||
.map_err(|_| internal_error("RPC connection closed while sending notification".into()))
|
||||
.map_err(|_| internal_error("RPC connection closed while sending notification"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -421,11 +421,11 @@ pub(crate) fn method_not_found(message: String) -> JSONRPCErrorError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn invalid_params(message: String) -> JSONRPCErrorError {
|
||||
pub(crate) fn invalid_params(message: impl Into<String>) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: -32602,
|
||||
data: None,
|
||||
message,
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,11 +437,11 @@ pub(crate) fn not_found(message: String) -> JSONRPCErrorError {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn internal_error(message: String) -> JSONRPCErrorError {
|
||||
pub(crate) fn internal_error(message: impl Into<String>) -> JSONRPCErrorError {
|
||||
JSONRPCErrorError {
|
||||
code: -32603,
|
||||
data: None,
|
||||
message,
|
||||
message: message.into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,8 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use codex_app_server_protocol::JSONRPCErrorError;
|
||||
use codex_app_server_protocol::RequestId;
|
||||
use codex_app_server_protocol::RuntimeInstallParams;
|
||||
use codex_app_server_protocol::RuntimeInstallResponse;
|
||||
use serde_json::to_value;
|
||||
use std::collections::HashSet;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -264,6 +266,14 @@ impl ExecServerHandler {
|
||||
self.file_system.copy(params).await
|
||||
}
|
||||
|
||||
pub(crate) async fn runtime_install(
|
||||
&self,
|
||||
params: RuntimeInstallParams,
|
||||
) -> Result<RuntimeInstallResponse, JSONRPCErrorError> {
|
||||
self.require_initialized_for("runtime")?;
|
||||
crate::runtime_install::install_runtime(params).await
|
||||
}
|
||||
|
||||
fn require_initialized_for(
|
||||
&self,
|
||||
method_family: &str,
|
||||
|
||||
@@ -24,11 +24,13 @@ use crate::protocol::HttpRequestParams;
|
||||
use crate::protocol::INITIALIZE_METHOD;
|
||||
use crate::protocol::INITIALIZED_METHOD;
|
||||
use crate::protocol::InitializeParams;
|
||||
use crate::protocol::RUNTIME_INSTALL_METHOD;
|
||||
use crate::protocol::ReadParams;
|
||||
use crate::protocol::TerminateParams;
|
||||
use crate::protocol::WriteParams;
|
||||
use crate::rpc::RpcRouter;
|
||||
use crate::server::ExecServerHandler;
|
||||
use codex_app_server_protocol::RuntimeInstallParams;
|
||||
|
||||
pub(crate) fn build_router() -> RpcRouter<ExecServerHandler> {
|
||||
let mut router = RpcRouter::new();
|
||||
@@ -114,5 +116,11 @@ pub(crate) fn build_router() -> RpcRouter<ExecServerHandler> {
|
||||
handler.fs_copy(params).await
|
||||
},
|
||||
);
|
||||
router.request(
|
||||
RUNTIME_INSTALL_METHOD,
|
||||
|handler: Arc<ExecServerHandler>, params: RuntimeInstallParams| async move {
|
||||
handler.runtime_install(params).await
|
||||
},
|
||||
);
|
||||
router
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user