use std::collections::BTreeMap;
use std::path::PathBuf;
use std::sync::Arc;
use arc_swap::ArcSwap;
pub mod boot;
pub mod capability_aggregator;
pub mod config;
pub mod contributes;
pub mod contributes_skills;
pub mod discovery;
pub mod doctor_render;
pub mod factory;
pub mod init_loop;
pub mod remote_credential_store;
pub mod report;
pub mod subprocess;
pub use remote_credential_store::RemoteCredentialStore;
pub use boot::{
register_plugin_registry_reload_hook, wire_plugin_registry, wire_plugin_registry_with_runtime,
SubprocessRuntime, WirePluginRegistryOutput,
};
pub use capability_aggregator::{
aggregate_plugin_gates, AggregatedGate, AggregatedGateState, PluginCapabilityAggregation,
UnmetRequirement,
};
pub use config::{resolve_search_paths, PluginDiscoveryConfig};
pub use contributes::{
merge_plugin_contributed_agents, AgentMergeConflict, AgentMergeReport, MergeResolution,
};
pub use contributes_skills::{merge_plugin_contributed_skills, SkillConflict, SkillsMergeReport};
pub use discovery::discover;
pub use factory::{
BoxError, FactoryInstantiateError, FactoryRegistrationError, PluginFactory,
PluginFactoryRegistry,
};
pub use init_loop::{run_plugin_init_loop, run_plugin_init_loop_with_factory, InitOutcome};
pub use report::{
DiagnosticLevel, DiscoveredPlugin, DiscoveryDiagnostic, DiscoveryDiagnosticKind,
PluginDiscoveryReport,
};
pub use subprocess::{
subprocess_plugin_factory, subprocess_plugin_factory_with_env, SubprocessNexoPlugin,
};
pub fn synthesize_instance_plugin(
base: &DiscoveredPlugin,
instance_label: &str,
) -> DiscoveredPlugin {
let mut clone = base.clone();
if !instance_label.is_empty() {
let new_id = format!("{}.{}", base.manifest.plugin.id, instance_label);
clone.manifest.plugin.id = new_id;
}
clone
}
#[derive(Debug)]
pub struct NexoPluginRegistry {
inner: ArcSwap<NexoPluginRegistrySnapshot>,
}
#[derive(Debug, Default, Clone)]
pub struct NexoPluginRegistrySnapshot {
pub plugins: Vec<DiscoveredPlugin>,
pub last_report: PluginDiscoveryReport,
pub skill_roots: BTreeMap<String, PathBuf>,
}
impl NexoPluginRegistry {
pub fn empty() -> Arc<Self> {
Arc::new(Self {
inner: ArcSwap::from_pointee(NexoPluginRegistrySnapshot::default()),
})
}
pub fn snapshot(&self) -> Arc<NexoPluginRegistrySnapshot> {
self.inner.load_full()
}
pub fn swap(&self, snap: Arc<NexoPluginRegistrySnapshot>) {
self.inner.store(snap);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn swap_replaces_snapshot_atomically() {
let registry = NexoPluginRegistry::empty();
assert_eq!(registry.snapshot().plugins.len(), 0);
let next = Arc::new(NexoPluginRegistrySnapshot {
plugins: Vec::new(),
last_report: PluginDiscoveryReport {
loaded_ids: vec!["dummy".to_string()],
..Default::default()
},
skill_roots: BTreeMap::new(),
});
registry.swap(next);
let observed = registry.snapshot();
assert_eq!(observed.last_report.loaded_ids, vec!["dummy".to_string()]);
}
}