mirror of
https://github.com/openai/codex.git
synced 2026-05-04 03:16:31 +00:00
progress
This commit is contained in:
46
codex-rs/plugin/src/lib.rs
Normal file
46
codex-rs/plugin/src/lib.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
//! Shared plugin identifiers and telemetry-facing summaries.
|
||||
|
||||
mod plugin_id;
|
||||
|
||||
pub use plugin_id::PluginId;
|
||||
pub use plugin_id::PluginIdError;
|
||||
pub use plugin_id::validate_plugin_segment;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AppConnectorId(pub String);
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct PluginCapabilitySummary {
|
||||
pub config_name: String,
|
||||
pub display_name: String,
|
||||
pub description: Option<String>,
|
||||
pub has_skills: bool,
|
||||
pub mcp_server_names: Vec<String>,
|
||||
pub app_connector_ids: Vec<AppConnectorId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PluginTelemetryMetadata {
|
||||
pub plugin_id: PluginId,
|
||||
pub capability_summary: Option<PluginCapabilitySummary>,
|
||||
}
|
||||
|
||||
impl PluginTelemetryMetadata {
|
||||
pub fn from_plugin_id(plugin_id: &PluginId) -> Self {
|
||||
Self {
|
||||
plugin_id: plugin_id.clone(),
|
||||
capability_summary: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginCapabilitySummary {
|
||||
pub fn telemetry_metadata(&self) -> Option<PluginTelemetryMetadata> {
|
||||
PluginId::parse(&self.config_name)
|
||||
.ok()
|
||||
.map(|plugin_id| PluginTelemetryMetadata {
|
||||
plugin_id,
|
||||
capability_summary: Some(self.clone()),
|
||||
})
|
||||
}
|
||||
}
|
||||
64
codex-rs/plugin/src/plugin_id.rs
Normal file
64
codex-rs/plugin/src/plugin_id.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
//! Stable plugin identifier parsing and validation shared with the plugin cache.
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PluginIdError {
|
||||
#[error("{0}")]
|
||||
Invalid(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct PluginId {
|
||||
pub plugin_name: String,
|
||||
pub marketplace_name: String,
|
||||
}
|
||||
|
||||
impl PluginId {
|
||||
pub fn new(plugin_name: String, marketplace_name: String) -> Result<Self, PluginIdError> {
|
||||
validate_plugin_segment(&plugin_name, "plugin name").map_err(PluginIdError::Invalid)?;
|
||||
validate_plugin_segment(&marketplace_name, "marketplace name")
|
||||
.map_err(PluginIdError::Invalid)?;
|
||||
Ok(Self {
|
||||
plugin_name,
|
||||
marketplace_name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse(plugin_key: &str) -> Result<Self, PluginIdError> {
|
||||
let Some((plugin_name, marketplace_name)) = plugin_key.rsplit_once('@') else {
|
||||
return Err(PluginIdError::Invalid(format!(
|
||||
"invalid plugin key `{plugin_key}`; expected <plugin>@<marketplace>"
|
||||
)));
|
||||
};
|
||||
if plugin_name.is_empty() || marketplace_name.is_empty() {
|
||||
return Err(PluginIdError::Invalid(format!(
|
||||
"invalid plugin key `{plugin_key}`; expected <plugin>@<marketplace>"
|
||||
)));
|
||||
}
|
||||
|
||||
Self::new(plugin_name.to_string(), marketplace_name.to_string()).map_err(|err| match err {
|
||||
PluginIdError::Invalid(message) => {
|
||||
PluginIdError::Invalid(format!("{message} in `{plugin_key}`"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_key(&self) -> String {
|
||||
format!("{}@{}", self.plugin_name, self.marketplace_name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Validates a single path segment used in plugin IDs and cache layout.
|
||||
pub fn validate_plugin_segment(segment: &str, kind: &str) -> Result<(), String> {
|
||||
if segment.is_empty() {
|
||||
return Err(format!("invalid {kind}: must not be empty"));
|
||||
}
|
||||
if !segment
|
||||
.chars()
|
||||
.all(|ch| ch.is_ascii_alphanumeric() || ch == '-' || ch == '_')
|
||||
{
|
||||
return Err(format!(
|
||||
"invalid {kind}: only ASCII letters, digits, `_`, and `-` are allowed"
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user