Address plugin refactor review comments

This commit is contained in:
xli-oai
2026-04-30 01:48:25 -07:00
parent 0e3c3971a0
commit 80ce900035
38 changed files with 406 additions and 465 deletions
+3
View File
@@ -1941,6 +1941,7 @@ dependencies = [
"codex-arg0",
"codex-config",
"codex-core",
"codex-core-plugins",
"codex-exec-server",
"codex-feedback",
"codex-protocol",
@@ -2100,6 +2101,7 @@ dependencies = [
"codex-app-server-protocol",
"codex-connectors",
"codex-core",
"codex-core-plugins",
"codex-features",
"codex-git-utils",
"codex-login",
@@ -2489,6 +2491,7 @@ dependencies = [
"codex-arg0",
"codex-config",
"codex-core",
"codex-core-plugins",
"codex-exec-server",
"codex-features",
"codex-login",
+1
View File
@@ -17,6 +17,7 @@ codex-app-server-protocol = { workspace = true }
codex-arg0 = { workspace = true }
codex-config = { workspace = true }
codex-core = { workspace = true }
codex-core-plugins = { workspace = true }
codex-exec-server = { workspace = true }
codex-feedback = { workspace = true }
codex-protocol = { workspace = true }
+1 -1
View File
@@ -100,7 +100,7 @@ pub mod legacy_core {
}
pub mod plugins {
pub use codex_core::plugins::PluginsManager;
pub use codex_core_plugins::PluginsManager;
}
pub mod review_format {
@@ -271,10 +271,6 @@ use codex_core::find_thread_name_by_id;
use codex_core::find_thread_names_by_ids;
use codex_core::find_thread_path_by_id_str;
use codex_core::path_utils;
use codex_core::plugins::PluginInstallError as CorePluginInstallError;
use codex_core::plugins::PluginInstallRequest;
use codex_core::plugins::PluginReadRequest;
use codex_core::plugins::PluginUninstallError as CorePluginUninstallError;
use codex_core::read_head_for_summary;
use codex_core::read_session_meta_line;
use codex_core::sandboxing::SandboxPermissions;
@@ -282,6 +278,10 @@ use codex_core::windows_sandbox::WindowsSandboxLevelExt;
use codex_core::windows_sandbox::WindowsSandboxSetupMode as CoreWindowsSandboxSetupMode;
use codex_core::windows_sandbox::WindowsSandboxSetupRequest;
use codex_core_plugins::OPENAI_CURATED_MARKETPLACE_NAME;
use codex_core_plugins::PluginInstallError as CorePluginInstallError;
use codex_core_plugins::PluginInstallRequest;
use codex_core_plugins::PluginReadRequest;
use codex_core_plugins::PluginUninstallError as CorePluginUninstallError;
use codex_core_plugins::loader::load_plugin_apps;
use codex_core_plugins::loader::load_plugin_mcp_servers;
use codex_core_plugins::manifest::PluginManifestInterface;
@@ -6306,9 +6306,7 @@ impl CodexMessageProcessor {
plugins_manager
.effective_skill_roots_for_layer_stack(
&config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
config.plugin_feature_flags(),
)
.await
} else {
@@ -6391,13 +6389,10 @@ impl CodexMessageProcessor {
config.features.enabled(Feature::Plugins) && workspace_codex_plugins_enabled;
let plugin_outcome = if plugins_enabled && config.features.enabled(Feature::PluginHooks)
{
let mut plugin_feature_flags = config.plugin_feature_flags();
plugin_feature_flags.plugins_enabled = plugins_enabled;
plugins_manager
.plugins_for_layer_stack(
&config.config_layer_stack,
plugins_enabled,
config.features.enabled(Feature::RemotePlugin),
/*plugin_hooks_feature_enabled*/ true,
)
.plugins_for_layer_stack(&config.config_layer_stack, plugin_feature_flags)
.await
} else {
codex_core::plugins::PluginLoadOutcome::default()
@@ -2,8 +2,8 @@ use codex_config::types::PluginConfig;
use codex_core::config::Config;
use codex_core::config::ConfigBuilder;
use codex_core::plugins::PluginId;
use codex_core::plugins::PluginInstallRequest;
use codex_core::plugins::PluginsManager;
use codex_core_plugins::PluginInstallRequest;
use codex_core_plugins::PluginsManager;
use codex_core_plugins::marketplace::MarketplacePluginInstallPolicy;
use codex_core_plugins::marketplace::find_marketplace_manifest_path;
use codex_core_plugins::marketplace_add::MarketplaceAddRequest;
+1
View File
@@ -13,6 +13,7 @@ clap = { workspace = true, features = ["derive"] }
codex-app-server-protocol = { workspace = true }
codex-connectors = { workspace = true }
codex-core = { workspace = true }
codex-core-plugins = { workspace = true }
codex-features = { workspace = true }
codex-git-utils = { workspace = true }
codex-login = { workspace = true }
+2 -8
View File
@@ -17,8 +17,7 @@ pub use codex_core::connectors::list_accessible_connectors_from_mcp_tools_with_o
pub use codex_core::connectors::list_cached_accessible_connectors_from_mcp_tools;
pub use codex_core::connectors::with_app_enabled_state;
use codex_core::plugins::AppConnectorId;
use codex_core::plugins::PluginsManager;
use codex_features::Feature;
use codex_core_plugins::PluginsManager;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_login::default_client::originator;
@@ -138,12 +137,7 @@ fn all_connectors_cache_key(config: &Config, auth: &CodexAuth) -> AllConnectorsC
async fn plugin_apps_for_config(config: &Config) -> Vec<codex_core::plugins::AppConnectorId> {
PluginsManager::new(config.codex_home.to_path_buf())
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await
.effective_apps()
}
+2 -2
View File
@@ -4,12 +4,12 @@ use anyhow::bail;
use clap::Parser;
use codex_core::config::Config;
use codex_core::config::find_codex_home;
use codex_core::plugins::PluginMarketplaceUpgradeOutcome;
use codex_core::plugins::PluginsManager;
use codex_core_plugins::PluginsManager;
use codex_core_plugins::marketplace_add::MarketplaceAddRequest;
use codex_core_plugins::marketplace_add::add_marketplace;
use codex_core_plugins::marketplace_remove::MarketplaceRemoveRequest;
use codex_core_plugins::marketplace_remove::remove_marketplace;
use codex_core_plugins::marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome as PluginMarketplaceUpgradeOutcome;
use codex_utils_cli::CliConfigOverrides;
#[derive(Debug, Parser)]
+1 -1
View File
@@ -14,7 +14,7 @@ use codex_core::config::Config;
use codex_core::config::edit::ConfigEditsBuilder;
use codex_core::config::find_codex_home;
use codex_core::config::load_global_mcp_servers;
use codex_core::plugins::PluginsManager;
use codex_core_plugins::PluginsManager;
use codex_mcp::McpOAuthLoginSupport;
use codex_mcp::ResolvedMcpOAuthScopes;
use codex_mcp::compute_auth_statuses;
+1
View File
@@ -14,6 +14,7 @@ mod mcp_types;
mod merge;
mod overrides;
pub mod permissions_toml;
mod plugin_edit;
pub mod profile_toml;
mod project_root_markers;
mod requirements_exec_policy;
+5 -104
View File
@@ -7,7 +7,6 @@ use std::path::PathBuf;
use tokio::task;
use toml::Value as TomlValue;
use toml_edit::DocumentMut;
use toml_edit::InlineTable;
use toml_edit::Item as TomlItem;
use toml_edit::Table as TomlTable;
use toml_edit::value;
@@ -20,6 +19,8 @@ use crate::CONFIG_TOML_FILE;
use crate::McpServerConfig;
use crate::McpServerEnvVar;
use crate::McpServerTransportConfig;
use crate::plugin_edit::PluginConfigEdit;
use crate::plugin_edit::apply_plugin_config_edit;
pub async fn load_global_mcp_servers(
codex_home: &Path,
@@ -69,11 +70,6 @@ pub struct ConfigEditsBuilder {
plugin_edits: Vec<PluginConfigEdit>,
}
enum PluginConfigEdit {
SetEnabled { plugin_id: String, enabled: bool },
Clear { plugin_id: String },
}
impl ConfigEditsBuilder {
pub fn new(codex_home: &Path) -> Self {
Self {
@@ -89,17 +85,13 @@ impl ConfigEditsBuilder {
}
pub fn set_plugin_enabled(mut self, plugin_id: &str, enabled: bool) -> Self {
self.plugin_edits.push(PluginConfigEdit::SetEnabled {
plugin_id: plugin_id.to_string(),
enabled,
});
self.plugin_edits
.push(PluginConfigEdit::set_enabled(plugin_id, enabled));
self
}
pub fn clear_plugin(mut self, plugin_id: &str) -> Self {
self.plugin_edits.push(PluginConfigEdit::Clear {
plugin_id: plugin_id.to_string(),
});
self.plugin_edits.push(PluginConfigEdit::clear(plugin_id));
self
}
@@ -158,97 +150,6 @@ fn replace_mcp_servers(doc: &mut DocumentMut, servers: &BTreeMap<String, McpServ
root.insert("mcp_servers", TomlItem::Table(table));
}
fn apply_plugin_config_edit(doc: &mut DocumentMut, edit: &PluginConfigEdit) -> bool {
match edit {
PluginConfigEdit::SetEnabled { plugin_id, enabled } => {
set_plugin_enabled(doc, plugin_id, *enabled)
}
PluginConfigEdit::Clear { plugin_id } => clear_plugin(doc, plugin_id),
}
}
fn set_plugin_enabled(doc: &mut DocumentMut, plugin_id: &str, enabled: bool) -> bool {
let root = doc.as_table_mut();
let plugins = ensure_table(root, "plugins", /*implicit*/ true);
let plugin = ensure_table(plugins, plugin_id, /*implicit*/ false);
let mut replacement = value(enabled);
if let Some(existing) = plugin.get("enabled") {
preserve_decor(existing, &mut replacement);
}
plugin["enabled"] = replacement;
true
}
fn clear_plugin(doc: &mut DocumentMut, plugin_id: &str) -> bool {
let root = doc.as_table_mut();
let Some(item) = root.get_mut("plugins") else {
return false;
};
match item {
TomlItem::Table(plugins) => plugins.remove(plugin_id).is_some(),
TomlItem::Value(value) => {
let Some(inline) = value.as_inline_table().cloned() else {
return false;
};
*item = TomlItem::Table(table_from_inline(&inline, /*implicit*/ true));
if let TomlItem::Table(plugins) = item {
return plugins.remove(plugin_id).is_some();
}
false
}
_ => false,
}
}
fn preserve_decor(existing: &TomlItem, replacement: &mut TomlItem) {
if let (TomlItem::Value(existing_value), TomlItem::Value(replacement_value)) =
(existing, replacement)
{
replacement_value
.decor_mut()
.clone_from(existing_value.decor());
}
}
fn ensure_table<'a>(parent: &'a mut TomlTable, key: &str, implicit: bool) -> &'a mut TomlTable {
match parent.get_mut(key) {
Some(TomlItem::Table(_)) => {}
Some(item @ TomlItem::Value(_)) => {
if let Some(inline) = item.as_value().and_then(toml_edit::Value::as_inline_table) {
*item = TomlItem::Table(table_from_inline(inline, implicit));
} else {
*item = TomlItem::Table(new_table(implicit));
}
}
Some(item) => {
*item = TomlItem::Table(new_table(implicit));
}
None => {
parent.insert(key, TomlItem::Table(new_table(implicit)));
}
}
let Some(TomlItem::Table(table)) = parent.get_mut(key) else {
unreachable!("inserted value should be a table");
};
table
}
fn new_table(implicit: bool) -> TomlTable {
let mut table = TomlTable::new();
table.set_implicit(implicit);
table
}
fn table_from_inline(inline: &InlineTable, implicit: bool) -> TomlTable {
let mut table = new_table(implicit);
for (key, value) in inline.iter() {
let mut value = value.clone();
value.decor_mut().set_suffix("");
table.insert(key, TomlItem::Value(value));
}
table
}
fn serialize_mcp_server(config: &McpServerConfig) -> TomlItem {
let mut entry = TomlTable::new();
entry.set_implicit(false);
-165
View File
@@ -2,8 +2,6 @@ use super::*;
use crate::McpServerToolConfig;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
#[cfg(unix)]
use std::os::unix::fs::symlink;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
@@ -84,166 +82,3 @@ approval_mode = "approve"
Ok(())
}
#[cfg(unix)]
#[tokio::test]
async fn plugin_edits_write_through_symlink_chain() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-symlink-test-{}-{unique_suffix}",
std::process::id()
));
let target_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-symlink-target-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
std::fs::create_dir_all(&target_home)?;
let target_path = target_home.join(CONFIG_TOML_FILE);
let link_path = codex_home.join("config-link.toml");
let config_path = codex_home.join(CONFIG_TOML_FILE);
std::fs::write(
&target_path,
r#"[plugins."linear@openai-curated"]
enabled = false
"#,
)?;
symlink(&target_path, &link_path)?;
symlink("config-link.toml", &config_path)?;
ConfigEditsBuilder::new(&codex_home)
.set_plugin_enabled("linear@openai-curated", /*enabled*/ true)
.apply()
.await?;
let meta = std::fs::symlink_metadata(&config_path)?;
assert!(meta.file_type().is_symlink());
assert_eq!(
std::fs::read_to_string(target_path)?,
r#"[plugins."linear@openai-curated"]
enabled = true
"#
);
std::fs::remove_dir_all(&codex_home)?;
std::fs::remove_dir_all(&target_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_edits_set_and_clear_enabled_entries() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
std::fs::write(
codex_home.join(CONFIG_TOML_FILE),
r#"[plugins."linear@openai-curated"]
enabled = false
[plugins."gmail@openai-curated"]
enabled = true
"#,
)?;
ConfigEditsBuilder::new(&codex_home)
.set_plugin_enabled("linear@openai-curated", /*enabled*/ true)
.clear_plugin("gmail@openai-curated")
.apply()
.await?;
let serialized = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE))?;
assert_eq!(
serialized,
r#"[plugins."linear@openai-curated"]
enabled = true
"#
);
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_clear_leaves_malformed_plugins_scalar_unchanged() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-clear-malformed-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
let config_path = codex_home.join(CONFIG_TOML_FILE);
let config = r#"plugins = "bad"
"#;
std::fs::write(&config_path, config)?;
ConfigEditsBuilder::new(&codex_home)
.clear_plugin("linear@openai-curated")
.apply()
.await?;
assert_eq!(std::fs::read_to_string(&config_path)?, config);
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_clear_missing_entry_does_not_create_config() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-clear-missing-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
let config_path = codex_home.join(CONFIG_TOML_FILE);
ConfigEditsBuilder::new(&codex_home)
.clear_plugin("linear@openai-curated")
.apply()
.await?;
assert!(!config_path.exists());
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_enabled_update_preserves_existing_value_decor() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-decor-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
let config_path = codex_home.join(CONFIG_TOML_FILE);
std::fs::write(
&config_path,
r#"[plugins."linear@openai-curated"]
enabled = false # keep
"#,
)?;
ConfigEditsBuilder::new(&codex_home)
.set_plugin_enabled("linear@openai-curated", /*enabled*/ true)
.apply()
.await?;
assert_eq!(
std::fs::read_to_string(&config_path)?,
r#"[plugins."linear@openai-curated"]
enabled = true # keep
"#
);
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
+120
View File
@@ -0,0 +1,120 @@
use toml_edit::DocumentMut;
use toml_edit::InlineTable;
use toml_edit::Item as TomlItem;
use toml_edit::Table as TomlTable;
use toml_edit::value;
pub(crate) enum PluginConfigEdit {
SetEnabled { plugin_id: String, enabled: bool },
Clear { plugin_id: String },
}
impl PluginConfigEdit {
pub(crate) fn set_enabled(plugin_id: &str, enabled: bool) -> Self {
Self::SetEnabled {
plugin_id: plugin_id.to_string(),
enabled,
}
}
pub(crate) fn clear(plugin_id: &str) -> Self {
Self::Clear {
plugin_id: plugin_id.to_string(),
}
}
}
pub(crate) fn apply_plugin_config_edit(doc: &mut DocumentMut, edit: &PluginConfigEdit) -> bool {
match edit {
PluginConfigEdit::SetEnabled { plugin_id, enabled } => {
set_plugin_enabled(doc, plugin_id, *enabled)
}
PluginConfigEdit::Clear { plugin_id } => clear_plugin(doc, plugin_id),
}
}
fn set_plugin_enabled(doc: &mut DocumentMut, plugin_id: &str, enabled: bool) -> bool {
let root = doc.as_table_mut();
let plugins = ensure_table(root, "plugins", /*implicit*/ true);
let plugin = ensure_table(plugins, plugin_id, /*implicit*/ false);
let mut replacement = value(enabled);
if let Some(existing) = plugin.get("enabled") {
preserve_decor(existing, &mut replacement);
}
plugin["enabled"] = replacement;
true
}
fn clear_plugin(doc: &mut DocumentMut, plugin_id: &str) -> bool {
let root = doc.as_table_mut();
let Some(item) = root.get_mut("plugins") else {
return false;
};
match item {
TomlItem::Table(plugins) => plugins.remove(plugin_id).is_some(),
TomlItem::Value(value) => {
let Some(inline) = value.as_inline_table().cloned() else {
return false;
};
*item = TomlItem::Table(table_from_inline(&inline, /*implicit*/ true));
if let TomlItem::Table(plugins) = item {
return plugins.remove(plugin_id).is_some();
}
false
}
_ => false,
}
}
fn preserve_decor(existing: &TomlItem, replacement: &mut TomlItem) {
if let (TomlItem::Value(existing_value), TomlItem::Value(replacement_value)) =
(existing, replacement)
{
replacement_value
.decor_mut()
.clone_from(existing_value.decor());
}
}
fn ensure_table<'a>(parent: &'a mut TomlTable, key: &str, implicit: bool) -> &'a mut TomlTable {
match parent.get_mut(key) {
Some(TomlItem::Table(_)) => {}
Some(item @ TomlItem::Value(_)) => {
if let Some(inline) = item.as_value().and_then(toml_edit::Value::as_inline_table) {
*item = TomlItem::Table(table_from_inline(inline, implicit));
} else {
*item = TomlItem::Table(new_table(implicit));
}
}
Some(item) => {
*item = TomlItem::Table(new_table(implicit));
}
None => {
parent.insert(key, TomlItem::Table(new_table(implicit)));
}
}
let Some(TomlItem::Table(table)) = parent.get_mut(key) else {
unreachable!("inserted value should be a table");
};
table
}
fn new_table(implicit: bool) -> TomlTable {
let mut table = TomlTable::new();
table.set_implicit(implicit);
table
}
fn table_from_inline(inline: &InlineTable, implicit: bool) -> TomlTable {
let mut table = new_table(implicit);
for (key, value) in inline.iter() {
let mut value = value.clone();
value.decor_mut().set_suffix("");
table.insert(key, TomlItem::Value(value));
}
table
}
#[cfg(test)]
#[path = "plugin_edit_tests.rs"]
mod tests;
+170
View File
@@ -0,0 +1,170 @@
use crate::CONFIG_TOML_FILE;
use crate::ConfigEditsBuilder;
use pretty_assertions::assert_eq;
#[cfg(unix)]
use std::os::unix::fs::symlink;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
#[cfg(unix)]
#[tokio::test]
async fn plugin_edits_write_through_symlink_chain() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-symlink-test-{}-{unique_suffix}",
std::process::id()
));
let target_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-symlink-target-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
std::fs::create_dir_all(&target_home)?;
let target_path = target_home.join(CONFIG_TOML_FILE);
let link_path = codex_home.join("config-link.toml");
let config_path = codex_home.join(CONFIG_TOML_FILE);
std::fs::write(
&target_path,
r#"[plugins."linear@openai-curated"]
enabled = false
"#,
)?;
symlink(&target_path, &link_path)?;
symlink("config-link.toml", &config_path)?;
ConfigEditsBuilder::new(&codex_home)
.set_plugin_enabled("linear@openai-curated", /*enabled*/ true)
.apply()
.await?;
let meta = std::fs::symlink_metadata(&config_path)?;
assert!(meta.file_type().is_symlink());
assert_eq!(
std::fs::read_to_string(target_path)?,
r#"[plugins."linear@openai-curated"]
enabled = true
"#
);
std::fs::remove_dir_all(&codex_home)?;
std::fs::remove_dir_all(&target_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_edits_set_and_clear_enabled_entries() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
std::fs::write(
codex_home.join(CONFIG_TOML_FILE),
r#"[plugins."linear@openai-curated"]
enabled = false
[plugins."gmail@openai-curated"]
enabled = true
"#,
)?;
ConfigEditsBuilder::new(&codex_home)
.set_plugin_enabled("linear@openai-curated", /*enabled*/ true)
.clear_plugin("gmail@openai-curated")
.apply()
.await?;
let serialized = std::fs::read_to_string(codex_home.join(CONFIG_TOML_FILE))?;
assert_eq!(
serialized,
r#"[plugins."linear@openai-curated"]
enabled = true
"#
);
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_clear_leaves_malformed_plugins_scalar_unchanged() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-clear-malformed-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
let config_path = codex_home.join(CONFIG_TOML_FILE);
let config = r#"plugins = "bad"
"#;
std::fs::write(&config_path, config)?;
ConfigEditsBuilder::new(&codex_home)
.clear_plugin("linear@openai-curated")
.apply()
.await?;
assert_eq!(std::fs::read_to_string(&config_path)?, config);
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_clear_missing_entry_does_not_create_config() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-clear-missing-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
let config_path = codex_home.join(CONFIG_TOML_FILE);
ConfigEditsBuilder::new(&codex_home)
.clear_plugin("linear@openai-curated")
.apply()
.await?;
assert!(!config_path.exists());
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
#[tokio::test]
async fn plugin_enabled_update_preserves_existing_value_decor() -> anyhow::Result<()> {
let unique_suffix = SystemTime::now().duration_since(UNIX_EPOCH)?.as_nanos();
let codex_home = std::env::temp_dir().join(format!(
"codex-config-plugin-edit-decor-test-{}-{unique_suffix}",
std::process::id()
));
std::fs::create_dir_all(&codex_home)?;
let config_path = codex_home.join(CONFIG_TOML_FILE);
std::fs::write(
&config_path,
r#"[plugins."linear@openai-curated"]
enabled = false # keep
"#,
)?;
ConfigEditsBuilder::new(&codex_home)
.set_plugin_enabled("linear@openai-curated", /*enabled*/ true)
.apply()
.await?;
assert_eq!(
std::fs::read_to_string(&config_path)?,
r#"[plugins."linear@openai-curated"]
enabled = true # keep
"#
);
std::fs::remove_dir_all(&codex_home)?;
Ok(())
}
+1
View File
@@ -17,6 +17,7 @@ codex-arg0 = { workspace = true }
codex-analytics = { workspace = true }
codex-config = { workspace = true }
codex-core = { workspace = true }
codex-core-plugins = { workspace = true }
codex-exec-server = { workspace = true }
codex-features = { workspace = true }
codex-login = { workspace = true }
+1 -1
View File
@@ -35,9 +35,9 @@ pub use codex_core::config::Permissions;
pub use codex_core::config::TerminalResizeReflowConfig;
pub use codex_core::config::ThreadStoreConfig;
pub use codex_core::config::find_codex_home;
pub use codex_core::plugins::PluginsManager;
pub use codex_core::skills::SkillsManager;
pub use codex_core::thread_store_from_config;
pub use codex_core_plugins::PluginsManager;
pub use codex_exec_server::EnvironmentManager;
pub use codex_exec_server::EnvironmentManagerArgs;
pub use codex_exec_server::ExecServerRuntimePaths;
+1
View File
@@ -45,6 +45,7 @@ pub use manager::ConfiguredMarketplaceListOutcome;
pub use manager::ConfiguredMarketplacePlugin;
pub use manager::PluginDetail;
pub use manager::PluginDetailsUnavailableReason;
pub use manager::PluginFeatureFlags;
pub use manager::PluginInstallError;
pub use manager::PluginInstallOutcome;
pub use manager::PluginInstallRequest;
+30 -46
View File
@@ -77,6 +77,16 @@ static CURATED_REPO_SYNC_STARTED: AtomicBool = AtomicBool::new(false);
const FEATURED_PLUGIN_IDS_CACHE_TTL: std::time::Duration =
std::time::Duration::from_secs(60 * 60 * 3);
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct PluginFeatureFlags {
/// Whether plugin loading is enabled at all.
pub plugins_enabled: bool,
/// Whether remotely installed plugins should be included in local plugin loading.
pub remote_plugins_enabled: bool,
/// Whether plugin-provided hooks should be loaded.
pub plugin_hooks_enabled: bool,
}
#[derive(Clone, PartialEq, Eq)]
struct FeaturedPluginIdsCacheKey {
chatgpt_base_url: String,
@@ -370,8 +380,7 @@ pub struct PluginsManager {
#[derive(Clone)]
struct CachedPluginLoadOutcome {
config_version: String,
remote_plugins_enabled: bool,
plugin_hooks_enabled: bool,
feature_flags: PluginFeatureFlags,
outcome: PluginLoadOutcome,
}
@@ -431,15 +440,11 @@ impl PluginsManager {
pub async fn plugins_for_config(
&self,
config_layer_stack: &ConfigLayerStack,
plugins_enabled: bool,
remote_plugins_enabled: bool,
plugin_hooks_enabled: bool,
feature_flags: PluginFeatureFlags,
) -> PluginLoadOutcome {
self.plugins_for_config_with_force_reload(
config_layer_stack,
plugins_enabled,
remote_plugins_enabled,
plugin_hooks_enabled,
feature_flags,
/*force_reload*/ false,
)
.await
@@ -448,32 +453,26 @@ impl PluginsManager {
pub(crate) async fn plugins_for_config_with_force_reload(
&self,
config_layer_stack: &ConfigLayerStack,
plugins_enabled: bool,
remote_plugins_enabled: bool,
plugin_hooks_enabled: bool,
feature_flags: PluginFeatureFlags,
force_reload: bool,
) -> PluginLoadOutcome {
if !plugins_enabled {
if !feature_flags.plugins_enabled {
return PluginLoadOutcome::default();
}
let config_version = version_for_toml(&config_layer_stack.effective_config());
if !force_reload
&& let Some(outcome) = self.cached_enabled_outcome(
&config_version,
remote_plugins_enabled,
plugin_hooks_enabled,
)
&& let Some(outcome) = self.cached_enabled_outcome(&config_version, feature_flags)
{
return outcome;
}
let outcome = load_plugins_from_layer_stack(
config_layer_stack,
self.remote_installed_plugin_configs(remote_plugins_enabled),
self.remote_installed_plugin_configs(feature_flags.remote_plugins_enabled),
&self.store,
self.restriction_product,
plugin_hooks_enabled,
feature_flags.plugin_hooks_enabled,
)
.await;
log_plugin_load_errors(&outcome);
@@ -483,8 +482,7 @@ impl PluginsManager {
};
*cache = Some(CachedPluginLoadOutcome {
config_version,
remote_plugins_enabled,
plugin_hooks_enabled,
feature_flags,
outcome: outcome.clone(),
});
outcome
@@ -511,19 +509,17 @@ impl PluginsManager {
pub async fn plugins_for_layer_stack(
&self,
config_layer_stack: &ConfigLayerStack,
plugins_enabled: bool,
remote_plugins_enabled: bool,
plugin_hooks_feature_enabled: bool,
feature_flags: PluginFeatureFlags,
) -> PluginLoadOutcome {
if !plugins_enabled {
if !feature_flags.plugins_enabled {
return PluginLoadOutcome::default();
}
load_plugins_from_layer_stack(
config_layer_stack,
self.remote_installed_plugin_configs(remote_plugins_enabled),
self.remote_installed_plugin_configs(feature_flags.remote_plugins_enabled),
&self.store,
self.restriction_product,
plugin_hooks_feature_enabled,
feature_flags.plugin_hooks_enabled,
)
.await
}
@@ -532,42 +528,30 @@ impl PluginsManager {
pub async fn effective_skill_roots_for_layer_stack(
&self,
config_layer_stack: &ConfigLayerStack,
plugins_enabled: bool,
remote_plugins_enabled: bool,
plugin_hooks_enabled: bool,
feature_flags: PluginFeatureFlags,
) -> Vec<AbsolutePathBuf> {
self.plugins_for_layer_stack(
config_layer_stack,
plugins_enabled,
remote_plugins_enabled,
plugin_hooks_enabled,
)
.await
.effective_skill_roots()
self.plugins_for_layer_stack(config_layer_stack, feature_flags)
.await
.effective_skill_roots()
}
fn cached_enabled_outcome(
&self,
config_version: &str,
remote_plugins_enabled: bool,
plugin_hooks_enabled: bool,
feature_flags: PluginFeatureFlags,
) -> Option<PluginLoadOutcome> {
match self.cached_enabled_outcome.read() {
Ok(cache) => cache
.as_ref()
.filter(|cached| {
cached.config_version == config_version
&& cached.remote_plugins_enabled == remote_plugins_enabled
&& cached.plugin_hooks_enabled == plugin_hooks_enabled
cached.config_version == config_version && cached.feature_flags == feature_flags
})
.map(|cached| cached.outcome.clone()),
Err(err) => err
.into_inner()
.as_ref()
.filter(|cached| {
cached.config_version == config_version
&& cached.remote_plugins_enabled == remote_plugins_enabled
&& cached.plugin_hooks_enabled == plugin_hooks_enabled
cached.config_version == config_version && cached.feature_flags == feature_flags
})
.map(|cached| cached.outcome.clone()),
}
+19 -14
View File
@@ -264,6 +264,14 @@ impl TestPluginConfig {
chatgpt_base_url: chatgpt_base_url(&effective_config),
}
}
fn plugin_feature_flags(&self) -> PluginFeatureFlags {
PluginFeatureFlags {
plugins_enabled: self.plugins_enabled,
remote_plugins_enabled: self.remote_plugins_enabled,
plugin_hooks_enabled: self.plugin_hooks_enabled,
}
}
}
fn feature_enabled(config: &TomlValue, feature_name: &str) -> bool {
@@ -327,9 +335,7 @@ impl PluginsManagerTestConfigExt for PluginsManager {
PluginsManager::plugins_for_config(
self,
&config.config_layer_stack,
config.plugins_enabled,
config.remote_plugins_enabled,
config.plugin_hooks_enabled,
config.plugin_feature_flags(),
)
.await
}
@@ -620,14 +626,11 @@ remote_plugin = true
let enabled_outcome = manager.plugins_for_test_config(&config).await;
assert_eq!(enabled_outcome.plugins().len(), 1);
let disabled_outcome = PluginsManager::plugins_for_config(
&manager,
&config.config_layer_stack,
config.plugins_enabled,
/*remote_plugins_enabled*/ false,
config.plugin_hooks_enabled,
)
.await;
let mut feature_flags = config.plugin_feature_flags();
feature_flags.remote_plugins_enabled = false;
let disabled_outcome =
PluginsManager::plugins_for_config(&manager, &config.config_layer_stack, feature_flags)
.await;
assert_eq!(disabled_outcome, PluginLoadOutcome::default());
}
@@ -730,9 +733,11 @@ async fn remote_installed_plugins_cache_refresh_clears_stale_cache_when_auth_is_
let outcome = manager
.plugins_for_config(
&ConfigLayerStack::default(),
/*plugins_enabled*/ true,
/*remote_plugins_enabled*/ true,
/*plugin_hooks_enabled*/ true,
PluginFeatureFlags {
plugins_enabled: true,
remote_plugins_enabled: true,
plugin_hooks_enabled: true,
},
)
.await;
assert_eq!(outcome, PluginLoadOutcome::default());
+2 -8
View File
@@ -2,10 +2,9 @@ use super::*;
use crate::SkillsManager;
use crate::config::CONFIG_TOML_FILE;
use crate::config::ConfigBuilder;
use crate::plugins::PluginsManager;
use crate::skills_load_input_from_config;
use codex_config::ConfigLayerStackOrdering;
use codex_features::Feature;
use codex_core_plugins::PluginsManager;
use codex_protocol::config_types::ReasoningSummary;
use codex_protocol::config_types::Verbosity;
use codex_protocol::openai_models::ReasoningEffort;
@@ -657,12 +656,7 @@ enabled = false
let skills_manager =
SkillsManager::new(home.path().abs(), /*bundled_skills_enabled*/ true);
let plugin_outcome = plugins_manager
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await;
let effective_skill_roots = plugin_outcome.effective_skill_roots();
let skills_input = skills_load_input_from_config(&config, effective_skill_roots);
+1 -1
View File
@@ -4,7 +4,6 @@ use crate::config::ThreadStoreConfig;
use crate::config::edit::ConfigEdit;
use crate::config::edit::ConfigEditsBuilder;
use crate::config::edit::apply_blocking;
use crate::plugins::PluginsManager;
use assert_matches::assert_matches;
use codex_config::CONFIG_TOML_FILE;
use codex_config::RequirementSource;
@@ -53,6 +52,7 @@ use codex_config::types::TuiKeymap;
use codex_config::types::TuiNotificationSettings;
use codex_config::types::WindowsSandboxModeToml;
use codex_config::types::WindowsToml;
use codex_core_plugins::PluginsManager;
use codex_exec_server::LOCAL_FS;
use codex_features::Feature;
use codex_features::FeaturesToml;
+10 -7
View File
@@ -996,17 +996,20 @@ impl Config {
}
}
pub fn plugin_feature_flags(&self) -> codex_core_plugins::PluginFeatureFlags {
codex_core_plugins::PluginFeatureFlags {
plugins_enabled: self.features.enabled(Feature::Plugins),
remote_plugins_enabled: self.features.enabled(Feature::RemotePlugin),
plugin_hooks_enabled: self.features.enabled(Feature::PluginHooks),
}
}
pub async fn to_mcp_config(
&self,
plugins_manager: &crate::plugins::PluginsManager,
plugins_manager: &codex_core_plugins::PluginsManager,
) -> McpConfig {
let loaded_plugins = plugins_manager
.plugins_for_config(
&self.config_layer_stack,
self.features.enabled(Feature::Plugins),
self.features.enabled(Feature::RemotePlugin),
self.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&self.config_layer_stack, self.plugin_feature_flags())
.await;
let mut configured_mcp_servers = self.mcp_servers.get().clone();
for plugin in loaded_plugins
+2 -7
View File
@@ -27,12 +27,12 @@ use tracing::warn;
use crate::config::Config;
use crate::mcp::McpManager;
use crate::plugins::PluginsManager;
use crate::session::INITIAL_SUBMIT_ID;
use codex_config::AppsRequirementsToml;
use codex_config::types::AppToolApproval;
use codex_config::types::AppsConfigToml;
use codex_config::types::ToolSuggestDiscoverableType;
use codex_core_plugins::PluginsManager;
use codex_features::Feature;
use codex_login::AuthManager;
use codex_login::CodexAuth;
@@ -409,12 +409,7 @@ fn write_cached_accessible_connectors(
async fn tool_suggest_connector_ids(config: &Config) -> HashSet<String> {
let mut connector_ids = PluginsManager::new(config.codex_home.to_path_buf())
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await
.capability_summaries()
.iter()
+1 -1
View File
@@ -2,8 +2,8 @@ use std::collections::HashMap;
use std::sync::Arc;
use crate::config::Config;
use crate::plugins::PluginsManager;
use codex_config::McpServerConfig;
use codex_core_plugins::PluginsManager;
use codex_login::CodexAuth;
use codex_mcp::ToolPluginProvenance;
use codex_mcp::configured_mcp_servers;
+2 -9
View File
@@ -758,9 +758,7 @@ async fn custom_mcp_tool_approval_mode(
.plugins_manager
.plugins_for_config(
&turn_context.config.config_layer_stack,
turn_context.config.features.enabled(Feature::Plugins),
turn_context.config.features.enabled(Feature::RemotePlugin),
turn_context.config.features.enabled(Feature::PluginHooks),
turn_context.config.plugin_feature_flags(),
)
.await
.plugins()
@@ -1810,12 +1808,7 @@ async fn persist_non_app_mcp_tool_approval(
let plugin_config_name = sess
.services
.plugins_manager
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await
.plugins()
.iter()
+1 -4
View File
@@ -13,7 +13,6 @@ use codex_config::types::ApprovalsReviewer;
use codex_config::types::AppsConfigToml;
use codex_config::types::McpServerConfig;
use codex_config::types::McpServerToolConfig;
use codex_features::Feature;
use codex_hooks::Hooks;
use codex_hooks::HooksConfig;
use codex_model_provider::create_model_provider;
@@ -1597,9 +1596,7 @@ enabled = true
.plugins_manager
.plugins_for_config(
&initial_config.config_layer_stack,
initial_config.features.enabled(Feature::Plugins),
initial_config.features.enabled(Feature::RemotePlugin),
initial_config.features.enabled(Feature::PluginHooks),
initial_config.plugin_feature_flags(),
)
.await;
std::fs::write(
-16
View File
@@ -6,22 +6,6 @@ pub(crate) mod test_support;
use codex_config::types::McpServerConfig;
pub use codex_core_plugins::ConfiguredMarketplace;
pub use codex_core_plugins::ConfiguredMarketplaceListOutcome;
pub use codex_core_plugins::ConfiguredMarketplacePlugin;
pub use codex_core_plugins::PluginDetail;
pub use codex_core_plugins::PluginDetailsUnavailableReason;
pub use codex_core_plugins::PluginInstallError;
pub use codex_core_plugins::PluginInstallOutcome;
pub use codex_core_plugins::PluginInstallRequest;
pub use codex_core_plugins::PluginReadOutcome;
pub use codex_core_plugins::PluginReadRequest;
pub use codex_core_plugins::PluginRemoteSyncError;
pub use codex_core_plugins::PluginUninstallError;
pub use codex_core_plugins::PluginsManager;
pub use codex_core_plugins::RemotePluginSyncResult;
pub use codex_core_plugins::marketplace_upgrade::ConfiguredMarketplaceUpgradeError as PluginMarketplaceUpgradeError;
pub use codex_core_plugins::marketplace_upgrade::ConfiguredMarketplaceUpgradeOutcome as PluginMarketplaceUpgradeOutcome;
pub use codex_plugin::AppConnectorId;
pub use codex_plugin::EffectiveSkillRoots;
pub use codex_plugin::PluginCapabilitySummary;
+1 -4
View File
@@ -23,7 +23,6 @@ use codex_config::CloudRequirementsLoader;
use codex_config::LoaderOverrides;
use codex_config::loader::load_config_layers_state;
use codex_exec_server::LOCAL_FS;
use codex_features::Feature;
use codex_utils_absolute_path::AbsolutePathBuf;
use crate::review_prompts::resolve_review_request;
@@ -624,9 +623,7 @@ pub async fn list_skills(sess: &Session, sub_id: String, cwds: Vec<PathBuf>, for
let effective_skill_roots = plugins_manager
.effective_skill_roots_for_layer_stack(
&config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
config.plugin_feature_flags(),
)
.await;
let skills_input = crate::SkillsLoadInput::new(
+4 -16
View File
@@ -274,7 +274,6 @@ use crate::exec_policy::ExecPolicyUpdateError;
use crate::guardian::GuardianReviewSessionManager;
use crate::mcp::McpManager;
use crate::network_policy_decision::execpolicy_network_rule_amendment;
use crate::plugins::PluginsManager;
use crate::rollout::map_session_init_error;
use crate::session_startup_prewarm::SessionStartupPrewarmHandle;
use crate::shell;
@@ -301,6 +300,7 @@ use crate::turn_timing::TurnTimingState;
use crate::turn_timing::record_turn_ttfm_metric;
use crate::unified_exec::UnifiedExecProcessManager;
use crate::windows_sandbox::WindowsSandboxLevelExt;
use codex_core_plugins::PluginsManager;
use codex_git_utils::get_git_repo_root;
use codex_mcp::compute_auth_statuses;
use codex_mcp::with_codex_apps_mcp;
@@ -476,12 +476,7 @@ impl Codex {
.as_ref()
.map(|environment| environment.get_filesystem());
let plugin_outcome = plugins_manager
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await;
let effective_skill_roots = plugin_outcome.effective_skill_roots();
let skills_input = skills_load_input_from_config(&config, effective_skill_roots);
@@ -2675,9 +2670,7 @@ impl Session {
.plugins_manager
.plugins_for_config(
&turn_context.config.config_layer_stack,
turn_context.config.features.enabled(Feature::Plugins),
turn_context.config.features.enabled(Feature::RemotePlugin),
turn_context.config.features.enabled(Feature::PluginHooks),
turn_context.config.plugin_feature_flags(),
)
.await;
if let Some(plugin_instructions) =
@@ -3375,12 +3368,7 @@ async fn build_hooks_for_config(
let plugin_hooks_enabled = config.features.enabled(Feature::PluginHooks);
let (plugin_hook_sources, plugin_hook_load_warnings) = if plugin_hooks_enabled {
let plugin_outcome = plugins_manager
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await;
(
plugin_outcome.effective_plugin_hook_sources(),
+2 -6
View File
@@ -3560,9 +3560,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
.plugins_manager
.plugins_for_config(
&per_turn_config.config_layer_stack,
per_turn_config.features.enabled(Feature::Plugins),
per_turn_config.features.enabled(Feature::RemotePlugin),
per_turn_config.features.enabled(Feature::PluginHooks),
per_turn_config.plugin_feature_flags(),
)
.await;
let effective_skill_roots = plugin_outcome.effective_skill_roots();
@@ -4994,9 +4992,7 @@ where
.plugins_manager
.plugins_for_config(
&per_turn_config.config_layer_stack,
per_turn_config.features.enabled(Feature::Plugins),
per_turn_config.features.enabled(Feature::RemotePlugin),
per_turn_config.features.enabled(Feature::PluginHooks),
per_turn_config.plugin_feature_flags(),
)
.await;
let effective_skill_roots = plugin_outcome.effective_skill_roots();
+2 -6
View File
@@ -172,9 +172,7 @@ pub(crate) async fn run_turn(
.plugins_manager
.plugins_for_config(
&turn_context.config.config_layer_stack,
turn_context.config.features.enabled(Feature::Plugins),
turn_context.config.features.enabled(Feature::RemotePlugin),
turn_context.config.features.enabled(Feature::PluginHooks),
turn_context.config.plugin_feature_flags(),
)
.await;
// Structured plugin:// mentions are resolved from the current session's
@@ -1131,9 +1129,7 @@ pub(crate) async fn built_tools(
.plugins_manager
.plugins_for_config(
&turn_context.config.config_layer_stack,
turn_context.config.features.enabled(Feature::Plugins),
turn_context.config.features.enabled(Feature::RemotePlugin),
turn_context.config.features.enabled(Feature::PluginHooks),
turn_context.config.plugin_feature_flags(),
)
.await;
+1 -3
View File
@@ -698,9 +698,7 @@ impl Session {
.plugins_manager
.plugins_for_config(
&per_turn_config.config_layer_stack,
per_turn_config.features.enabled(Feature::Plugins),
per_turn_config.features.enabled(Feature::RemotePlugin),
per_turn_config.features.enabled(Feature::PluginHooks),
per_turn_config.plugin_feature_flags(),
)
.await;
let effective_skill_roots = plugin_outcome.effective_skill_roots();
+2 -9
View File
@@ -8,8 +8,6 @@ use tokio::runtime::Handle;
use tokio::sync::broadcast;
use tracing::warn;
use codex_features::Feature;
use crate::SkillsManager;
use crate::config::Config;
use crate::file_watcher::FileWatcher;
@@ -18,8 +16,8 @@ use crate::file_watcher::Receiver;
use crate::file_watcher::ThrottledWatchReceiver;
use crate::file_watcher::WatchPath;
use crate::file_watcher::WatchRegistration;
use crate::plugins::PluginsManager;
use crate::skills_load_input_from_config;
use codex_core_plugins::PluginsManager;
#[cfg(not(test))]
const WATCHER_THROTTLE_INTERVAL: Duration = Duration::from_secs(10);
@@ -64,12 +62,7 @@ impl SkillsWatcher {
fs: Option<Arc<dyn codex_exec_server::ExecutorFileSystem>>,
) -> WatchRegistration {
let plugin_outcome = plugins_manager
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await;
let effective_skill_roots = plugin_outcome.effective_skill_roots();
let skills_input = skills_load_input_from_config(config, effective_skill_roots);
+1 -1
View File
@@ -9,7 +9,6 @@ use crate::exec_policy::ExecPolicyManager;
use crate::guardian::GuardianRejection;
use crate::guardian::GuardianRejectionCircuitBreaker;
use crate::mcp::McpManager;
use crate::plugins::PluginsManager;
use crate::skills_watcher::SkillsWatcher;
use crate::tools::code_mode::CodeModeService;
use crate::tools::network_approval::NetworkApprovalService;
@@ -17,6 +16,7 @@ use crate::tools::sandboxing::ApprovalStore;
use crate::unified_exec::UnifiedExecProcessManager;
use arc_swap::ArcSwap;
use codex_analytics::AnalyticsEventsClient;
use codex_core_plugins::PluginsManager;
use codex_exec_server::EnvironmentManager;
use codex_hooks::Hooks;
use codex_login::AuthManager;
+1 -1
View File
@@ -8,7 +8,6 @@ use crate::environment_selection::selected_primary_environment;
use crate::environment_selection::validate_environment_selections;
use crate::file_watcher::FileWatcher;
use crate::mcp::McpManager;
use crate::plugins::PluginsManager;
use crate::rollout::RolloutRecorder;
use crate::rollout::truncation;
use crate::session::Codex;
@@ -23,6 +22,7 @@ use crate::tasks::interrupted_turn_history_marker;
use codex_analytics::AnalyticsEventsClient;
use codex_app_server_protocol::ThreadHistoryBuilder;
use codex_app_server_protocol::TurnStatus;
use codex_core_plugins::PluginsManager;
use codex_exec_server::EnvironmentManager;
use codex_login::AuthManager;
use codex_login::CodexAuth;
@@ -315,7 +315,7 @@ async fn refresh_missing_suggested_connectors(
fn verified_plugin_suggestion_completed(
tool_id: &str,
config: &crate::config::Config,
plugins_manager: &crate::plugins::PluginsManager,
plugins_manager: &codex_core_plugins::PluginsManager,
) -> bool {
plugins_manager
.list_marketplaces_for_config(
@@ -1,7 +1,5 @@
use super::*;
use crate::config::edit::ConfigEditsBuilder;
use crate::plugins::PluginInstallRequest;
use crate::plugins::PluginsManager;
use crate::plugins::test_support::load_plugins_config;
use crate::plugins::test_support::write_curated_plugin_sha;
use crate::plugins::test_support::write_openai_curated_marketplace;
@@ -12,6 +10,8 @@ use codex_config::types::ToolSuggestConfig;
use codex_config::types::ToolSuggestDisabledTool;
use codex_config::types::ToolSuggestDiscoverable;
use codex_config::types::ToolSuggestDiscoverableType;
use codex_core_plugins::PluginInstallRequest;
use codex_core_plugins::PluginsManager;
use codex_core_plugins::startup_sync::curated_plugins_repo_path;
use codex_rmcp_client::ElicitationResponse;
use codex_tools::DiscoverablePluginInfo;
+1 -6
View File
@@ -233,12 +233,7 @@ impl App {
tokio::spawn(async move {
let plugins = PluginsManager::new(config.codex_home.to_path_buf())
.plugins_for_config(
&config.config_layer_stack,
config.features.enabled(Feature::Plugins),
config.features.enabled(Feature::RemotePlugin),
config.features.enabled(Feature::PluginHooks),
)
.plugins_for_config(&config.config_layer_stack, config.plugin_feature_flags())
.await
.capability_summaries()
.to_vec();