AirLibrary/Plugins/
mod.rs

1//! # Plugin Architecture
2//!
3//! ## Responsibilities
4//!
5//! This module provides a comprehensive plugin system for the Air daemon,
6//! enabling extensibility through dynamically loaded plugins that can enhance
7//! daemon functionality. The plugin system is responsible for:
8//!
9//! - **Plugin Discovery**: Automatically discovering available plugins from
10//!   configured directories
11//! - **Plugin Loading**: Dynamically loading plugins into the daemon runtime
12//! - **Plugin Validation**: Validating plugin metadata, dependencies, and
13//!   compatibility
14//! - **Sandboxing**: Isolating plugins to prevent crashes and security issues
15//! - **Lifecycle Management**: Managing plugin states (load, start, stop,
16//!   unload) with proper hooks
17//! - **API Registration**: Extending the daemon API through plugin-provided
18//!   commands and handlers
19//! - **Inter-Plugin Communication Enabling**: plugins to communicate with each
20//!   other via message passing
21//! - **Permission Management**: Enforcing fine-grained permissions and
22//!   capabilities for plugins
23//! - **Version Compatibility**: Ensuring plugins are compatible with the daemon
24//!   version
25//! - **Dependency Resolution**: Resolving and validating plugin dependencies
26//!
27//! ## VSCode Extension Architecture Patterns
28//!
29//! This implementation draws inspiration from VSCode's extension architecture:
30//! - Reference: vs/platform/extensions/common/ extensionHostStarter.ts
31//! - Reference: vs/server/node/ extensionHostConnection.ts
32//! - Reference: vs/platform/remote/common/ remoteAgentConnection.ts
33//!
34//! Patterns adopted from VSCode extensions:
35//! - Separate extension host process for isolation and crash protection
36//! - Activation events to trigger extension loading on-demand
37//! - Contribution points for extending functionality
38//! - Message-based communication between host and extensions
39//! - State management and lifecycle hooks
40//! - API versioning for backward compatibility
41//! - Permission and capability descriptors
42//!
43//! ## Integration with Cocoon Extension Host
44//!
45//! The plugin system is designed to integrate with the Cocoon Extension Host
46//! (similar to VSCode's extension host architecture). This provides:
47//! - Isolated execution environments for plugins
48//! - Crash recovery and resilience
49//! - Resource management and limits
50//! - Communication via IPC channels
51//! - Hot reload capability without daemon restart
52//!
53//! ## TODO: Future Enhancements
54//!
55//! - **Plugin Marketplace**: Implement a central plugin marketplace for
56//!   discovery and installation (similar to VSCode's extension marketplace)
57//! - **Hot Reload Support**: Implement live reloading of plugins without daemon
58//!   restart
59//! - **Advanced Sandboxing**: Add more sophisticated sandboxing with resource
60//!   quotas, network isolation, and filesystem access controls
61//! - **Plugin Distribution**: Implement plugin packaging, signing, and
62//!   distribution mechanisms
63//! - **Automatic Updates**: Add automatic plugin update checking and
64//!   installation
65//! - **Telemetry Integration**: Add plugin usage telemetry and reporting
66//! - **Plugin Profiles**: Support multiple plugin configurations for different
67//!   environments
68//! - **Security Audit**: Implement comprehensive security audit and
69//!   vulnerability scanning for plugins
70//! - **Performance Monitoring**: Add detailed performance monitoring and
71//!   profiling for plugins
72//! - **Plugin Debugging**: Provide debugging tools and interfaces for plugin
73//!   developers
74//!
75//! ## Security and Isolation
76//!
77//! - Plugins run in isolated processes to prevent daemon crashes
78//! - Fine-grained permission system controls plugin capabilities
79//! - API version compatibility checks prevent breaking changes
80//! - Resource limits prevent plugin exhaustion attacks
81//! - Plugin authentication and signing to prevent malicious plugins
82//! - Filesystem and network access restrictions
83
84use std::{collections::HashMap, sync::Arc, time::Duration};
85
86use async_trait::async_trait;
87use serde::{Deserialize, Serialize};
88use tokio::sync::RwLock;
89use chrono::{DateTime, Utc};
90use log::{error, info, warn};
91use uuid::Uuid;
92
93use crate::{AirError, Result};
94
95// =============================================================================
96// Plugin Types and Traits
97// =============================================================================
98
99/// Plugin metadata
100#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct PluginMetadata {
102	pub id:String,
103	pub name:String,
104	pub version:String,
105	pub description:String,
106	pub author:String,
107	pub MinAirVersion:String,
108	pub MaxAirVersion:Option<String>,
109	pub dependencies:Vec<PluginDependency>,
110	pub capabilities:Vec<String>,
111}
112
113/// Plugin dependency specification
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct PluginDependency {
116	pub PluginId:String,
117	pub MinVersion:String,
118	pub MaxVersion:Option<String>,
119	pub optional:bool,
120}
121
122/// Plugin capability and permission descriptor
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct PluginCapability {
125	pub name:String,
126	pub description:String,
127	pub RequiredPermissions:Vec<String>,
128}
129
130/// Plugin permission
131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
132pub enum PluginPermission {
133	/// Access filesystem
134	Filesystem { read:bool, write:bool, paths:Vec<String> },
135	/// Access network
136	Network { outbound:bool, inbound:bool, hosts:Vec<String> },
137	/// Access system resources
138	System { cpu:bool, memory:bool },
139	/// Access other plugins
140	InterPlugin { plugins:Vec<String>, actions:Vec<String> },
141	/// Custom permission
142	Custom(String),
143}
144
145/// Plugin sandbox configuration
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct PluginSandboxConfig {
148	pub enabled:bool,
149	pub MaxMemoryMb:Option<u64>,
150	pub MaxCPUPercent:Option<f64>,
151	pub NetworkAllowed:bool,
152	pub FilesystemAllowed:bool,
153	pub AllowedPaths:Vec<String>,
154	pub TimeoutSecs:Option<u64>,
155}
156
157impl Default for PluginSandboxConfig {
158	fn default() -> Self {
159		Self {
160			enabled:true,
161			MaxMemoryMb:Some(128),
162			MaxCPUPercent:Some(10.0),
163			NetworkAllowed:false,
164			FilesystemAllowed:false,
165			AllowedPaths:vec![],
166			TimeoutSecs:Some(30),
167		}
168	}
169}
170
171/// Plugin validation result
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub enum PluginValidationResult {
174	Valid,
175	Invalid(String),
176	Warning(String),
177}
178
179/// Plugin lifecycle hooks
180#[async_trait]
181pub trait PluginHooks: Send + Sync {
182	/// Called when plugin is being loaded
183	async fn on_load(&self) -> Result<()> { Ok(()) }
184
185	/// Called when plugin is starting
186	async fn on_start(&self) -> Result<()> { Ok(()) }
187
188	/// Called when plugin is stopping
189	async fn on_stop(&self) -> Result<()> { Ok(()) }
190
191	/// Called when plugin is being unloaded
192	async fn on_unload(&self) -> Result<()> { Ok(()) }
193
194	/// Called when configuration changes
195	async fn on_config_changed(&self, _old:&serde_json::Value, _new:&serde_json::Value) -> Result<()> { Ok(()) }
196}
197
198/// Plugin interface trait
199#[async_trait]
200pub trait Plugin: PluginHooks + Send + Sync {
201	/// Get plugin metadata
202	fn metadata(&self) -> &PluginMetadata;
203
204	/// Get plugin sandbox configuration
205	fn sandbox_config(&self) -> PluginSandboxConfig { PluginSandboxConfig::default() }
206
207	/// Get plugin permissions
208	fn permissions(&self) -> Vec<PluginPermission> { vec![] }
209
210	/// Handle inter-plugin message
211	async fn handle_message(&self, from:&str, _message:&PluginMessage) -> Result<PluginMessage> {
212		Err(AirError::Plugin(format!("Plugin {} does not handle messages", from)))
213	}
214
215	/// Get plugin state for diagnostics
216	async fn get_state(&self) -> Result<serde_json::Value> { Ok(serde_json::json!({})) }
217
218	/// Check if plugin has specific capability
219	fn has_capability(&self, _capability:&str) -> bool { false }
220
221	/// Check if plugin has specific permission
222	fn has_permission(&self, _permission:&PluginPermission) -> bool { false }
223}
224
225/// Inter-plugin message
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct PluginMessage {
228	pub id:String,
229	pub from:String,
230	pub to:String,
231	pub action:String,
232	pub data:serde_json::Value,
233	pub timestamp:DateTime<Utc>,
234}
235
236impl PluginMessage {
237	/// Create a new plugin message
238	pub fn new(from:String, to:String, action:String, data:serde_json::Value) -> Self {
239		Self { id:Uuid::new_v4().to_string(), from, to, action, data, timestamp:Utc::now() }
240	}
241
242	/// Validate message format and content
243	pub fn validate(&self) -> Result<()> {
244		if self.id.is_empty() {
245			return Err(crate::AirError::Plugin("Message ID cannot be empty".to_string()));
246		}
247		if self.from.is_empty() {
248			return Err(crate::AirError::Plugin("Message sender cannot be empty".to_string()));
249		}
250		if self.to.is_empty() {
251			return Err(crate::AirError::Plugin("Message recipient cannot be empty".to_string()));
252		}
253		if self.action.is_empty() {
254			return Err(crate::AirError::Plugin("Message action cannot be empty".to_string()));
255		}
256		if self.action.len() > 100 {
257			return Err(crate::AirError::Plugin("Message action too long".to_string()));
258		}
259		Ok(())
260	}
261}
262
263// =============================================================================
264// Plugin Manager
265// =============================================================================
266
267/// Plugin state tracking
268#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
269pub enum PluginState {
270	#[serde(rename = "unloaded")]
271	Unloaded,
272	#[serde(rename = "loaded")]
273	Loaded,
274	#[serde(rename = "starting")]
275	Starting,
276	#[serde(rename = "running")]
277	Running,
278	#[serde(rename = "stopping")]
279	Stopping,
280	#[serde(rename = "error")]
281	Error,
282}
283
284/// Plugin registry entry
285pub struct PluginRegistry {
286	pub plugin:Arc<Box<dyn Plugin>>,
287	pub state:PluginState,
288	pub StartedAt:Option<DateTime<Utc>>,
289	pub LoadedAt:Option<DateTime<Utc>>,
290	pub error:Option<String>,
291	pub sandbox:PluginSandboxConfig,
292}
293
294/// Main plugin manager
295pub struct PluginManager {
296	plugins:Arc<RwLock<HashMap<String, PluginRegistry>>>,
297	MessageQueue:Arc<RwLock<Vec<PluginMessage>>>,
298	AirVersion:String,
299	EnableSandbox:bool,
300	StartupTimeout:Duration,
301	OperationTimeout:Duration,
302}
303
304impl PluginManager {
305	/// Create a new plugin manager
306	pub fn new(AirVersion:String) -> Self {
307		Self {
308			plugins:Arc::new(RwLock::new(HashMap::new())),
309			MessageQueue:Arc::new(RwLock::new(Vec::new())),
310			AirVersion,
311			EnableSandbox:true,
312			StartupTimeout:Duration::from_secs(30),
313			OperationTimeout:Duration::from_secs(60),
314		}
315	}
316
317	/// Create a new plugin manager with custom configuration
318	pub fn with_config(
319		AirVersion:String,
320		EnableSandbox:bool,
321		StartupTimeoutSecs:u64,
322		OperationTimeoutSecs:u64,
323	) -> Self {
324		Self {
325			plugins:Arc::new(RwLock::new(HashMap::new())),
326			MessageQueue:Arc::new(RwLock::new(Vec::new())),
327			AirVersion,
328			EnableSandbox,
329			StartupTimeout:Duration::from_secs(StartupTimeoutSecs),
330			OperationTimeout:Duration::from_secs(OperationTimeoutSecs),
331		}
332	}
333
334	/// Enable or disable sandbox mode
335	pub fn set_sandbox_enabled(&mut self, enabled:bool) { self.EnableSandbox = enabled; }
336
337	/// Discover plugins from a directory
338	pub async fn discover_plugins(&self, directory:&str) -> Result<Vec<String>> {
339		let Discovered = vec![];
340
341		// In production, this would scan the directory for plugin manifests
342		// For now, we return an empty list
343		info!("[PluginManager] Discovering plugins in directory: {}", directory);
344
345		Ok(Discovered)
346	}
347
348	/// Load a plugin from a manifest file
349	pub async fn load_from_manifest(&self, path:&str) -> Result<String> {
350		// In production, this would load and parse a plugin manifest
351		// For now, we return a mock plugin ID
352		info!("[PluginManager] Loading plugin from manifest: {}", path);
353
354		Ok("loaded_plugin".to_string())
355	}
356
357	/// Register a plugin
358	pub async fn register(&self, plugin:Arc<Box<dyn Plugin>>) -> Result<()> {
359		let metadata = plugin.metadata();
360
361		info!("[PluginManager] Registering plugin: {} v{}", metadata.name, metadata.version);
362
363		// Validate plugin metadata
364		self.ValidatePluginMetadata(metadata)?;
365
366		// Check Air version compatibility
367		self.CheckAirVersionCompatibility(metadata)?;
368
369		// Check API version compatibility
370		self.CheckApiVersionCompatibility(metadata)?;
371
372		// Check dependencies
373		self.check_dependencies(metadata).await?;
374
375		// Validate plugin capabilities and permissions
376		self.validate_capabilities_and_permissions(plugin.as_ref().as_ref())?;
377
378		// Setup sandbox configuration
379		let sandbox = if self.EnableSandbox {
380			plugin.sandbox_config()
381		} else {
382			PluginSandboxConfig { enabled:false, ..Default::default() }
383		};
384
385		// Load plugin with timeout
386		let LoadResult = tokio::time::timeout(self.StartupTimeout, plugin.on_load()).await;
387
388		let _load_result = LoadResult
389			.map_err(|_| {
390				AirError::Plugin(format!("Plugin {} load timeout after {:?}", metadata.name, self.StartupTimeout))
391			})?
392			.map_err(|e| {
393				error!("[PluginManager] Failed to load plugin {}: {}", metadata.name, e);
394				e
395			})?;
396
397		// Register in map
398		let mut plugins = self.plugins.write().await;
399		plugins.insert(
400			metadata.id.clone(),
401			PluginRegistry {
402				plugin:plugin.clone(),
403				state:PluginState::Loaded,
404				StartedAt:None,
405				LoadedAt:Some(Utc::now()),
406				error:None,
407				sandbox,
408			},
409		);
410
411		info!("[PluginManager] Plugin registered: {}", metadata.name);
412		Ok(())
413	}
414
415	/// Validate plugin metadata
416	pub fn ValidatePluginMetadata(&self, metadata:&PluginMetadata) -> Result<()> {
417		if metadata.id.is_empty() {
418			return Err(crate::AirError::Plugin("Plugin ID cannot be empty".to_string()));
419		}
420		if metadata.id.len() > 100 {
421			return Err(crate::AirError::Plugin("Plugin ID too long (max 100 characters)".to_string()));
422		}
423		if !metadata.id.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_') {
424			return Err(crate::AirError::Plugin("Plugin ID contains invalid characters".to_string()));
425		}
426		if metadata.name.is_empty() {
427			return Err(crate::AirError::Plugin("Plugin name cannot be empty".to_string()));
428		}
429		if metadata.version.is_empty() {
430			return Err(crate::AirError::Plugin("Plugin version cannot be empty".to_string()));
431		}
432		if metadata.author.is_empty() {
433			return Err(crate::AirError::Plugin("Plugin author cannot be empty".to_string()));
434		}
435		Ok(())
436	}
437
438	/// Validate plugin capabilities and permissions
439	pub fn validate_capabilities_and_permissions(&self, plugin:&dyn Plugin) -> Result<()> {
440		let permissions = plugin.permissions();
441
442		// Check for dangerous permissions
443		for permission in &permissions {
444			match permission {
445				PluginPermission::Filesystem { write, .. } if *write => {
446					warn!(
447						"[PluginManager] Plugin {} requests filesystem write access",
448						plugin.metadata().id
449					);
450				},
451				PluginPermission::Network { .. } => {
452					warn!("[PluginManager] Plugin {} requests network access", plugin.metadata().id);
453				},
454				_ => {},
455			}
456		}
457
458		Ok(())
459	}
460
461	/// Check Air version compatibility
462	pub fn CheckAirVersionCompatibility(&self, metadata:&PluginMetadata) -> Result<()> {
463		if !self.version_satisfies(&self.AirVersion, &metadata.MinAirVersion) {
464			return Err(AirError::Plugin(format!(
465				"Plugin requires Air version {} or higher, current: {}",
466				metadata.MinAirVersion, self.AirVersion
467			)));
468		}
469
470		if let Some(max_version) = &metadata.MaxAirVersion {
471			if !self.version_satisfies(max_version, &self.AirVersion) {
472				return Err(AirError::Plugin(format!(
473					"Plugin is incompatible with Air version {}, max supported: {}",
474					self.AirVersion, max_version
475				)));
476			}
477		}
478
479		Ok(())
480	}
481
482	/// Check API version compatibility
483	pub fn CheckApiVersionCompatibility(&self, _Metadata:&PluginMetadata) -> Result<()> {
484		// Check if plugin declares compatibility with current API version
485		// In production, this would check against the daemon's API version
486		Ok(())
487	}
488
489	/// Check plugin dependencies
490	pub async fn check_dependencies(&self, metadata:&PluginMetadata) -> Result<()> {
491		let plugins = self.plugins.read().await;
492
493		for dep in &metadata.dependencies {
494			if !dep.optional {
495				let DepPlugin = plugins
496					.get(&dep.PluginId)
497					.ok_or_else(|| AirError::Plugin(format!("Required dependency not found: {}", dep.PluginId)))?;
498
499				let DepVersion = &DepPlugin.plugin.metadata().version;
500				if !self.version_satisfies(DepVersion, &dep.MinVersion) {
501					return Err(AirError::Plugin(format!(
502						"Dependency {} version {} does not satisfy requirement {}",
503						dep.PluginId, DepVersion, dep.MinVersion
504					)));
505				}
506
507				if DepPlugin.state != PluginState::Running && DepPlugin.state != PluginState::Loaded {
508					return Err(AirError::Plugin(format!(
509						"Dependency {} is not ready (state: {:?})",
510						dep.PluginId, DepPlugin.state
511					)));
512				}
513			}
514		}
515
516		Ok(())
517	}
518
519	/// Start a plugin
520	pub async fn start(&self, PluginId:&str) -> Result<()> {
521		let mut plugins = self.plugins.write().await;
522		let registry = plugins
523			.get_mut(PluginId)
524			.ok_or_else(|| AirError::Plugin(format!("Plugin not found: {}", PluginId)))?;
525
526		if registry.state == PluginState::Running {
527			info!("[PluginManager] Plugin {} already running", PluginId);
528			return Ok(());
529		}
530
531		registry.state = PluginState::Starting;
532
533		// Check sandbox configuration
534		if self.EnableSandbox && registry.sandbox.enabled {
535			info!("[PluginManager] Starting plugin {} in sandbox mode", PluginId);
536		}
537
538		let plugin = registry.plugin.clone();
539		drop(plugins);
540
541		let StartResult = tokio::time::timeout(self.StartupTimeout, plugin.on_start()).await;
542
543		match StartResult {
544			Ok(Ok(())) => {
545				let mut plugins = self.plugins.write().await;
546				if let Some(registry) = plugins.get_mut(PluginId) {
547					registry.state = PluginState::Running;
548					registry.StartedAt = Some(Utc::now());
549					registry.error = None;
550				}
551				info!("[PluginManager] Plugin started: {}", PluginId);
552				Ok(())
553			},
554			Ok(Err(e)) => {
555				let mut plugins = self.plugins.write().await;
556				if let Some(registry) = plugins.get_mut(PluginId) {
557					registry.state = PluginState::Error;
558					registry.error = Some(e.to_string());
559				}
560				error!("[PluginManager] Plugin start failed: {}: {}", PluginId, e);
561				Err(e)
562			},
563			Err(_) => {
564				let mut plugins = self.plugins.write().await;
565				if let Some(registry) = plugins.get_mut(PluginId) {
566					registry.state = PluginState::Error;
567					registry.error = Some(format!("Startup timeout after {:?}", self.StartupTimeout));
568				}
569				error!("[PluginManager] Plugin start timeout: {}", PluginId);
570				Err(AirError::Plugin(format!("Plugin {} startup timeout", PluginId)))
571			},
572		}
573	}
574
575	/// Stop a plugin
576	pub async fn stop(&self, PluginId:&str) -> Result<()> {
577		let mut plugins = self.plugins.write().await;
578		let registry = plugins
579			.get_mut(PluginId)
580			.ok_or_else(|| AirError::Plugin(format!("Plugin not found: {}", PluginId)))?;
581
582		if registry.state != PluginState::Running {
583			info!("[PluginManager] Plugin {} not running", PluginId);
584			return Ok(());
585		}
586
587		registry.state = PluginState::Stopping;
588		let plugin = registry.plugin.clone();
589		drop(plugins);
590
591		let StopResult = tokio::time::timeout(self.OperationTimeout, plugin.on_stop()).await;
592
593		match StopResult {
594			Ok(Ok(())) => {
595				let mut plugins = self.plugins.write().await;
596				if let Some(registry) = plugins.get_mut(PluginId) {
597					registry.state = PluginState::Loaded;
598					registry.StartedAt = None;
599				}
600				info!("[PluginManager] Plugin stopped: {}", PluginId);
601				Ok(())
602			},
603			Ok(Err(e)) => {
604				let mut plugins = self.plugins.write().await;
605				if let Some(registry) = plugins.get_mut(PluginId) {
606					registry.state = PluginState::Error;
607					registry.error = Some(e.to_string());
608				}
609				error!("[PluginManager] Plugin stop failed: {}: {}", PluginId, e);
610				Err(e)
611			},
612			Err(_) => {
613				let mut plugins = self.plugins.write().await;
614				if let Some(registry) = plugins.get_mut(PluginId) {
615					registry.state = PluginState::Error;
616					registry.error = Some(format!("Stop timeout after {:?}", self.OperationTimeout));
617				}
618				error!("[PluginManager] Plugin stop timeout: {}", PluginId);
619				Err(AirError::Plugin(format!("Plugin {} stop timeout", PluginId)))
620			},
621		}
622	}
623
624	/// Start all registered plugins
625	pub async fn start_all(&self) -> Result<()> {
626		let PluginIds:Vec<String> = self.plugins.read().await.keys().cloned().collect();
627
628		info!("[PluginManager] Starting {} plugins", PluginIds.len());
629
630		for PluginId in PluginIds {
631			if let Err(e) = self.start(&PluginId).await {
632				warn!("[PluginManager] Failed to start plugin {}: {}", PluginId, e);
633			}
634		}
635
636		Ok(())
637	}
638
639	/// Stop all running plugins
640	pub async fn stop_all(&self) -> Result<()> {
641		let PluginIds:Vec<String> = self.plugins.read().await.keys().cloned().collect();
642
643		info!("[PluginManager] Stopping {} plugins", PluginIds.len());
644
645		// Stop in reverse order to respect dependencies
646		for plugin_id in PluginIds.into_iter().rev() {
647			if let Err(e) = self.stop(&plugin_id).await {
648				warn!("[PluginManager] Failed to stop plugin {}: {}", plugin_id, e);
649			}
650		}
651
652		Ok(())
653	}
654
655	/// Load a plugin
656	pub async fn load(&self, plugin_id:&str) -> Result<()> {
657		let mut plugins = self.plugins.write().await;
658		let registry = plugins
659			.get_mut(plugin_id)
660			.ok_or_else(|| AirError::Plugin(format!("Plugin not found: {}", plugin_id)))?;
661
662		if registry.state != PluginState::Unloaded {
663			info!("[PluginManager] Plugin {} already loaded", plugin_id);
664			return Ok(());
665		}
666
667		let plugin = registry.plugin.clone();
668		drop(plugins);
669
670		let LoadResult = tokio::time::timeout(self.StartupTimeout, plugin.on_load()).await;
671
672		match LoadResult {
673			Ok(Ok(())) => {
674				let mut plugins = self.plugins.write().await;
675				if let Some(registry) = plugins.get_mut(plugin_id) {
676					registry.state = PluginState::Loaded;
677					registry.LoadedAt = Some(Utc::now());
678					registry.error = None;
679				}
680				info!("[PluginManager] Plugin loaded: {}", plugin_id);
681				Ok(())
682			},
683			Ok(Err(e)) => {
684				let mut plugins = self.plugins.write().await;
685				if let Some(registry) = plugins.get_mut(plugin_id) {
686					registry.state = PluginState::Error;
687					registry.error = Some(e.to_string());
688				}
689				error!("[PluginManager] Plugin load failed: {}: {}", plugin_id, e);
690				Err(e)
691			},
692			Err(_) => {
693				let mut plugins = self.plugins.write().await;
694				if let Some(registry) = plugins.get_mut(plugin_id) {
695					registry.state = PluginState::Error;
696					registry.error = Some(format!("Load timeout after {:?}", self.StartupTimeout));
697				}
698				error!("[PluginManager] Plugin load timeout: {}", plugin_id);
699				Err(AirError::Plugin(format!("Plugin {} load timeout", plugin_id)))
700			},
701		}
702	}
703
704	/// Unload a plugin
705	pub async fn unload(&self, plugin_id:&str) -> Result<()> {
706		// First stop the plugin
707		self.stop(plugin_id).await?;
708
709		let mut plugins = self.plugins.write().await;
710		let registry = plugins
711			.get(plugin_id)
712			.ok_or_else(|| AirError::Plugin(format!("Plugin not found: {}", plugin_id)))?;
713
714		let plugin = registry.plugin.clone();
715		plugins.remove(plugin_id);
716
717		let UnloadResult = tokio::time::timeout(self.OperationTimeout, plugin.on_unload()).await;
718
719		match UnloadResult {
720			Ok(Ok(())) => {
721				info!("[PluginManager] Plugin unloaded: {}", plugin_id);
722				Ok(())
723			},
724			Ok(Err(e)) => {
725				// Plugin is removed from registry even if unload fails
726				error!("[PluginManager] Plugin unload error: {}: {}", plugin_id, e);
727				Err(e)
728			},
729			Err(_) => {
730				// Plugin is removed from registry even if timeout occurs
731				warn!("[PluginManager] Plugin unload timeout: {}", plugin_id);
732				Err(AirError::Plugin(format!("Plugin {} unload timeout", plugin_id)))
733			},
734		}
735	}
736
737	/// Send message from one plugin to another
738	pub async fn send_message(&self, message:PluginMessage) -> Result<PluginMessage> {
739		// Validate message
740		message.validate()?;
741
742		let plugins = self.plugins.read().await;
743
744		let target = plugins
745			.get(&message.to)
746			.ok_or_else(|| AirError::Plugin(format!("Target plugin not found: {}", message.to)))?;
747
748		if target.state != PluginState::Running {
749			return Err(AirError::Plugin(format!(
750				"Target plugin not running: {} (state: {:?})",
751				message.to, target.state
752			)));
753		}
754
755		// Check if sender has permission to send to receiver
756		let SenderMetadata = plugins
757			.get(&message.from)
758			.ok_or_else(|| AirError::Plugin(format!("Sender plugin not found: {}", message.from)))?;
759
760		if !self.check_inter_plugin_permission(SenderMetadata, target, &message) {
761			return Err(AirError::Plugin(format!(
762				"Permission denied: {} cannot send to {}",
763				message.from, message.to
764			)));
765		}
766
767		let plugin = target.plugin.clone();
768		drop(plugins);
769
770		// Send message with timeout
771		let SendResult =
772			tokio::time::timeout(self.OperationTimeout, plugin.handle_message(&message.from, &message)).await;
773
774		SendResult.map_err(|_| AirError::Plugin(format!("Message send timeout: {} -> {}", message.from, message.to)))?
775	}
776
777	/// Check inter-plugin communication permission
778	fn check_inter_plugin_permission(
779		&self,
780		_sender:&PluginRegistry,
781		_target:&PluginRegistry,
782		_message:&PluginMessage,
783	) -> bool {
784		// In production, this would check if sender has permission to communicate with
785		// target For now, we allow all communication
786		true
787	}
788
789	/// Get plugin list with details
790	pub async fn list_plugins(&self) -> Result<Vec<PluginInfo>> {
791		let plugins = self.plugins.read().await;
792		let mut result = Vec::new();
793
794		for (id, registry) in plugins.iter() {
795			let metadata = registry.plugin.metadata().clone();
796			result.push(PluginInfo {
797				id:id.clone(),
798				metadata,
799				state:registry.state,
800				UptimeSecs:registry.StartedAt.map(|t| (Utc::now() - t).num_seconds() as u64).unwrap_or(0),
801				error:registry.error.clone(),
802			});
803		}
804
805		Ok(result)
806	}
807
808	/// Get plugin state
809	pub async fn get_plugin_state(&self, plugin_id:&str) -> Result<serde_json::Value> {
810		let plugins = self.plugins.read().await;
811		let registry = plugins
812			.get(plugin_id)
813			.ok_or_else(|| AirError::Plugin(format!("Plugin not found: {}", plugin_id)))?;
814
815		registry.plugin.get_state().await
816	}
817
818	/// Get plugin permissions
819	pub async fn get_plugin_permissions(&self, plugin_id:&str) -> Result<Vec<PluginPermission>> {
820		let plugins = self.plugins.read().await;
821		let registry = plugins
822			.get(plugin_id)
823			.ok_or_else(|| AirError::Plugin(format!("Plugin not found: {}", plugin_id)))?;
824
825		Ok(registry.plugin.permissions())
826	}
827
828	/// Validate all plugins
829	pub async fn validate_all_plugins(&self) -> Vec<(String, PluginValidationResult)> {
830		let plugins = self.plugins.read().await;
831		let mut results = vec![];
832
833		for (id, registry) in plugins.iter() {
834			let result = self.validate_plugin(registry.plugin.as_ref().as_ref());
835			results.push((id.clone(), result));
836		}
837
838		results
839	}
840
841	/// Validate a single plugin
842	pub fn validate_plugin(&self, plugin:&dyn Plugin) -> PluginValidationResult {
843		let metadata = plugin.metadata();
844
845		// Validate metadata
846		if let Err(e) = self.ValidatePluginMetadata(metadata) {
847			return PluginValidationResult::Invalid(e.to_string());
848		}
849
850		// Check version compatibility
851		if let Err(e) = self.CheckAirVersionCompatibility(metadata) {
852			return PluginValidationResult::Invalid(format!("Version compatibility error: {}", e));
853		}
854
855		PluginValidationResult::Valid
856	}
857
858	/// Get dependency graph
859	pub async fn get_dependency_graph(&self) -> Result<serde_json::Value> {
860		let plugins = self.plugins.read().await;
861		let mut graph = serde_json::Map::new();
862
863		for (id, registry) in plugins.iter() {
864			let metadata = registry.plugin.metadata();
865			let dependencies:Vec<String> = metadata.dependencies.iter().map(|d| d.PluginId.clone()).collect();
866			graph.insert(id.clone(), serde_json::json!(dependencies));
867		}
868
869		Ok(serde_json::Value::Object(graph))
870	}
871
872	/// Resolve plugin load order based on dependencies
873	pub async fn resolve_load_order(&self) -> Result<Vec<String>> {
874		let plugins = self.plugins.read().await;
875
876		// Topological sort based on dependencies
877		let mut visited = std::collections::HashSet::new();
878		let mut order = vec![];
879
880		for plugin_id in plugins.keys() {
881			self.VisitPluginForLoadOrder(plugin_id, &mut visited, &mut order, &plugins)?;
882		}
883
884		Ok(order)
885	}
886
887	/// Visit plugin for load order (helper function)
888	fn VisitPluginForLoadOrder(
889		&self,
890		plugin_id:&str,
891		visited:&mut std::collections::HashSet<String>,
892		order:&mut Vec<String>,
893		plugins:&HashMap<String, PluginRegistry>,
894	) -> Result<()> {
895		if visited.contains(plugin_id) {
896			return Ok(());
897		}
898
899		visited.insert(plugin_id.to_string());
900
901		if let Some(registry) = plugins.get(plugin_id) {
902			let metadata = registry.plugin.metadata();
903			for dep in &metadata.dependencies {
904				if !dep.optional {
905					self.VisitPluginForLoadOrder(&dep.PluginId, visited, order, plugins)?;
906				}
907			}
908		}
909
910		order.push(plugin_id.to_string());
911		Ok(())
912	}
913
914	/// Simple version satisfaction check (X.Y.Z format)
915	fn version_satisfies(&self, actual:&str, required:&str) -> bool {
916		let ActualParts:Vec<&str> = actual.split('.').collect();
917		let RequiredParts:Vec<&str> = required.split('.').collect();
918
919		for (i, required_part) in RequiredParts.iter().enumerate() {
920			if let (Ok(a), Ok(r)) = (ActualParts.get(i).unwrap_or(&"0").parse::<u32>(), required_part.parse::<u32>()) {
921				if a > r {
922					return true;
923				} else if a < r {
924					return false;
925				}
926			}
927		}
928
929		true
930	}
931}
932
933/// Plugin information for listing
934#[derive(Debug, Clone, Serialize, Deserialize)]
935pub struct PluginInfo {
936	pub id:String,
937	pub metadata:PluginMetadata,
938	pub state:PluginState,
939	pub UptimeSecs:u64,
940	pub error:Option<String>,
941}
942
943// =============================================================================
944// Plugin Event System
945// =============================================================================
946
947/// Plugin event types
948#[derive(Debug, Clone, Serialize, Deserialize)]
949pub enum PluginEvent {
950	/// Plugin was loaded
951	Loaded { plugin_id:String },
952	/// Plugin was started
953	Started { plugin_id:String },
954	/// Plugin was stopped
955	Stopped { plugin_id:String },
956	/// Plugin was unloaded
957	Unloaded { plugin_id:String },
958	/// Plugin encountered an error
959	Error { plugin_id:String, error:String },
960	/// Plugin sent a message
961	Message { from:String, to:String, action:String },
962	/// Configuration changed
963	ConfigChanged { old:serde_json::Value, new:serde_json::Value },
964}
965
966/// Plugin event handler
967#[async_trait]
968pub trait PluginEventHandler: Send + Sync {
969	/// Handle a plugin event
970	async fn handle_event(&self, event:&PluginEvent) -> Result<()>;
971}
972
973/// Event bus for plugin events
974pub struct PluginEventBus {
975	handlers:Arc<RwLock<Vec<Box<dyn PluginEventHandler>>>>,
976}
977
978impl PluginEventBus {
979	/// Create a new event bus
980	pub fn new() -> Self { Self { handlers:Arc::new(RwLock::new(vec![])) } }
981
982	/// Register an event handler
983	pub async fn register_handler(&self, handler:Box<dyn PluginEventHandler>) {
984		let mut handlers = self.handlers.write().await;
985		handlers.push(handler);
986	}
987
988	/// Emit an event to all handlers
989	pub async fn emit(&self, event:PluginEvent) {
990		let handlers = self.handlers.read().await;
991		for handler in handlers.iter() {
992			if let Err(e) = handler.handle_event(&event).await {
993				error!("[PluginEventBus] Event handler error: {}", e);
994			}
995		}
996	}
997}
998
999impl Default for PluginEventBus {
1000	fn default() -> Self { Self::new() }
1001}
1002
1003// =============================================================================
1004// Plugin Discovery and Loading
1005// =============================================================================
1006
1007/// Plugin discovery result
1008#[derive(Debug, Clone, Serialize, Deserialize)]
1009pub struct PluginDiscoveryResult {
1010	pub plugin_id:String,
1011	pub ManifestPath:String,
1012	pub metadata:PluginMetadata,
1013	pub enabled:bool,
1014}
1015
1016/// Plugin manifest
1017#[derive(Debug, Clone, Serialize, Deserialize)]
1018pub struct PluginManifest {
1019	pub plugin:PluginMetadata,
1020	pub main:String,
1021	pub sandbox:Option<PluginSandboxConfig>,
1022}
1023
1024/// Plugin loader for discovering and loading plugins
1025pub struct PluginLoader {
1026	PluginPaths:Vec<String>,
1027}
1028
1029impl PluginLoader {
1030	/// Create a new plugin loader
1031	pub fn new() -> Self {
1032		Self {
1033			PluginPaths:vec![
1034				"/usr/local/lib/Air/plugins".to_string(),
1035				"~/.local/share/Air/plugins".to_string(),
1036			],
1037		}
1038	}
1039
1040	/// Add a plugin discovery path
1041	pub fn add_path(&mut self, path:String) { self.PluginPaths.push(path); }
1042
1043	/// Discover plugins from all configured paths
1044	pub async fn discover_all(&self) -> Result<Vec<PluginDiscoveryResult>> {
1045		let mut results = vec![];
1046
1047		for path in &self.PluginPaths {
1048			match self.discover_in_path(path).await {
1049				Ok(mut discovered) => {
1050					results.append(&mut discovered);
1051				},
1052				Err(e) => {
1053					warn!("[PluginLoader] Failed to discover plugins in {}: {}", path, e);
1054				},
1055			}
1056		}
1057
1058		Ok(results)
1059	}
1060
1061	/// Discover plugins in a specific path
1062	pub async fn discover_in_path(&self, path:&str) -> Result<Vec<PluginDiscoveryResult>> {
1063		let Results = vec![];
1064
1065		// In production, this would scan the directory for plugin manifests
1066		// For now, we return an empty list
1067		info!("[PluginLoader] Discovering plugins in: {}", path);
1068
1069		Ok(Results)
1070	}
1071
1072	/// Load a plugin from a discovery result
1073	pub async fn load_from_discovery(&self, discovery:&PluginDiscoveryResult) -> Result<Arc<Box<dyn Plugin>>> {
1074		// In production, this would load the plugin from the manifest
1075		// For now, we return an error
1076		Err(AirError::Plugin(format!(
1077			"Plugin loading not yet implemented: {}",
1078			discovery.plugin_id
1079		)))
1080	}
1081}
1082
1083impl Default for PluginLoader {
1084	fn default() -> Self { Self::new() }
1085}
1086
1087// =============================================================================
1088// API Version Management
1089// =============================================================================
1090
1091/// API version information
1092#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1093pub struct ApiVersion {
1094	pub major:u32,
1095	pub minor:u32,
1096	pub patch:u32,
1097	pub PreRelease:Option<String>,
1098}
1099
1100impl ApiVersion {
1101	/// Get the current API version
1102	pub fn current() -> Self { Self { major:1, minor:0, patch:0, PreRelease:None } }
1103
1104	/// Parse version from string
1105	pub fn parse(version:&str) -> Result<Self> {
1106		let parts:Vec<&str> = version.split('.').collect();
1107		if parts.len() < 3 {
1108			return Err(crate::AirError::Plugin("Invalid version format".to_string()));
1109		}
1110
1111		Ok(Self {
1112			major:parts[0]
1113				.parse()
1114				.map_err(|_| crate::AirError::Plugin("Invalid major version".to_string()))?,
1115			minor:parts[1]
1116				.parse()
1117				.map_err(|_| crate::AirError::Plugin("Invalid minor version".to_string()))?,
1118			patch:parts[2]
1119				.parse()
1120				.map_err(|_| crate::AirError::Plugin("Invalid patch version".to_string()))?,
1121			PreRelease:if parts.len() > 3 { Some(parts[3].to_string()) } else { None },
1122		})
1123	}
1124
1125	/// Check if this version is compatible with another
1126	pub fn IsCompatible(&self, other:&ApiVersion) -> bool {
1127		// Same major version means compatible
1128		if self.major != other.major {
1129			return false;
1130		}
1131
1132		// If minor version is higher, it might have breaking changes
1133		if other.minor < self.minor {
1134			return false;
1135		}
1136
1137		true
1138	}
1139}
1140
1141/// API version manager
1142pub struct ApiVersionManager {
1143	CurrentVersion:ApiVersion,
1144	CompatibleVersions:Vec<ApiVersion>,
1145}
1146
1147impl ApiVersionManager {
1148	/// Create a new API version manager
1149	pub fn new() -> Self {
1150		let current = ApiVersion::current();
1151		Self { CurrentVersion:current.clone(), CompatibleVersions:vec![current] }
1152	}
1153
1154	/// Get the current API version
1155	pub fn current(&self) -> &ApiVersion { &self.CurrentVersion }
1156
1157	/// Check if a version is compatible
1158	pub fn IsCompatible(&self, version:&ApiVersion) -> bool { self.CurrentVersion.IsCompatible(version) }
1159
1160	/// Register a compatible API version
1161	pub fn register_compatible(&mut self, version:ApiVersion) {
1162		if self.IsCompatible(&version) && !self.CompatibleVersions.contains(&version) {
1163			self.CompatibleVersions.push(version);
1164		}
1165	}
1166}
1167
1168impl Default for ApiVersionManager {
1169	fn default() -> Self { Self::new() }
1170}
1171
1172// =============================================================================
1173// Plugin Isolation and Sandboxing
1174// =============================================================================
1175
1176/// Plugin sandbox manager
1177pub struct PluginSandboxManager {
1178	sandboxes:Arc<RwLock<HashMap<String, PluginSandboxConfig>>>,
1179}
1180
1181impl PluginSandboxManager {
1182	/// Create a new sandbox manager
1183	pub fn new() -> Self { Self { sandboxes:Arc::new(RwLock::new(HashMap::new())) } }
1184
1185	/// Create a sandbox for a plugin
1186	pub async fn create_sandbox(&self, plugin_id:String, config:PluginSandboxConfig) -> Result<()> {
1187		let mut sandboxes = self.sandboxes.write().await;
1188		sandboxes.insert(plugin_id, config);
1189		Ok(())
1190	}
1191
1192	/// Get sandbox configuration
1193	pub async fn get_sandbox(&self, plugin_id:&str) -> Option<PluginSandboxConfig> {
1194		let sandboxes = self.sandboxes.read().await;
1195		sandboxes.get(plugin_id).cloned()
1196	}
1197
1198	/// Remove a sandbox
1199	pub async fn remove_sandbox(&self, plugin_id:&str) {
1200		let mut sandboxes = self.sandboxes.write().await;
1201		sandboxes.remove(plugin_id);
1202	}
1203
1204	/// Check if a plugin is running in a sandbox
1205	pub async fn is_sandboxed(&self, plugin_id:&str) -> bool {
1206		let sandboxes = self.sandboxes.read().await;
1207		sandboxes.get(plugin_id).map_or(false, |s| s.enabled)
1208	}
1209}
1210
1211impl Default for PluginSandboxManager {
1212	fn default() -> Self { Self::new() }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217	use super::*;
1218
1219	struct TestPlugin;
1220
1221	#[async_trait]
1222	impl PluginHooks for TestPlugin {}
1223
1224	#[async_trait]
1225	impl Plugin for TestPlugin {
1226		fn metadata(&self) -> &PluginMetadata {
1227			&PluginMetadata {
1228				id:"test".to_string(),
1229				name:"Test Plugin".to_string(),
1230				version:"1.0.0".to_string(),
1231				description:"A test plugin".to_string(),
1232				author:"Test".to_string(),
1233				MinAirVersion:"0.1.0".to_string(),
1234				MaxAirVersion:None,
1235				dependencies:vec![],
1236				capabilities:vec![],
1237			}
1238		}
1239	}
1240
1241	#[tokio::test]
1242	async fn test_plugin_manager_creation() {
1243		let manager = PluginManager::new("0.1.0".to_string());
1244		let plugins = manager.list_plugins().await.unwrap();
1245		assert!(plugins.is_empty());
1246	}
1247
1248	#[tokio::test]
1249	async fn test_plugin_registration() {
1250		let manager = PluginManager::new("0.1.0".to_string());
1251		let plugin = Arc::new(Box::new(TestPlugin) as Box<dyn Plugin>);
1252
1253		let result = manager.register(plugin.clone()).await;
1254		assert!(result.is_ok());
1255
1256		let plugins = manager.list_plugins().await.unwrap();
1257		assert_eq!(plugins.len(), 1);
1258		assert_eq!(plugins[0].id, "test");
1259	}
1260
1261	#[tokio::test]
1262	async fn test_plugin_lifecycle() {
1263		let manager = PluginManager::new("0.1.0".to_string());
1264		let plugin = Arc::new(Box::new(TestPlugin) as Box<dyn Plugin>);
1265
1266		manager.register(plugin.clone()).await.unwrap();
1267
1268		// Start the plugin
1269		let result = manager.start("test").await;
1270		assert!(result.is_ok());
1271
1272		// Check state
1273		let plugins = manager.list_plugins().await.unwrap();
1274		assert_eq!(plugins[0].state, PluginState::Running);
1275
1276		// Stop the plugin
1277		let result = manager.stop("test").await;
1278		assert!(result.is_ok());
1279
1280		// Check state
1281		let plugins = manager.list_plugins().await.unwrap();
1282		assert_eq!(plugins[0].state, PluginState::Loaded);
1283	}
1284
1285	#[tokio::test]
1286	async fn test_version_satisfaction() {
1287		let manager = PluginManager::new("1.0.0".to_string());
1288
1289		assert!(manager.version_satisfies("1.0.0", "0.1.0"));
1290		assert!(manager.version_satisfies("1.2.0", "1.0.0"));
1291		assert!(manager.version_satisfies("1.0.5", "1.0.0"));
1292		assert!(!manager.version_satisfies("0.9.0", "1.0.0"));
1293	}
1294
1295	#[tokio::test]
1296	async fn test_plugin_message_validation() {
1297		let message = PluginMessage::new(
1298			"sender".to_string(),
1299			"receiver".to_string(),
1300			"action".to_string(),
1301			serde_json::json!({}),
1302		);
1303
1304		assert!(message.validate().is_ok());
1305	}
1306
1307	#[tokio::test]
1308	fn test_api_version_compatibility() {
1309		let v1 = ApiVersion { major:1, minor:0, patch:0, PreRelease:None };
1310		let v2 = ApiVersion { major:1, minor:1, patch:0, PreRelease:None };
1311		let v3 = ApiVersion { major:2, minor:0, patch:0, PreRelease:None };
1312
1313		assert!(v1.is_compatible(&v2));
1314		assert!(!v1.is_compatible(&v3));
1315	}
1316
1317	#[tokio::test]
1318	fn test_sandbox_config_default() {
1319		let config = PluginSandboxConfig::default();
1320		assert!(config.enabled);
1321		assert_eq!(config.MaxMemoryMb, Some(128));
1322		assert!(!config.NetworkAllowed);
1323		assert!(!config.FilesystemAllowed);
1324	}
1325
1326	#[tokio::test]
1327	fn test_plugin_metadata_validation() {
1328		let manager = PluginManager::new("1.0.0".to_string());
1329		let metadata = PluginMetadata {
1330			id:"test_plugin".to_string(),
1331			name:"Test Plugin".to_string(),
1332			version:"1.0.0".to_string(),
1333			description:"A test plugin".to_string(),
1334			author:"Test".to_string(),
1335			MinAirVersion:"1.0.0".to_string(),
1336			MaxAirVersion:None,
1337			dependencies:vec![],
1338			capabilities:vec![],
1339		};
1340
1341		assert!(manager.validate_plugin_metadata(&metadata).is_ok());
1342
1343		let InvalidMetadata = PluginMetadata {
1344			id:"".to_string(),
1345			name:"Invalid".to_string(),
1346			version:"1.0.0".to_string(),
1347			description:"Invalid plugin".to_string(),
1348			author:"Test".to_string(),
1349			MinAirVersion:"1.0.0".to_string(),
1350			MaxAirVersion:None,
1351			dependencies:vec![],
1352			capabilities:vec![],
1353		};
1354
1355		assert!(manager.validate_plugin_metadata(&invalid_metadata).is_err());
1356	}
1357}