Download codex-rs/cli/src/desktop_app/windows.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 4.26 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/cli/src/desktop_app/windows.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/cli/src/desktop_app/windows.rs
-
curl -L -o windows.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/cli/src/desktop_app/windows.rs
4.26 kB
| use anyhow::Context as _; | |
| use std::path::Path; | |
| use std::path::PathBuf; | |
| use tokio::process::Command; | |
| const CODEX_WINDOWS_INSTALLER_URL: &str = | |
| "https://get.microsoft.com/installer/download/9PLM9XGG6VKS?cid=website_cta_psi"; | |
| const CODEX_MICROSOFT_STORE_WEB_URL: &str = "https://apps.microsoft.com/detail/9plm9xgg6vks"; | |
| pub async fn run_windows_app_open_or_install( | |
| workspace: PathBuf, | |
| download_url_override: Option<String>, | |
| ) -> anyhow::Result<()> { | |
| let workspace_path = workspace.display().to_string(); | |
| let display_workspace = display_workspace_path(&workspace); | |
| if codex_app_is_installed().await? { | |
| eprintln!("Opening workspace {display_workspace} in the Desktop app..."); | |
| open_url(&codex_new_thread_url(&workspace_path)).await?; | |
| return Ok(()); | |
| } | |
| eprintln!("Desktop app not found; opening Windows installer..."); | |
| let download_url = download_url_override | |
| .as_deref() | |
| .unwrap_or(CODEX_WINDOWS_INSTALLER_URL); | |
| if open_url(download_url).await.is_err() && download_url_override.is_none() { | |
| open_url(CODEX_MICROSOFT_STORE_WEB_URL).await?; | |
| } | |
| eprintln!("After installing the Desktop app, open workspace {display_workspace}."); | |
| Ok(()) | |
| } | |
| async fn codex_app_is_installed() -> anyhow::Result<bool> { | |
| // This package identity is stable across Codex- and ChatGPT-branded builds. | |
| let output = Command::new("powershell.exe") | |
| .arg("-NoProfile") | |
| .arg("-Command") | |
| .arg( | |
| "Get-StartApps | Where-Object AppID -Like 'OpenAI.Codex_*!App' | Select-Object -First 1 -ExpandProperty AppID", | |
| ) | |
| .output() | |
| .await | |
| .context("failed to invoke `powershell.exe`")?; | |
| if !output.status.success() { | |
| return Ok(false); | |
| } | |
| Ok(!String::from_utf8_lossy(&output.stdout).trim().is_empty()) | |
| } | |
| async fn open_url(url: &str) -> anyhow::Result<()> { | |
| let status = Command::new("powershell.exe") | |
| .arg("-NoProfile") | |
| .arg("-Command") | |
| .arg("& { param($target) Start-Process -FilePath $target }") | |
| .arg(url) | |
| .status() | |
| .await | |
| .with_context(|| format!("failed to open {url}"))?; | |
| if status.success() { | |
| Ok(()) | |
| } else { | |
| anyhow::bail!("failed to open {url} with {status}"); | |
| } | |
| } | |
| fn codex_new_thread_url(workspace: &str) -> String { | |
| let mut serializer = url::form_urlencoded::Serializer::new(String::new()); | |
| serializer.append_pair("path", workspace); | |
| let query = serializer.finish(); | |
| format!("codex://threads/new?{query}") | |
| } | |
| fn display_workspace_path(workspace: &Path) -> String { | |
| let path = workspace.display().to_string(); | |
| if let Some(path) = path.strip_prefix(r"\\?\UNC\") { | |
| format!(r"\\{path}") | |
| } else if let Some(path) = path.strip_prefix(r"\\?\") { | |
| path.to_string() | |
| } else { | |
| path | |
| } | |
| } | |
| mod tests { | |
| use super::codex_new_thread_url; | |
| use super::display_workspace_path; | |
| use pretty_assertions::assert_eq; | |
| use std::path::Path; | |
| fn display_workspace_path_removes_windows_extended_prefix() { | |
| assert_eq!( | |
| display_workspace_path(Path::new(r"\\?\C:\Users\fcoury\code\codex")), | |
| r"C:\Users\fcoury\code\codex" | |
| ); | |
| } | |
| fn display_workspace_path_preserves_unc_prefix() { | |
| assert_eq!( | |
| display_workspace_path(Path::new(r"\\?\UNC\server\share\codex")), | |
| r"\\server\share\codex" | |
| ); | |
| } | |
| fn display_workspace_path_leaves_regular_paths_unchanged() { | |
| assert_eq!( | |
| display_workspace_path(Path::new(r"C:\Users\fcoury\code\codex")), | |
| r"C:\Users\fcoury\code\codex" | |
| ); | |
| } | |
| fn codex_new_thread_url_encodes_windows_workspace_path() { | |
| assert_eq!( | |
| codex_new_thread_url(r"C:\Users\akuma\repos\koba"), | |
| r"codex://threads/new?path=C%3A%5CUsers%5Cakuma%5Crepos%5Ckoba" | |
| ); | |
| } | |
| fn codex_new_thread_url_preserves_verbatim_workspace_path() { | |
| assert_eq!( | |
| codex_new_thread_url(r"\\?\C:\Users\akuma\repos\koba"), | |
| r"codex://threads/new?path=%5C%5C%3F%5CC%3A%5CUsers%5Cakuma%5Crepos%5Ckoba" | |
| ); | |
| } | |
| } | |