connectors: own app metadata types (#29723)

## Why

Connector metadata is consumed by connector discovery, ChatGPT
integration, core, and TUI code. Treating app-server's wire DTO as the
shared domain model reverses the intended dependency direction.

## What changed

- Added connector-owned app branding, review, screenshot, metadata, and
info types.
- Added explicit conversions in app-server and TUI while preserving
app-server's wire payloads.
- Removed production app-server-protocol dependencies from connectors
and ChatGPT connector code.

## Stack

This is PR 4 of 6, stacked on [PR
#29722](https://github.com/openai/codex/pull/29722). Review only the
delta from `codex/split-config-layer-types`. Next: [PR
#29724](https://github.com/openai/codex/pull/29724).

## Validation

- Connector and tools coverage passed.
- App-server app-list coverage passed: 13 tests.
This commit is contained in:
Adam Perry @ OpenAI
2026-06-23 22:08:23 -07:00
committed by GitHub
Unverified
parent 1d65ccabd5
commit e639e8c4bd
34 changed files with 400 additions and 32 deletions
+7 -1
View File
@@ -7,6 +7,7 @@ use super::app_server_event_targets::server_request_thread_id;
use crate::app_command::AppCommand;
use crate::app_event::AppEvent;
use crate::app_event::ConnectorsSnapshot;
use crate::app_info::app_info_from_api;
use crate::app_server_session::AppServerSession;
use crate::app_server_session::status_account_display_from_auth_mode;
use codex_app_server_client::AppServerEvent;
@@ -127,7 +128,12 @@ impl App {
ServerNotification::AppListUpdated(notification) => {
self.chat_widget.on_connectors_loaded(
Ok(ConnectorsSnapshot {
connectors: notification.data.clone(),
connectors: notification
.data
.iter()
.cloned()
.map(app_info_from_api)
.collect(),
}),
/*is_final*/ false,
);
+2 -1
View File
@@ -7,6 +7,7 @@
use super::plugin_mentions::fetch_plugin_mentions;
use super::*;
use crate::app_event::ConnectorsSnapshot;
use crate::app_info::app_info_from_api;
use crate::config_update::format_config_error;
use codex_app_server_protocol::AppsListParams;
use codex_app_server_protocol::AppsListResponse;
@@ -870,7 +871,7 @@ pub(super) async fn fetch_connectors_list(
.await
.wrap_err("app/list failed in TUI")?;
Ok(ConnectorsSnapshot {
connectors: response.data,
connectors: response.data.into_iter().map(app_info_from_api).collect(),
})
}
+1 -1
View File
@@ -12,7 +12,6 @@ use std::path::PathBuf;
use codex_app_server_protocol::AddCreditsNudgeCreditType;
use codex_app_server_protocol::AddCreditsNudgeEmailStatus;
use codex_app_server_protocol::AppInfo;
use codex_app_server_protocol::ConsumeAccountRateLimitResetCreditResponse;
use codex_app_server_protocol::GetAccountRateLimitsResponse;
use codex_app_server_protocol::GetAccountTokenUsageResponse;
@@ -29,6 +28,7 @@ use codex_app_server_protocol::PluginReadResponse;
use codex_app_server_protocol::PluginUninstallResponse;
use codex_app_server_protocol::SkillsListResponse;
use codex_app_server_protocol::ThreadGoalStatus;
use codex_connectors::AppInfo;
use codex_file_search::FileMatch;
use codex_protocol::ThreadId;
use codex_protocol::openai_models::ModelPreset;
+122
View File
@@ -0,0 +1,122 @@
use codex_app_server_protocol::AppBranding as ApiAppBranding;
use codex_app_server_protocol::AppInfo as ApiAppInfo;
use codex_app_server_protocol::AppMetadata as ApiAppMetadata;
use codex_app_server_protocol::AppReview as ApiAppReview;
use codex_app_server_protocol::AppScreenshot as ApiAppScreenshot;
use codex_connectors::AppBranding;
use codex_connectors::AppInfo;
use codex_connectors::AppMetadata;
use codex_connectors::AppReview;
use codex_connectors::AppScreenshot;
/// Converts the app-server wire type owned by `codex-app-server-protocol` into connector-domain
/// app metadata owned by `codex-connectors`.
///
/// The types stay separate so app-server protocol ownership does not leak into the connector
/// domain crate. Because this crate owns neither type, Rust's orphan rules require an explicit
/// conversion function instead of a `From` implementation.
pub(crate) fn app_info_from_api(app: ApiAppInfo) -> AppInfo {
let ApiAppInfo {
id,
name,
description,
logo_url,
logo_url_dark,
distribution_channel,
branding,
app_metadata,
labels,
install_url,
is_accessible,
is_enabled,
plugin_display_names,
} = app;
AppInfo {
id,
name,
description,
logo_url,
logo_url_dark,
distribution_channel,
branding: branding.map(app_branding_from_api),
app_metadata: app_metadata.map(app_metadata_from_api),
labels,
install_url,
is_accessible,
is_enabled,
plugin_display_names,
}
}
fn app_branding_from_api(branding: ApiAppBranding) -> AppBranding {
let ApiAppBranding {
category,
developer,
website,
privacy_policy,
terms_of_service,
is_discoverable_app,
} = branding;
AppBranding {
category,
developer,
website,
privacy_policy,
terms_of_service,
is_discoverable_app,
}
}
fn app_review_from_api(review: ApiAppReview) -> AppReview {
let ApiAppReview { status } = review;
AppReview { status }
}
fn app_screenshot_from_api(screenshot: ApiAppScreenshot) -> AppScreenshot {
let ApiAppScreenshot {
url,
file_id,
user_prompt,
} = screenshot;
AppScreenshot {
url,
file_id,
user_prompt,
}
}
fn app_metadata_from_api(metadata: ApiAppMetadata) -> AppMetadata {
let ApiAppMetadata {
review,
categories,
sub_categories,
seo_description,
screenshots,
developer,
version,
version_id,
version_notes,
first_party_type,
first_party_requires_install,
show_in_composer_when_unlinked,
} = metadata;
AppMetadata {
review: review.map(app_review_from_api),
categories,
sub_categories,
seo_description,
screenshots: screenshots.map(|screenshots| {
screenshots
.into_iter()
.map(app_screenshot_from_api)
.collect()
}),
developer,
version,
version_id,
version_notes,
first_party_type,
first_party_requires_install,
show_in_composer_when_unlinked,
}
}
@@ -241,7 +241,7 @@ use crate::history_cell;
use crate::skills_helpers::skill_display_name;
use crate::tui::FrameRequester;
use crate::ui_consts::LIVE_PREFIX_COLS;
use codex_app_server_protocol::AppInfo;
use codex_connectors::AppInfo;
#[cfg(test)]
use codex_core_skills::model::SkillInterface;
use codex_core_skills::model::SkillMetadata;
+1 -1
View File
@@ -80,7 +80,6 @@ use crate::token_usage::TokenUsageInfo;
use crate::version::CODEX_CLI_VERSION;
use codex_app_server_protocol::AddCreditsNudgeCreditType;
use codex_app_server_protocol::AddCreditsNudgeEmailStatus;
use codex_app_server_protocol::AppInfo;
use codex_app_server_protocol::AppSummary;
use codex_app_server_protocol::CodexErrorInfo as AppServerCodexErrorInfo;
use codex_app_server_protocol::CollabAgentTool;
@@ -123,6 +122,7 @@ use codex_config::ConstraintResult;
use codex_config::types::ApprovalsReviewer;
use codex_config::types::Notifications;
use codex_config::types::WindowsSandboxModeToml;
use codex_connectors::AppInfo;
use codex_core_skills::model::SkillMetadata;
use codex_features::FEATURES;
use codex_features::Feature;
+1 -1
View File
@@ -10,10 +10,10 @@ use crate::bottom_pane::SkillsToggleView;
use crate::bottom_pane::popup_consts::standard_popup_hint_line;
use crate::skills_helpers::skill_description;
use crate::skills_helpers::skill_display_name;
use codex_app_server_protocol::AppInfo;
use codex_app_server_protocol::SkillMetadata as ProtocolSkillMetadata;
use codex_app_server_protocol::SkillsListEntry;
use codex_app_server_protocol::SkillsListResponse;
use codex_connectors::AppInfo;
use codex_core_skills::model::SkillDependencies;
use codex_core_skills::model::SkillInterface;
use codex_core_skills::model::SkillMetadata;
@@ -1,7 +1,6 @@
use super::*;
use crate::app_event::ConnectorsSnapshot;
use crate::chatwidget::connectors::ConnectorsCacheState;
use codex_app_server_protocol::AppInfo;
use codex_app_server_protocol::HookErrorInfo;
use codex_app_server_protocol::HooksListEntry;
use codex_app_server_protocol::HooksListResponse;
@@ -11,6 +10,7 @@ use codex_app_server_protocol::PluginAvailability;
use codex_app_server_protocol::PluginShareContext;
use codex_app_server_protocol::PluginShareDiscoverability;
use codex_app_server_protocol::PluginSource;
use codex_connectors::AppInfo;
use codex_features::Stage;
use pretty_assertions::assert_eq;
+1
View File
@@ -91,6 +91,7 @@ mod app_backtrack;
mod app_command;
mod app_event;
mod app_event_sender;
mod app_info;
mod app_server_approval_conversions;
mod app_server_session;
mod approval_events;