Codex Handbook
app-server-protocol/src/protocol/v2/apps.rs 174 lines
use super::shared::default_enabled;use schemars::JsonSchema;use serde::Deserialize;use serde::Serialize;use std::collections::HashMap;use ts_rs::TS;#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]/// EXPERIMENTAL - list available apps/connectors.pub struct AppsListParams {    /// Opaque pagination cursor returned by a previous call.    #[ts(optional = nullable)]    pub cursor: Option<String>,    /// Optional page size; defaults to a reasonable server-side value.    #[ts(optional = nullable)]    pub limit: Option<u32>,    /// Optional thread id used to evaluate app feature gating from that thread's config.    #[ts(optional = nullable)]    pub thread_id: Option<String>,    /// When true, bypass app caches and fetch the latest data from sources.    #[serde(default, skip_serializing_if = "std::ops::Not::not")]    pub force_refetch: bool,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]/// EXPERIMENTAL - app metadata returned by app-list APIs.pub struct AppBranding {    pub category: Option<String>,    pub developer: Option<String>,    pub website: Option<String>,    pub privacy_policy: Option<String>,    pub terms_of_service: Option<String>,    pub is_discoverable_app: bool,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct AppReview {    pub status: String,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct AppScreenshot {    pub url: Option<String>,    #[serde(alias = "file_id")]    pub file_id: Option<String>,    #[serde(alias = "user_prompt")]    pub user_prompt: String,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct AppMetadata {    pub review: Option<AppReview>,    pub categories: Option<Vec<String>>,    pub sub_categories: Option<Vec<String>>,    pub seo_description: Option<String>,    pub screenshots: Option<Vec<AppScreenshot>>,    pub developer: Option<String>,    pub version: Option<String>,    pub version_id: Option<String>,    pub version_notes: Option<String>,    pub first_party_type: Option<String>,    pub first_party_requires_install: Option<bool>,    pub show_in_composer_when_unlinked: Option<bool>,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]/// EXPERIMENTAL - app metadata returned by app-list APIs.pub struct AppInfo {    pub id: String,    pub name: String,    pub description: Option<String>,    pub logo_url: Option<String>,    pub logo_url_dark: Option<String>,    pub distribution_channel: Option<String>,    pub branding: Option<AppBranding>,    pub app_metadata: Option<AppMetadata>,    pub labels: Option<HashMap<String, String>>,    pub install_url: Option<String>,    #[serde(default)]    pub is_accessible: bool,    /// Whether this app is enabled in config.toml.    /// Example:    /// ```toml    /// [apps.bad_app]    /// enabled = false    /// ```    #[serde(default = "default_enabled")]    pub is_enabled: bool,    #[serde(default)]    pub plugin_display_names: Vec<String>,}impl AppInfo {    pub fn category(&self) -> Option<String> {        self.branding            .as_ref()            .and_then(|branding| non_empty_category(branding.category.as_deref()))            .or_else(|| {                self.app_metadata                    .as_ref()                    .and_then(|metadata| metadata.categories.as_ref())                    .and_then(|categories| {                        categories                            .iter()                            .find_map(|category| non_empty_category(Some(category.as_str())))                    })            })    }}fn non_empty_category(category: Option<&str>) -> Option<String> {    let category = category?.trim();    if category.is_empty() {        None    } else {        Some(category.to_string())    }}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]/// EXPERIMENTAL - app metadata summary for plugin responses.pub struct AppSummary {    pub id: String,    pub name: String,    pub description: Option<String>,    pub install_url: Option<String>,    pub category: Option<String>,}impl From<AppInfo> for AppSummary {    fn from(value: AppInfo) -> Self {        let category = value.category();        Self {            id: value.id,            name: value.name,            description: value.description,            install_url: value.install_url,            category,        }    }}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]/// EXPERIMENTAL - app list response.pub struct AppsListResponse {    pub data: Vec<AppInfo>,    /// Opaque cursor to pass to the next call to continue after the last item.    /// If None, there are no more items to return.    pub next_cursor: Option<String>,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]/// EXPERIMENTAL - notification emitted when the app list changes.pub struct AppListUpdatedNotification {    pub data: Vec<AppInfo>,}