Files
codex/codex-rs/app-server/src/request_processors/update_processor.rs
T
2026-06-23 12:20:05 -07:00

102 lines
3.5 KiB
Rust

use crate::error_code::internal_error;
use crate::error_code::invalid_request;
use codex_app_server_protocol::ClientResponsePayload;
use codex_app_server_protocol::InstallationMethod;
use codex_app_server_protocol::JSONRPCErrorError;
use codex_app_server_protocol::UpdateApplyResponse;
use codex_app_server_protocol::UpdateCheckResponse;
use codex_install_context::InstallContext;
use codex_install_context::InstallMethod;
use codex_install_context::StandalonePlatform;
use codex_update::UpdateAction;
use std::process::Stdio;
use tokio::process::Command;
#[derive(Clone, Copy, Default)]
pub(crate) struct UpdateRequestProcessor;
impl UpdateRequestProcessor {
pub(crate) async fn check(&self) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
let context = InstallContext::current();
let installation_method = installation_method(&context.method);
let can_auto_update = UpdateAction::from_install_context(context).is_some();
let latest_version = codex_update::latest_version()
.await
.map_err(|err| internal_error(format!("failed to check for Codex updates: {err}")))?;
Ok(Some(
UpdateCheckResponse {
current_version: env!("CARGO_PKG_VERSION").to_string(),
latest_version,
installation_method,
can_auto_update,
}
.into(),
))
}
pub(crate) async fn apply(&self) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
let Some(action) = codex_update::get_update_action() else {
return Err(invalid_request(
"automatic updates are unavailable for this Codex installation",
));
};
#[cfg(windows)]
let mut command = if action == UpdateAction::StandaloneWindows {
let (program, args) = action.command_args();
let mut command = Command::new(program);
command.args(args);
command
} else {
let mut command = Command::new("cmd");
command.args(["/C", &action.command_str()]);
command
};
#[cfg(not(windows))]
let mut command = {
let (program, args) = action.command_args();
let mut command = Command::new(program);
command.args(args);
command
};
let status = command
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.await
.map_err(|err| {
internal_error(format!(
"failed to run Codex update via `{}`: {err}",
action.command_str()
))
})?;
if !status.success() {
return Err(internal_error(format!(
"Codex update via `{}` failed with status {status}",
action.command_str()
)));
}
Ok(Some(UpdateApplyResponse {}.into()))
}
}
fn installation_method(method: &InstallMethod) -> InstallationMethod {
match method {
InstallMethod::Npm => InstallationMethod::Npm,
InstallMethod::Bun => InstallationMethod::Bun,
InstallMethod::Brew => InstallationMethod::Brew,
InstallMethod::Standalone {
platform: StandalonePlatform::Unix,
..
} => InstallationMethod::StandaloneUnix,
InstallMethod::Standalone {
platform: StandalonePlatform::Windows,
..
} => InstallationMethod::StandaloneWindows,
InstallMethod::Other => InstallationMethod::Other,
}
}