更改项目名为NebulaShell
This commit is contained in:
77
store/@{NebulaShell}/plugin-bridge/README.md
Normal file
77
store/@{NebulaShell}/plugin-bridge/README.md
Normal file
@@ -0,0 +1,77 @@
|
||||
# plugin-bridge 插件桥接器
|
||||
|
||||
提供插件间的事件共享、广播、桥接和 RPC 服务调用。
|
||||
|
||||
## 功能
|
||||
|
||||
- **事件总线**: 插件间共享事件(发布/订阅)
|
||||
- **广播**: 向多个插件发送消息
|
||||
- **桥接**: 将不同插件的事件互相映射
|
||||
- **RPC 服务调用**: 插件 A 调用插件 B 的方法并获取返回值
|
||||
|
||||
## 事件总线(发布/订阅 + 解耦)
|
||||
|
||||
```python
|
||||
bridge = plugin_mgr.get("plugin-bridge")
|
||||
bus = bridge.event_bus
|
||||
|
||||
# 订阅事件(发布者和订阅者解耦)
|
||||
bus.on("http.request", lambda event: print(f"收到请求: {event.payload}"))
|
||||
|
||||
# 发布事件
|
||||
bus.emit(BridgeEvent(
|
||||
type="http.request",
|
||||
source_plugin="http-api",
|
||||
payload={"path": "/api/users"}
|
||||
))
|
||||
```
|
||||
|
||||
## RPC 服务调用
|
||||
|
||||
```python
|
||||
# 插件 B 注册服务
|
||||
bridge.services.register("plugin-b", "get_user", lambda user_id: {"id": user_id, "name": "test"})
|
||||
|
||||
# 插件 A 调用插件 B 的服务
|
||||
result = bridge.services.call("plugin-b", "get_user", 123)
|
||||
print(result) # {"id": 123, "name": "test"}
|
||||
```
|
||||
|
||||
## 广播
|
||||
|
||||
```python
|
||||
broadcast = bridge.broadcast
|
||||
|
||||
# 创建频道
|
||||
broadcast.create_channel("system", ["lifecycle", "metrics"])
|
||||
|
||||
# 广播消息
|
||||
broadcast.broadcast("system", {"action": "shutdown"}, "plugin-loader")
|
||||
```
|
||||
|
||||
## 桥接
|
||||
|
||||
```python
|
||||
bridge_mgr = bridge.bridge
|
||||
|
||||
# 创建桥接:将 http-api 的事件映射到 metrics
|
||||
bridge_mgr.create_bridge(
|
||||
name="http-to-metrics",
|
||||
from_plugin="http-api",
|
||||
to_plugin="metrics",
|
||||
event_mapping={
|
||||
"http.request": "metrics.http_request",
|
||||
"http.error": "metrics.http_error",
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## 事件历史
|
||||
|
||||
```python
|
||||
# 查询历史
|
||||
history = bus.get_history("http.request")
|
||||
|
||||
# 清空历史
|
||||
bus.clear_history()
|
||||
```
|
||||
8
store/@{NebulaShell}/plugin-bridge/SIGNATURE
Normal file
8
store/@{NebulaShell}/plugin-bridge/SIGNATURE
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"signature": "yHmcdnBP6fx7TYFqHyiQeVYiP+S/9o7gx+fCC7nELQ2ZM55yXn9e4qpYWgPGAEw4zmuZbnKwLj0JQ1sE8BW28059+HWCj34ytUY/gckNvEkN+cGrqefwxWPGU19tysDC9Iy+HgBc+t34/igLZvRbcqpCpE0KH9SGfe34de6C60fL/HYZ1v3A29R05VmoPUBIOUY3X/9R5q4fYkjQqzvJ9LXujRR7Uyg8vP4dQo3k/MdxALg0xemXrMNRvX9F2g7i7DLCG8ABNxLHl7u5BymNXqBBClSu+/Fuf0HeyzLyYoOUP0Jhbxf56ep8jFLZRTU1qbt6itmaZgF8YSUh4oq1rWNYHZLZYH9sO6H32XsqXSq/509DkKXWJDZtIvJB/yrmVpt1Anj8YfMyA4pZ/R+htMa+coOlCAw20lnN0IMJW8oduKoYHFKMKkE7b++TzUv+7jon7WRWW8/2BXUFGV62jUSkPzI5o4TOgflHcCbLJ6SuOutxTpGiereVdDxlLRUVwBcRxY89DM9LKzqBPCbfG4Q6bVTtIvnyHn/ARQuYYXw41QzJGUYss/pS0YIH0YgYUHR88RCFqlZI53JXv1Y7kzieEprEWBBWEr6YxmYhx010W36hI0mM7YpBK3XWVkN7oJFBDt7DzFSQEYeeKDV/U0ZZgA5ufSiB8LYLYVjpz9Y=",
|
||||
"signer": "NebulaShell",
|
||||
"algorithm": "RSA-SHA256",
|
||||
"timestamp": 1775964952.957446,
|
||||
"plugin_hash": "97113f6d132bf58ea11688416b0fa3dda3a3642f3b82fd1e0b65ad06f8aad39c",
|
||||
"author": "NebulaShell"
|
||||
}
|
||||
205
store/@{NebulaShell}/plugin-bridge/main.py
Normal file
205
store/@{NebulaShell}/plugin-bridge/main.py
Normal file
@@ -0,0 +1,205 @@
|
||||
"""插件桥接器 - 共享事件、广播、桥接"""
|
||||
from typing import Any, Callable, Optional
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from oss.logger.logger import Log
|
||||
from oss.plugin.types import Plugin, register_plugin_type
|
||||
|
||||
|
||||
@dataclass
|
||||
class BridgeEvent:
|
||||
"""桥接事件"""
|
||||
type: str
|
||||
source_plugin: str
|
||||
payload: Any = None
|
||||
context: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
class EventBus:
|
||||
"""事件总线"""
|
||||
|
||||
def __init__(self):
|
||||
self._handlers: dict[str, list[Callable]] = {}
|
||||
self._history: list[BridgeEvent] = []
|
||||
|
||||
def emit(self, event: BridgeEvent):
|
||||
"""发布事件"""
|
||||
self._history.append(event)
|
||||
handlers = self._handlers.get(event.type, [])
|
||||
wildcard_handlers = self._handlers.get("*", [])
|
||||
for handler in handlers + wildcard_handlers:
|
||||
try:
|
||||
handler(event)
|
||||
except Exception as e:
|
||||
import traceback; print(f"[main.py] 错误:{type(e).__name__}:{e}"); traceback.print_exc()
|
||||
pass
|
||||
|
||||
def on(self, event_type: str, handler: Callable):
|
||||
"""订阅事件"""
|
||||
if event_type not in self._handlers:
|
||||
self._handlers[event_type] = []
|
||||
self._handlers[event_type].append(handler)
|
||||
|
||||
def off(self, event_type: str, handler: Callable):
|
||||
"""取消订阅"""
|
||||
if event_type in self._handlers:
|
||||
try:
|
||||
self._handlers[event_type].remove(handler)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def once(self, event_type: str, handler: Callable):
|
||||
"""仅触发一次"""
|
||||
def wrapper(event):
|
||||
self.off(event_type, wrapper)
|
||||
handler(event)
|
||||
self.on(event_type, wrapper)
|
||||
|
||||
def get_history(self, event_type: str = None) -> list[BridgeEvent]:
|
||||
"""获取事件历史"""
|
||||
if event_type:
|
||||
return [e for e in self._history if e.type == event_type]
|
||||
return self._history.copy()
|
||||
|
||||
def clear_history(self):
|
||||
"""清空事件历史"""
|
||||
self._history.clear()
|
||||
|
||||
|
||||
class BroadcastManager:
|
||||
"""广播管理器"""
|
||||
|
||||
def __init__(self, event_bus: EventBus):
|
||||
self.event_bus = event_bus
|
||||
self._channels: dict[str, list[str]] = {}
|
||||
|
||||
def create_channel(self, name: str, plugins: list[str]):
|
||||
"""创建广播频道"""
|
||||
self._channels[name] = plugins
|
||||
|
||||
def broadcast(self, channel: str, payload: Any, source_plugin: str = ""):
|
||||
"""广播到指定频道"""
|
||||
if channel not in self._channels:
|
||||
return
|
||||
event = BridgeEvent(
|
||||
type=f"broadcast.{channel}",
|
||||
source_plugin=source_plugin,
|
||||
payload=payload
|
||||
)
|
||||
self.event_bus.emit(event)
|
||||
|
||||
def get_channels(self) -> dict[str, list[str]]:
|
||||
"""获取所有频道"""
|
||||
return self._channels.copy()
|
||||
|
||||
|
||||
class ServiceRegistry:
|
||||
"""服务注册表(RPC)"""
|
||||
|
||||
def __init__(self):
|
||||
self._services: dict[str, dict[str, Callable]] = {}
|
||||
|
||||
def register(self, plugin_name: str, service_name: str, handler: Callable):
|
||||
"""注册服务"""
|
||||
if plugin_name not in self._services:
|
||||
self._services[plugin_name] = {}
|
||||
self._services[plugin_name][service_name] = handler
|
||||
|
||||
def unregister(self, plugin_name: str, service_name: str = None):
|
||||
"""注销服务"""
|
||||
if plugin_name in self._services:
|
||||
if service_name:
|
||||
self._services[plugin_name].pop(service_name, None)
|
||||
else:
|
||||
del self._services[plugin_name]
|
||||
|
||||
def call(self, plugin_name: str, service_name: str, *args, **kwargs) -> Any:
|
||||
"""远程调用"""
|
||||
if plugin_name not in self._services:
|
||||
raise RuntimeError(f"插件 '{plugin_name}' 未注册服务")
|
||||
if service_name not in self._services[plugin_name]:
|
||||
raise RuntimeError(f"插件 '{plugin_name}' 未注册服务 '{service_name}'")
|
||||
return self._services[plugin_name][service_name](*args, **kwargs)
|
||||
|
||||
def list_services(self, plugin_name: str = None) -> dict[str, dict[str, Callable]]:
|
||||
"""列出服务"""
|
||||
if plugin_name:
|
||||
return self._services.get(plugin_name, {}).copy()
|
||||
return {k: v.copy() for k, v in self._services.items()}
|
||||
|
||||
|
||||
class BridgeManager:
|
||||
"""桥接管理器"""
|
||||
|
||||
def __init__(self, event_bus: EventBus):
|
||||
self.event_bus = event_bus
|
||||
self._bridges: dict[str, dict[str, Any]] = {}
|
||||
|
||||
def create_bridge(self, name: str, from_plugin: str, to_plugin: str, event_mapping: dict[str, str]):
|
||||
"""创建桥接:将 from_plugin 的事件映射到 to_plugin"""
|
||||
self._bridges[name] = {
|
||||
"from": from_plugin,
|
||||
"to": to_plugin,
|
||||
"mapping": event_mapping,
|
||||
}
|
||||
# 注册桥接处理器
|
||||
for src_event, dst_event in event_mapping.items():
|
||||
def handler(event, dst_event=dst_event):
|
||||
bridged = BridgeEvent(
|
||||
type=dst_event,
|
||||
source_plugin=event.source_plugin,
|
||||
payload=event.payload,
|
||||
context={**event.context, "_bridged_from": event.type}
|
||||
)
|
||||
self.event_bus.emit(bridged)
|
||||
self.event_bus.on(src_event, handler)
|
||||
|
||||
def remove_bridge(self, name: str):
|
||||
"""移除桥接"""
|
||||
if name in self._bridges:
|
||||
del self._bridges[name]
|
||||
|
||||
def get_bridges(self) -> dict[str, dict[str, Any]]:
|
||||
"""获取所有桥接"""
|
||||
return self._bridges.copy()
|
||||
|
||||
|
||||
class PluginBridgePlugin(Plugin):
|
||||
"""插件桥接器插件"""
|
||||
|
||||
def __init__(self):
|
||||
self.event_bus = EventBus()
|
||||
self.broadcast = None
|
||||
self.bridge = None
|
||||
self.services = ServiceRegistry()
|
||||
self.storage = None # 共享存储接口
|
||||
|
||||
def init(self, deps: dict = None):
|
||||
"""初始化"""
|
||||
self.broadcast = BroadcastManager(self.event_bus)
|
||||
self.bridge = BridgeManager(self.event_bus)
|
||||
|
||||
def start(self):
|
||||
"""启动"""
|
||||
Log.info("plugin-bridge", "事件总线、广播、桥接、RPC、共享存储已启动")
|
||||
|
||||
def stop(self):
|
||||
"""停止"""
|
||||
self.event_bus.clear_history()
|
||||
|
||||
def set_plugin_storage(self, storage_plugin):
|
||||
"""设置存储插件引用"""
|
||||
if storage_plugin:
|
||||
self.storage = storage_plugin.get_shared()
|
||||
|
||||
|
||||
# 注册类型
|
||||
register_plugin_type("BridgeEvent", BridgeEvent)
|
||||
register_plugin_type("EventBus", EventBus)
|
||||
register_plugin_type("BroadcastManager", BroadcastManager)
|
||||
register_plugin_type("BridgeManager", BridgeManager)
|
||||
register_plugin_type("ServiceRegistry", ServiceRegistry)
|
||||
|
||||
|
||||
def New():
|
||||
return PluginBridgePlugin()
|
||||
20
store/@{NebulaShell}/plugin-bridge/manifest.json
Normal file
20
store/@{NebulaShell}/plugin-bridge/manifest.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"metadata": {
|
||||
"name": "plugin-bridge",
|
||||
"version": "1.1.0",
|
||||
"author": "NebulaShell",
|
||||
"description": "插件桥接器 - 共享事件/广播/桥接/多语言支持",
|
||||
"type": "core"
|
||||
},
|
||||
"config": {
|
||||
"enabled": true,
|
||||
"args": {
|
||||
"max_events": 1000,
|
||||
"event_ttl": 3600,
|
||||
"broadcast_enabled": true,
|
||||
"queue_size": 5000
|
||||
}
|
||||
},
|
||||
"dependencies": ["plugin-storage", "i18n"],
|
||||
"permissions": ["plugin-storage", "lifecycle"]
|
||||
}
|
||||
Reference in New Issue
Block a user