Download codex-rs/core-plugins/src/remote/share/checkout.rs from SaylorTwift/codex: direct link, hf CLI and curl.
- Browser
- Download file 16.6 kB
-
https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core-plugins/src/remote/share/checkout.rs
- Command line
-
hf download hf://SaylorTwift/codex/codex-rs/core-plugins/src/remote/share/checkout.rs
-
curl -L -o checkout.rs https://huggingface.co/SaylorTwift/codex/resolve/main/codex-rs/core-plugins/src/remote/share/checkout.rs
16.6 kB
| use super::super::REMOTE_WORKSPACE_SHARED_WITH_ME_MARKETPLACE_NAME; | |
| use super::super::REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME; | |
| use super::super::REMOTE_WORKSPACE_SHARED_WITH_ME_UNLISTED_MARKETPLACE_NAME; | |
| use super::super::RemotePluginCatalogError; | |
| use super::super::RemotePluginServiceConfig; | |
| use super::local_paths; | |
| use codex_app_server_protocol::PluginAuthPolicy; | |
| use codex_app_server_protocol::PluginInstallPolicy; | |
| use codex_login::CodexAuth; | |
| use codex_plugin::PluginId; | |
| use codex_plugin::validate_plugin_segment; | |
| use codex_utils_absolute_path::AbsolutePathBuf; | |
| use serde_json::Value as JsonValue; | |
| use serde_json::json; | |
| use std::collections::BTreeMap; | |
| use std::fs; | |
| use std::io; | |
| use std::io::Write; | |
| use std::path::Component; | |
| use std::path::Path; | |
| const PERSONAL_MARKETPLACE_NAME: &str = "codex-curated"; | |
| const PERSONAL_MARKETPLACE_DISPLAY_NAME: &str = "Personal"; | |
| const PERSONAL_MARKETPLACE_RELATIVE_PATH: &str = ".agents/plugins/marketplace.json"; | |
| pub struct RemotePluginShareCheckoutResult { | |
| pub remote_plugin_id: String, | |
| pub plugin_id: String, | |
| pub plugin_name: String, | |
| pub plugin_path: AbsolutePathBuf, | |
| pub marketplace_name: String, | |
| pub marketplace_path: AbsolutePathBuf, | |
| pub remote_version: Option<String>, | |
| } | |
| pub async fn checkout_remote_plugin_share( | |
| config: &RemotePluginServiceConfig, | |
| auth: Option<&CodexAuth>, | |
| codex_home: &Path, | |
| remote_plugin_id: &str, | |
| ) -> Result<RemotePluginShareCheckoutResult, RemotePluginCatalogError> { | |
| let detail = super::super::fetch_remote_plugin_detail_with_download_urls( | |
| config, | |
| auth, | |
| REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME, | |
| remote_plugin_id, | |
| ) | |
| .await?; | |
| let plugin_name = detail.summary.name.clone(); | |
| let remote_version = detail.release_version.clone(); | |
| validate_plugin_segment(&plugin_name, "plugin name").map_err(|reason| { | |
| RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "remote plugin `{remote_plugin_id}` returned invalid plugin name: {reason}" | |
| )) | |
| })?; | |
| if !is_checkout_supported_share_marketplace(&detail.marketplace_name) | |
| || detail.summary.share_context.is_none() | |
| { | |
| return Err(RemotePluginCatalogError::PluginShareCheckoutNotAvailable { | |
| remote_plugin_id: remote_plugin_id.to_string(), | |
| }); | |
| } | |
| let home = crate::marketplace::home_dir().ok_or_else(|| { | |
| RemotePluginCatalogError::UnexpectedResponse( | |
| "could not determine home directory for personal plugin marketplace".to_string(), | |
| ) | |
| })?; | |
| let home = AbsolutePathBuf::try_from(home).map_err(|err| { | |
| RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to resolve home directory for personal plugin marketplace: {err}" | |
| )) | |
| })?; | |
| let local_paths = load_share_local_paths_for_checkout(codex_home)?; | |
| let (local_plugin_path, already_checked_out) = | |
| editable_plugin_path_for_checkout(&home, &plugin_name, remote_plugin_id, &local_paths)?; | |
| let mut created_checkout_path = false; | |
| if !already_checked_out { | |
| let bundle = crate::remote_bundle::validate_remote_plugin_bundle( | |
| remote_plugin_id, | |
| &detail.marketplace_name, | |
| &plugin_name, | |
| detail.release_version.as_deref(), | |
| detail.bundle_download_url.as_deref(), | |
| /*app_manifest*/ None, | |
| ) | |
| .map_err(|err| { | |
| RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to prepare remote plugin bundle checkout: {err}" | |
| )) | |
| })?; | |
| crate::remote_bundle::download_and_extract_remote_plugin_bundle_to_path( | |
| config, | |
| bundle, | |
| local_plugin_path.clone(), | |
| ) | |
| .await | |
| .map_err(|err| { | |
| RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to check out remote plugin bundle: {err}" | |
| )) | |
| })?; | |
| created_checkout_path = true; | |
| } | |
| let marketplace = match update_personal_marketplace( | |
| &home, | |
| &plugin_name, | |
| &local_plugin_path, | |
| detail.summary.install_policy, | |
| detail.summary.auth_policy, | |
| detail | |
| .summary | |
| .interface | |
| .as_ref() | |
| .and_then(|interface| interface.category.clone()), | |
| ) { | |
| Ok(marketplace) => marketplace, | |
| Err(err) => { | |
| return Err(clean_up_created_checkout_path( | |
| created_checkout_path, | |
| &local_plugin_path, | |
| err, | |
| )); | |
| } | |
| }; | |
| if let Err(err) = local_paths::record_plugin_share_local_path( | |
| codex_home, | |
| remote_plugin_id, | |
| local_plugin_path.clone(), | |
| ) { | |
| let err = RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to record plugin share local path mapping: {err}" | |
| )); | |
| return Err(clean_up_created_checkout_path( | |
| created_checkout_path, | |
| &local_plugin_path, | |
| err, | |
| )); | |
| } | |
| let plugin_id = PluginId::new(plugin_name.clone(), marketplace.name.clone()) | |
| .map_err(|err| { | |
| RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to build checked out plugin id: {err}" | |
| )) | |
| })? | |
| .as_key(); | |
| Ok(RemotePluginShareCheckoutResult { | |
| remote_plugin_id: remote_plugin_id.to_string(), | |
| plugin_id, | |
| plugin_name, | |
| plugin_path: local_plugin_path, | |
| marketplace_name: marketplace.name, | |
| marketplace_path: marketplace.path, | |
| remote_version, | |
| }) | |
| } | |
| fn is_checkout_supported_share_marketplace(marketplace_name: &str) -> bool { | |
| matches!( | |
| marketplace_name, | |
| REMOTE_WORKSPACE_SHARED_WITH_ME_MARKETPLACE_NAME | |
| | REMOTE_WORKSPACE_SHARED_WITH_ME_PRIVATE_MARKETPLACE_NAME | |
| | REMOTE_WORKSPACE_SHARED_WITH_ME_UNLISTED_MARKETPLACE_NAME | |
| ) | |
| } | |
| fn load_share_local_paths_for_checkout( | |
| codex_home: &Path, | |
| ) -> Result<BTreeMap<String, AbsolutePathBuf>, RemotePluginCatalogError> { | |
| match local_paths::load_plugin_share_local_paths(codex_home) { | |
| Ok(paths) => Ok(paths), | |
| Err(err) if err.kind() == io::ErrorKind::InvalidData => Ok(BTreeMap::new()), | |
| Err(err) => Err(RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to load plugin share local path mapping: {err}" | |
| ))), | |
| } | |
| } | |
| fn editable_plugin_path_for_checkout( | |
| home: &AbsolutePathBuf, | |
| plugin_name: &str, | |
| remote_plugin_id: &str, | |
| local_paths: &BTreeMap<String, AbsolutePathBuf>, | |
| ) -> Result<(AbsolutePathBuf, bool), RemotePluginCatalogError> { | |
| if let Some(existing_path) = local_paths.get(remote_plugin_id) | |
| && existing_path.as_path().exists() | |
| { | |
| ensure_path_can_be_listed_in_personal_marketplace(home, existing_path)?; | |
| return Ok((existing_path.clone(), true)); | |
| } | |
| let local_plugin_path = local_paths | |
| .get(remote_plugin_id) | |
| .cloned() | |
| .unwrap_or_else(|| home.join("plugins").join(plugin_name)); | |
| ensure_path_can_be_listed_in_personal_marketplace(home, &local_plugin_path)?; | |
| if local_plugin_path.as_path().exists() { | |
| return Err(RemotePluginCatalogError::InvalidPluginPath { | |
| path: local_plugin_path.to_path_buf(), | |
| reason: format!( | |
| "cannot check out remote plugin `{remote_plugin_id}` because the local plugin path already exists" | |
| ), | |
| }); | |
| } | |
| Ok((local_plugin_path, false)) | |
| } | |
| fn clean_up_created_checkout_path( | |
| created_checkout_path: bool, | |
| local_plugin_path: &AbsolutePathBuf, | |
| original_err: RemotePluginCatalogError, | |
| ) -> RemotePluginCatalogError { | |
| if !created_checkout_path { | |
| return original_err; | |
| } | |
| match remove_created_checkout_path(local_plugin_path) { | |
| Ok(()) => original_err, | |
| Err(cleanup_err) => RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "{original_err}; additionally failed to clean up checked out plugin path `{}`: {cleanup_err}", | |
| local_plugin_path.display() | |
| )), | |
| } | |
| } | |
| fn remove_created_checkout_path(local_plugin_path: &AbsolutePathBuf) -> io::Result<()> { | |
| if local_plugin_path.as_path().is_dir() { | |
| fs::remove_dir_all(local_plugin_path.as_path()) | |
| } else { | |
| fs::remove_file(local_plugin_path.as_path()) | |
| } | |
| } | |
| fn ensure_path_can_be_listed_in_personal_marketplace( | |
| home: &AbsolutePathBuf, | |
| path: &AbsolutePathBuf, | |
| ) -> Result<(), RemotePluginCatalogError> { | |
| personal_marketplace_relative_plugin_path(home, path).map(|_| ()) | |
| } | |
| struct PersonalMarketplaceUpdate { | |
| name: String, | |
| path: AbsolutePathBuf, | |
| } | |
| fn update_personal_marketplace( | |
| home: &AbsolutePathBuf, | |
| plugin_name: &str, | |
| local_plugin_path: &AbsolutePathBuf, | |
| install_policy: PluginInstallPolicy, | |
| auth_policy: PluginAuthPolicy, | |
| category: Option<String>, | |
| ) -> Result<PersonalMarketplaceUpdate, RemotePluginCatalogError> { | |
| let marketplace_path = home.join(PERSONAL_MARKETPLACE_RELATIVE_PATH); | |
| let relative_plugin_path = personal_marketplace_relative_plugin_path(home, local_plugin_path)?; | |
| let mut marketplace = read_or_create_personal_marketplace(marketplace_path.as_path())?; | |
| let Some(marketplace_object) = marketplace.as_object_mut() else { | |
| return Err(invalid_marketplace_file( | |
| marketplace_path.as_path(), | |
| "personal marketplace file must contain a JSON object", | |
| )); | |
| }; | |
| let marketplace_name = marketplace_object | |
| .entry("name") | |
| .or_insert_with(|| json!(PERSONAL_MARKETPLACE_NAME)) | |
| .as_str() | |
| .ok_or_else(|| { | |
| invalid_marketplace_file( | |
| marketplace_path.as_path(), | |
| "marketplace name must be a string", | |
| ) | |
| })? | |
| .to_string(); | |
| validate_plugin_segment(&marketplace_name, "marketplace name").map_err(|reason| { | |
| invalid_marketplace_file( | |
| marketplace_path.as_path(), | |
| &format!("marketplace name is invalid: {reason}"), | |
| ) | |
| })?; | |
| let plugins = marketplace_object | |
| .entry("plugins") | |
| .or_insert_with(|| json!([])) | |
| .as_array_mut() | |
| .ok_or_else(|| { | |
| invalid_marketplace_file( | |
| marketplace_path.as_path(), | |
| "marketplace plugins must be an array", | |
| ) | |
| })?; | |
| let new_entry = personal_marketplace_plugin_entry( | |
| plugin_name, | |
| &relative_plugin_path, | |
| install_policy, | |
| auth_policy, | |
| category, | |
| ); | |
| if let Some(existing_entry) = plugins | |
| .iter_mut() | |
| .find(|entry| entry.get("name").and_then(JsonValue::as_str) == Some(plugin_name)) | |
| { | |
| let existing_path = existing_entry | |
| .get("source") | |
| .and_then(|source| source.get("path")) | |
| .and_then(JsonValue::as_str); | |
| if existing_path != Some(relative_plugin_path.as_str()) { | |
| return Err(invalid_marketplace_file( | |
| marketplace_path.as_path(), | |
| &format!( | |
| "marketplace already contains plugin `{plugin_name}` with a different source path" | |
| ), | |
| )); | |
| } | |
| *existing_entry = new_entry; | |
| } else { | |
| plugins.push(new_entry); | |
| } | |
| let contents = serde_json::to_string_pretty(&marketplace) | |
| .map_err(|err| RemotePluginCatalogError::UnexpectedResponse(err.to_string()))?; | |
| write_json_atomically(marketplace_path.as_path(), &format!("{contents}\n")).map_err(|err| { | |
| RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to update personal plugin marketplace: {err}" | |
| )) | |
| })?; | |
| Ok(PersonalMarketplaceUpdate { | |
| name: marketplace_name, | |
| path: marketplace_path, | |
| }) | |
| } | |
| fn read_or_create_personal_marketplace( | |
| marketplace_path: &Path, | |
| ) -> Result<JsonValue, RemotePluginCatalogError> { | |
| match std::fs::read_to_string(marketplace_path) { | |
| Ok(contents) => serde_json::from_str(&contents).map_err(|err| { | |
| invalid_marketplace_file( | |
| marketplace_path, | |
| &format!("failed to parse personal marketplace file: {err}"), | |
| ) | |
| }), | |
| Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(json!({ | |
| "name": PERSONAL_MARKETPLACE_NAME, | |
| "interface": { | |
| "displayName": PERSONAL_MARKETPLACE_DISPLAY_NAME, | |
| }, | |
| "plugins": [], | |
| })), | |
| Err(err) => Err(RemotePluginCatalogError::UnexpectedResponse(format!( | |
| "failed to read personal plugin marketplace: {err}" | |
| ))), | |
| } | |
| } | |
| fn personal_marketplace_plugin_entry( | |
| plugin_name: &str, | |
| relative_plugin_path: &str, | |
| install_policy: PluginInstallPolicy, | |
| auth_policy: PluginAuthPolicy, | |
| category: Option<String>, | |
| ) -> JsonValue { | |
| let mut entry = json!({ | |
| "name": plugin_name, | |
| "source": { | |
| "source": "local", | |
| "path": relative_plugin_path, | |
| }, | |
| "policy": { | |
| "installation": plugin_install_policy_value(install_policy), | |
| "authentication": plugin_auth_policy_value(auth_policy), | |
| }, | |
| }); | |
| if let Some(category) = category | |
| && !category.trim().is_empty() | |
| && let Some(object) = entry.as_object_mut() | |
| { | |
| object.insert("category".to_string(), json!(category)); | |
| } | |
| entry | |
| } | |
| fn plugin_install_policy_value(policy: PluginInstallPolicy) -> &'static str { | |
| match policy { | |
| PluginInstallPolicy::NotAvailable => "NOT_AVAILABLE", | |
| PluginInstallPolicy::Available => "AVAILABLE", | |
| PluginInstallPolicy::InstalledByDefault => "INSTALLED_BY_DEFAULT", | |
| } | |
| } | |
| fn plugin_auth_policy_value(policy: PluginAuthPolicy) -> &'static str { | |
| match policy { | |
| PluginAuthPolicy::OnInstall => "ON_INSTALL", | |
| PluginAuthPolicy::OnUse => "ON_USE", | |
| } | |
| } | |
| fn personal_marketplace_relative_plugin_path( | |
| home: &AbsolutePathBuf, | |
| local_plugin_path: &AbsolutePathBuf, | |
| ) -> Result<String, RemotePluginCatalogError> { | |
| let relative = local_plugin_path | |
| .as_path() | |
| .strip_prefix(home.as_path()) | |
| .map_err(|_| RemotePluginCatalogError::InvalidPluginPath { | |
| path: local_plugin_path.to_path_buf(), | |
| reason: "local plugin path must be inside the home directory to be listed in the personal marketplace".to_string(), | |
| })?; | |
| let mut segments = Vec::new(); | |
| for component in relative.components() { | |
| match component { | |
| Component::Normal(segment) => { | |
| let segment = segment.to_str().ok_or_else(|| { | |
| RemotePluginCatalogError::InvalidPluginPath { | |
| path: local_plugin_path.to_path_buf(), | |
| reason: "local plugin path contains non-UTF-8 segments".to_string(), | |
| } | |
| })?; | |
| segments.push(segment.to_string()); | |
| } | |
| Component::CurDir => {} | |
| Component::ParentDir | Component::RootDir | Component::Prefix(_) => { | |
| return Err(RemotePluginCatalogError::InvalidPluginPath { | |
| path: local_plugin_path.to_path_buf(), | |
| reason: | |
| "local plugin path cannot be represented as a personal marketplace path" | |
| .to_string(), | |
| }); | |
| } | |
| } | |
| } | |
| if segments.is_empty() { | |
| return Err(RemotePluginCatalogError::InvalidPluginPath { | |
| path: local_plugin_path.to_path_buf(), | |
| reason: "local plugin path must not be the home directory".to_string(), | |
| }); | |
| } | |
| Ok(format!("./{}", segments.join("/"))) | |
| } | |
| fn invalid_marketplace_file(path: &Path, message: &str) -> RemotePluginCatalogError { | |
| RemotePluginCatalogError::InvalidPluginPath { | |
| path: path.to_path_buf(), | |
| reason: message.to_string(), | |
| } | |
| } | |
| fn write_json_atomically(write_path: &Path, contents: &str) -> io::Result<()> { | |
| let parent = write_path.parent().ok_or_else(|| { | |
| io::Error::new( | |
| io::ErrorKind::InvalidInput, | |
| format!("path {} has no parent directory", write_path.display()), | |
| ) | |
| })?; | |
| std::fs::create_dir_all(parent)?; | |
| let mut tmp = tempfile::NamedTempFile::new_in(parent)?; | |
| tmp.write_all(contents.as_bytes())?; | |
| tmp.persist(write_path).map_err(|err| err.error)?; | |
| Ok(()) | |
| } | |