mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex] Track plugin install and import telemetry failures (#28731)
## Summary - Track plugin install failures through the unified `codex_plugin_install_failed` event for local installs, remote install preflight failures, bundle failures, and remote catalog/backend failures. - Send classified `error_type` values in plugin install failure analytics instead of raw error strings. - Stop sending raw external-agent import errors in analytics while preserving raw failure details in app-facing import notifications/history. - Keep raw plugin/migration diagnostics in `tracing::warn!` logs. - Keep remote failure plugin names as the existing local placeholder (`unknown`) and remove the extra telemetry plugin-name override. - Change `ExternalAgentConfigImportParams.source` from a generated enum to `string | null`, with legacy `claudeCode` / `claudeCowork` inputs normalized to existing analytics values. ## Testing
This commit is contained in:
committed by
GitHub
Unverified
parent
5867b529ae
commit
3959ab0ffc
@@ -69,6 +69,7 @@ use codex_plugin::AppConnectorId;
|
||||
use codex_plugin::PluginCapabilitySummary;
|
||||
use codex_plugin::PluginId;
|
||||
use codex_plugin::PluginIdError;
|
||||
use codex_plugin::PluginTelemetryMetadata;
|
||||
use codex_plugin::app_connector_ids_from_declarations;
|
||||
use codex_plugin::prompt_safe_plugin_description;
|
||||
use codex_protocol::protocol::HookEventName;
|
||||
@@ -1079,12 +1080,29 @@ impl PluginsManager {
|
||||
&self,
|
||||
request: PluginInstallRequest,
|
||||
) -> Result<PluginInstallOutcome, PluginInstallError> {
|
||||
let resolved = find_installable_marketplace_plugin(
|
||||
let resolved = match find_installable_marketplace_plugin(
|
||||
&request.marketplace_path,
|
||||
&request.plugin_name,
|
||||
self.restriction_product,
|
||||
)?;
|
||||
self.install_resolved_plugin(resolved).await
|
||||
) {
|
||||
Ok(resolved) => resolved,
|
||||
Err(err) => {
|
||||
self.track_plugin_install_resolution_failed(&err);
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
let plugin_id = resolved.plugin_id.clone();
|
||||
match self.install_resolved_plugin(resolved).await {
|
||||
Ok(outcome) => Ok(outcome),
|
||||
Err(err) => {
|
||||
self.track_plugin_install_failed(
|
||||
&plugin_id,
|
||||
plugin_install_error_type(&err),
|
||||
err.to_string(),
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn install_plugin_with_remote_sync(
|
||||
@@ -1093,21 +1111,101 @@ impl PluginsManager {
|
||||
auth: Option<&CodexAuth>,
|
||||
request: PluginInstallRequest,
|
||||
) -> Result<PluginInstallOutcome, PluginInstallError> {
|
||||
let resolved = find_installable_marketplace_plugin(
|
||||
let resolved = match find_installable_marketplace_plugin(
|
||||
&request.marketplace_path,
|
||||
&request.plugin_name,
|
||||
self.restriction_product,
|
||||
)?;
|
||||
) {
|
||||
Ok(resolved) => resolved,
|
||||
Err(err) => {
|
||||
self.track_plugin_install_resolution_failed(&err);
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
let plugin_id = resolved.plugin_id.as_key();
|
||||
// This only forwards the backend mutation before the local install flow.
|
||||
crate::remote_legacy::enable_remote_plugin(
|
||||
if let Err(err) = crate::remote_legacy::enable_remote_plugin(
|
||||
&remote_plugin_service_config(config),
|
||||
auth,
|
||||
&plugin_id,
|
||||
)
|
||||
.await
|
||||
.map_err(PluginInstallError::from)?;
|
||||
self.install_resolved_plugin(resolved).await
|
||||
{
|
||||
let err = PluginInstallError::from(err);
|
||||
self.track_plugin_install_failed(
|
||||
&resolved.plugin_id,
|
||||
plugin_install_error_type(&err),
|
||||
err.to_string(),
|
||||
);
|
||||
return Err(err);
|
||||
}
|
||||
let plugin_id = resolved.plugin_id.clone();
|
||||
match self.install_resolved_plugin(resolved).await {
|
||||
Ok(outcome) => Ok(outcome),
|
||||
Err(err) => {
|
||||
self.track_plugin_install_failed(
|
||||
&plugin_id,
|
||||
plugin_install_error_type(&err),
|
||||
err.to_string(),
|
||||
);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn track_plugin_install_resolution_failed(&self, err: &MarketplaceError) {
|
||||
let plugin_id = match err {
|
||||
MarketplaceError::PluginNotFound {
|
||||
plugin_name,
|
||||
marketplace_name,
|
||||
}
|
||||
| MarketplaceError::PluginNotAvailable {
|
||||
plugin_name,
|
||||
marketplace_name,
|
||||
} => PluginId::new(plugin_name.clone(), marketplace_name.clone()).ok(),
|
||||
MarketplaceError::Io { .. }
|
||||
| MarketplaceError::MarketplaceNotFound { .. }
|
||||
| MarketplaceError::InvalidMarketplaceFile { .. }
|
||||
| MarketplaceError::PluginsDisabled
|
||||
| MarketplaceError::InvalidPlugin(_) => None,
|
||||
};
|
||||
if let Some(plugin_id) = plugin_id {
|
||||
self.track_plugin_install_failed(
|
||||
&plugin_id,
|
||||
marketplace_error_type(err),
|
||||
err.to_string(),
|
||||
);
|
||||
} else {
|
||||
tracing::warn!(
|
||||
error_type = %marketplace_error_type(err),
|
||||
error = %err,
|
||||
"plugin install failed while resolving marketplace plugin"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn track_plugin_install_failed(
|
||||
&self,
|
||||
plugin_id: &PluginId,
|
||||
error_type: &'static str,
|
||||
error_message: String,
|
||||
) {
|
||||
tracing::warn!(
|
||||
plugin_id = %plugin_id.as_key(),
|
||||
error_type = %error_type,
|
||||
error = %error_message,
|
||||
"plugin install failed"
|
||||
);
|
||||
let analytics_events_client = match self.analytics_events_client.read() {
|
||||
Ok(client) => client.clone(),
|
||||
Err(err) => err.into_inner().clone(),
|
||||
};
|
||||
if let Some(analytics_events_client) = analytics_events_client {
|
||||
analytics_events_client.track_plugin_install_failed(
|
||||
PluginTelemetryMetadata::from_plugin_id(plugin_id),
|
||||
error_type.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn install_resolved_plugin(
|
||||
@@ -2173,6 +2271,54 @@ impl PluginInstallError {
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_install_error_type(err: &PluginInstallError) -> &'static str {
|
||||
match err {
|
||||
PluginInstallError::Marketplace(err) => marketplace_error_type(err),
|
||||
PluginInstallError::Remote(err) => remote_plugin_mutation_error_type(err),
|
||||
PluginInstallError::Store(err) => plugin_store_error_type(err),
|
||||
PluginInstallError::Config(_) => "config",
|
||||
PluginInstallError::Join(_) => "join",
|
||||
}
|
||||
}
|
||||
|
||||
fn marketplace_error_type(err: &MarketplaceError) -> &'static str {
|
||||
match err {
|
||||
MarketplaceError::Io { .. } => "marketplace_io",
|
||||
MarketplaceError::MarketplaceNotFound { .. } => "marketplace_not_found",
|
||||
MarketplaceError::InvalidMarketplaceFile { .. } => "invalid_marketplace_file",
|
||||
MarketplaceError::PluginNotFound { .. } => "plugin_not_found",
|
||||
MarketplaceError::PluginNotAvailable { .. } => "plugin_not_available",
|
||||
MarketplaceError::PluginsDisabled => "plugins_disabled",
|
||||
MarketplaceError::InvalidPlugin(_) => "invalid_plugin",
|
||||
}
|
||||
}
|
||||
|
||||
fn remote_plugin_mutation_error_type(err: &RemotePluginMutationError) -> &'static str {
|
||||
match err {
|
||||
RemotePluginMutationError::AuthRequired => "remote_mutation_auth_required",
|
||||
RemotePluginMutationError::UnsupportedAuthMode => "remote_mutation_unsupported_auth_mode",
|
||||
RemotePluginMutationError::AuthToken(_) => "remote_mutation_auth_token",
|
||||
RemotePluginMutationError::InvalidBaseUrl(_) => "remote_mutation_invalid_base_url",
|
||||
RemotePluginMutationError::InvalidBaseUrlPath => "remote_mutation_invalid_base_url_path",
|
||||
RemotePluginMutationError::Request { .. } => "remote_mutation_request",
|
||||
RemotePluginMutationError::UnexpectedStatus { .. } => "remote_mutation_unexpected_status",
|
||||
RemotePluginMutationError::Decode { .. } => "remote_mutation_decode",
|
||||
RemotePluginMutationError::UnexpectedPluginId { .. } => {
|
||||
"remote_mutation_unexpected_plugin_id"
|
||||
}
|
||||
RemotePluginMutationError::UnexpectedEnabledState { .. } => {
|
||||
"remote_mutation_unexpected_enabled_state"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_store_error_type(err: &PluginStoreError) -> &'static str {
|
||||
match err {
|
||||
PluginStoreError::Io { .. } => "store_io",
|
||||
PluginStoreError::Invalid(_) => "store_invalid",
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PluginUninstallError {
|
||||
#[error("{0}")]
|
||||
|
||||
Reference in New Issue
Block a user