Download codex-rs/app-server-daemon/src/managed_install.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 7.34 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server-daemon/src/managed_install.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/app-server-daemon/src/managed_install.rs
-
curl -L -o managed_install.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/app-server-daemon/src/managed_install.rs
7.34 kB
| //! Resolves both package and legacy standalone layouts and compares installed executables. | |
| use std::path::Path; | |
| use std::path::PathBuf; | |
| use std::process::Stdio; | |
| use std::time::Duration; | |
| use anyhow::Context; | |
| use anyhow::Result; | |
| use anyhow::anyhow; | |
| use serde::Deserialize; | |
| use serde::Serialize; | |
| use tokio::fs; | |
| use tokio::process::Command; | |
| use tokio::time::timeout; | |
| /// New daemons own their packages, regardless of how the calling CLI was installed. | |
| /// Preserve legacy launch state, including logs left after a daemon is stopped; | |
| /// settings, installer selections, and lock files alone do not prove a prior launch. | |
| pub(crate) fn package_root(codex_home: &Path) -> PathBuf { | |
| let dedicated = codex_home.join("packages/app-server-daemon"); | |
| if !matches!(dedicated.join("current").symlink_metadata(), | |
| Err(error) if error.kind() == std::io::ErrorKind::NotFound) | |
| { | |
| return dedicated; | |
| } | |
| let state = codex_home.join("app-server-daemon"); | |
| for (package, artifacts) in [ | |
| ( | |
| "app-server-daemon", | |
| [ | |
| crate::DAEMON_PID_FILE_NAME, | |
| "daemon.stderr.log", | |
| crate::DAEMON_UPDATE_PID_FILE_NAME, | |
| "daemon-updater.stderr.log", | |
| ], | |
| ), | |
| ( | |
| "standalone", | |
| [ | |
| crate::LEGACY_PID_FILE_NAME, | |
| "app-server.stderr.log", | |
| crate::LEGACY_UPDATE_PID_FILE_NAME, | |
| "app-server-updater.stderr.log", | |
| ], | |
| ), | |
| ] { | |
| if artifacts.iter().any(|name| { | |
| !matches!(state.join(name).symlink_metadata(), | |
| Err(error) if error.kind() == std::io::ErrorKind::NotFound) | |
| }) { | |
| return codex_home.join("packages").join(package); | |
| } | |
| } | |
| dedicated | |
| } | |
| /// Resolve both packaged and legacy binaries without requiring a valid install. | |
| pub(crate) fn managed_codex_bin(codex_home: &Path) -> PathBuf { | |
| let root = package_root(codex_home); | |
| let current = root.join("current"); | |
| let packaged = current.join("bin").join(managed_codex_file_name()); | |
| let legacy = current.join(managed_codex_file_name()); | |
| if packaged.is_file() | |
| || !legacy.is_file() && (cfg!(windows) || root.ends_with("app-server-daemon")) | |
| { | |
| packaged | |
| } else { | |
| legacy | |
| } | |
| } | |
| /// Only latest-channel stable releases may run the public latest-version updater. | |
| pub(crate) fn is_stable_standalone_release(codex_home: &Path, codex_bin: &Path) -> bool { | |
| let standalone = package_root(codex_home); | |
| let Ok(releases) = std::fs::canonicalize(standalone.join("releases")) else { | |
| return false; | |
| }; | |
| let Ok(release) = std::fs::canonicalize(standalone.join("current")) else { | |
| return false; | |
| }; | |
| if release.parent() != Some(releases.as_path()) { | |
| return false; | |
| } | |
| let Some(release_name) = release.file_name().and_then(|name| name.to_str()) else { | |
| return false; | |
| }; | |
| // GNU packages can seed the new directory; retain legacy updater eligibility. | |
| if standalone.ends_with("standalone") && release_name.ends_with("-gnu") { | |
| return false; | |
| } | |
| let targets = [ | |
| "aarch64-apple-darwin", | |
| "x86_64-apple-darwin", | |
| "aarch64-unknown-linux-gnu", | |
| "x86_64-unknown-linux-gnu", | |
| "aarch64-unknown-linux-musl", | |
| "x86_64-unknown-linux-musl", | |
| "aarch64-pc-windows-msvc", | |
| "x86_64-pc-windows-msvc", | |
| ]; | |
| let Some(version) = targets | |
| .iter() | |
| .find_map(|target| release_name.strip_suffix(&format!("-{target}"))) | |
| else { | |
| return false; | |
| }; | |
| let components: Vec<_> = version.split('.').collect(); | |
| components.len() == 3 | |
| && components.iter().all(|component| { | |
| !component.is_empty() && component.bytes().all(|byte| byte.is_ascii_digit()) | |
| }) | |
| && std::fs::read_to_string(standalone.join("auto-update-version")) | |
| .is_ok_and(|selected| selected == release_name) | |
| && std::fs::canonicalize(codex_bin).is_ok_and(|bin| bin.starts_with(&release)) | |
| } | |
| /// Older managed binaries can serve app-server requests without owning an updater. | |
| pub(crate) async fn supports_daemon_update_loop(codex_bin: &Path) -> bool { | |
| supports_daemon_command(codex_bin, &["pid-update-loop", "--help"]).await | |
| } | |
| /// Probe an internal daemon command without running a long-lived process. | |
| pub(crate) async fn supports_daemon_command(codex_bin: &Path, args: &[&str]) -> bool { | |
| let mut command = Command::new(codex_bin); | |
| command.creation_flags(windows_sys::Win32::System::Threading::CREATE_NO_WINDOW); | |
| timeout( | |
| Duration::from_secs(5), | |
| command | |
| .args(["app-server", "daemon"]) | |
| .args(args) | |
| .stdin(Stdio::null()) | |
| .stdout(Stdio::null()) | |
| .stderr(Stdio::null()) | |
| .kill_on_drop(true) | |
| .status(), | |
| ) | |
| .await | |
| .is_ok_and(|result| result.is_ok_and(|status| status.success())) | |
| } | |
| pub(crate) async fn resolved_managed_codex_bin(codex_bin: &Path) -> Result<PathBuf> { | |
| fs::canonicalize(codex_bin).await.with_context(|| { | |
| format!( | |
| "failed to resolve managed Codex binary {}", | |
| codex_bin.display() | |
| ) | |
| }) | |
| } | |
| pub(crate) async fn managed_codex_version(codex_bin: &Path) -> Result<String> { | |
| let mut command = Command::new(codex_bin); | |
| command.creation_flags(windows_sys::Win32::System::Threading::CREATE_NO_WINDOW); | |
| let output = command | |
| .arg("--version") | |
| .kill_on_drop(true) | |
| .output() | |
| .await | |
| .with_context(|| { | |
| format!( | |
| "failed to invoke managed Codex binary {}", | |
| codex_bin.display() | |
| ) | |
| })?; | |
| if !output.status.success() { | |
| return Err(anyhow!( | |
| "managed Codex binary {} exited with status {}", | |
| codex_bin.display(), | |
| output.status | |
| )); | |
| } | |
| let stdout = String::from_utf8(output.stdout).with_context(|| { | |
| format!( | |
| "managed Codex version was not utf-8: {}", | |
| codex_bin.display() | |
| ) | |
| })?; | |
| parse_codex_version(&stdout) | |
| } | |
| pub(crate) struct ExecutableIdentity { | |
| digest: [u8; 32], | |
| } | |
| pub(crate) async fn executable_identity(executable: &Path) -> Result<ExecutableIdentity> { | |
| let bytes = fs::read(executable) | |
| .await | |
| .with_context(|| format!("failed to read executable {}", executable.display()))?; | |
| Ok(executable_identity_from_bytes(&bytes)) | |
| } | |
| pub(crate) fn executable_identity_from_bytes(bytes: &[u8]) -> ExecutableIdentity { | |
| ExecutableIdentity { | |
| digest: *blake3::hash(bytes).as_bytes(), | |
| } | |
| } | |
| fn managed_codex_file_name() -> &'static str { | |
| if cfg!(windows) { "codex.exe" } else { "codex" } | |
| } | |
| fn parse_codex_version(output: &str) -> Result<String> { | |
| let version = output | |
| .split_whitespace() | |
| .nth(1) | |
| .filter(|version| !version.is_empty()) | |
| .ok_or_else(|| anyhow!("managed Codex version output was malformed"))?; | |
| Ok(version.to_string()) | |
| } | |
| mod tests; | |
| mod path_tests; | |