629 lines
16 KiB
Markdown
629 lines
16 KiB
Markdown
|
|
# Phase 8:插件生态(Plugin Ecosystem)
|
|||
|
|
|
|||
|
|
日期:2026-04-04
|
|||
|
|
状态:待开始
|
|||
|
|
前置依赖:Phase 6(工具系统重构)、Phase 7(Hook 拦截层)
|
|||
|
|
Demo参考:claw-code-main — plugins/, PluginLifecycle
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 阶段目标
|
|||
|
|
|
|||
|
|
建立完整的**插件生命周期管理系统**,允许第三方开发者通过插件扩展 Jarvis 的功能。
|
|||
|
|
|
|||
|
|
**核心能力**:
|
|||
|
|
- 插件安装/卸载/启用/禁用
|
|||
|
|
- 插件沙箱隔离执行
|
|||
|
|
- 插件提供的工具/Hook/命令注册
|
|||
|
|
- 内置插件集
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 插件架构
|
|||
|
|
|
|||
|
|
### 2.1 插件结构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
my-plugin/
|
|||
|
|
├── manifest.json # 插件清单
|
|||
|
|
├── SKILL.md # 插件技能文档(可选)
|
|||
|
|
├── README.md # 插件说明
|
|||
|
|
│
|
|||
|
|
├── tools/ # 插件提供的工具
|
|||
|
|
│ ├── __init__.py
|
|||
|
|
│ └── my_tool.py
|
|||
|
|
│
|
|||
|
|
├── hooks/ # 插件提供的 Hook
|
|||
|
|
│ ├── __init__.py
|
|||
|
|
│ └── my_hook.py
|
|||
|
|
│
|
|||
|
|
├── commands/ # 插件提供的命令
|
|||
|
|
│ ├── __init__.py
|
|||
|
|
│ └── my_command.py
|
|||
|
|
│
|
|||
|
|
└── static/ # 静态资源
|
|||
|
|
└── icon.png
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2.2 插件清单 (manifest.json)
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"id": "my-plugin",
|
|||
|
|
"name": "My Plugin",
|
|||
|
|
"version": "1.0.0",
|
|||
|
|
"description": "A sample plugin for Jarvis",
|
|||
|
|
"author": "Developer Name",
|
|||
|
|
"license": "MIT",
|
|||
|
|
"homepage": "https://github.com/developer/my-plugin",
|
|||
|
|
|
|||
|
|
"compatibility": {
|
|||
|
|
"jarvis_version": ">=2.0.0",
|
|||
|
|
"api_version": "1.0"
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
"capabilities": {
|
|||
|
|
"tools": [
|
|||
|
|
{
|
|||
|
|
"name": "my_tool",
|
|||
|
|
"description": "Does something useful",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"input": {"type": "string"}
|
|||
|
|
},
|
|||
|
|
"required": ["input"]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"hooks": [
|
|||
|
|
{
|
|||
|
|
"name": "my_pre_hook",
|
|||
|
|
"type": "pre_tool_use",
|
|||
|
|
"description": "Custom pre-hook"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"commands": [
|
|||
|
|
{
|
|||
|
|
"name": "/my-command",
|
|||
|
|
"description": "Custom slash command"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
"permissions": {
|
|||
|
|
"network": false,
|
|||
|
|
"filesystem": "read",
|
|||
|
|
"database": false
|
|||
|
|
},
|
|||
|
|
|
|||
|
|
"dependencies": {}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 核心组件
|
|||
|
|
|
|||
|
|
### 3.1 PluginManifest
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# backend/app/agents/plugins/manifest.py
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class ToolSpec:
|
|||
|
|
"""工具规格"""
|
|||
|
|
name: str
|
|||
|
|
description: str
|
|||
|
|
parameters: dict
|
|||
|
|
return_schema: dict | None = None
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class HookSpec:
|
|||
|
|
"""Hook 规格"""
|
|||
|
|
name: str
|
|||
|
|
type: HookType
|
|||
|
|
description: str
|
|||
|
|
filter_names: list[str] | None = None
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class CommandSpec:
|
|||
|
|
"""命令规格"""
|
|||
|
|
name: str
|
|||
|
|
description: str
|
|||
|
|
parameters: list | None = None
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class PluginManifest:
|
|||
|
|
"""插件清单"""
|
|||
|
|
id: str
|
|||
|
|
name: str
|
|||
|
|
version: str
|
|||
|
|
description: str
|
|||
|
|
author: str
|
|||
|
|
license: str
|
|||
|
|
homepage: str | None = None
|
|||
|
|
|
|||
|
|
compatibility: dict = field(default_factory=dict)
|
|||
|
|
capabilities: dict = field(default_factory=dict)
|
|||
|
|
permissions: dict = field(default_factory=dict)
|
|||
|
|
dependencies: dict = field(default_factory=dict)
|
|||
|
|
|
|||
|
|
@classmethod
|
|||
|
|
def from_file(cls, path: Path) -> "PluginManifest":
|
|||
|
|
"""从文件加载"""
|
|||
|
|
with open(path / "manifest.json") as f:
|
|||
|
|
data = json.load(f)
|
|||
|
|
return cls(**data)
|
|||
|
|
|
|||
|
|
def validate(self) -> list[str]:
|
|||
|
|
"""验证清单有效性"""
|
|||
|
|
errors = []
|
|||
|
|
|
|||
|
|
if not self.id:
|
|||
|
|
errors.append("Plugin ID is required")
|
|||
|
|
if not self.version:
|
|||
|
|
errors.append("Plugin version is required")
|
|||
|
|
if "tools" in self.capabilities:
|
|||
|
|
for tool in self.capabilities["tools"]:
|
|||
|
|
if "name" not in tool:
|
|||
|
|
errors.append(f"Tool missing name in plugin {self.id}")
|
|||
|
|
|
|||
|
|
return errors
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3.2 PluginManager
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# backend/app/agents/plugins/manager.py
|
|||
|
|
|
|||
|
|
class PluginState(Enum):
|
|||
|
|
"""插件状态"""
|
|||
|
|
INSTALLED = "installed"
|
|||
|
|
ENABLED = "enabled"
|
|||
|
|
DISABLED = "disabled"
|
|||
|
|
ERROR = "error"
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class Plugin:
|
|||
|
|
"""插件实例"""
|
|||
|
|
manifest: PluginManifest
|
|||
|
|
path: Path
|
|||
|
|
state: PluginState
|
|||
|
|
module: Any = None
|
|||
|
|
error_message: str | None = None
|
|||
|
|
|
|||
|
|
tools: list[ToolManifest] = field(default_factory=list)
|
|||
|
|
hooks: list[HookConfig] = field(default_factory=list)
|
|||
|
|
commands: list[CommandSpec] = field(default_factory=list)
|
|||
|
|
|
|||
|
|
class PluginManager:
|
|||
|
|
"""插件管理器"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
plugin_dir: Path,
|
|||
|
|
tool_registry: ToolRegistry,
|
|||
|
|
hook_manager: HookManager
|
|||
|
|
):
|
|||
|
|
self.plugin_dir = plugin_dir
|
|||
|
|
self.tool_registry = tool_registry
|
|||
|
|
self.hook_manager = hook_manager
|
|||
|
|
self._plugins: dict[str, Plugin] = {}
|
|||
|
|
self._hooks = []
|
|||
|
|
|
|||
|
|
async def install(self, plugin_path: Path | str) -> Plugin:
|
|||
|
|
"""
|
|||
|
|
安装插件
|
|||
|
|
|
|||
|
|
流程:
|
|||
|
|
1. 验证插件清单
|
|||
|
|
2. 检查兼容性
|
|||
|
|
3. 复制到插件目录
|
|||
|
|
4. 加载插件模块
|
|||
|
|
"""
|
|||
|
|
plugin_path = Path(plugin_path)
|
|||
|
|
|
|||
|
|
# 1. 加载清单
|
|||
|
|
manifest = PluginManifest.from_file(plugin_path)
|
|||
|
|
|
|||
|
|
# 2. 验证
|
|||
|
|
errors = manifest.validate()
|
|||
|
|
if errors:
|
|||
|
|
raise PluginValidationError(errors)
|
|||
|
|
|
|||
|
|
# 3. 检查版本兼容性
|
|||
|
|
await self._check_compatibility(manifest)
|
|||
|
|
|
|||
|
|
# 4. 创建插件目录
|
|||
|
|
target_dir = self.plugin_dir / manifest.id
|
|||
|
|
if target_dir.exists():
|
|||
|
|
raise PluginAlreadyExistsError(manifest.id)
|
|||
|
|
|
|||
|
|
shutil.copytree(plugin_path, target_dir)
|
|||
|
|
|
|||
|
|
# 5. 创建插件实例
|
|||
|
|
plugin = Plugin(
|
|||
|
|
manifest=manifest,
|
|||
|
|
path=target_dir,
|
|||
|
|
state=PluginState.INSTALLED
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 6. 加载模块
|
|||
|
|
await self._load_plugin(plugin)
|
|||
|
|
|
|||
|
|
self._plugins[manifest.id] = plugin
|
|||
|
|
return plugin
|
|||
|
|
|
|||
|
|
async def uninstall(self, plugin_id: str) -> bool:
|
|||
|
|
"""卸载插件"""
|
|||
|
|
plugin = self._plugins.get(plugin_id)
|
|||
|
|
if not plugin:
|
|||
|
|
raise PluginNotFoundError(plugin_id)
|
|||
|
|
|
|||
|
|
# 禁用插件
|
|||
|
|
if plugin.state == PluginState.ENABLED:
|
|||
|
|
await self.disable(plugin_id)
|
|||
|
|
|
|||
|
|
# 移除工具和 Hook
|
|||
|
|
for tool in plugin.tools:
|
|||
|
|
self.tool_registry.unregister(tool.name)
|
|||
|
|
|
|||
|
|
for hook in plugin.hooks:
|
|||
|
|
self.hook_manager.remove_hook(hook)
|
|||
|
|
|
|||
|
|
# 删除插件目录
|
|||
|
|
shutil.rmtree(plugin.path)
|
|||
|
|
|
|||
|
|
del self._plugins[plugin_id]
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
async def enable(self, plugin_id: str) -> Plugin:
|
|||
|
|
"""启用插件"""
|
|||
|
|
plugin = self._plugins.get(plugin_id)
|
|||
|
|
if not plugin:
|
|||
|
|
raise PluginNotFoundError(plugin_id)
|
|||
|
|
|
|||
|
|
# 注册工具
|
|||
|
|
for tool in plugin.tools:
|
|||
|
|
self.tool_registry.register(
|
|||
|
|
tool,
|
|||
|
|
self._create_tool_executor(plugin, tool.name)
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 注册 Hook
|
|||
|
|
for hook in plugin.hooks:
|
|||
|
|
self.hook_manager.add_hook(hook)
|
|||
|
|
|
|||
|
|
plugin.state = PluginState.ENABLED
|
|||
|
|
return plugin
|
|||
|
|
|
|||
|
|
async def disable(self, plugin_id: str) -> Plugin:
|
|||
|
|
"""禁用插件"""
|
|||
|
|
plugin = self._plugins.get(plugin_id)
|
|||
|
|
if not plugin:
|
|||
|
|
raise PluginNotFoundError(plugin_id)
|
|||
|
|
|
|||
|
|
# 注销工具
|
|||
|
|
for tool in plugin.tools:
|
|||
|
|
self.tool_registry.unregister(tool.name)
|
|||
|
|
|
|||
|
|
# 注销 Hook
|
|||
|
|
for hook in plugin.hooks:
|
|||
|
|
self.hook_manager.remove_hook(hook)
|
|||
|
|
|
|||
|
|
plugin.state = PluginState.DISABLED
|
|||
|
|
return plugin
|
|||
|
|
|
|||
|
|
async def reload(self, plugin_id: str) -> Plugin:
|
|||
|
|
"""重新加载插件"""
|
|||
|
|
plugin = self._plugins.get(plugin_id)
|
|||
|
|
if not plugin:
|
|||
|
|
raise PluginNotFoundError(plugin_id)
|
|||
|
|
|
|||
|
|
was_enabled = plugin.state == PluginState.ENABLED
|
|||
|
|
|
|||
|
|
if was_enabled:
|
|||
|
|
await self.disable(plugin_id)
|
|||
|
|
|
|||
|
|
await self._load_plugin(plugin)
|
|||
|
|
|
|||
|
|
if was_enabled:
|
|||
|
|
await self.enable(plugin_id)
|
|||
|
|
|
|||
|
|
return plugin
|
|||
|
|
|
|||
|
|
async def list_plugins(self) -> list[Plugin]:
|
|||
|
|
"""列出所有插件"""
|
|||
|
|
return list(self._plugins.values())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 插件隔离
|
|||
|
|
|
|||
|
|
### 4.1 PluginSandbox
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# backend/app/agents/plugins/sandbox.py
|
|||
|
|
|
|||
|
|
class PluginSandbox:
|
|||
|
|
"""插件沙箱"""
|
|||
|
|
|
|||
|
|
def __init__(self, plugin: Plugin, permissions: dict):
|
|||
|
|
self.plugin = plugin
|
|||
|
|
self.permissions = permissions
|
|||
|
|
self._env = {}
|
|||
|
|
|
|||
|
|
async def load_module(self) -> Any:
|
|||
|
|
"""在沙箱中加载插件模块"""
|
|||
|
|
# 设置受限的环境
|
|||
|
|
self._env = {
|
|||
|
|
"__name__": f"plugin.{self.plugin.manifest.id}",
|
|||
|
|
"__file__": str(self.plugin.path),
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if not self.permissions.get("filesystem", False):
|
|||
|
|
# 只读文件系统
|
|||
|
|
self._env["__builtins__"] = {
|
|||
|
|
k: v for k, v in __builtins__.items()
|
|||
|
|
if k in ["print", "len", "range", "str", "int", "float", "list", "dict", "tuple", "set", "bool", "type", "isinstance", "hasattr", "getattr", "setattr", "open"]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 加载模块
|
|||
|
|
spec = importlib.util.spec_from_file_location(
|
|||
|
|
f"plugin_{self.plugin.manifest.id}",
|
|||
|
|
self.plugin.path / "__init__.py"
|
|||
|
|
)
|
|||
|
|
module = importlib.util.module_from_spec(spec)
|
|||
|
|
|
|||
|
|
sys.modules[f"plugin_{self.plugin.manifest.id}"] = module
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
spec.loader.exec_module(module)
|
|||
|
|
except Exception as e:
|
|||
|
|
raise PluginLoadError(str(e))
|
|||
|
|
|
|||
|
|
return module
|
|||
|
|
|
|||
|
|
def check_permission(self, permission: str) -> bool:
|
|||
|
|
"""检查权限"""
|
|||
|
|
return self.permissions.get(permission, False)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 插件市场
|
|||
|
|
|
|||
|
|
### 5.1 PluginMarketplace
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# backend/app/services/plugin_marketplace.py
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class MarketplacePlugin:
|
|||
|
|
"""市场中的插件"""
|
|||
|
|
manifest: PluginManifest
|
|||
|
|
download_count: int
|
|||
|
|
rating: float
|
|||
|
|
reviews_count: int
|
|||
|
|
last_updated: datetime
|
|||
|
|
|
|||
|
|
class PluginMarketplace:
|
|||
|
|
"""插件市场"""
|
|||
|
|
|
|||
|
|
def __init__(self, http_client: httpx.AsyncClient):
|
|||
|
|
self.http_client = http_client
|
|||
|
|
self.cache: dict[str, MarketplacePlugin] = {}
|
|||
|
|
self.cache_ttl = timedelta(hours=1)
|
|||
|
|
|
|||
|
|
async def search(
|
|||
|
|
self,
|
|||
|
|
query: str,
|
|||
|
|
category: str | None = None,
|
|||
|
|
limit: int = 20
|
|||
|
|
) -> list[MarketplacePlugin]:
|
|||
|
|
"""搜索插件"""
|
|||
|
|
# TODO: 连接到实际的市场 API
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
async def get_plugin(
|
|||
|
|
self,
|
|||
|
|
plugin_id: str
|
|||
|
|
) -> MarketplacePlugin | None:
|
|||
|
|
"""获取插件详情"""
|
|||
|
|
# 检查缓存
|
|||
|
|
if plugin_id in self.cache:
|
|||
|
|
return self.cache[plugin_id]
|
|||
|
|
|
|||
|
|
# TODO: 从市场 API 获取
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
async def download_plugin(
|
|||
|
|
self,
|
|||
|
|
plugin_id: str,
|
|||
|
|
target_dir: Path
|
|||
|
|
) -> Path:
|
|||
|
|
"""下载插件"""
|
|||
|
|
# TODO: 实现下载逻辑
|
|||
|
|
pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 内置插件
|
|||
|
|
|
|||
|
|
### 6.1 内置插件列表
|
|||
|
|
|
|||
|
|
| 插件 | 描述 | 提供的工具 |
|
|||
|
|
|------|------|-----------|
|
|||
|
|
| `code-helper` | 代码助手 | `lint`, `format`, `explain_code` |
|
|||
|
|
| `git-helper` | Git 助手 | `git_status`, `git_log`, `git_diff` |
|
|||
|
|
| `web-helper` | Web 助手 | `fetch_url`, `parse_html` |
|
|||
|
|
| `file-organizer` | 文件整理 | `organize_files`, `cleanup_duplicates` |
|
|||
|
|
|
|||
|
|
### 6.2 BuiltinPlugins
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# backend/app/agents/plugins/builtins.py
|
|||
|
|
|
|||
|
|
BUILTIN_PLUGINS = [
|
|||
|
|
{
|
|||
|
|
"id": "code-helper",
|
|||
|
|
"name": "Code Helper",
|
|||
|
|
"version": "1.0.0",
|
|||
|
|
"description": "代码辅助工具集",
|
|||
|
|
"capabilities": {
|
|||
|
|
"tools": [
|
|||
|
|
{
|
|||
|
|
"name": "lint",
|
|||
|
|
"description": "检查代码风格",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"code": {"type": "string"},
|
|||
|
|
"language": {"type": "string"}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"name": "format",
|
|||
|
|
"description": "格式化代码",
|
|||
|
|
"parameters": {
|
|||
|
|
"type": "object",
|
|||
|
|
"properties": {
|
|||
|
|
"code": {"type": "string"},
|
|||
|
|
"language": {"type": "string"}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 7. API 接口
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# backend/app/routers/plugins.py
|
|||
|
|
|
|||
|
|
@router.get("/api/plugins")
|
|||
|
|
async def list_plugins(
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""列出用户已安装的插件"""
|
|||
|
|
plugins = await plugin_manager.list_plugins()
|
|||
|
|
return {
|
|||
|
|
"plugins": [
|
|||
|
|
{
|
|||
|
|
"id": p.manifest.id,
|
|||
|
|
"name": p.manifest.name,
|
|||
|
|
"version": p.manifest.version,
|
|||
|
|
"state": p.state.value,
|
|||
|
|
"description": p.manifest.description
|
|||
|
|
}
|
|||
|
|
for p in plugins
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@router.post("/api/plugins/install")
|
|||
|
|
async def install_plugin(
|
|||
|
|
request: InstallPluginRequest,
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""安装插件"""
|
|||
|
|
plugin = await plugin_manager.install(Path(request.path))
|
|||
|
|
return {"plugin_id": plugin.manifest.id}
|
|||
|
|
|
|||
|
|
@router.post("/api/plugins/{plugin_id}/enable")
|
|||
|
|
async def enable_plugin(
|
|||
|
|
plugin_id: str,
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""启用插件"""
|
|||
|
|
plugin = await plugin_manager.enable(plugin_id)
|
|||
|
|
return {"state": plugin.state.value}
|
|||
|
|
|
|||
|
|
@router.post("/api/plugins/{plugin_id}/disable")
|
|||
|
|
async def disable_plugin(
|
|||
|
|
plugin_id: str,
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""禁用插件"""
|
|||
|
|
plugin = await plugin_manager.disable(plugin_id)
|
|||
|
|
return {"state": plugin.state.value}
|
|||
|
|
|
|||
|
|
@router.delete("/api/plugins/{plugin_id}")
|
|||
|
|
async def uninstall_plugin(
|
|||
|
|
plugin_id: str,
|
|||
|
|
current_user: User = Depends(get_current_user)
|
|||
|
|
):
|
|||
|
|
"""卸载插件"""
|
|||
|
|
await plugin_manager.uninstall(plugin_id)
|
|||
|
|
return {"status": "ok"}
|
|||
|
|
|
|||
|
|
@router.get("/api/marketplace/plugins")
|
|||
|
|
async def search_marketplace(
|
|||
|
|
q: str | None = None,
|
|||
|
|
category: str | None = None,
|
|||
|
|
limit: int = 20
|
|||
|
|
):
|
|||
|
|
"""搜索插件市场"""
|
|||
|
|
plugins = await marketplace.search(q, category, limit)
|
|||
|
|
return {"plugins": plugins}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 8. 文件结构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
backend/app/agents/plugins/
|
|||
|
|
├── __init__.py
|
|||
|
|
├── manifest.py # 插件清单
|
|||
|
|
├── manager.py # 插件管理器
|
|||
|
|
├── sandbox.py # 插件沙箱
|
|||
|
|
│
|
|||
|
|
├── builtins/ # 内置插件
|
|||
|
|
│ ├── __init__.py
|
|||
|
|
│ ├── code_helper/
|
|||
|
|
│ ├── git_helper/
|
|||
|
|
│ └── web_helper/
|
|||
|
|
│
|
|||
|
|
├── marketplace.py # 插件市场
|
|||
|
|
│
|
|||
|
|
└── router.py # API 路由(可选)
|
|||
|
|
|
|||
|
|
backend/app/routers/
|
|||
|
|
└── plugins.py # 插件 API 路由
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 9. 验收标准
|
|||
|
|
|
|||
|
|
| 检查点 | 标准 |
|
|||
|
|
|--------|------|
|
|||
|
|
| 插件安装 | 可以从目录安装插件 |
|
|||
|
|
| 插件启用 | 插件的工具和 Hook 正确注册 |
|
|||
|
|
| 插件禁用 | 插件的工具和 Hook 正确注销 |
|
|||
|
|
| 插件卸载 | 插件目录正确删除 |
|
|||
|
|
| 插件隔离 | 插件无法访问未授权资源 |
|
|||
|
|
| 市场搜索 | 可以搜索插件市场 |
|
|||
|
|
| 内置插件 | 内置插件默认安装 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 10. Demo 借鉴
|
|||
|
|
|
|||
|
|
| claw-code | Jarvis 对应 |
|
|||
|
|
|-----------|------------|
|
|||
|
|
| `src/plugins/builtinPlugins.ts` | `plugins/builtins.py` |
|
|||
|
|
| `src/plugins/bundled/index.ts` | `plugins/builtins/` |
|
|||
|
|
| `PluginLifecycle` | `PluginManager` |
|
|||
|
|
| `PluginInstallationManager` | `PluginManager.install` |
|
|||
|
|
| `/plugin`, `/reload-plugins` | `/api/plugins/*` |
|