[codex] cover app-owned HTTP requests [ci changed_files]

This commit is contained in:
Cooper Gamble
2026-06-03 23:44:25 +00:00
parent ec2e111e04
commit d766866d3c
52 changed files with 1415 additions and 358 deletions
+48 -18
View File
@@ -910,7 +910,11 @@ impl MessageProcessor {
}
ClientRequest::ExperimentalFeatureEnablementSet { params, .. } => {
self.config_processor
.experimental_feature_enablement_set(request_id.clone(), params)
.experimental_feature_enablement_set(
request_id.clone(),
params,
app_server_client_name.as_deref(),
)
.await
}
ClientRequest::RemoteControlEnable { .. } => self
@@ -1143,13 +1147,17 @@ impl MessageProcessor {
self.thread_processor.conversation_summary(params).await
}
ClientRequest::SkillsList { params, .. } => {
self.catalog_processor.skills_list(params).await
self.catalog_processor
.skills_list(params, app_server_client_name.as_deref())
.await
}
ClientRequest::SkillsExtraRootsSet { params, .. } => {
self.catalog_processor.skills_extra_roots_set(params).await
}
ClientRequest::HooksList { params, .. } => {
self.catalog_processor.hooks_list(params).await
self.catalog_processor
.hooks_list(params, app_server_client_name.as_deref())
.await
}
ClientRequest::MarketplaceAdd { params, .. } => {
self.marketplace_processor.marketplace_add(params).await
@@ -1161,45 +1169,67 @@ impl MessageProcessor {
self.marketplace_processor.marketplace_upgrade(params).await
}
ClientRequest::PluginList { params, .. } => {
self.plugin_processor.plugin_list(params).await
self.plugin_processor
.plugin_list(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginInstalled { params, .. } => {
self.plugin_processor.plugin_installed(params).await
self.plugin_processor
.plugin_installed(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginRead { params, .. } => {
self.plugin_processor.plugin_read(params).await
self.plugin_processor
.plugin_read(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginSkillRead { params, .. } => {
self.plugin_processor.plugin_skill_read(params).await
self.plugin_processor
.plugin_skill_read(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginShareSave { params, .. } => {
self.plugin_processor.plugin_share_save(params).await
self.plugin_processor
.plugin_share_save(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginShareUpdateTargets { params, .. } => {
self.plugin_processor
.plugin_share_update_targets(params)
.plugin_share_update_targets(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginShareList { params, .. } => {
self.plugin_processor.plugin_share_list(params).await
self.plugin_processor
.plugin_share_list(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginShareCheckout { params, .. } => {
self.plugin_processor.plugin_share_checkout(params).await
self.plugin_processor
.plugin_share_checkout(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginShareDelete { params, .. } => {
self.plugin_processor.plugin_share_delete(params).await
self.plugin_processor
.plugin_share_delete(params, app_server_client_name.as_deref())
.await
}
ClientRequest::AppsList { params, .. } => {
self.apps_processor.apps_list(&request_id, params).await
self.apps_processor
.apps_list(&request_id, params, app_server_client_name.as_deref())
.await
}
ClientRequest::SkillsConfigWrite { params, .. } => {
self.catalog_processor.skills_config_write(params).await
}
ClientRequest::PluginInstall { params, .. } => {
self.plugin_processor.plugin_install(params).await
self.plugin_processor
.plugin_install(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PluginUninstall { params, .. } => {
self.plugin_processor.plugin_uninstall(params).await
self.plugin_processor
.plugin_uninstall(params, app_server_client_name.as_deref())
.await
}
ClientRequest::ModelList { params, .. } => {
self.catalog_processor
@@ -1208,7 +1238,7 @@ impl MessageProcessor {
}
ClientRequest::ExperimentalFeatureList { params, .. } => {
self.catalog_processor
.experimental_feature_list(params)
.experimental_feature_list(params, app_server_client_name.as_deref())
.await
}
ClientRequest::PermissionProfileList { params, .. } => {
@@ -1279,12 +1309,12 @@ impl MessageProcessor {
}
ClientRequest::McpServerStatusList { params, .. } => {
self.mcp_processor
.mcp_server_status_list(&request_id, params)
.mcp_server_status_list(&request_id, params, app_server_client_name.as_deref())
.await
}
ClientRequest::McpResourceRead { params, .. } => {
self.mcp_processor
.mcp_resource_read(&request_id, params)
.mcp_resource_read(&request_id, params, app_server_client_name.as_deref())
.await
}
ClientRequest::McpServerToolCall { params, .. } => {
@@ -533,6 +533,22 @@ pub(crate) use self::thread_summary::summary_to_thread;
pub(crate) use self::thread_summary::thread_settings_from_config_snapshot;
pub(crate) use self::thread_summary::thread_settings_from_core_snapshot;
fn http_state_context(
config: &Config,
app_server_client_name: Option<&str>,
) -> codex_http_state::HttpStateContext {
codex_http_state::HttpStateContext::new(
config.codex_home.to_path_buf(),
http_state_surface(app_server_client_name),
)
}
fn http_state_surface(app_server_client_name: Option<&str>) -> codex_http_state::HttpStateSurface {
codex_http_state::HttpStateSurface::from_app_server_client_name(
app_server_client_name.unwrap_or_default(),
)
}
pub(crate) fn build_api_turns_from_rollout_items(items: &[RolloutItem]) -> Vec<Turn> {
let mut builder = ThreadHistoryBuilder::new();
for item in items {
@@ -183,6 +183,7 @@ impl AccountRequestProcessor {
config_manager: &ConfigManager,
thread_manager: &Arc<ThreadManager>,
auth: Option<CodexAuth>,
http_state_surface: Option<HttpStateSurface>,
) {
match config_manager
.load_latest_config(/*fallback_cwd*/ None)
@@ -202,6 +203,9 @@ impl AccountRequestProcessor {
refresh_config_manager.clone(),
);
})),
http_state_surface.map(|surface| {
HttpStateContext::new(config.codex_home.to_path_buf(), surface)
}),
);
}
Err(err) => {
@@ -257,6 +261,7 @@ impl AccountRequestProcessor {
access_token,
chatgpt_account_id,
chatgpt_plan_type,
http_state_surface,
)
.await;
}
@@ -317,8 +322,10 @@ impl AccountRequestProcessor {
self.outgoing.send_result(request_id, result).await;
if logged_in {
self.send_login_success_notifications(/*login_id*/ None)
.await;
self.send_login_success_notifications(
/*login_id*/ None, /*http_state_surface*/ None,
)
.await;
}
}
@@ -411,11 +418,7 @@ impl AccountRequestProcessor {
});
}
let outgoing_clone = self.outgoing.clone();
let config_manager = self.config_manager.clone();
let thread_manager = Arc::clone(&self.thread_manager);
let chatgpt_base_url = self.config.chatgpt_base_url.clone();
let active_login = self.active_login.clone();
let processor = self.clone();
let auth_url = server.auth_url.clone();
tokio::spawn(async move {
let (success, error_msg) = match tokio::time::timeout(
@@ -432,19 +435,17 @@ impl AccountRequestProcessor {
}
};
Self::send_chatgpt_login_completion_notifications(
&outgoing_clone,
config_manager,
thread_manager,
chatgpt_base_url,
login_id,
success,
error_msg,
)
.await;
processor
.send_chatgpt_login_completion_notifications(
login_id,
success,
error_msg,
http_state_surface,
)
.await;
// Clear the active login if it matches this attempt. It may have been replaced or cancelled.
let mut guard = active_login.lock().await;
let mut guard = processor.active_login.lock().await;
if guard.as_ref().map(ActiveLogin::login_id) == Some(login_id) {
*guard = None;
}
@@ -494,11 +495,7 @@ impl AccountRequestProcessor {
let verification_url = device_code.verification_url.clone();
let user_code = device_code.user_code.clone();
let outgoing_clone = self.outgoing.clone();
let config_manager = self.config_manager.clone();
let thread_manager = Arc::clone(&self.thread_manager);
let chatgpt_base_url = self.config.chatgpt_base_url.clone();
let active_login = self.active_login.clone();
let processor = self.clone();
tokio::spawn(async move {
let (success, error_msg) = tokio::select! {
_ = cancel.cancelled() => {
@@ -512,18 +509,16 @@ impl AccountRequestProcessor {
}
};
Self::send_chatgpt_login_completion_notifications(
&outgoing_clone,
config_manager,
thread_manager,
chatgpt_base_url,
login_id,
success,
error_msg,
)
.await;
processor
.send_chatgpt_login_completion_notifications(
login_id,
success,
error_msg,
http_state_surface,
)
.await;
let mut guard = active_login.lock().await;
let mut guard = processor.active_login.lock().await;
if guard.as_ref().map(ActiveLogin::login_id) == Some(login_id) {
*guard = None;
}
@@ -571,6 +566,7 @@ impl AccountRequestProcessor {
access_token: String,
chatgpt_account_id: String,
chatgpt_plan_type: Option<String>,
http_state_surface: HttpStateSurface,
) {
let result = self
.login_chatgpt_auth_tokens_response(access_token, chatgpt_account_id, chatgpt_plan_type)
@@ -579,7 +575,7 @@ impl AccountRequestProcessor {
self.outgoing.send_result(request_id, result).await;
if logged_in {
self.send_login_success_notifications(/*login_id*/ None)
self.send_login_success_notifications(/*login_id*/ None, Some(http_state_surface))
.await;
}
}
@@ -634,11 +630,16 @@ impl AccountRequestProcessor {
Ok(LoginAccountResponse::ChatgptAuthTokens {})
}
async fn send_login_success_notifications(&self, login_id: Option<Uuid>) {
async fn send_login_success_notifications(
&self,
login_id: Option<Uuid>,
http_state_surface: Option<HttpStateSurface>,
) {
Self::maybe_refresh_remote_installed_plugins_cache_for_current_config(
&self.config_manager,
&self.thread_manager,
self.auth_manager.auth_cached(),
http_state_surface,
)
.await;
@@ -661,44 +662,45 @@ impl AccountRequestProcessor {
}
async fn send_chatgpt_login_completion_notifications(
outgoing: &OutgoingMessageSender,
config_manager: ConfigManager,
thread_manager: Arc<ThreadManager>,
chatgpt_base_url: String,
&self,
login_id: Uuid,
success: bool,
error_msg: Option<String>,
http_state_surface: HttpStateSurface,
) {
let payload_v2 = AccountLoginCompletedNotification {
login_id: Some(login_id.to_string()),
success,
error: error_msg,
};
outgoing
self.outgoing
.send_server_notification(ServerNotification::AccountLoginCompleted(payload_v2))
.await;
if success {
let auth_manager = thread_manager.auth_manager();
let auth_manager = self.thread_manager.auth_manager();
auth_manager.reload().await;
config_manager
.replace_cloud_config_bundle_loader(auth_manager.clone(), chatgpt_base_url);
config_manager
self.config_manager.replace_cloud_config_bundle_loader(
auth_manager.clone(),
self.config.chatgpt_base_url.clone(),
);
self.config_manager
.sync_default_client_residency_requirement()
.await;
let auth = auth_manager.auth_cached();
Self::maybe_refresh_remote_installed_plugins_cache_for_current_config(
&config_manager,
&thread_manager,
&self.config_manager,
&self.thread_manager,
auth.clone(),
Some(http_state_surface),
)
.await;
let payload_v2 = AccountUpdatedNotification {
auth_mode: auth.as_ref().map(CodexAuth::api_auth_mode),
plan_type: auth.as_ref().and_then(CodexAuth::account_plan_type),
};
outgoing
self.outgoing
.send_server_notification(ServerNotification::AccountUpdated(payload_v2))
.await;
}
@@ -724,6 +726,7 @@ impl AccountRequestProcessor {
&self.config_manager,
&self.thread_manager,
self.auth_manager.auth_cached(),
/*http_state_surface*/ None,
)
.await;
@@ -1,4 +1,5 @@
use super::*;
use codex_http_state::HttpStateContext;
pub(crate) struct AppsRequestProcessor {
auth_manager: Arc<AuthManager>,
@@ -35,8 +36,9 @@ impl AppsRequestProcessor {
&self,
request_id: &ConnectionRequestId,
params: AppsListParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.apps_list_inner(request_id, params)
self.apps_list_inner(request_id, params, app_server_client_name)
.await
.map(|response| response.map(Into::into))
}
@@ -45,6 +47,7 @@ impl AppsRequestProcessor {
&self,
request_id: &ConnectionRequestId,
params: AppsListParams,
app_server_client_name: Option<&str>,
) -> Result<Option<AppsListResponse>, JSONRPCErrorError> {
let thread = if let Some(thread_id) = params.thread_id.as_deref() {
let (_, loaded_thread) = self.load_thread(thread_id).await?;
@@ -64,7 +67,11 @@ impl AppsRequestProcessor {
.set_enabled(Feature::Apps, thread.enabled(Feature::Apps));
}
let auth = self.auth_manager.auth().await;
let auth = self
.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await;
let http_state = http_state_context(&config, app_server_client_name);
if !config
.features
.apps_enabled_for_auth(auth.as_ref().is_some_and(CodexAuth::uses_codex_backend))
@@ -76,7 +83,7 @@ impl AppsRequestProcessor {
}
if !self
.workspace_codex_plugins_enabled(&config, auth.as_ref())
.workspace_codex_plugins_enabled(&config, auth.as_ref(), &http_state)
.await
{
return Ok(Some(AppsListResponse {
@@ -92,7 +99,7 @@ impl AppsRequestProcessor {
tokio::spawn(async move {
tokio::select! {
_ = shutdown_token.cancelled() => {}
_ = Self::apps_list_task(outgoing, request, params, config, environment_manager) => {}
_ = Self::apps_list_task(outgoing, request, params, config, environment_manager, http_state) => {}
}
});
Ok(None)
@@ -108,11 +115,15 @@ impl AppsRequestProcessor {
params: AppsListParams,
config: Config,
environment_manager: Arc<EnvironmentManager>,
http_state: HttpStateContext,
) {
let retry_params = params.clone();
let retry_config = config.clone();
let retry_environment_manager = Arc::clone(&environment_manager);
let result = Self::apps_list_response(&outgoing, params, config, environment_manager).await;
let retry_http_state = http_state.clone();
let result =
Self::apps_list_response(&outgoing, params, config, environment_manager, http_state)
.await;
let should_retry = result
.as_ref()
.is_ok_and(|(_, codex_apps_ready)| !codex_apps_ready);
@@ -128,6 +139,7 @@ impl AppsRequestProcessor {
retry_params,
retry_config,
retry_environment_manager,
retry_http_state,
)
.await
{
@@ -141,6 +153,7 @@ impl AppsRequestProcessor {
params: AppsListParams,
config: Config,
environment_manager: Arc<EnvironmentManager>,
http_state: HttpStateContext,
) -> Result<(AppsListResponse, bool), JSONRPCErrorError> {
let AppsListParams {
cursor,
@@ -157,8 +170,11 @@ impl AppsRequestProcessor {
};
let (mut accessible_connectors, mut all_connectors) = tokio::join!(
connectors::list_cached_accessible_connectors_from_mcp_tools(&config),
connectors::list_cached_all_connectors(&config)
connectors::list_cached_accessible_connectors_from_mcp_tools_with_http_state(
&config,
Some(&http_state),
),
connectors::list_cached_all_connectors_with_http_state(&config, Some(&http_state))
);
let cached_all_connectors = all_connectors.clone();
@@ -166,12 +182,14 @@ impl AppsRequestProcessor {
let accessible_config = config.clone();
let accessible_tx = tx.clone();
let accessible_http_state = http_state.clone();
tokio::spawn(async move {
let result =
connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager(
connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager_and_http_state(
&accessible_config,
force_refetch,
Arc::clone(&environment_manager),
Some(accessible_http_state),
)
.await
.map_err(|err| format!("failed to load accessible apps: {err}"));
@@ -180,9 +198,13 @@ impl AppsRequestProcessor {
let all_config = config.clone();
tokio::spawn(async move {
let result = connectors::list_all_connectors_with_options(&all_config, force_refetch)
.await
.map_err(|err| format!("failed to list apps: {err}"));
let result = connectors::list_all_connectors_with_options_and_http_state(
&all_config,
force_refetch,
Some(http_state),
)
.await
.map_err(|err| format!("failed to list apps: {err}"));
let _ = tx.send(AppListLoadResult::Directory(result));
});
@@ -303,11 +325,13 @@ impl AppsRequestProcessor {
&self,
config: &Config,
auth: Option<&CodexAuth>,
http_state: &HttpStateContext,
) -> bool {
match workspace_settings::codex_plugins_enabled_for_workspace(
match workspace_settings::codex_plugins_enabled_for_workspace_with_http_state(
config,
auth,
Some(&self.workspace_settings_cache),
Some(http_state.clone()),
)
.await
{
@@ -1,5 +1,6 @@
use super::*;
use codex_config::config_toml::ConfigToml;
use codex_http_state::HttpStateContext;
use codex_http_state::HttpStateSurface;
use futures::StreamExt;
@@ -121,8 +122,9 @@ impl CatalogRequestProcessor {
pub(crate) async fn skills_list(
&self,
params: SkillsListParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.skills_list_response(params)
self.skills_list_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -130,8 +132,9 @@ impl CatalogRequestProcessor {
pub(crate) async fn hooks_list(
&self,
params: HooksListParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.hooks_list_response(params)
self.hooks_list_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -167,8 +170,9 @@ impl CatalogRequestProcessor {
pub(crate) async fn experimental_feature_list(
&self,
params: ExperimentalFeatureListParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.experimental_feature_list_response(params)
self.experimental_feature_list_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -229,11 +233,13 @@ impl CatalogRequestProcessor {
&self,
config: &Config,
auth: Option<&CodexAuth>,
http_state: &HttpStateContext,
) -> bool {
match workspace_settings::codex_plugins_enabled_for_workspace(
match workspace_settings::codex_plugins_enabled_for_workspace_with_http_state(
config,
auth,
Some(&self.workspace_settings_cache),
Some(http_state.clone()),
)
.await
{
@@ -320,6 +326,7 @@ impl CatalogRequestProcessor {
async fn experimental_feature_list_response(
&self,
params: ExperimentalFeatureListParams,
app_server_client_name: Option<&str>,
) -> Result<ExperimentalFeatureListResponse, JSONRPCErrorError> {
let ExperimentalFeatureListParams {
cursor,
@@ -343,9 +350,13 @@ impl CatalogRequestProcessor {
}
None => self.load_latest_config(/*fallback_cwd*/ None).await?,
};
let auth = self.auth_manager.auth().await;
let http_state = http_state_context(&config, app_server_client_name);
let auth = self
.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await;
let workspace_codex_plugins_enabled = self
.workspace_codex_plugins_enabled(&config, auth.as_ref())
.workspace_codex_plugins_enabled(&config, auth.as_ref(), &http_state)
.await;
let data = FEATURES
@@ -509,6 +520,7 @@ impl CatalogRequestProcessor {
async fn skills_list_response(
&self,
params: SkillsListParams,
app_server_client_name: Option<&str>,
) -> Result<SkillsListResponse, JSONRPCErrorError> {
let SkillsListParams { cwds, force_reload } = params;
let cwds = if cwds.is_empty() {
@@ -518,9 +530,13 @@ impl CatalogRequestProcessor {
};
let config = self.load_latest_config(/*fallback_cwd*/ None).await?;
let auth = self.auth_manager.auth().await;
let http_state = http_state_context(&config, app_server_client_name);
let auth = self
.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await;
let workspace_codex_plugins_enabled = self
.workspace_codex_plugins_enabled(&config, auth.as_ref())
.workspace_codex_plugins_enabled(&config, auth.as_ref(), &http_state)
.await;
let skills_manager = self.thread_manager.skills_manager();
let plugins_manager = self.thread_manager.plugins_manager();
@@ -615,6 +631,7 @@ impl CatalogRequestProcessor {
async fn hooks_list_response(
&self,
params: HooksListParams,
app_server_client_name: Option<&str>,
) -> Result<HooksListResponse, JSONRPCErrorError> {
let HooksListParams { cwds } = params;
let cwds = if cwds.is_empty() {
@@ -623,7 +640,10 @@ impl CatalogRequestProcessor {
cwds
};
let auth = self.auth_manager.auth().await;
let auth = self
.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await;
let plugins_manager = self.thread_manager.plugins_manager();
let mut data = Vec::new();
for cwd in cwds {
@@ -651,8 +671,9 @@ impl CatalogRequestProcessor {
continue;
}
};
let http_state = http_state_context(&config, app_server_client_name);
let workspace_codex_plugins_enabled = self
.workspace_codex_plugins_enabled(&config, auth.as_ref())
.workspace_codex_plugins_enabled(&config, auth.as_ref(), &http_state)
.await;
let plugins_enabled =
config.features.enabled(Feature::Plugins) && workspace_codex_plugins_enabled;
@@ -1,5 +1,7 @@
use std::sync::Arc;
use super::http_state_context;
use super::http_state_surface;
use crate::config_manager::ConfigManager;
use crate::config_manager_service::ConfigManagerError;
use crate::error_code::internal_error;
@@ -150,6 +152,7 @@ impl ConfigRequestProcessor {
&self,
request_id: ConnectionRequestId,
params: ExperimentalFeatureEnablementSetParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
let should_refresh_apps_list = params.enablement.get("apps").copied() == Some(true);
let response = self
@@ -162,8 +165,10 @@ impl ConfigRequestProcessor {
)
.await;
if should_refresh_apps_list {
self.refresh_apps_list_after_experimental_feature_enablement_set()
.await;
self.refresh_apps_list_after_experimental_feature_enablement_set(
app_server_client_name,
)
.await;
}
Ok(None)
}
@@ -195,7 +200,10 @@ impl ConfigRequestProcessor {
Ok(response)
}
async fn refresh_apps_list_after_experimental_feature_enablement_set(&self) {
async fn refresh_apps_list_after_experimental_feature_enablement_set(
&self,
app_server_client_name: Option<&str>,
) {
let config = match self.load_latest_config(/*fallback_cwd*/ None).await {
Ok(config) => config,
Err(error) => {
@@ -206,7 +214,11 @@ impl ConfigRequestProcessor {
return;
}
};
let auth = self.auth_manager.auth().await;
let http_state = http_state_context(&config, app_server_client_name);
let auth = self
.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await;
if !config.features.apps_enabled_for_auth(
auth.as_ref()
.is_some_and(codex_login::CodexAuth::uses_codex_backend),
@@ -218,11 +230,16 @@ impl ConfigRequestProcessor {
let environment_manager = self.thread_manager.environment_manager();
tokio::spawn(async move {
let (all_connectors_result, accessible_connectors_result) = tokio::join!(
connectors::list_all_connectors_with_options(&config, /*force_refetch*/ true),
connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager(
connectors::list_all_connectors_with_options_and_http_state(
&config,
/*force_refetch*/ true,
Some(http_state.clone()),
),
connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager_and_http_state(
&config,
/*force_refetch*/ true,
Arc::clone(&environment_manager),
Some(http_state),
),
);
let all_connectors = match all_connectors_result {
@@ -1,4 +1,5 @@
use super::*;
use codex_http_state::HttpStateContext;
const MCP_TOOL_THREAD_ID_META_KEY: &str = "threadId";
@@ -47,8 +48,9 @@ impl McpRequestProcessor {
&self,
request_id: &ConnectionRequestId,
params: ListMcpServerStatusParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.list_mcp_server_status(request_id, params)
self.list_mcp_server_status(request_id, params, app_server_client_name)
.await
.map(|()| None)
}
@@ -57,8 +59,9 @@ impl McpRequestProcessor {
&self,
request_id: &ConnectionRequestId,
params: McpResourceReadParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.read_mcp_resource(request_id, params)
self.read_mcp_resource(request_id, params, app_server_client_name)
.await
.map(|()| None)
}
@@ -195,6 +198,7 @@ impl McpRequestProcessor {
&self,
request_id: &ConnectionRequestId,
params: ListMcpServerStatusParams,
app_server_client_name: Option<&str>,
) -> Result<(), JSONRPCErrorError> {
let request = request_id.clone();
@@ -213,7 +217,11 @@ impl McpRequestProcessor {
let mcp_config = config
.to_mcp_config(self.thread_manager.plugins_manager().as_ref())
.await;
let auth = self.auth_manager.auth().await;
let http_state = http_state_context(&config, app_server_client_name);
let auth = self
.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await;
let environment_manager = self.thread_manager.environment_manager();
// This status path has no turn-selected environment. Use config cwd
// as the local stdio fallback; named environment stdio MCPs must
@@ -229,6 +237,7 @@ impl McpRequestProcessor {
mcp_config,
auth,
runtime_context,
http_state,
)
.await;
});
@@ -242,6 +251,7 @@ impl McpRequestProcessor {
mcp_config: codex_mcp::McpConfig,
auth: Option<CodexAuth>,
runtime_context: McpRuntimeContext,
http_state: HttpStateContext,
) {
let result = Self::list_mcp_server_status_response(
request_id.request_id.to_string(),
@@ -249,6 +259,7 @@ impl McpRequestProcessor {
mcp_config,
auth,
runtime_context,
http_state,
)
.await;
outgoing.send_result(request_id, result).await;
@@ -260,6 +271,7 @@ impl McpRequestProcessor {
mcp_config: codex_mcp::McpConfig,
auth: Option<CodexAuth>,
runtime_context: McpRuntimeContext,
http_state: HttpStateContext,
) -> Result<ListMcpServerStatusResponse, JSONRPCErrorError> {
let detail = match params.detail.unwrap_or(McpServerStatusDetail::Full) {
McpServerStatusDetail::Full => McpSnapshotDetail::Full,
@@ -271,6 +283,7 @@ impl McpRequestProcessor {
auth.as_ref(),
request_id,
runtime_context,
Some(http_state),
detail,
)
.await;
@@ -341,6 +354,7 @@ impl McpRequestProcessor {
&self,
request_id: &ConnectionRequestId,
params: McpResourceReadParams,
app_server_client_name: Option<&str>,
) -> Result<(), JSONRPCErrorError> {
let outgoing = Arc::clone(&self.outgoing);
let McpResourceReadParams {
@@ -364,7 +378,11 @@ impl McpRequestProcessor {
let mcp_config = config
.to_mcp_config(self.thread_manager.plugins_manager().as_ref())
.await;
let auth = self.auth_manager.auth().await;
let http_state = http_state_context(&config, app_server_client_name);
let auth = self
.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await;
let environment_manager = self.thread_manager.environment_manager();
// This threadless resource-read path has no turn cwd or turn-selected
// environment. Use config cwd only as the local stdio fallback; named
@@ -378,6 +396,7 @@ impl McpRequestProcessor {
&mcp_config,
auth.as_ref(),
runtime_context,
Some(http_state),
&server,
&uri,
)
@@ -26,6 +26,14 @@ pub(crate) struct PluginRequestProcessor {
workspace_settings_cache: Arc<workspace_settings::WorkspaceSettingsCache>,
}
fn remote_plugin_service_config(
config: &Config,
app_server_client_name: Option<&str>,
) -> RemotePluginServiceConfig {
RemotePluginServiceConfig::new(config.chatgpt_base_url.clone())
.with_http_state(http_state_context(config, app_server_client_name))
}
fn plugin_skills_to_info(
skills: &[codex_core::skills::SkillMetadata],
disabled_skill_paths: &HashSet<AbsolutePathBuf>,
@@ -341,11 +349,21 @@ impl PluginRequestProcessor {
}
}
async fn auth_for_app_server_client(
&self,
app_server_client_name: Option<&str>,
) -> Option<CodexAuth> {
self.auth_manager
.auth_for_surface(http_state_surface(app_server_client_name))
.await
}
pub(crate) async fn plugin_list(
&self,
params: PluginListParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_list_response(params)
self.plugin_list_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -353,8 +371,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_installed(
&self,
params: PluginInstalledParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_installed_response(params)
self.plugin_installed_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -362,8 +381,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_read(
&self,
params: PluginReadParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_read_response(params)
self.plugin_read_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -371,8 +391,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_skill_read(
&self,
params: PluginSkillReadParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_skill_read_response(params)
self.plugin_skill_read_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -380,8 +401,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_share_save(
&self,
params: PluginShareSaveParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_share_save_response(params)
self.plugin_share_save_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -389,8 +411,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_share_update_targets(
&self,
params: PluginShareUpdateTargetsParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_share_update_targets_response(params)
self.plugin_share_update_targets_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -398,8 +421,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_share_list(
&self,
params: PluginShareListParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_share_list_response(params)
self.plugin_share_list_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -407,8 +431,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_share_checkout(
&self,
params: PluginShareCheckoutParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_share_checkout_response(params)
self.plugin_share_checkout_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -416,8 +441,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_share_delete(
&self,
params: PluginShareDeleteParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_share_delete_response(params)
self.plugin_share_delete_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -425,8 +451,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_install(
&self,
params: PluginInstallParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_install_response(params)
self.plugin_install_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -434,8 +461,9 @@ impl PluginRequestProcessor {
pub(crate) async fn plugin_uninstall(
&self,
params: PluginUninstallParams,
app_server_client_name: Option<&str>,
) -> Result<Option<ClientResponsePayload>, JSONRPCErrorError> {
self.plugin_uninstall_response(params)
self.plugin_uninstall_response(params, app_server_client_name)
.await
.map(|response| Some(response.into()))
}
@@ -491,11 +519,13 @@ impl PluginRequestProcessor {
&self,
config: &Config,
auth: Option<&CodexAuth>,
app_server_client_name: Option<&str>,
) -> bool {
match workspace_settings::codex_plugins_enabled_for_workspace(
match workspace_settings::codex_plugins_enabled_for_workspace_with_http_state(
config,
auth,
Some(&self.workspace_settings_cache),
Some(http_state_context(config, app_server_client_name)),
)
.await
{
@@ -512,6 +542,7 @@ impl PluginRequestProcessor {
async fn plugin_list_response(
&self,
params: PluginListParams,
app_server_client_name: Option<&str>,
) -> Result<PluginListResponse, JSONRPCErrorError> {
let plugins_manager = self.thread_manager.plugins_manager();
let PluginListParams {
@@ -534,9 +565,11 @@ impl PluginRequestProcessor {
if !config.features.enabled(Feature::Plugins) {
return Ok(empty_response());
}
let auth = self.auth_manager.auth().await;
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
if !self
.workspace_codex_plugins_enabled(&config, auth.as_ref())
.workspace_codex_plugins_enabled(&config, auth.as_ref(), app_server_client_name)
.await
{
return Ok(empty_response());
@@ -548,6 +581,7 @@ impl PluginRequestProcessor {
auth.clone(),
&roots,
Some(self.effective_plugins_changed_callback()),
Some(http_state_context(&config, app_server_client_name)),
);
}
let (mut data, marketplace_load_errors) = if include_local {
@@ -616,9 +650,8 @@ impl PluginRequestProcessor {
// TODO(remote plugins): Remove this once remote plugins are ready and vertical plugins are
// served directly from the normal remote catalog.
if include_vertical && !config.features.enabled(Feature::RemotePlugin) {
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
match codex_core_plugins::remote::fetch_openai_curated_remote_collection_marketplace(
&remote_plugin_service_config,
auth.as_ref(),
@@ -655,9 +688,8 @@ impl PluginRequestProcessor {
remote_sources.push(RemoteMarketplaceSource::SharedWithMe);
}
if !remote_sources.is_empty() {
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
match codex_core_plugins::remote::fetch_remote_marketplaces(
&remote_plugin_service_config,
auth.as_ref(),
@@ -707,7 +739,11 @@ impl PluginRequestProcessor {
.any(|marketplace| marketplace.name == OPENAI_CURATED_MARKETPLACE_NAME)
{
match plugins_manager
.featured_plugin_ids_for_config(&plugins_input, auth.as_ref())
.featured_plugin_ids_for_config_with_http_state(
&plugins_input,
auth.as_ref(),
http_state_context(&config, app_server_client_name),
)
.await
{
Ok(featured_plugin_ids) => featured_plugin_ids,
@@ -733,6 +769,7 @@ impl PluginRequestProcessor {
async fn plugin_installed_response(
&self,
params: PluginInstalledParams,
app_server_client_name: Option<&str>,
) -> Result<PluginInstalledResponse, JSONRPCErrorError> {
let plugins_manager = self.thread_manager.plugins_manager();
let PluginInstalledParams {
@@ -753,9 +790,11 @@ impl PluginRequestProcessor {
if !config.features.enabled(Feature::Plugins) {
return Ok(empty_response());
}
let auth = self.auth_manager.auth().await;
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
if !self
.workspace_codex_plugins_enabled(&config, auth.as_ref())
.workspace_codex_plugins_enabled(&config, auth.as_ref(), app_server_client_name)
.await
{
return Ok(empty_response());
@@ -768,6 +807,7 @@ impl PluginRequestProcessor {
&plugins_input,
auth.clone(),
Some(self.effective_plugins_changed_callback()),
Some(http_state_context(&config, app_server_client_name)),
);
let (mut data, marketplace_load_errors) = self
@@ -783,9 +823,11 @@ impl PluginRequestProcessor {
data.extend(
self.load_remote_installed_plugins(
plugins_manager,
&config,
&plugins_input,
&remote_installed_plugin_visible_scopes,
auth.as_ref(),
app_server_client_name,
)
.await,
);
@@ -883,9 +925,11 @@ impl PluginRequestProcessor {
async fn load_remote_installed_plugins(
&self,
plugins_manager: Arc<codex_core_plugins::PluginsManager>,
config: &Config,
plugins_input: &codex_core_plugins::PluginsConfigInput,
visible_scopes: &[RemotePluginScope],
auth: Option<&CodexAuth>,
app_server_client_name: Option<&str>,
) -> Vec<PluginMarketplaceEntry> {
let remote_marketplaces = if let Some(remote_marketplaces) =
plugins_manager.build_remote_installed_plugin_marketplaces_from_cache(visible_scopes)
@@ -893,11 +937,12 @@ impl PluginRequestProcessor {
Ok(remote_marketplaces)
} else {
plugins_manager
.build_and_cache_remote_installed_plugin_marketplaces(
.build_and_cache_remote_installed_plugin_marketplaces_with_http_state(
plugins_input,
auth,
visible_scopes,
Some(self.effective_plugins_changed_callback()),
http_state_context(config, app_server_client_name),
)
.await
};
@@ -924,6 +969,7 @@ impl PluginRequestProcessor {
async fn plugin_read_response(
&self,
params: PluginReadParams,
app_server_client_name: Option<&str>,
) -> Result<PluginReadResponse, JSONRPCErrorError> {
let plugins_manager = self.thread_manager.plugins_manager();
let PluginReadParams {
@@ -965,10 +1011,11 @@ impl PluginRequestProcessor {
);
let share_context = match share_context {
Some(context) => {
let auth = self.auth_manager.auth().await;
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
match codex_core_plugins::remote::fetch_remote_plugin_share_context(
&remote_plugin_service_config,
auth.as_ref(),
@@ -1016,6 +1063,7 @@ impl PluginRequestProcessor {
&config,
&outcome.plugin.apps,
Arc::clone(&environment_manager),
app_server_client_name,
)
.await;
let visible_skills = outcome
@@ -1071,10 +1119,11 @@ impl PluginRequestProcessor {
"remote plugin read is not enabled for marketplace {remote_marketplace_name}"
)));
}
let auth = self.auth_manager.auth().await;
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
validate_remote_plugin_id(&plugin_name)?;
let remote_detail = codex_core_plugins::remote::fetch_remote_plugin_detail(
&remote_plugin_service_config,
@@ -1097,6 +1146,7 @@ impl PluginRequestProcessor {
&config,
&plugin_apps,
Arc::clone(&environment_manager),
app_server_client_name,
)
.await;
remote_plugin_detail_to_info(remote_detail, app_summaries)
@@ -1109,6 +1159,7 @@ impl PluginRequestProcessor {
async fn plugin_skill_read_response(
&self,
params: PluginSkillReadParams,
app_server_client_name: Option<&str>,
) -> Result<PluginSkillReadResponse, JSONRPCErrorError> {
let PluginSkillReadParams {
remote_marketplace_name,
@@ -1129,10 +1180,11 @@ impl PluginRequestProcessor {
));
}
let auth = self.auth_manager.auth().await;
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
let remote_skill_detail = codex_core_plugins::remote::fetch_remote_plugin_skill_detail(
&remote_plugin_service_config,
auth.as_ref(),
@@ -1153,8 +1205,11 @@ impl PluginRequestProcessor {
async fn plugin_share_save_response(
&self,
params: PluginShareSaveParams,
app_server_client_name: Option<&str>,
) -> Result<PluginShareSaveResponse, JSONRPCErrorError> {
let (config, auth) = self.load_plugin_share_config_and_auth().await?;
let (config, auth) = self
.load_plugin_share_config_and_auth(app_server_client_name)
.await?;
if !config.features.enabled(Feature::PluginSharing) {
return Err(invalid_request("plugin sharing is disabled"));
}
@@ -1183,9 +1238,8 @@ impl PluginRequestProcessor {
validate_client_plugin_share_targets(share_targets)?;
}
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
let access_policy = codex_core_plugins::remote::RemotePluginShareAccessPolicy {
discoverability: discoverability.map(remote_plugin_share_discoverability),
share_targets: share_targets.map(remote_plugin_share_targets),
@@ -1211,8 +1265,11 @@ impl PluginRequestProcessor {
async fn plugin_share_update_targets_response(
&self,
params: PluginShareUpdateTargetsParams,
app_server_client_name: Option<&str>,
) -> Result<PluginShareUpdateTargetsResponse, JSONRPCErrorError> {
let (config, auth) = self.load_plugin_share_config_and_auth().await?;
let (config, auth) = self
.load_plugin_share_config_and_auth(app_server_client_name)
.await?;
if !config.features.enabled(Feature::PluginSharing) {
return Err(invalid_request("plugin sharing is disabled"));
}
@@ -1226,9 +1283,8 @@ impl PluginRequestProcessor {
}
validate_client_plugin_share_targets(&share_targets)?;
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
let result = codex_core_plugins::remote::update_remote_plugin_share_targets(
&remote_plugin_service_config,
auth.as_ref(),
@@ -1254,11 +1310,13 @@ impl PluginRequestProcessor {
async fn plugin_share_list_response(
&self,
_params: PluginShareListParams,
app_server_client_name: Option<&str>,
) -> Result<PluginShareListResponse, JSONRPCErrorError> {
let (config, auth) = self.load_plugin_share_config_and_auth().await?;
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let (config, auth) = self
.load_plugin_share_config_and_auth(app_server_client_name)
.await?;
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
let data = codex_core_plugins::remote::list_remote_plugin_shares(
&remote_plugin_service_config,
auth.as_ref(),
@@ -1285,8 +1343,11 @@ impl PluginRequestProcessor {
async fn plugin_share_checkout_response(
&self,
params: PluginShareCheckoutParams,
app_server_client_name: Option<&str>,
) -> Result<PluginShareCheckoutResponse, JSONRPCErrorError> {
let (config, auth) = self.load_plugin_share_config_and_auth().await?;
let (config, auth) = self
.load_plugin_share_config_and_auth(app_server_client_name)
.await?;
if !config.features.enabled(Feature::PluginSharing) {
return Err(invalid_request("plugin sharing is disabled"));
}
@@ -1295,9 +1356,8 @@ impl PluginRequestProcessor {
return Err(invalid_request("invalid remote plugin id"));
}
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
let result = codex_core_plugins::remote::checkout_remote_plugin_share(
&remote_plugin_service_config,
auth.as_ref(),
@@ -1321,16 +1381,18 @@ impl PluginRequestProcessor {
async fn plugin_share_delete_response(
&self,
params: PluginShareDeleteParams,
app_server_client_name: Option<&str>,
) -> Result<PluginShareDeleteResponse, JSONRPCErrorError> {
let (config, auth) = self.load_plugin_share_config_and_auth().await?;
let (config, auth) = self
.load_plugin_share_config_and_auth(app_server_client_name)
.await?;
let PluginShareDeleteParams { remote_plugin_id } = params;
if remote_plugin_id.is_empty() || !is_valid_remote_plugin_id(&remote_plugin_id) {
return Err(invalid_request("invalid remote plugin id"));
}
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
codex_core_plugins::remote::delete_remote_plugin_share(
&remote_plugin_service_config,
auth.as_ref(),
@@ -1345,18 +1407,22 @@ impl PluginRequestProcessor {
async fn load_plugin_share_config_and_auth(
&self,
app_server_client_name: Option<&str>,
) -> Result<(Config, Option<CodexAuth>), JSONRPCErrorError> {
let config = self.load_latest_config(/*fallback_cwd*/ None).await?;
if !config.features.enabled(Feature::Plugins) {
return Err(invalid_request("plugin sharing is not enabled"));
}
let auth = self.auth_manager.auth().await;
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
Ok((config, auth))
}
async fn plugin_install_response(
&self,
params: PluginInstallParams,
app_server_client_name: Option<&str>,
) -> Result<PluginInstallResponse, JSONRPCErrorError> {
let PluginInstallParams {
marketplace_path,
@@ -1367,7 +1433,11 @@ impl PluginRequestProcessor {
(Some(marketplace_path), None) => marketplace_path,
(None, Some(remote_marketplace_name)) => {
return self
.remote_plugin_install_response(remote_marketplace_name, plugin_name)
.remote_plugin_install_response(
remote_marketplace_name,
plugin_name,
app_server_client_name,
)
.await;
}
(Some(_), Some(_)) | (None, None) => {
@@ -1378,10 +1448,12 @@ impl PluginRequestProcessor {
};
let config_cwd = marketplace_path.as_path().parent().map(Path::to_path_buf);
let config = self.load_latest_config(config_cwd.clone()).await?;
let auth = self.auth_manager.auth().await;
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
if !self
.workspace_codex_plugins_enabled(&config, auth.as_ref())
.workspace_codex_plugins_enabled(&config, auth.as_ref(), app_server_client_name)
.await
{
return Err(invalid_request(
@@ -1418,13 +1490,13 @@ impl PluginRequestProcessor {
}
let plugin_apps = load_plugin_apps(result.installed_path.as_path()).await;
let auth = self.auth_manager.auth().await;
let apps_needing_auth = self
.plugin_apps_needing_auth_for_install(
&config,
auth.as_ref().is_some_and(CodexAuth::is_chatgpt_auth),
&result.plugin_id.as_key(),
&plugin_apps,
app_server_client_name,
)
.await;
@@ -1438,6 +1510,7 @@ impl PluginRequestProcessor {
&self,
remote_marketplace_name: String,
remote_plugin_id: String,
app_server_client_name: Option<&str>,
) -> Result<PluginInstallResponse, JSONRPCErrorError> {
let config = self.load_latest_config(/*fallback_cwd*/ None).await?;
if !config.features.enabled(Feature::Plugins) {
@@ -1447,10 +1520,11 @@ impl PluginRequestProcessor {
}
validate_remote_plugin_id(&remote_plugin_id)?;
let auth = self.auth_manager.auth().await;
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
let remote_detail =
codex_core_plugins::remote::fetch_remote_plugin_detail_with_download_urls(
&remote_plugin_service_config,
@@ -1519,6 +1593,7 @@ impl PluginRequestProcessor {
&config.plugins_config_input(),
auth.clone(),
Some(self.effective_plugins_changed_callback()),
Some(http_state_context(&config, app_server_client_name)),
);
let mut plugin_metadata =
@@ -1540,6 +1615,7 @@ impl PluginRequestProcessor {
auth.as_ref().is_some_and(CodexAuth::is_chatgpt_auth),
&result.plugin_id.as_key(),
&plugin_apps,
app_server_client_name,
)
.await;
@@ -1555,18 +1631,25 @@ impl PluginRequestProcessor {
is_chatgpt_auth: bool,
plugin_id: &str,
plugin_apps: &[codex_plugin::AppConnectorId],
app_server_client_name: Option<&str>,
) -> Vec<AppSummary> {
if plugin_apps.is_empty() || !config.features.apps_enabled_for_auth(is_chatgpt_auth) {
return Vec::new();
}
let environment_manager = self.thread_manager.environment_manager();
let http_state = http_state_context(config, app_server_client_name);
let (all_connectors_result, accessible_connectors_result) = tokio::join!(
connectors::list_all_connectors_with_options(config, /*force_refetch*/ false),
connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager(
connectors::list_all_connectors_with_options_and_http_state(
config,
/*force_refetch*/ false,
Some(http_state.clone()),
),
connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager_and_http_state(
config,
/*force_refetch*/ true,
Arc::clone(&environment_manager)
Arc::clone(&environment_manager),
Some(http_state.clone()),
),
);
@@ -1577,7 +1660,7 @@ impl PluginRequestProcessor {
plugin = plugin_id,
"failed to load app metadata after plugin install: {err:#}"
);
connectors::list_cached_all_connectors(config)
connectors::list_cached_all_connectors_with_http_state(config, Some(&http_state))
.await
.unwrap_or_default()
}
@@ -1591,9 +1674,12 @@ impl PluginRequestProcessor {
"failed to load accessible apps after plugin install: {err:#}"
);
(
connectors::list_cached_accessible_connectors_from_mcp_tools(config)
.await
.unwrap_or_default(),
connectors::list_cached_accessible_connectors_from_mcp_tools_with_http_state(
config,
Some(&http_state),
)
.await
.unwrap_or_default(),
false,
)
}
@@ -1697,6 +1783,7 @@ impl PluginRequestProcessor {
async fn plugin_uninstall_response(
&self,
params: PluginUninstallParams,
app_server_client_name: Option<&str>,
) -> Result<PluginUninstallResponse, JSONRPCErrorError> {
let PluginUninstallParams { plugin_id } = params;
if codex_plugin::PluginId::parse(&plugin_id).is_err()
@@ -1705,7 +1792,9 @@ impl PluginRequestProcessor {
return Err(invalid_request("invalid remote plugin id"));
}
if is_valid_remote_plugin_id(&plugin_id) {
return self.remote_plugin_uninstall_response(plugin_id).await;
return self
.remote_plugin_uninstall_response(plugin_id, app_server_client_name)
.await;
}
let plugins_manager = self.thread_manager.plugins_manager();
@@ -1788,6 +1877,7 @@ impl PluginRequestProcessor {
async fn remote_plugin_uninstall_response(
&self,
plugin_id: String,
app_server_client_name: Option<&str>,
) -> Result<PluginUninstallResponse, JSONRPCErrorError> {
let config = self.load_latest_config(/*fallback_cwd*/ None).await?;
if !config.features.enabled(Feature::Plugins) {
@@ -1795,10 +1885,11 @@ impl PluginRequestProcessor {
}
validate_remote_plugin_id(&plugin_id)?;
let auth = self.auth_manager.auth().await;
let remote_plugin_service_config = RemotePluginServiceConfig {
chatgpt_base_url: config.chatgpt_base_url.clone(),
};
let auth = self
.auth_for_app_server_client(app_server_client_name)
.await;
let remote_plugin_service_config =
remote_plugin_service_config(&config, app_server_client_name);
let uninstall_result = codex_core_plugins::remote::uninstall_remote_plugin(
&remote_plugin_service_config,
auth.as_ref(),
@@ -1819,6 +1910,7 @@ impl PluginRequestProcessor {
&config.plugins_config_input(),
auth.clone(),
Some(self.effective_plugins_changed_callback()),
Some(http_state_context(&config, app_server_client_name)),
);
}
@@ -1833,29 +1925,37 @@ async fn load_plugin_app_summaries(
config: &Config,
plugin_apps: &[codex_plugin::AppConnectorId],
environment_manager: Arc<EnvironmentManager>,
app_server_client_name: Option<&str>,
) -> Vec<AppSummary> {
if plugin_apps.is_empty() {
return Vec::new();
}
let connectors =
match connectors::list_all_connectors_with_options(config, /*force_refetch*/ false).await {
Ok(connectors) => connectors,
Err(err) => {
warn!("failed to load app metadata for plugin/read: {err:#}");
connectors::list_cached_all_connectors(config)
.await
.unwrap_or_default()
}
};
let http_state = http_state_context(config, app_server_client_name);
let connectors = match connectors::list_all_connectors_with_options_and_http_state(
config,
/*force_refetch*/ false,
Some(http_state.clone()),
)
.await
{
Ok(connectors) => connectors,
Err(err) => {
warn!("failed to load app metadata for plugin/read: {err:#}");
connectors::list_cached_all_connectors_with_http_state(config, Some(&http_state))
.await
.unwrap_or_default()
}
};
let plugin_connectors = connectors::connectors_for_plugin_apps(connectors, plugin_apps);
let accessible_connectors =
match connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager(
match connectors::list_accessible_connectors_from_mcp_tools_with_environment_manager_and_http_state(
config,
/*force_refetch*/ false,
environment_manager,
Some(http_state),
)
.await
{