mirror of
https://github.com/openai/codex.git
synced 2026-04-24 06:35:50 +00:00
fix
This commit is contained in:
2
codex-rs/Cargo.lock
generated
2
codex-rs/Cargo.lock
generated
@@ -2018,6 +2018,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"toml 0.9.11+spec-1.1.0",
|
||||
"tracing",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2445,6 +2446,7 @@ dependencies = [
|
||||
name = "codex-plugin"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"codex-utils-absolute-path",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
|
||||
@@ -32,6 +32,7 @@ serde_json = { workspace = true }
|
||||
tokio = { workspace = true, features = ["fs", "macros", "rt"] }
|
||||
toml = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
zip = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::RwLock;
|
||||
|
||||
use codex_config::ConfigLayerStack;
|
||||
use codex_protocol::protocol::Product;
|
||||
use codex_protocol::protocol::SkillScope;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
@@ -12,11 +13,9 @@ use toml::Value as TomlValue;
|
||||
use tracing::info;
|
||||
use tracing::warn;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::config_loader::CloudRequirementsLoader;
|
||||
use crate::config_loader::LoaderOverrides;
|
||||
use crate::config_loader::load_config_layers_state;
|
||||
use crate::plugins::PluginsManager;
|
||||
use crate::skills::SkillLoadOutcome;
|
||||
use crate::skills::build_implicit_skill_path_indexes;
|
||||
use crate::skills::config_rules::SkillConfigRules;
|
||||
@@ -28,38 +27,27 @@ use crate::skills::loader::skill_roots;
|
||||
use crate::skills::system::install_system_skills;
|
||||
use crate::skills::system::uninstall_system_skills;
|
||||
use codex_config::SkillsConfig;
|
||||
use codex_plugin::EffectiveSkillRoots;
|
||||
|
||||
pub struct SkillsManager {
|
||||
codex_home: PathBuf,
|
||||
plugins_manager: Arc<PluginsManager>,
|
||||
restriction_product: Option<Product>,
|
||||
cache_by_cwd: RwLock<HashMap<PathBuf, SkillLoadOutcome>>,
|
||||
cache_by_config: RwLock<HashMap<ConfigSkillsCacheKey, SkillLoadOutcome>>,
|
||||
}
|
||||
|
||||
impl SkillsManager {
|
||||
pub fn new(
|
||||
codex_home: PathBuf,
|
||||
plugins_manager: Arc<PluginsManager>,
|
||||
bundled_skills_enabled: bool,
|
||||
) -> Self {
|
||||
Self::new_with_restriction_product(
|
||||
codex_home,
|
||||
plugins_manager,
|
||||
bundled_skills_enabled,
|
||||
Some(Product::Codex),
|
||||
)
|
||||
pub fn new(codex_home: PathBuf, bundled_skills_enabled: bool) -> Self {
|
||||
Self::new_with_restriction_product(codex_home, bundled_skills_enabled, Some(Product::Codex))
|
||||
}
|
||||
|
||||
pub fn new_with_restriction_product(
|
||||
codex_home: PathBuf,
|
||||
plugins_manager: Arc<PluginsManager>,
|
||||
bundled_skills_enabled: bool,
|
||||
restriction_product: Option<Product>,
|
||||
) -> Self {
|
||||
let manager = Self {
|
||||
codex_home,
|
||||
plugins_manager,
|
||||
restriction_product,
|
||||
cache_by_cwd: RwLock::new(HashMap::new()),
|
||||
cache_by_config: RwLock::new(HashMap::new()),
|
||||
@@ -80,9 +68,20 @@ impl SkillsManager {
|
||||
/// This path uses a cache keyed by the effective skill-relevant config state rather than just
|
||||
/// cwd so role-local and session-local skill overrides cannot bleed across sessions that happen
|
||||
/// to share a directory.
|
||||
pub fn skills_for_config(&self, config: &Config) -> SkillLoadOutcome {
|
||||
let roots = self.skill_roots_for_config(config);
|
||||
let skill_config_rules = skill_config_rules_from_stack(&config.config_layer_stack);
|
||||
pub fn skills_for_config(
|
||||
&self,
|
||||
cwd: &Path,
|
||||
effective_skill_roots: &[PathBuf],
|
||||
config_layer_stack: &ConfigLayerStack,
|
||||
bundled_skills_enabled: bool,
|
||||
) -> SkillLoadOutcome {
|
||||
let roots = self.skill_roots_for_config(
|
||||
cwd,
|
||||
effective_skill_roots,
|
||||
config_layer_stack,
|
||||
bundled_skills_enabled,
|
||||
);
|
||||
let skill_config_rules = skill_config_rules_from_stack(&config_layer_stack);
|
||||
let cache_key = config_skills_cache_key(&roots, &skill_config_rules);
|
||||
if let Some(outcome) = self.cached_outcome_for_config(&cache_key) {
|
||||
return outcome;
|
||||
@@ -97,14 +96,19 @@ impl SkillsManager {
|
||||
outcome
|
||||
}
|
||||
|
||||
pub(crate) fn skill_roots_for_config(&self, config: &Config) -> Vec<SkillRoot> {
|
||||
let loaded_plugins = self.plugins_manager.plugins_for_config(config);
|
||||
pub(crate) fn skill_roots_for_config(
|
||||
&self,
|
||||
cwd: &Path,
|
||||
effective_skill_roots: &[PathBuf],
|
||||
config_layer_stack: &ConfigLayerStack,
|
||||
bundled_skills_enabled: bool,
|
||||
) -> Vec<SkillRoot> {
|
||||
let mut roots = skill_roots(
|
||||
&config.config_layer_stack,
|
||||
&config.cwd,
|
||||
loaded_plugins.effective_skill_roots(),
|
||||
&config_layer_stack,
|
||||
&cwd,
|
||||
effective_skill_roots.to_vec(),
|
||||
);
|
||||
if !config.bundled_skills_enabled() {
|
||||
if !bundled_skills_enabled {
|
||||
roots.retain(|root| root.scope != SkillScope::System);
|
||||
}
|
||||
roots
|
||||
@@ -127,7 +131,7 @@ impl SkillsManager {
|
||||
pub async fn skills_for_cwd_with_extra_user_roots(
|
||||
&self,
|
||||
cwd: &Path,
|
||||
config: &Config,
|
||||
effective_skill_roots: &[PathBuf],
|
||||
force_reload: bool,
|
||||
extra_user_roots: &[PathBuf],
|
||||
) -> SkillLoadOutcome {
|
||||
@@ -171,13 +175,10 @@ impl SkillsManager {
|
||||
}
|
||||
};
|
||||
|
||||
let loaded_plugins = self
|
||||
.plugins_manager
|
||||
.plugins_for_config_with_force_reload(config, force_reload);
|
||||
let mut roots = skill_roots(
|
||||
&config_layer_stack,
|
||||
cwd,
|
||||
loaded_plugins.effective_skill_roots(),
|
||||
effective_skill_roots.to_vec(),
|
||||
);
|
||||
if !bundled_skills_enabled_from_stack(&config_layer_stack) {
|
||||
roots.retain(|root| root.scope != SkillScope::System);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use super::LoadedPlugin;
|
||||
use super::PluginLoadOutcome;
|
||||
use super::PluginManifestPaths;
|
||||
use super::curated_plugins_repo_path;
|
||||
use super::load_plugin_manifest;
|
||||
@@ -49,6 +51,7 @@ use codex_plugin::PluginCapabilitySummary;
|
||||
use codex_plugin::PluginId;
|
||||
use codex_plugin::PluginIdError;
|
||||
use codex_plugin::PluginTelemetryMetadata;
|
||||
use codex_plugin::prompt_safe_plugin_description;
|
||||
use codex_protocol::protocol::Product;
|
||||
use codex_protocol::protocol::SkillScope;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
@@ -76,7 +79,6 @@ const DEFAULT_MCP_CONFIG_FILE: &str = ".mcp.json";
|
||||
const DEFAULT_APP_CONFIG_FILE: &str = ".app.json";
|
||||
pub const OPENAI_CURATED_MARKETPLACE_NAME: &str = "openai-curated";
|
||||
static CURATED_REPO_SYNC_STARTED: AtomicBool = AtomicBool::new(false);
|
||||
const MAX_CAPABILITY_SUMMARY_DESCRIPTION_LEN: usize = 1024;
|
||||
const FEATURED_PLUGIN_IDS_CACHE_TTL: std::time::Duration =
|
||||
std::time::Duration::from_secs(60 * 60 * 3);
|
||||
|
||||
@@ -185,53 +187,6 @@ pub struct ConfiguredMarketplaceListOutcome {
|
||||
pub errors: Vec<MarketplaceListError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct LoadedPlugin {
|
||||
pub config_name: String,
|
||||
pub manifest_name: Option<String>,
|
||||
pub manifest_description: Option<String>,
|
||||
pub root: AbsolutePathBuf,
|
||||
pub enabled: bool,
|
||||
pub skill_roots: Vec<PathBuf>,
|
||||
pub disabled_skill_paths: HashSet<PathBuf>,
|
||||
pub has_enabled_skills: bool,
|
||||
pub mcp_servers: HashMap<String, McpServerConfig>,
|
||||
pub apps: Vec<AppConnectorId>,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
impl LoadedPlugin {
|
||||
fn is_active(&self) -> bool {
|
||||
self.enabled && self.error.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
fn plugin_capability_summary_from_loaded(plugin: &LoadedPlugin) -> Option<PluginCapabilitySummary> {
|
||||
if !plugin.is_active() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut mcp_server_names: Vec<String> = plugin.mcp_servers.keys().cloned().collect();
|
||||
mcp_server_names.sort_unstable();
|
||||
|
||||
let summary = PluginCapabilitySummary {
|
||||
config_name: plugin.config_name.clone(),
|
||||
display_name: plugin
|
||||
.manifest_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| plugin.config_name.clone()),
|
||||
description: prompt_safe_plugin_description(plugin.manifest_description.as_deref()),
|
||||
has_skills: plugin.has_enabled_skills,
|
||||
mcp_server_names,
|
||||
app_connector_ids: plugin.apps.clone(),
|
||||
};
|
||||
|
||||
(summary.has_skills
|
||||
|| !summary.mcp_server_names.is_empty()
|
||||
|| !summary.app_connector_ids.is_empty())
|
||||
.then_some(summary)
|
||||
}
|
||||
|
||||
impl From<PluginDetail> for PluginCapabilitySummary {
|
||||
fn from(value: PluginDetail) -> Self {
|
||||
let has_skills = value.skills.iter().any(|skill| {
|
||||
@@ -250,95 +205,6 @@ impl From<PluginDetail> for PluginCapabilitySummary {
|
||||
}
|
||||
}
|
||||
|
||||
fn prompt_safe_plugin_description(description: Option<&str>) -> Option<String> {
|
||||
let description = description?
|
||||
.split_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
if description.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(
|
||||
description
|
||||
.chars()
|
||||
.take(MAX_CAPABILITY_SUMMARY_DESCRIPTION_LEN)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PluginLoadOutcome {
|
||||
plugins: Vec<LoadedPlugin>,
|
||||
capability_summaries: Vec<PluginCapabilitySummary>,
|
||||
}
|
||||
|
||||
impl Default for PluginLoadOutcome {
|
||||
fn default() -> Self {
|
||||
Self::from_plugins(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginLoadOutcome {
|
||||
fn from_plugins(plugins: Vec<LoadedPlugin>) -> Self {
|
||||
let capability_summaries = plugins
|
||||
.iter()
|
||||
.filter_map(plugin_capability_summary_from_loaded)
|
||||
.collect::<Vec<_>>();
|
||||
Self {
|
||||
plugins,
|
||||
capability_summaries,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn effective_skill_roots(&self) -> Vec<PathBuf> {
|
||||
let mut skill_roots: Vec<PathBuf> = self
|
||||
.plugins
|
||||
.iter()
|
||||
.filter(|plugin| plugin.is_active())
|
||||
.flat_map(|plugin| plugin.skill_roots.iter().cloned())
|
||||
.collect();
|
||||
skill_roots.sort_unstable();
|
||||
skill_roots.dedup();
|
||||
skill_roots
|
||||
}
|
||||
|
||||
pub fn effective_mcp_servers(&self) -> HashMap<String, McpServerConfig> {
|
||||
let mut mcp_servers = HashMap::new();
|
||||
for plugin in self.plugins.iter().filter(|plugin| plugin.is_active()) {
|
||||
for (name, config) in &plugin.mcp_servers {
|
||||
mcp_servers
|
||||
.entry(name.clone())
|
||||
.or_insert_with(|| config.clone());
|
||||
}
|
||||
}
|
||||
mcp_servers
|
||||
}
|
||||
|
||||
pub fn effective_apps(&self) -> Vec<AppConnectorId> {
|
||||
let mut apps = Vec::new();
|
||||
let mut seen_connector_ids = std::collections::HashSet::new();
|
||||
|
||||
for plugin in self.plugins.iter().filter(|plugin| plugin.is_active()) {
|
||||
for connector_id in &plugin.apps {
|
||||
if seen_connector_ids.insert(connector_id.clone()) {
|
||||
apps.push(connector_id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apps
|
||||
}
|
||||
|
||||
pub fn capability_summaries(&self) -> &[PluginCapabilitySummary] {
|
||||
&self.capability_summaries
|
||||
}
|
||||
|
||||
pub fn plugins(&self) -> &[LoadedPlugin] {
|
||||
&self.plugins
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct RemotePluginSyncResult {
|
||||
/// Plugin ids newly installed into the local plugin cache.
|
||||
|
||||
@@ -7,7 +7,9 @@ use crate::config_loader::ConfigLayerEntry;
|
||||
use crate::config_loader::ConfigLayerStack;
|
||||
use crate::config_loader::ConfigRequirements;
|
||||
use crate::config_loader::ConfigRequirementsToml;
|
||||
use crate::plugins::LoadedPlugin;
|
||||
use crate::plugins::MarketplacePluginInstallPolicy;
|
||||
use crate::plugins::PluginLoadOutcome;
|
||||
use crate::plugins::test_support::TEST_CURATED_PLUGIN_SHA;
|
||||
use crate::plugins::test_support::write_curated_plugin_sha_with as write_curated_plugin_sha;
|
||||
use crate::plugins::test_support::write_file;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::config::types::McpServerConfig;
|
||||
|
||||
mod discoverable;
|
||||
mod injection;
|
||||
mod manager;
|
||||
@@ -13,22 +15,25 @@ pub(crate) mod test_support;
|
||||
mod toggles;
|
||||
|
||||
pub use codex_plugin::AppConnectorId;
|
||||
pub use codex_plugin::EffectiveSkillRoots;
|
||||
pub use codex_plugin::PluginCapabilitySummary;
|
||||
pub use codex_plugin::PluginId;
|
||||
pub use codex_plugin::PluginIdError;
|
||||
pub use codex_plugin::PluginTelemetryMetadata;
|
||||
|
||||
pub type LoadedPlugin = codex_plugin::LoadedPlugin<McpServerConfig>;
|
||||
pub type PluginLoadOutcome = codex_plugin::PluginLoadOutcome<McpServerConfig>;
|
||||
|
||||
pub(crate) use discoverable::list_tool_suggest_discoverable_plugins;
|
||||
pub(crate) use injection::build_plugin_injections;
|
||||
pub use manager::ConfiguredMarketplace;
|
||||
pub use manager::ConfiguredMarketplaceListOutcome;
|
||||
pub use manager::ConfiguredMarketplacePlugin;
|
||||
pub use manager::LoadedPlugin;
|
||||
pub use manager::OPENAI_CURATED_MARKETPLACE_NAME;
|
||||
pub use manager::PluginDetail;
|
||||
pub use manager::PluginInstallError;
|
||||
pub use manager::PluginInstallOutcome;
|
||||
pub use manager::PluginInstallRequest;
|
||||
pub use manager::PluginLoadOutcome;
|
||||
pub use manager::PluginReadOutcome;
|
||||
pub use manager::PluginReadRequest;
|
||||
pub use manager::PluginRemoteSyncError;
|
||||
|
||||
@@ -13,6 +13,7 @@ path = "src/lib.rs"
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
|
||||
pub mod mention_syntax;
|
||||
|
||||
mod load_outcome;
|
||||
mod plugin_id;
|
||||
mod plugin_namespace;
|
||||
|
||||
pub use load_outcome::EffectiveSkillRoots;
|
||||
pub use load_outcome::LoadedPlugin;
|
||||
pub use load_outcome::PluginLoadOutcome;
|
||||
pub use load_outcome::prompt_safe_plugin_description;
|
||||
pub use plugin_id::PluginId;
|
||||
pub use plugin_id::PluginIdError;
|
||||
pub use plugin_id::validate_plugin_segment;
|
||||
|
||||
Reference in New Issue
Block a user