feat: add multi-repository batch install support
This commit is contained in:
@@ -8,8 +8,9 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
|
||||
|
||||
- **One-Click Install**: Install all plugins with a single command
|
||||
- **Auto-Update**: Automatically updates previously installed plugins
|
||||
- **Public GitHub Support**: Install plugins from any public GitHub repository
|
||||
- **Public GitHub Support**: Install plugins from one or many public GitHub repositories
|
||||
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
|
||||
- **Multi-Repository Picker**: Combine multiple repositories in one request and review them in a single grouped dialog
|
||||
- **Interactive Selection Dialog**: Filter by type, search by keyword, review plugin descriptions, then install only the checked subset
|
||||
- **i18n**: Supports 11 languages
|
||||
|
||||
@@ -20,7 +21,7 @@ User Input
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Discover Plugins from GitHub │
|
||||
│ Discover Plugins from GitHub Repos │
|
||||
│ (fetch file tree + parse .py) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
@@ -33,7 +34,7 @@ User Input
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Show Selection Dialog │
|
||||
│ (type filter + search + desc) │
|
||||
│ (repo groups + filters + search) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├── [Cancel] → End
|
||||
@@ -57,9 +58,9 @@ User Input
|
||||
|
||||
## Interactive Installation Workflow
|
||||
|
||||
Each request handles one repository. To mix repositories, send another request after the previous installation completes.
|
||||
The `repo` parameter accepts one or more `owner/repo` values separated by commas, semicolons, or new lines.
|
||||
|
||||
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can filter by plugin type, search by keyword, review plugin descriptions, and check exactly which plugins to install before the API calls start.
|
||||
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event. The dialog merges results from every requested repository, groups them by repository, supports type filters and keyword search, and lets you check exactly which plugins to install before the API calls start.
|
||||
|
||||
## Quick Start: Install Popular Collections
|
||||
|
||||
@@ -83,21 +84,27 @@ Install all plugins from suurt8ll/open_webui_functions
|
||||
|
||||
# Add OpenRouter pipe integration
|
||||
Install all plugins from rbb-dev/Open-WebUI-OpenRouter-pipe
|
||||
|
||||
# Mix multiple repositories in one request
|
||||
Install all plugins from Fu-Jie/openwebui-extensions, Classic298/open-webui-plugins
|
||||
```
|
||||
|
||||
Each line is a separate request. Already installed plugins are automatically updated.
|
||||
Use any line as-is, or combine repositories in one request. Already installed plugins are automatically updated.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
For more advanced usage patterns:
|
||||
|
||||
```
|
||||
# Combine repositories in one request
|
||||
"Install all plugins from Fu-Jie/openwebui-extensions, iChristGit/OpenWebui-Tools"
|
||||
|
||||
# Filter by plugin type
|
||||
"Install only tool plugins from iChristGit/OpenWebui-Tools"
|
||||
"Install only action plugins from Classic298/open-webui-plugins"
|
||||
"Install only action plugins from Classic298/open-webui-plugins, Haervwe/open-webui-tools"
|
||||
|
||||
# Exclude specific plugins
|
||||
"Install all plugins from Haervwe/open-webui-tools, exclude_keywords=test,deprecated"
|
||||
"Install all plugins from Haervwe/open-webui-tools, Classic298/open-webui-plugins, exclude_keywords=test,deprecated"
|
||||
|
||||
# Install from your own repository
|
||||
"Install all plugins from your-username/my-plugin-collection"
|
||||
@@ -105,7 +112,7 @@ For more advanced usage patterns:
|
||||
|
||||
## Default Repository
|
||||
|
||||
When no repository is specified, the tool uses `Fu-Jie/openwebui-extensions` (my personal collection).
|
||||
When no repository is specified, the tool uses `Fu-Jie/openwebui-extensions` (my personal collection). You can also combine it with additional repositories in the same request.
|
||||
|
||||
## Plugin Detection Rules
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@
|
||||
|
||||
- 一键安装:单个命令安装所有插件
|
||||
- 自动更新:自动更新之前安装过的插件
|
||||
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
|
||||
- 公开 GitHub 支持:支持从一个或多个公开 GitHub 仓库安装插件
|
||||
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
|
||||
- 多仓库选择器:一次请求可合并多个仓库,并在同一个分组对话框中查看
|
||||
- 交互式选择对话框:先按类型筛选、按关键词搜索并查看描述信息,再勾选要安装的插件,只安装所选子集
|
||||
- 国际化:支持 11 种语言
|
||||
|
||||
@@ -20,7 +21,7 @@
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ 从 GitHub 发现插件 │
|
||||
│ 从 GitHub 多仓库发现插件 │
|
||||
│ (获取文件树 + 解析 .py 文件) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
@@ -33,7 +34,7 @@
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ 显示选择对话框 │
|
||||
│ (类型筛选 + 搜索 + 描述) │
|
||||
│ (仓库分组 + 筛选 + 搜索) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├── [取消] → 结束
|
||||
@@ -57,9 +58,9 @@
|
||||
|
||||
## 交互式安装工作流
|
||||
|
||||
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
|
||||
`repo` 参数现在支持多个 `owner/repo`,可用逗号、分号或换行分隔。
|
||||
|
||||
在插件发现和过滤完成后,OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先按类型筛选、通过关键词搜索、查看插件描述,再开始调用安装 API。
|
||||
在插件发现和过滤完成后,OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。对话框会合并所有目标仓库的结果,按仓库分组展示,并支持类型筛选、关键词搜索和描述查看,再开始调用安装 API。
|
||||
|
||||
## 快速开始:安装热门插件集
|
||||
|
||||
@@ -83,21 +84,27 @@
|
||||
|
||||
# 添加 OpenRouter 管道集成
|
||||
从 rbb-dev/Open-WebUI-OpenRouter-pipe 安装所有插件
|
||||
|
||||
# 一次请求混合多个仓库
|
||||
从 Fu-Jie/openwebui-extensions、Classic298/open-webui-plugins 安装所有插件
|
||||
```
|
||||
|
||||
每一行是一个独立的请求。已安装的插件会自动更新。
|
||||
你可以直接使用任意一行,也可以在一次请求里组合多个仓库。已安装的插件会自动更新。
|
||||
|
||||
## 使用示例
|
||||
|
||||
更多高级用法:
|
||||
|
||||
```
|
||||
# 一次请求组合多个仓库
|
||||
"从 Fu-Jie/openwebui-extensions、iChristGit/OpenWebui-Tools 安装所有插件"
|
||||
|
||||
# 按插件类型过滤
|
||||
"从 iChristGit/OpenWebui-Tools 仅安装 tool 插件"
|
||||
"从 Classic298/open-webui-plugins 仅安装 action 插件"
|
||||
"从 Classic298/open-webui-plugins、Haervwe/open-webui-tools 仅安装 action 插件"
|
||||
|
||||
# 排除特定插件
|
||||
"从 Haervwe/open-webui-tools 安装所有插件, exclude_keywords=test,deprecated"
|
||||
"从 Haervwe/open-webui-tools、Classic298/open-webui-plugins 安装所有插件, exclude_keywords=test,deprecated"
|
||||
|
||||
# 从你自己的仓库安装
|
||||
"从 your-username/my-plugin-collection 安装所有插件"
|
||||
@@ -105,7 +112,7 @@
|
||||
|
||||
## 默认仓库
|
||||
|
||||
未指定仓库时,工具会使用 `Fu-Jie/openwebui-extensions`(我的个人合集)。
|
||||
未指定仓库时,工具会使用 `Fu-Jie/openwebui-extensions`(我的个人合集)。你也可以在同一次请求里把它和其他仓库一起传入。
|
||||
|
||||
## 插件检测规则
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
|
||||
|
||||
- **One-Click Install**: Install all plugins with a single command
|
||||
- **Auto-Update**: Automatically updates previously installed plugins
|
||||
- **Public GitHub Support**: Install plugins from any public GitHub repository
|
||||
- **Public GitHub Support**: Install plugins from one or many public GitHub repositories
|
||||
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
|
||||
- **Multi-Repository Picker**: Combine multiple repositories in one request and review them in a single grouped dialog
|
||||
- **Interactive Selection Dialog**: Filter by type, search by keyword, review plugin descriptions, then install only the checked subset
|
||||
- **i18n**: Supports 11 languages
|
||||
|
||||
@@ -24,7 +25,7 @@ User Input
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Discover Plugins from GitHub │
|
||||
│ Discover Plugins from GitHub Repos │
|
||||
│ (fetch file tree + parse .py) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
@@ -37,7 +38,7 @@ User Input
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Show Selection Dialog │
|
||||
│ (type filter + search + desc) │
|
||||
│ (repo groups + filters + search) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├── [Cancel] → End
|
||||
@@ -61,9 +62,9 @@ User Input
|
||||
|
||||
## Interactive Installation Workflow
|
||||
|
||||
Each request handles one repository. To mix repositories, send another request after the previous installation completes.
|
||||
The `repo` parameter accepts one or more `owner/repo` values separated by commas, semicolons, or new lines.
|
||||
|
||||
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can filter by plugin type, search by keyword, review plugin descriptions, and check exactly which plugins to install before the API calls start.
|
||||
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event. The dialog merges results from every requested repository, groups them by repository, supports type filters and keyword search, and lets you check exactly which plugins to install before the API calls start.
|
||||
|
||||
## Quick Start: Install Popular Collections
|
||||
|
||||
@@ -87,21 +88,27 @@ Install all plugins from suurt8ll/open_webui_functions
|
||||
|
||||
# Add OpenRouter pipe integration
|
||||
Install all plugins from rbb-dev/Open-WebUI-OpenRouter-pipe
|
||||
|
||||
# Mix multiple repositories in one request
|
||||
Install all plugins from Fu-Jie/openwebui-extensions, Classic298/open-webui-plugins
|
||||
```
|
||||
|
||||
Each line is a separate request. Already installed plugins are automatically updated.
|
||||
Use any line as-is, or combine repositories in one request. Already installed plugins are automatically updated.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
For more advanced usage patterns:
|
||||
|
||||
```
|
||||
# Combine repositories in one request
|
||||
"Install all plugins from Fu-Jie/openwebui-extensions, iChristGit/OpenWebui-Tools"
|
||||
|
||||
# Filter by plugin type
|
||||
"Install only tool plugins from iChristGit/OpenWebui-Tools"
|
||||
"Install only action plugins from Classic298/open-webui-plugins"
|
||||
"Install only action plugins from Classic298/open-webui-plugins, Haervwe/open-webui-tools"
|
||||
|
||||
# Exclude specific plugins
|
||||
"Install all plugins from Haervwe/open-webui-tools, exclude_keywords=test,deprecated"
|
||||
"Install all plugins from Haervwe/open-webui-tools, Classic298/open-webui-plugins, exclude_keywords=test,deprecated"
|
||||
|
||||
# Install from your own repository
|
||||
"Install all plugins from your-username/my-plugin-collection"
|
||||
@@ -109,7 +116,7 @@ For more advanced usage patterns:
|
||||
|
||||
## Default Repository
|
||||
|
||||
When no repository is specified, the tool uses `Fu-Jie/openwebui-extensions` (my personal collection).
|
||||
When no repository is specified, the tool uses `Fu-Jie/openwebui-extensions` (my personal collection). You can also combine it with additional repositories in the same request.
|
||||
|
||||
## Plugin Detection Rules
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
- 一键安装:单个命令安装所有插件
|
||||
- 自动更新:自动更新之前安装过的插件
|
||||
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
|
||||
- 公开 GitHub 支持:支持从一个或多个公开 GitHub 仓库安装插件
|
||||
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
|
||||
- 多仓库选择器:一次请求可合并多个仓库,并在同一个分组对话框中查看
|
||||
- 交互式选择对话框:先按类型筛选、按关键词搜索并查看描述信息,再勾选要安装的插件,只安装所选子集
|
||||
- 国际化:支持 11 种语言
|
||||
|
||||
@@ -24,7 +25,7 @@
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ 从 GitHub 发现插件 │
|
||||
│ 从 GitHub 多仓库发现插件 │
|
||||
│ (获取文件树 + 解析 .py 文件) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
@@ -37,7 +38,7 @@
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ 显示选择对话框 │
|
||||
│ (类型筛选 + 搜索 + 描述) │
|
||||
│ (仓库分组 + 筛选 + 搜索) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├── [取消] → 结束
|
||||
@@ -61,9 +62,9 @@
|
||||
|
||||
## 交互式安装工作流
|
||||
|
||||
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
|
||||
`repo` 参数现在支持多个 `owner/repo`,可用逗号、分号或换行分隔。
|
||||
|
||||
在插件发现和过滤完成后,OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先按类型筛选、通过关键词搜索、查看插件描述,再开始调用安装 API。
|
||||
在插件发现和过滤完成后,OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。对话框会合并所有目标仓库的结果,按仓库分组展示,并支持类型筛选、关键词搜索和描述查看,再开始调用安装 API。
|
||||
|
||||
## 快速开始:安装热门插件集
|
||||
|
||||
@@ -87,21 +88,27 @@
|
||||
|
||||
# 添加 OpenRouter 管道集成
|
||||
从 rbb-dev/Open-WebUI-OpenRouter-pipe 安装所有插件
|
||||
|
||||
# 一次请求混合多个仓库
|
||||
从 Fu-Jie/openwebui-extensions、Classic298/open-webui-plugins 安装所有插件
|
||||
```
|
||||
|
||||
每一行是一个独立的请求。已安装的插件会自动更新。
|
||||
你可以直接使用任意一行,也可以在一次请求里组合多个仓库。已安装的插件会自动更新。
|
||||
|
||||
## 使用示例
|
||||
|
||||
更多高级用法:
|
||||
|
||||
```
|
||||
# 一次请求组合多个仓库
|
||||
"从 Fu-Jie/openwebui-extensions、iChristGit/OpenWebui-Tools 安装所有插件"
|
||||
|
||||
# 按插件类型过滤
|
||||
"从 iChristGit/OpenWebui-Tools 仅安装 tool 插件"
|
||||
"从 Classic298/open-webui-plugins 仅安装 action 插件"
|
||||
"从 Classic298/open-webui-plugins、Haervwe/open-webui-tools 仅安装 action 插件"
|
||||
|
||||
# 排除特定插件
|
||||
"从 Haervwe/open-webui-tools 安装所有插件, exclude_keywords=test,deprecated"
|
||||
"从 Haervwe/open-webui-tools、Classic298/open-webui-plugins 安装所有插件, exclude_keywords=test,deprecated"
|
||||
|
||||
# 从你自己的仓库安装
|
||||
"从 your-username/my-plugin-collection 安装所有插件"
|
||||
@@ -109,7 +116,7 @@
|
||||
|
||||
## 默认仓库
|
||||
|
||||
未指定仓库时,工具会使用 `Fu-Jie/openwebui-extensions`(我的个人合集)。
|
||||
未指定仓库时,工具会使用 `Fu-Jie/openwebui-extensions`(我的个人合集)。你也可以在同一次请求里把它和其他仓库一起传入。
|
||||
|
||||
## 插件检测规则
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ Installing plugins in OpenWebUI should not feel like an all-or-nothing jump. Wit
|
||||
|
||||
### 🌍 Multi-Repository Support
|
||||
Install plugins from **any public GitHub repository**, including your own community collections:
|
||||
- Use one request per repository, then call the tool again to combine multiple sources
|
||||
- Use one request to combine multiple repositories in a single grouped picker
|
||||
- **Default**: Fu-Jie/openwebui-extensions (my personal collection)
|
||||
- Works with public repositories in `owner/repo` format
|
||||
- Mix and match plugins: install from my collection first, then add community collections in subsequent calls
|
||||
- Works with public repositories in `owner/repo` format, separated by commas, semicolons, or new lines
|
||||
- Mix and match plugins from multiple sources before installation starts
|
||||
|
||||
### 🔧 Container-Friendly
|
||||
- Automatically handles port mapping issues in containerized deployments
|
||||
@@ -38,7 +38,7 @@ Install plugins from **any public GitHub repository**, including your own commun
|
||||
|
||||
## How It Works: Interactive Installation Workflow
|
||||
|
||||
Each request handles one repository. To combine multiple repositories, send another request after the previous installation completes.
|
||||
The `repo` parameter now accepts one or more `owner/repo` values separated by commas, semicolons, or new lines.
|
||||
|
||||
1. **Start with My Collection**
|
||||
```
|
||||
@@ -46,17 +46,17 @@ Each request handles one repository. To combine multiple repositories, send anot
|
||||
```
|
||||
Review the selection dialog, keep the plugins you want checked, and then install them.
|
||||
|
||||
2. **Add a Community Collection**
|
||||
2. **Mix in a Community Collection**
|
||||
```
|
||||
"Install all plugins from iChristGit/OpenWebui-Tools"
|
||||
"Install all plugins from Fu-Jie/openwebui-extensions, iChristGit/OpenWebui-Tools"
|
||||
```
|
||||
Add more plugins from a different repository. Already installed plugins are updated seamlessly.
|
||||
Review both repositories in one grouped dialog, then install only the subset you want.
|
||||
|
||||
3. **Install a Specific Type**
|
||||
3. **Install a Specific Type Across Repositories**
|
||||
```
|
||||
"Install only pipe plugins from Haervwe/open-webui-tools"
|
||||
"Install only pipe plugins from Haervwe/open-webui-tools, Classic298/open-webui-plugins"
|
||||
```
|
||||
Pick specific plugin types from another repository, or exclude certain keywords.
|
||||
Pick specific plugin types across repositories, or exclude certain keywords.
|
||||
|
||||
4. **Use Your Own Public Repository**
|
||||
```
|
||||
@@ -85,23 +85,23 @@ OpenRouter API pipe integration for advanced model access.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
Each line below is a separate request:
|
||||
Each line below can be used directly. The third example combines repositories in one request:
|
||||
|
||||
```
|
||||
# Start with my collection
|
||||
"Install all plugins"
|
||||
|
||||
# Add community plugins in a new request
|
||||
# Add community plugins
|
||||
"Install all plugins from iChristGit/OpenWebui-Tools"
|
||||
|
||||
# Add only one plugin type from another repository
|
||||
"Install only tool plugins from Haervwe/open-webui-tools"
|
||||
# Combine repositories in one picker
|
||||
"Install all plugins from Fu-Jie/openwebui-extensions, Classic298/open-webui-plugins"
|
||||
|
||||
# Continue building your setup
|
||||
"Install only action plugins from Classic298/open-webui-plugins"
|
||||
# Add only one plugin type from multiple repositories
|
||||
"Install only tool plugins from Haervwe/open-webui-tools, Classic298/open-webui-plugins"
|
||||
|
||||
# Filter out unwanted plugins
|
||||
"Install all plugins from Haervwe/open-webui-tools, exclude_keywords=test,deprecated"
|
||||
"Install all plugins from Haervwe/open-webui-tools, Classic298/open-webui-plugins, exclude_keywords=test,deprecated"
|
||||
|
||||
# Install from your own public repository
|
||||
"Install all plugins from your-username/my-plugin-collection"
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
|
||||
### 🌍 多仓库支持
|
||||
支持从**任意公开 GitHub 仓库**安装插件,包括你自己的社区合集:
|
||||
- 每次请求处理一个仓库,需要时可再次调用工具来组合多个来源
|
||||
- 一次请求即可组合多个仓库,并在同一个分组选择器中查看
|
||||
- **默认**:Fu-Jie/openwebui-extensions(我的个人合集)
|
||||
- 支持公开仓库,格式为 `owner/repo`
|
||||
- 混合搭配:先从我的合集安装,再通过后续调用添加社区合集
|
||||
- 支持公开仓库,格式为 `owner/repo`,可用逗号、分号或换行分隔
|
||||
- 混合搭配:安装前就能在同一个对话框中选择多个来源的插件
|
||||
|
||||
### 🔧 容器友好
|
||||
- 自动处理容器部署中的端口映射问题
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
## 工作流程:交互式安装
|
||||
|
||||
每次请求处理一个仓库。如需组合多个仓库,请在上一次安装完成后再发起下一次请求。
|
||||
`repo` 参数现在支持多个 `owner/repo`,可用逗号、分号或换行分隔。
|
||||
|
||||
1. **先从我的合集开始**
|
||||
```
|
||||
@@ -46,17 +46,17 @@
|
||||
```
|
||||
查看选择对话框,保留想安装的勾选项后开始安装。
|
||||
|
||||
2. **再添加社区合集**
|
||||
2. **混合加入社区合集**
|
||||
```
|
||||
"从 iChristGit/OpenWebui-Tools 安装所有插件"
|
||||
"从 Fu-Jie/openwebui-extensions、iChristGit/OpenWebui-Tools 安装所有插件"
|
||||
```
|
||||
从不同仓库添加更多插件。已安装的插件会无缝更新。
|
||||
在一个按仓库分组的选择对话框里查看两个来源,再安装真正需要的子集。
|
||||
|
||||
3. **按类型继续安装**
|
||||
3. **跨仓库按类型继续安装**
|
||||
```
|
||||
"从 Haervwe/open-webui-tools 仅安装 pipe 插件"
|
||||
"从 Haervwe/open-webui-tools、Classic298/open-webui-plugins 仅安装 pipe 插件"
|
||||
```
|
||||
从另一个仓库选择特定类型的插件,或排除某些关键词。
|
||||
一次性在多个仓库里选择特定类型的插件,或排除某些关键词。
|
||||
|
||||
4. **使用你自己的公开仓库**
|
||||
```
|
||||
@@ -85,23 +85,23 @@ OpenRouter API pipe 集成,提供高级模型访问。
|
||||
|
||||
## 使用示例
|
||||
|
||||
下面每一行都是一次独立请求:
|
||||
下面每一行都可以直接使用,其中第三行演示了单次请求组合多个仓库:
|
||||
|
||||
```
|
||||
# 先从我的合集开始
|
||||
"安装所有插件"
|
||||
|
||||
# 在下一次请求中加入社区插件
|
||||
# 添加社区插件
|
||||
"从 iChristGit/OpenWebui-Tools 安装所有插件"
|
||||
|
||||
# 从其他仓库只安装某一种类型
|
||||
"从 Haervwe/open-webui-tools 仅安装 tool 插件"
|
||||
# 在同一个选择器里组合多个仓库
|
||||
"从 Fu-Jie/openwebui-extensions、Classic298/open-webui-plugins 安装所有插件"
|
||||
|
||||
# 继续补充你的插件组合
|
||||
"从 Classic298/open-webui-plugins 安装仅 action 插件"
|
||||
# 从多个仓库只安装某一种类型
|
||||
"从 Haervwe/open-webui-tools、Classic298/open-webui-plugins 仅安装 tool 插件"
|
||||
|
||||
# 过滤不想安装的插件
|
||||
"从 Haervwe/open-webui-tools 安装所有插件,exclude_keywords=test,deprecated"
|
||||
"从 Haervwe/open-webui-tools、Classic298/open-webui-plugins 安装所有插件,exclude_keywords=test,deprecated"
|
||||
|
||||
# 从你自己的公开仓库安装
|
||||
"从 your-username/my-plugin-collection 安装所有插件"
|
||||
|
||||
@@ -12,8 +12,9 @@ One-click batch install plugins from GitHub repositories to your OpenWebUI insta
|
||||
|
||||
- **One-Click Install**: Install all plugins with a single command
|
||||
- **Auto-Update**: Automatically updates previously installed plugins
|
||||
- **Public GitHub Support**: Install plugins from any public GitHub repository
|
||||
- **Public GitHub Support**: Install plugins from one or many public GitHub repositories
|
||||
- **Multi-Type Support**: Supports Pipe, Action, Filter, and Tool plugins
|
||||
- **Multi-Repository Picker**: Combine multiple repositories in one request and review them in a single grouped dialog
|
||||
- **Interactive Selection Dialog**: Filter by type, search by keyword, review plugin descriptions, then install only the checked subset
|
||||
- **i18n**: Supports 11 languages
|
||||
|
||||
@@ -24,7 +25,7 @@ User Input
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Discover Plugins from GitHub │
|
||||
│ Discover Plugins from GitHub Repos │
|
||||
│ (fetch file tree + parse .py) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
@@ -37,7 +38,7 @@ User Input
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Show Selection Dialog │
|
||||
│ (type filter + search + desc) │
|
||||
│ (repo groups + filters + search) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├── [Cancel] → End
|
||||
@@ -61,9 +62,9 @@ User Input
|
||||
|
||||
## Interactive Installation Workflow
|
||||
|
||||
Each request handles one repository. To mix repositories, send another request after the previous installation completes.
|
||||
The `repo` parameter accepts one or more `owner/repo` values separated by commas, semicolons, or new lines.
|
||||
|
||||
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event so you can filter by plugin type, search by keyword, review plugin descriptions, and check exactly which plugins to install before the API calls start.
|
||||
After plugin discovery and filtering, OpenWebUI opens a browser dialog built with the `execute` event. The dialog merges results from every requested repository, groups them by repository, supports type filters and keyword search, and lets you check exactly which plugins to install before the API calls start.
|
||||
|
||||
## Quick Start: Install Popular Collections
|
||||
|
||||
@@ -87,21 +88,27 @@ Install all plugins from suurt8ll/open_webui_functions
|
||||
|
||||
# Add OpenRouter pipe integration
|
||||
Install all plugins from rbb-dev/Open-WebUI-OpenRouter-pipe
|
||||
|
||||
# Mix multiple repositories in one request
|
||||
Install all plugins from Fu-Jie/openwebui-extensions, Classic298/open-webui-plugins
|
||||
```
|
||||
|
||||
Each line is a separate request. Already installed plugins are automatically updated.
|
||||
Use any line as-is, or combine repositories in one request. Already installed plugins are automatically updated.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
For more advanced usage patterns:
|
||||
|
||||
```
|
||||
# Combine repositories in one request
|
||||
"Install all plugins from Fu-Jie/openwebui-extensions, iChristGit/OpenWebui-Tools"
|
||||
|
||||
# Filter by plugin type
|
||||
"Install only tool plugins from iChristGit/OpenWebui-Tools"
|
||||
"Install only action plugins from Classic298/open-webui-plugins"
|
||||
"Install only action plugins from Classic298/open-webui-plugins, Haervwe/open-webui-tools"
|
||||
|
||||
# Exclude specific plugins
|
||||
"Install all plugins from Haervwe/open-webui-tools, exclude_keywords=test,deprecated"
|
||||
"Install all plugins from Haervwe/open-webui-tools, Classic298/open-webui-plugins, exclude_keywords=test,deprecated"
|
||||
|
||||
# Install from your own repository
|
||||
"Install all plugins from your-username/my-plugin-collection"
|
||||
@@ -109,7 +116,7 @@ For more advanced usage patterns:
|
||||
|
||||
## Default Repository
|
||||
|
||||
When no repository is specified, the tool uses `Fu-Jie/openwebui-extensions` (my personal collection).
|
||||
When no repository is specified, the tool uses `Fu-Jie/openwebui-extensions` (my personal collection). You can also combine it with additional repositories in the same request.
|
||||
|
||||
## Plugin Detection Rules
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
|
||||
- 一键安装:单个命令安装所有插件
|
||||
- 自动更新:自动更新之前安装过的插件
|
||||
- 公开 GitHub 支持:支持从任何公开 GitHub 仓库安装插件
|
||||
- 公开 GitHub 支持:支持从一个或多个公开 GitHub 仓库安装插件
|
||||
- 多类型支持:支持 Pipe、Action、Filter 和 Tool 插件
|
||||
- 多仓库选择器:一次请求可合并多个仓库,并在同一个分组对话框中查看
|
||||
- 交互式选择对话框:先按类型筛选、按关键词搜索并查看描述信息,再勾选要安装的插件,只安装所选子集
|
||||
- 国际化:支持 11 种语言
|
||||
|
||||
@@ -24,7 +25,7 @@
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ 从 GitHub 发现插件 │
|
||||
│ 从 GitHub 多仓库发现插件 │
|
||||
│ (获取文件树 + 解析 .py 文件) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
@@ -37,7 +38,7 @@
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ 显示选择对话框 │
|
||||
│ (类型筛选 + 搜索 + 描述) │
|
||||
│ (仓库分组 + 筛选 + 搜索) │
|
||||
└─────────────────────────────────────┘
|
||||
│
|
||||
├── [取消] → 结束
|
||||
@@ -61,9 +62,9 @@
|
||||
|
||||
## 交互式安装工作流
|
||||
|
||||
每次请求处理一个仓库。如需混合多个来源,请在上一次安装完成后再发起下一次请求。
|
||||
`repo` 参数现在支持多个 `owner/repo`,可用逗号、分号或换行分隔。
|
||||
|
||||
在插件发现和过滤完成后,OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。你可以先按类型筛选、通过关键词搜索、查看插件描述,再开始调用安装 API。
|
||||
在插件发现和过滤完成后,OpenWebUI 会通过 `execute` 事件打开浏览器选择对话框。对话框会合并所有目标仓库的结果,按仓库分组展示,并支持类型筛选、关键词搜索和描述查看,再开始调用安装 API。
|
||||
|
||||
## 快速开始:安装热门插件集
|
||||
|
||||
@@ -87,21 +88,27 @@
|
||||
|
||||
# 添加 OpenRouter 管道集成
|
||||
从 rbb-dev/Open-WebUI-OpenRouter-pipe 安装所有插件
|
||||
|
||||
# 一次请求混合多个仓库
|
||||
从 Fu-Jie/openwebui-extensions、Classic298/open-webui-plugins 安装所有插件
|
||||
```
|
||||
|
||||
每一行是一个独立的请求。已安装的插件会自动更新。
|
||||
你可以直接使用任意一行,也可以在一次请求里组合多个仓库。已安装的插件会自动更新。
|
||||
|
||||
## 使用示例
|
||||
|
||||
更多高级用法:
|
||||
|
||||
```
|
||||
# 一次请求组合多个仓库
|
||||
"从 Fu-Jie/openwebui-extensions、iChristGit/OpenWebui-Tools 安装所有插件"
|
||||
|
||||
# 按插件类型过滤
|
||||
"从 iChristGit/OpenWebui-Tools 仅安装 tool 插件"
|
||||
"从 Classic298/open-webui-plugins 仅安装 action 插件"
|
||||
"从 Classic298/open-webui-plugins、Haervwe/open-webui-tools 仅安装 action 插件"
|
||||
|
||||
# 排除特定插件
|
||||
"从 Haervwe/open-webui-tools 安装所有插件, exclude_keywords=test,deprecated"
|
||||
"从 Haervwe/open-webui-tools、Classic298/open-webui-plugins 安装所有插件, exclude_keywords=test,deprecated"
|
||||
|
||||
# 从你自己的仓库安装
|
||||
"从 your-username/my-plugin-collection 安装所有插件"
|
||||
@@ -109,7 +116,7 @@
|
||||
|
||||
## 默认仓库
|
||||
|
||||
未指定仓库时,工具会使用 `Fu-Jie/openwebui-extensions`(我的个人合集)。
|
||||
未指定仓库时,工具会使用 `Fu-Jie/openwebui-extensions`(我的个人合集)。你也可以在同一次请求里把它和其他仓库一起传入。
|
||||
|
||||
## 插件检测规则
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import os
|
||||
import re
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple
|
||||
from typing import Any, Dict, List, Optional, Set, Tuple
|
||||
|
||||
import httpx
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -655,12 +655,14 @@ class PluginCandidate:
|
||||
metadata: Dict[str, str],
|
||||
content: str,
|
||||
function_id: str,
|
||||
source_repo: str,
|
||||
):
|
||||
self.plugin_type = plugin_type
|
||||
self.file_path = file_path
|
||||
self.metadata = metadata
|
||||
self.content = content
|
||||
self.function_id = function_id
|
||||
self.source_repo = source_repo
|
||||
|
||||
@property
|
||||
def title(self) -> str:
|
||||
@@ -670,6 +672,10 @@ class PluginCandidate:
|
||||
def version(self) -> str:
|
||||
return self.metadata.get("version", "unknown")
|
||||
|
||||
@property
|
||||
def selection_id(self) -> str:
|
||||
return f"{self.source_repo}::{self.file_path}::{self.function_id}"
|
||||
|
||||
|
||||
def extract_metadata(content: str) -> Dict[str, str]:
|
||||
docstring = _extract_module_docstring(content)
|
||||
@@ -884,23 +890,72 @@ def _candidate_debug_data(candidate: PluginCandidate) -> Dict[str, str]:
|
||||
return {
|
||||
"title": candidate.title,
|
||||
"type": candidate.plugin_type,
|
||||
"source_repo": candidate.source_repo,
|
||||
"file_path": candidate.file_path,
|
||||
"function_id": candidate.function_id,
|
||||
"version": candidate.version,
|
||||
}
|
||||
|
||||
|
||||
def _parse_repo_inputs(repo_value: str) -> List[str]:
|
||||
parts = re.split(r"[\n,;,;、]+", str(repo_value or DEFAULT_REPO))
|
||||
repos: List[str] = []
|
||||
seen: Set[str] = set()
|
||||
|
||||
for part in parts:
|
||||
candidate = part.strip().strip("/")
|
||||
if not candidate:
|
||||
continue
|
||||
normalized = candidate.lower()
|
||||
if normalized in seen:
|
||||
continue
|
||||
seen.add(normalized)
|
||||
repos.append(candidate)
|
||||
|
||||
return repos or [DEFAULT_REPO]
|
||||
|
||||
|
||||
def _format_repo_summary(repos: List[str]) -> str:
|
||||
if not repos:
|
||||
return DEFAULT_REPO
|
||||
if len(repos) <= 2:
|
||||
return ", ".join(repos)
|
||||
return f"{repos[0]}, {repos[1]} +{len(repos) - 2}"
|
||||
|
||||
|
||||
def _sort_candidates_by_repo_order(
|
||||
candidates: List[PluginCandidate],
|
||||
repos: List[str],
|
||||
) -> List[PluginCandidate]:
|
||||
repo_order = {repo.lower(): index for index, repo in enumerate(repos)}
|
||||
fallback_index = len(repo_order)
|
||||
return sorted(
|
||||
candidates,
|
||||
key=lambda item: (
|
||||
repo_order.get(item.source_repo.lower(), fallback_index),
|
||||
item.source_repo.lower(),
|
||||
item.plugin_type,
|
||||
item.file_path,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _filter_candidates(
|
||||
candidates: List[PluginCandidate],
|
||||
plugin_types: List[str],
|
||||
repo: str,
|
||||
repos: List[str],
|
||||
exclude_keywords: str = "",
|
||||
) -> List[PluginCandidate]:
|
||||
allowed_types = {item.strip().lower() for item in plugin_types if item.strip()}
|
||||
filtered = [c for c in candidates if c.plugin_type.lower() in allowed_types]
|
||||
|
||||
if repo.lower() == DEFAULT_REPO.lower():
|
||||
filtered = [c for c in filtered if not _matches_self_plugin(c)]
|
||||
includes_default_repo = any(item.lower() == DEFAULT_REPO.lower() for item in repos)
|
||||
if includes_default_repo:
|
||||
filtered = [
|
||||
c
|
||||
for c in filtered
|
||||
if not (c.source_repo.lower() == DEFAULT_REPO.lower() and _matches_self_plugin(c))
|
||||
]
|
||||
|
||||
exclude_list = [item.strip().lower() for item in exclude_keywords.split(",") if item.strip()]
|
||||
if exclude_list:
|
||||
@@ -917,7 +972,8 @@ def _filter_candidates(
|
||||
|
||||
|
||||
def _build_confirmation_hint(lang: str, repo: str, exclude_keywords: str) -> str:
|
||||
is_default_repo = repo.lower() == DEFAULT_REPO.lower()
|
||||
repo_list = _parse_repo_inputs(repo)
|
||||
is_default_repo = any(item.lower() == DEFAULT_REPO.lower() for item in repo_list)
|
||||
excluded_parts: List[str] = []
|
||||
|
||||
if exclude_keywords:
|
||||
@@ -1035,7 +1091,7 @@ def _build_selection_dialog_js(
|
||||
" const typeEntries = Object.entries(typeMap);",
|
||||
" const getVisibleOptions = () => options.filter((item) => {",
|
||||
" const matchesType = !activeFilter || item.type === activeFilter;",
|
||||
" const haystack = [item.title, item.description, item.file_path, item.type].join(' ').toLowerCase();",
|
||||
" const haystack = [item.title, item.description, item.file_path, item.type, item.repo].join(' ').toLowerCase();",
|
||||
" const matchesSearch = !searchTerm || haystack.includes(searchTerm);",
|
||||
" return matchesType && matchesSearch;",
|
||||
" });",
|
||||
@@ -1074,6 +1130,28 @@ def _build_selection_dialog_js(
|
||||
" submitBtn.style.cursor = selected.size === 0 ? 'not-allowed' : 'pointer';",
|
||||
" renderTypeButtons();",
|
||||
" };",
|
||||
" const renderOptionCard = (item) => {",
|
||||
" const checked = selected.has(item.id) ? 'checked' : '';",
|
||||
" const description = item.description ? `",
|
||||
" <div style=\"display:grid;gap:4px\">",
|
||||
" <div style=\"font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.description_label)}</div>",
|
||||
" <div style=\"font-size:13px;color:#334155;line-height:1.55;word-break:break-word\">${formatMultilineText(item.description)}</div>",
|
||||
" </div>",
|
||||
" ` : '';",
|
||||
" return `",
|
||||
" <label style=\"display:flex;gap:14px;align-items:flex-start;padding:14px;border:1px solid #e2e8f0;border-radius:14px;background:#fff;cursor:pointer\">",
|
||||
" <input type=\"checkbox\" data-plugin-id=\"${escapeHtml(item.id)}\" ${checked} style=\"margin-top:3px;width:16px;height:16px;accent-color:#0f172a;flex-shrink:0\" />",
|
||||
" <div style=\"min-width:0;display:grid;gap:6px\">",
|
||||
" <div style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap\">",
|
||||
" <span style=\"display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#f1f5f9;color:#334155;font-size:12px;font-weight:700;text-transform:uppercase\">${escapeHtml(item.type)}</span>",
|
||||
" <span style=\"font-size:15px;font-weight:700;color:#0f172a;word-break:break-word\">${escapeHtml(item.title)}</span>",
|
||||
" </div>",
|
||||
" <div style=\"font-size:12px;color:#475569;word-break:break-word\">${escapeHtml(ui.repo_label)}: ${escapeHtml(item.repo)} · ${escapeHtml(ui.version_label)}: ${escapeHtml(item.version)} · ${escapeHtml(ui.file_label)}: ${escapeHtml(item.file_path)}</div>",
|
||||
" ${description}",
|
||||
" </div>",
|
||||
" </label>",
|
||||
" `;",
|
||||
" };",
|
||||
" const renderList = () => {",
|
||||
" const visibleOptions = getVisibleOptions();",
|
||||
" if (!visibleOptions.length) {",
|
||||
@@ -1081,28 +1159,22 @@ def _build_selection_dialog_js(
|
||||
" updateState();",
|
||||
" return;",
|
||||
" }",
|
||||
" listEl.innerHTML = visibleOptions.map((item) => {",
|
||||
" const checked = selected.has(item.id) ? 'checked' : '';",
|
||||
" const description = item.description ? `",
|
||||
" <div style=\"display:grid;gap:4px\">",
|
||||
" <div style=\"font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:0.04em\">${escapeHtml(ui.description_label)}</div>",
|
||||
" <div style=\"font-size:13px;color:#334155;line-height:1.55;word-break:break-word\">${formatMultilineText(item.description)}</div>",
|
||||
" const groups = visibleOptions.reduce((bucket, item) => {",
|
||||
" if (!bucket[item.repo]) {",
|
||||
" bucket[item.repo] = [];",
|
||||
" }",
|
||||
" bucket[item.repo].push(item);",
|
||||
" return bucket;",
|
||||
" }, {});",
|
||||
" listEl.innerHTML = Object.entries(groups).map(([repoName, items]) => `",
|
||||
" <section style=\"display:grid;gap:10px\">",
|
||||
" <div style=\"display:flex;justify-content:space-between;gap:12px;align-items:center;flex-wrap:wrap;padding:0 2px\">",
|
||||
" <div style=\"font-size:13px;font-weight:700;color:#0f172a;word-break:break-word\">${escapeHtml(repoName)}</div>",
|
||||
" <div style=\"display:inline-flex;align-items:center;gap:8px;padding:4px 10px;border-radius:999px;background:#f8fafc;color:#475569;font-size:12px;font-weight:600\">${items.length}</div>",
|
||||
" </div>",
|
||||
" ` : '';",
|
||||
" return `",
|
||||
" <label style=\"display:flex;gap:14px;align-items:flex-start;padding:14px;border:1px solid #e2e8f0;border-radius:14px;background:#fff;cursor:pointer\">",
|
||||
" <input type=\"checkbox\" data-plugin-id=\"${escapeHtml(item.id)}\" ${checked} style=\"margin-top:3px;width:16px;height:16px;accent-color:#0f172a;flex-shrink:0\" />",
|
||||
" <div style=\"min-width:0;display:grid;gap:6px\">",
|
||||
" <div style=\"display:flex;gap:10px;align-items:center;flex-wrap:wrap\">",
|
||||
" <span style=\"display:inline-flex;align-items:center;padding:2px 8px;border-radius:999px;background:#f1f5f9;color:#334155;font-size:12px;font-weight:700;text-transform:uppercase\">${escapeHtml(item.type)}</span>",
|
||||
" <span style=\"font-size:15px;font-weight:700;color:#0f172a;word-break:break-word\">${escapeHtml(item.title)}</span>",
|
||||
" </div>",
|
||||
" <div style=\"font-size:12px;color:#475569;word-break:break-word\">${escapeHtml(ui.version_label)}: ${escapeHtml(item.version)} · ${escapeHtml(ui.file_label)}: ${escapeHtml(item.file_path)}</div>",
|
||||
" ${description}",
|
||||
" </div>",
|
||||
" </label>",
|
||||
" `;",
|
||||
" }).join('');",
|
||||
" <div style=\"display:grid;gap:12px\">${items.map((item) => renderOptionCard(item)).join('')}</div>",
|
||||
" </section>",
|
||||
" `).join('');",
|
||||
" listEl.querySelectorAll('input[data-plugin-id]').forEach((input) => {",
|
||||
" input.addEventListener('change', () => {",
|
||||
" const pluginId = input.getAttribute('data-plugin-id') || '';",
|
||||
@@ -1184,11 +1256,13 @@ async def _request_plugin_selection(
|
||||
if not event_call:
|
||||
return candidates, None
|
||||
|
||||
repo_list = _parse_repo_inputs(repo)
|
||||
options = [
|
||||
{
|
||||
"id": candidate.function_id,
|
||||
"id": candidate.selection_id,
|
||||
"title": candidate.title,
|
||||
"type": candidate.plugin_type,
|
||||
"repo": candidate.source_repo,
|
||||
"version": candidate.version,
|
||||
"file_path": candidate.file_path,
|
||||
"description": candidate.metadata.get("description", ""),
|
||||
@@ -1199,7 +1273,7 @@ async def _request_plugin_selection(
|
||||
"title": _t(lang, "confirm_title"),
|
||||
"list_title": _t(lang, "status_list_title", count=len(candidates)),
|
||||
"repo_label": _selection_t(lang, "repo_label"),
|
||||
"repo": repo,
|
||||
"repo": _format_repo_summary(repo_list),
|
||||
"hint": hint.strip(),
|
||||
"select_all": _selection_t(lang, "select_all"),
|
||||
"clear_all": _selection_t(lang, "clear_all"),
|
||||
@@ -1247,7 +1321,7 @@ async def _request_plugin_selection(
|
||||
|
||||
selected_id_set = {str(item).strip() for item in selected_ids if str(item).strip()}
|
||||
selected_candidates = [
|
||||
candidate for candidate in candidates if candidate.function_id in selected_id_set
|
||||
candidate for candidate in candidates if candidate.selection_id in selected_id_set
|
||||
]
|
||||
return selected_candidates, None
|
||||
|
||||
@@ -1294,11 +1368,13 @@ async def fetch_github_file(
|
||||
async def discover_plugins(
|
||||
url: str,
|
||||
skip_keywords: str = "test",
|
||||
source_repo: str = "",
|
||||
) -> Tuple[List[PluginCandidate], List[Tuple[str, str]]]:
|
||||
parsed = parse_github_url(url)
|
||||
if not parsed:
|
||||
return [], [("url", "invalid github url")]
|
||||
owner, repo, branch = parsed
|
||||
resolved_repo = source_repo or f"{owner}/{repo}"
|
||||
|
||||
is_default_repo = (owner.lower() == "fu-jie" and repo.lower() == "openwebui-extensions")
|
||||
|
||||
@@ -1363,6 +1439,7 @@ async def discover_plugins(
|
||||
metadata=metadata,
|
||||
content=content,
|
||||
function_id=build_function_id(item_path, metadata),
|
||||
source_repo=resolved_repo,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1370,10 +1447,30 @@ async def discover_plugins(
|
||||
return candidates, skipped
|
||||
|
||||
|
||||
async def discover_plugins_from_repos(
|
||||
repos: List[str],
|
||||
skip_keywords: str = "test",
|
||||
) -> Tuple[List[PluginCandidate], List[Tuple[str, str]]]:
|
||||
tasks = [
|
||||
discover_plugins(f"https://github.com/{repo}", skip_keywords, source_repo=repo)
|
||||
for repo in repos
|
||||
]
|
||||
results = await asyncio.gather(*tasks)
|
||||
|
||||
all_candidates: List[PluginCandidate] = []
|
||||
all_skipped: List[Tuple[str, str]] = []
|
||||
|
||||
for repo, (candidates, skipped) in zip(repos, results):
|
||||
all_candidates.extend(candidates)
|
||||
all_skipped.extend([(f"{repo}:{path}", reason) for path, reason in skipped])
|
||||
|
||||
return _sort_candidates_by_repo_order(all_candidates, repos), all_skipped
|
||||
|
||||
|
||||
class ListParams(BaseModel):
|
||||
repo: str = Field(
|
||||
default=DEFAULT_REPO,
|
||||
description="GitHub repository (owner/repo)",
|
||||
description="One or more GitHub repositories (owner/repo), separated by commas, semicolons, or new lines",
|
||||
)
|
||||
plugin_types: List[str] = Field(
|
||||
default=["pipe", "action", "filter", "tool"],
|
||||
@@ -1384,7 +1481,7 @@ class ListParams(BaseModel):
|
||||
class InstallParams(BaseModel):
|
||||
repo: str = Field(
|
||||
default=DEFAULT_REPO,
|
||||
description="GitHub repository (owner/repo)",
|
||||
description="One or more GitHub repositories (owner/repo), separated by commas, semicolons, or new lines",
|
||||
)
|
||||
plugin_types: List[str] = Field(
|
||||
default=["pipe", "action", "filter", "tool"],
|
||||
@@ -1426,18 +1523,22 @@ class Tools:
|
||||
if valves and hasattr(valves, "SKIP_KEYWORDS") and valves.SKIP_KEYWORDS:
|
||||
skip_keywords = valves.SKIP_KEYWORDS
|
||||
|
||||
repo_url = f"https://github.com/{repo}"
|
||||
candidates, _ = await discover_plugins(repo_url, skip_keywords)
|
||||
repo_list = _parse_repo_inputs(repo)
|
||||
candidates, _ = await discover_plugins_from_repos(repo_list, skip_keywords)
|
||||
|
||||
if not candidates:
|
||||
return _t(lang, "err_no_plugins")
|
||||
|
||||
filtered = _filter_candidates(candidates, plugin_types, repo)
|
||||
filtered = _filter_candidates(candidates, plugin_types, repo_list)
|
||||
if not filtered:
|
||||
return _t(lang, "err_no_match")
|
||||
|
||||
lines = [f"## {_t(lang, 'status_list_title', count=len(filtered))}\n"]
|
||||
current_repo = ""
|
||||
for c in filtered:
|
||||
if c.source_repo != current_repo:
|
||||
lines.append(f"\n### {c.source_repo}")
|
||||
current_repo = c.source_repo
|
||||
lines.append(
|
||||
_t(lang, "list_item", type=c.plugin_type, title=c.title)
|
||||
)
|
||||
@@ -1507,15 +1608,15 @@ class Tools:
|
||||
|
||||
await _emit_status(event_emitter, _t(lang, "status_fetching"), done=False)
|
||||
|
||||
repo_url = f"https://github.com/{repo}"
|
||||
candidates, _ = await discover_plugins(repo_url, skip_keywords)
|
||||
repo_list = _parse_repo_inputs(repo)
|
||||
candidates, _ = await discover_plugins_from_repos(repo_list, skip_keywords)
|
||||
|
||||
if not candidates:
|
||||
return await _finalize_message(
|
||||
event_emitter, _t(lang, "err_no_plugins"), notification_type="error"
|
||||
)
|
||||
|
||||
filtered = _filter_candidates(candidates, plugin_types, repo, exclude_keywords)
|
||||
filtered = _filter_candidates(candidates, plugin_types, repo_list, exclude_keywords)
|
||||
|
||||
if not filtered:
|
||||
return await _finalize_message(
|
||||
@@ -1542,6 +1643,7 @@ class Tools:
|
||||
"Starting OpenWebUI install requests",
|
||||
{
|
||||
"repo": repo,
|
||||
"repos": repo_list,
|
||||
"base_url": base_url,
|
||||
"note": "Backend uses default port 8080 (containerized environment)",
|
||||
"plugin_count": len(selected_candidates),
|
||||
|
||||
@@ -9,6 +9,7 @@ Batch Install Plugins from GitHub v1.1.0 upgrades the install confirmation step
|
||||
## Highlights
|
||||
|
||||
- **Interactive Selection Dialog**: Opens a checkbox-based browser dialog with type filters, keyword search, and visible plugin descriptions
|
||||
- **Multi-Repository Input**: Accepts multiple `owner/repo` values in one request and groups the dialog by repository
|
||||
- **Selective Installation**: The install loop now runs only for the plugins the user keeps selected
|
||||
- **Repository Context**: Displays the current repository and only shows useful exclusion information inside the dialog
|
||||
- **Localized UI**: Dialog controls are localized for all supported languages
|
||||
@@ -17,9 +18,10 @@ Batch Install Plugins from GitHub v1.1.0 upgrades the install confirmation step
|
||||
## Technical Notes
|
||||
|
||||
- Replaced the install confirmation step with `__event_call__({"type": "execute"})`
|
||||
- Added a repository parser plus multi-repository discovery fan-out before filtering and installation
|
||||
- Returns a structured payload containing `confirmed` and `selected_ids`
|
||||
- Preserves the existing 120-second timeout for user interaction
|
||||
- Keeps installation ordering aligned with the filtered candidate list
|
||||
- Keeps installation ordering aligned with the requested repository order and filtered candidate list
|
||||
|
||||
## Validation
|
||||
|
||||
@@ -31,3 +33,4 @@ Batch Install Plugins from GitHub v1.1.0 upgrades the install confirmation step
|
||||
- No new Valves are required
|
||||
- Existing prompts continue to work
|
||||
- Users now get a plugin picker before installation begins
|
||||
- One request can now merge multiple repositories into the same selection dialog
|
||||
|
||||
@@ -9,6 +9,7 @@ Batch Install Plugins from GitHub v1.1.0 将原本的安装确认步骤升级为
|
||||
## 亮点
|
||||
|
||||
- **交互式选择对话框**:不再只使用基础 confirmation 事件,而是打开带类型筛选、关键词搜索、描述信息和复选框的浏览器对话框
|
||||
- **多仓库输入**:一次请求支持多个 `owner/repo`,并在对话框中按仓库分组展示
|
||||
- **选择性安装**:安装循环只会处理用户最终保留勾选的插件
|
||||
- **仓库上下文**:对话框中会显示当前仓库,并且只展示真正有用的排除信息
|
||||
- **本地化 UI**:对话框控件已为所有支持语言提供本地化文本
|
||||
@@ -17,9 +18,10 @@ Batch Install Plugins from GitHub v1.1.0 将原本的安装确认步骤升级为
|
||||
## 技术说明
|
||||
|
||||
- 使用 `__event_call__({"type": "execute"})` 替换安装确认步骤
|
||||
- 新增仓库解析和多仓库发现聚合流程,再进入过滤与安装阶段
|
||||
- 返回包含 `confirmed` 与 `selected_ids` 的结构化结果
|
||||
- 保留原有的 120 秒用户交互超时时间
|
||||
- 安装顺序仍与过滤后的候选列表保持一致
|
||||
- 安装顺序仍与用户传入的仓库顺序和过滤后的候选列表保持一致
|
||||
|
||||
## 验证
|
||||
|
||||
@@ -31,3 +33,4 @@ Batch Install Plugins from GitHub v1.1.0 将原本的安装确认步骤升级为
|
||||
- 不需要新增 Valves
|
||||
- 现有提示词仍可继续使用
|
||||
- 安装开始前会新增一个插件选择器步骤
|
||||
- 现在单次请求就可以把多个仓库合并到同一个选择对话框
|
||||
|
||||
Reference in New Issue
Block a user