orchflow_core/manager/
plugins.rs1use super::{Event, Manager};
2use async_trait::async_trait;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::sync::Arc;
6
7#[async_trait]
8pub trait Plugin: Send + Sync {
9 fn id(&self) -> &str;
11
12 fn metadata(&self) -> PluginMetadata;
14
15 async fn init(&mut self, context: PluginContext) -> Result<(), String>;
17
18 async fn handle_event(&mut self, event: &Event) -> Result<(), String>;
20
21 async fn handle_request(&mut self, method: &str, _params: Value) -> Result<Value, String> {
23 Err(format!("Unknown method: {method}"))
24 }
25
26 async fn shutdown(&mut self) -> Result<(), String>;
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct PluginMetadata {
32 pub name: String,
33 pub version: String,
34 pub author: String,
35 pub description: String,
36 pub capabilities: Vec<String>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct PluginInfo {
41 pub id: String,
42 pub name: String,
43 pub version: String,
44 pub author: String,
45 pub description: String,
46 pub capabilities: Vec<String>,
47 pub loaded: bool,
48}
49
50#[derive(Clone)]
51pub struct PluginContext {
52 pub manager: Arc<Manager>,
53 pub plugin_id: String,
54}
55
56impl PluginContext {
57 pub async fn execute(&self, action: super::Action) -> Result<Value, String> {
59 self.manager
60 .execute_action(action)
61 .await
62 .map_err(|e| e.to_string())
63 }
64
65 pub async fn subscribe(&self, events: Vec<String>) -> Result<(), String> {
67 self.manager
68 .subscribe_plugin(&self.plugin_id, events)
69 .await
70 .map_err(|e| e.to_string())
71 }
72}
73
74pub(super) async fn dispatch_event_to_plugins(manager: &Manager, event: &Event) {
76 let plugins = manager.plugins.read().await;
77 let subscriptions = manager.plugin_subscriptions.read().await;
78
79 let event_type = match event {
81 Event::OrchestratorStarted => "orchestrator_started",
82 Event::OrchestratorStopping => "orchestrator_stopping",
83 Event::SessionCreated { .. } => "session_created",
84 Event::SessionUpdated { .. } => "session_updated",
85 Event::SessionDeleted { .. } => "session_deleted",
86 Event::PaneCreated { .. } => "pane_created",
87 Event::PaneOutput { .. } => "pane_output",
88 Event::PaneClosed { .. } => "pane_closed",
89 Event::PaneDestroyed { .. } => "pane_destroyed",
90 Event::PaneFocused { .. } => "pane_focused",
91 Event::PaneResized { .. } => "pane_resized",
92 Event::FileOpened { .. } => "file_opened",
93 Event::FileSaved { .. } => "file_saved",
94 Event::FileChanged { .. } => "file_changed",
95 Event::FileWatchStarted { .. } => "file_watch_started",
96 Event::FileWatchStopped { .. } => "file_watch_stopped",
97 Event::FileWatchEvent { .. } => "file_watch_event",
98 Event::CommandExecuted { .. } => "command_executed",
99 Event::CommandCompleted { .. } => "command_completed",
100 Event::PluginLoaded { .. } => "plugin_loaded",
101 Event::PluginUnloaded { .. } => "plugin_unloaded",
102 Event::PluginError { .. } => "plugin_error",
103 Event::Custom { event_type, .. } => event_type.as_str(),
104 Event::FileRead { .. } => "file_read",
105 };
106
107 for (plugin_id, plugin) in plugins.iter() {
109 if let Some(events) = subscriptions.get(plugin_id) {
111 if events.contains(&event_type.to_string()) || events.contains(&"*".to_string()) {
112 let plugin_id_clone = plugin_id.clone();
114 let plugin_clone = Arc::clone(plugin);
115 let event_clone = event.clone();
116 let event_type_clone = event_type.to_string();
117
118 tokio::spawn(async move {
120 let mut plugin_guard = plugin_clone.lock().await;
121 if let Err(e) = plugin_guard.handle_event(&event_clone).await {
122 tracing::error!(
123 "Plugin {} error handling event {}: {}",
124 plugin_id_clone,
125 event_type_clone,
126 e
127 );
128 }
129 });
130 }
131 }
132 }
133}