sen_plugin_host/lib.rs
1//! sen-plugin-host: WASM plugin host runtime for sen-rs
2//!
3//! This crate provides the runtime for loading and executing WASM plugins.
4//! It enables building plugin-extensible CLI applications with minimal effort.
5//!
6//! # Architecture
7//!
8//! ```text
9//! ┌─────────────────────────────────────────────────────────────┐
10//! │ Your Application │
11//! ├─────────────────────────────────────────────────────────────┤
12//! │ PluginRegistry │
13//! │ ├─ load_plugin("./plugins/hello.wasm") │
14//! │ ├─ execute("hello", &["World"]) │
15//! │ └─ list_commands() │
16//! ├─────────────────────────────────────────────────────────────┤
17//! │ HotReloadWatcher (optional) │
18//! │ └─ Watches directories, auto-loads/unloads plugins │
19//! ├─────────────────────────────────────────────────────────────┤
20//! │ PluginLoader │
21//! │ ├─ Compiles WASM modules │
22//! │ ├─ Validates API version │
23//! │ └─ Creates sandboxed instances │
24//! ├─────────────────────────────────────────────────────────────┤
25//! │ wasmtime (sandboxed execution) │
26//! │ ├─ CPU limits (fuel) │
27//! │ ├─ Stack limits (1MB) │
28//! │ └─ Memory isolation │
29//! └─────────────────────────────────────────────────────────────┘
30//! ```
31//!
32//! # Features
33//!
34//! - **Plugin Loading**: Load and execute WASM plugins with sandboxed execution
35//! - **Hot Reload**: Automatically reload plugins when files change
36//! - **Plugin Discovery**: Scan directories for plugin files
37//! - **CPU Limits**: Prevent infinite loops with fuel-based execution limits
38//! - **Stack Limits**: Prevent stack overflow attacks (1MB limit)
39//! - `sen-integration`: Enable integration with sen-rs Router (adds `bridge` module)
40//!
41//! # Security
42//!
43//! Plugins run in a sandboxed WASM environment with multiple protections:
44//!
45//! | Protection | Description |
46//! |------------|-------------|
47//! | CPU Limit | 10M instructions per execution (fuel) |
48//! | Stack Limit | 1MB maximum WASM stack |
49//! | Memory Isolation | Each plugin has isolated linear memory |
50//! | No System Access | No filesystem, network, or OS access |
51//! | API Versioning | Rejects plugins with incompatible API versions |
52//!
53//! # Quick Start
54//!
55//! ## Building Plugins
56//!
57//! See the `sen-plugin-sdk` crate for detailed plugin development guide.
58//!
59//! ```bash
60//! # Add the WASM target (one-time setup)
61//! rustup target add wasm32-unknown-unknown
62//!
63//! # Build plugin
64//! cargo build --release --target wasm32-unknown-unknown
65//! ```
66//!
67//! ## Loading and Executing Plugins
68//!
69//! ### Direct Loading
70//!
71//! ```rust,ignore
72//! use sen_plugin_host::PluginLoader;
73//!
74//! let loader = PluginLoader::new()?;
75//! let wasm_bytes = std::fs::read("plugin.wasm")?;
76//! let mut plugin = loader.load(&wasm_bytes)?;
77//!
78//! // Check plugin metadata
79//! println!("Command: {}", plugin.manifest.command.name);
80//! println!("Description: {}", plugin.manifest.command.about);
81//!
82//! // Execute
83//! let result = plugin.instance.execute(&["World".to_string()])?;
84//! match result {
85//! ExecuteResult::Success(output) => println!("{}", output),
86//! ExecuteResult::Error(err) => eprintln!("Error: {}", err.message),
87//! }
88//! ```
89//!
90//! ### Using Registry (Recommended)
91//!
92//! ```rust,ignore
93//! use sen_plugin_host::PluginRegistry;
94//!
95//! let registry = PluginRegistry::new()?;
96//!
97//! // Load plugins
98//! registry.load_plugin("./plugins/hello.wasm").await?;
99//! registry.load_plugin("./plugins/greet.wasm").await?;
100//!
101//! // List available commands
102//! for cmd in registry.list_commands().await {
103//! println!(" {}", cmd);
104//! }
105//!
106//! // Execute by command name
107//! let result = registry.execute("hello", &["World".to_string()]).await?;
108//! ```
109//!
110//! ## Hot Reload
111//!
112//! Automatically load/reload/unload plugins when files change:
113//!
114//! ```rust,ignore
115//! use sen_plugin_host::{PluginRegistry, HotReloadWatcher, WatcherConfig};
116//! use std::time::Duration;
117//!
118//! let registry = PluginRegistry::new()?;
119//!
120//! // Watch directory for plugin changes
121//! let _watcher = HotReloadWatcher::new(
122//! registry.clone(),
123//! vec!["./plugins"],
124//! WatcherConfig {
125//! debounce: Duration::from_millis(300),
126//! load_existing: true, // Load existing plugins on start
127//! },
128//! ).await?;
129//!
130//! // Plugins are now automatically managed:
131//! // - New .wasm files are loaded
132//! // - Modified .wasm files are reloaded
133//! // - Deleted .wasm files are unloaded
134//!
135//! // Your application continues running...
136//! loop {
137//! let cmd = read_user_input();
138//! if let Ok(result) = registry.execute(&cmd, &args).await {
139//! // Handle result
140//! }
141//! }
142//! ```
143//!
144//! ## Plugin Discovery
145//!
146//! Scan directories for plugins without hot reload:
147//!
148//! ```rust,ignore
149//! use sen_plugin_host::PluginScanner;
150//!
151//! let scanner = PluginScanner::new()?;
152//! let result = scanner.scan_directory("./plugins")?;
153//!
154//! println!("Found {} plugins", result.plugins.len());
155//! for plugin in &result.plugins {
156//! println!(" - {} ({})", plugin.manifest.command.name, plugin.manifest.command.about);
157//! }
158//!
159//! if !result.failures.is_empty() {
160//! eprintln!("Failed to load {} plugins:", result.failures.len());
161//! for (path, error) in &result.failures {
162//! eprintln!(" - {}: {}", path.display(), error);
163//! }
164//! }
165//! ```
166//!
167//! # sen-rs Router Integration
168//!
169//! Enable the `sen-integration` feature to integrate plugins with sen-rs Router:
170//!
171//! ```toml
172//! [dependencies]
173//! sen-plugin-host = { version = "0.7", features = ["sen-integration"] }
174//! ```
175//!
176//! ```rust,ignore
177//! use sen::Router;
178//! use sen_plugin_host::{PluginLoader, RouterPluginExt};
179//!
180//! let loader = PluginLoader::new()?;
181//! let plugin = loader.load(&wasm_bytes)?;
182//!
183//! // Register plugin as a router command
184//! let router = Router::new()
185//! .plugin(plugin)
186//! .with_state(MyState);
187//!
188//! // Plugin command is now available via the router
189//! let response = router.execute_with(&["app", "hello", "World"]).await;
190//! ```
191//!
192//! # Examples
193//!
194//! See `examples/wasm-cli/` for a complete example of a plugin-extensible CLI
195//! with hot reload support.
196
197pub mod audit;
198pub mod discovery;
199pub mod loader;
200pub mod permission;
201pub mod registry;
202pub mod watcher;
203
204#[cfg(feature = "wasi")]
205pub mod wasi;
206
207#[cfg(feature = "sen-integration")]
208pub mod bridge;
209
210pub use discovery::{default_plugin_dirs, DiscoveryError, DiscoveryResult, PluginScanner};
211pub use loader::{EffectHandler, LoadedPlugin, LoaderError, PluginInstance, PluginLoader};
212pub use registry::{PluginRegistry, RegistryError};
213pub use sen_plugin_api::{
214 ArgSpec, Capabilities, CommandSpec, Effect, EffectResult, ExecuteError, ExecuteResult,
215 HttpResponse, NetPattern, PathPattern, PluginManifest, StdioCapability,
216};
217pub use watcher::{HotReloadWatcher, WatcherConfig, WatcherError};
218
219// Permission system re-exports
220pub use permission::{PermissionConfig, PermissionConfigBuilder, PermissionPresets};
221
222#[cfg(feature = "sen-integration")]
223pub use bridge::{generate_plugin_help, register_plugins_from_spec, RouterPluginExt, WasmHandler};