mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-22 16:35:20 +08:00
fix(pipeline): decouple Runner from extension permissions (LANG-54) (#2723)
* fix(pipeline): decouple runner from extension allowlist Co-authored-by: multica-agent <[email protected]> * fix(pipeline): complete runner diagnostics and real runtime coverage Co-authored-by: multica-agent <[email protected]> --------- Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
co-authored by
multica-agent
parent
5a6ddbff6f
commit
d9e4e7d9df
@@ -3,7 +3,7 @@ import { Bot, Puzzle, ExternalLink, Loader2, Store } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import { getCloudServiceClientSync } from '@/app/infra/http';
|
||||
import { getCloudServiceClientSync, httpClient } from '@/app/infra/http';
|
||||
import { useInstalledPluginIcon } from '@/app/infra/hooks/useInstalledPluginIcon';
|
||||
import type { IDynamicFormItemOption } from '@/app/infra/entities/form/dynamic';
|
||||
import type { PluginV4 } from '@/app/infra/entities/plugin';
|
||||
@@ -233,6 +233,45 @@ export default function RunnerSelect({
|
||||
useState<RunnerCatalog['installedPluginDescriptions']>({});
|
||||
const [catalogLoading, setCatalogLoading] = useState(true);
|
||||
const [catalogError, setCatalogError] = useState(false);
|
||||
const [runnerProblem, setRunnerProblem] = useState<
|
||||
'missing' | 'disabled' | 'runtime' | null
|
||||
>(null);
|
||||
const [diagnosing, setDiagnosing] = useState(false);
|
||||
const diagnoseRunner = useCallback(async () => {
|
||||
const pluginId = runnerPluginId(value);
|
||||
if (!pluginId) return null;
|
||||
const status = await httpClient.getPluginSystemStatus();
|
||||
if (!status.is_enable || !status.is_connected) return 'runtime' as const;
|
||||
const { plugins } = await httpClient.getPlugins();
|
||||
const plugin = plugins.find((item) => {
|
||||
const metadata = item.manifest.manifest.metadata;
|
||||
return `${metadata.author}/${metadata.name}` === pluginId;
|
||||
});
|
||||
if (!plugin) return 'missing' as const;
|
||||
if (!plugin.enabled) return 'disabled' as const;
|
||||
return options.some((option) => option.name === value)
|
||||
? null
|
||||
: ('missing' as const);
|
||||
}, [value, options]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
setRunnerProblem(null);
|
||||
setDiagnosing(true);
|
||||
void diagnoseRunner()
|
||||
.then((problem) => {
|
||||
if (!cancelled) setRunnerProblem(problem);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setRunnerProblem('runtime');
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setDiagnosing(false);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [diagnoseRunner]);
|
||||
const [pendingInstall, setPendingInstall] = useState(() =>
|
||||
readPendingRunnerInstall(installScope),
|
||||
);
|
||||
@@ -405,6 +444,8 @@ export default function RunnerSelect({
|
||||
option={selectedOption}
|
||||
eventProcessor={usage === 'event'}
|
||||
/>
|
||||
) : value ? (
|
||||
<span className="truncate">{value}</span>
|
||||
) : (
|
||||
<SelectValue
|
||||
placeholder={t(
|
||||
@@ -539,6 +580,24 @@ export default function RunnerSelect({
|
||||
</a>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{!diagnosing && runnerProblem && (
|
||||
<div
|
||||
role="alert"
|
||||
className="space-y-1 text-xs text-amber-700 dark:text-amber-400"
|
||||
>
|
||||
<p>{t(`agents.runnerDiagnosis.${runnerProblem}`)}</p>
|
||||
<a
|
||||
href="/home/plugins"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center gap-1 underline"
|
||||
>
|
||||
{t('agents.runnerDiagnosis.repair')}
|
||||
<ExternalLink className="size-3" />
|
||||
</a>
|
||||
<p>{t('agents.runnerDiagnosis.draft')}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{installError && (
|
||||
<p role="alert" className="text-sm text-destructive">
|
||||
|
||||
@@ -353,6 +353,9 @@ export default function PipelineExtension({
|
||||
<div className="space-y-6">
|
||||
{/* Plugins Section */}
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.extensions.pluginScopeHint')}
|
||||
</p>
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-semibold text-foreground">
|
||||
{t('pipelines.extensions.pluginsTitle')}
|
||||
|
||||
@@ -723,6 +723,9 @@ const PipelineFormComponent = forwardRef<
|
||||
<Card key={stage.name}>
|
||||
<CardHeader>
|
||||
<CardTitle>{extractI18nObject(stage.label)}</CardTitle>
|
||||
<CardDescription>
|
||||
{t('pipelines.extensions.runnerScopeHint')}
|
||||
</CardDescription>
|
||||
{stage.description && (
|
||||
<CardDescription>
|
||||
{extractI18nObject(stage.description)}
|
||||
|
||||
@@ -1611,6 +1611,16 @@ const enUS = {
|
||||
restoringRunnerInstall:
|
||||
'Restoring the Runner plugin installation and waiting for the runner…',
|
||||
noInstalledRunners: 'No Runner extension is installed yet.',
|
||||
runnerDiagnosis: {
|
||||
missing:
|
||||
'The selected Runner or its plugin is missing. Install or restore the plugin.',
|
||||
disabled: 'The selected Runner plugin is disabled. Enable it in Plugins.',
|
||||
runtime:
|
||||
'The Plugin Runtime is unavailable or disabled. Restore its connection before running.',
|
||||
repair: 'Manage plugins and runtime',
|
||||
draft:
|
||||
'Repair opens in a new tab. Your Runner selection and unsaved draft stay here.',
|
||||
},
|
||||
installingRunner: 'Installing {{runner}}...',
|
||||
runnerInstallSuccess: '{{runner}} is installed and ready to select',
|
||||
selectedRunnerUnavailable: 'Selected runner is unavailable',
|
||||
@@ -2219,6 +2229,10 @@ const enUS = {
|
||||
addPlugin: 'Add Plugin',
|
||||
selectPlugins: 'Select Plugins',
|
||||
pluginsTitle: 'Plugins',
|
||||
pluginScopeHint:
|
||||
'Only controls event listeners, tools and commands for this pipeline; does not affect the Runner selected in AI settings.',
|
||||
runnerScopeHint:
|
||||
'Select the Runner independently here, regardless of extension plugin switches. Its plugin must still be installed and available.',
|
||||
mcpServersTitle: 'MCP Servers',
|
||||
mcpResourcesTitle: 'MCP Resources',
|
||||
noMCPServersSelected: 'No MCP servers selected',
|
||||
|
||||
@@ -1635,6 +1635,17 @@ const esES = {
|
||||
restoringRunnerInstall:
|
||||
'Restaurando la instalación del plugin y esperando al ejecutor…',
|
||||
noInstalledRunners: 'Aún no hay extensiones de ejecutor instaladas.',
|
||||
runnerDiagnosis: {
|
||||
missing:
|
||||
'No se encuentra el Runner seleccionado o su complemento. Instale o restaure el complemento.',
|
||||
disabled:
|
||||
'El complemento del Runner seleccionado está deshabilitado. Actívelo en Complementos.',
|
||||
runtime:
|
||||
'El entorno de ejecución de complementos no está disponible o está deshabilitado. Restablezca la conexión antes de ejecutar.',
|
||||
repair: 'Gestionar complementos y entorno de ejecución',
|
||||
draft:
|
||||
'La reparación se abre en otra pestaña. La selección del Runner y el borrador sin guardar se conservan aquí.',
|
||||
},
|
||||
installingRunner: 'Instalando {{runner}}...',
|
||||
runnerInstallSuccess:
|
||||
'{{runner}} está instalado y listo para seleccionarse',
|
||||
@@ -2253,6 +2264,10 @@ const esES = {
|
||||
addPlugin: 'Añadir plugin',
|
||||
selectPlugins: 'Seleccionar plugins',
|
||||
pluginsTitle: 'Plugins',
|
||||
pluginScopeHint:
|
||||
'Solo controla los escuchadores de eventos, herramientas y comandos de este pipeline; no afecta al Runner elegido en los ajustes de IA.',
|
||||
runnerScopeHint:
|
||||
'El Runner se elige aquí independientemente de los interruptores de plugins de extensiones. Su plugin debe estar instalado y disponible.',
|
||||
mcpServersTitle: 'Servidores MCP',
|
||||
mcpResourcesTitle: 'Recursos MCP',
|
||||
noMCPServersSelected: 'No hay servidores MCP seleccionados',
|
||||
|
||||
@@ -1618,6 +1618,17 @@ const jaJP = {
|
||||
restoringRunnerInstall:
|
||||
'Runner プラグインのインストールを復元し、ランナーを待機しています…',
|
||||
noInstalledRunners: 'Runner 拡張機能はまだインストールされていません。',
|
||||
runnerDiagnosis: {
|
||||
missing:
|
||||
'選択した Runner またはプラグインが見つかりません。プラグインをインストールまたは復元してください。',
|
||||
disabled:
|
||||
'選択した Runner プラグインは無効です。プラグイン画面で有効にしてください。',
|
||||
runtime:
|
||||
'プラグインランタイムが利用できないか無効です。接続を復旧してから実行してください。',
|
||||
repair: 'プラグインとランタイムを管理',
|
||||
draft:
|
||||
'修復画面は新しいタブで開きます。Runner の選択と未保存の下書きは保持されます。',
|
||||
},
|
||||
installingRunner: '{{runner}} をインストールしています...',
|
||||
runnerInstallSuccess:
|
||||
'{{runner}} をインストールしました。インストール済み一覧から選択できます',
|
||||
@@ -2228,6 +2239,10 @@ const jaJP = {
|
||||
addPlugin: 'プラグインを追加',
|
||||
selectPlugins: 'プラグインを選択',
|
||||
pluginsTitle: 'プラグイン',
|
||||
pluginScopeHint:
|
||||
'このパイプラインのイベントリスナー、ツール、コマンドのみを制御します。AI 設定で選択した Runner には影響しません。',
|
||||
runnerScopeHint:
|
||||
'Runner は拡張機能のプラグインスイッチと独立して選択します。対応するプラグインはインストール済みで利用可能である必要があります。',
|
||||
mcpServersTitle: 'MCPサーバー',
|
||||
mcpResourcesTitle: 'MCPリソース',
|
||||
noMCPServersSelected: 'MCPサーバーが選択されていません',
|
||||
|
||||
@@ -1632,6 +1632,17 @@ const ruRU = {
|
||||
restoringRunnerInstall:
|
||||
'Восстановление установки плагина и ожидание исполнителя…',
|
||||
noInstalledRunners: 'Расширения исполнителей ещё не установлены.',
|
||||
runnerDiagnosis: {
|
||||
missing:
|
||||
'Выбранный Runner или его плагин не найден. Установите или восстановите плагин.',
|
||||
disabled:
|
||||
'Плагин выбранного Runner отключён. Включите его на странице плагинов.',
|
||||
runtime:
|
||||
'Среда выполнения плагинов недоступна или отключена. Восстановите соединение перед запуском.',
|
||||
repair: 'Управление плагинами и средой выполнения',
|
||||
draft:
|
||||
'Исправление откроется в новой вкладке. Выбор Runner и несохранённый черновик останутся здесь.',
|
||||
},
|
||||
installingRunner: 'Установка {{runner}}...',
|
||||
runnerInstallSuccess: '{{runner}} установлен и доступен для выбора',
|
||||
selectedRunnerUnavailable: 'Выбранный исполнитель недоступен',
|
||||
@@ -2243,6 +2254,10 @@ const ruRU = {
|
||||
addPlugin: 'Добавить плагин',
|
||||
selectPlugins: 'Выберите плагины',
|
||||
pluginsTitle: 'Плагины',
|
||||
pluginScopeHint:
|
||||
'Управляет только обработчиками событий, инструментами и командами этого конвейера, но не Runner, выбранным в настройках ИИ.',
|
||||
runnerScopeHint:
|
||||
'Runner выбирается независимо от переключателей плагинов расширений. Его плагин должен быть установлен и доступен.',
|
||||
mcpServersTitle: 'MCP-серверы',
|
||||
mcpResourcesTitle: 'MCP-ресурсы',
|
||||
noMCPServersSelected: 'MCP-серверы не выбраны',
|
||||
|
||||
@@ -1577,6 +1577,17 @@ const thTH = {
|
||||
viewMarketplace: 'ดูตลาด',
|
||||
restoringRunnerInstall: 'กำลังกู้คืนการติดตั้งปลั๊กอินและรอรันเนอร์…',
|
||||
noInstalledRunners: 'ยังไม่ได้ติดตั้งส่วนขยายรันเนอร์',
|
||||
runnerDiagnosis: {
|
||||
missing:
|
||||
'ไม่พบ Runner ที่เลือกหรือปลั๊กอิน โปรดติดตั้งหรือกู้คืนปลั๊กอิน',
|
||||
disabled:
|
||||
'ปลั๊กอิน Runner ที่เลือกถูกปิดใช้งาน โปรดเปิดใช้งานในหน้าปลั๊กอิน',
|
||||
runtime:
|
||||
'รันไทม์ปลั๊กอินไม่พร้อมใช้งานหรือถูกปิด โปรดกู้คืนการเชื่อมต่อก่อนเรียกใช้',
|
||||
repair: 'จัดการปลั๊กอินและรันไทม์',
|
||||
draft:
|
||||
'หน้าซ่อมแซมจะเปิดในแท็บใหม่ การเลือก Runner และฉบับร่างที่ยังไม่บันทึกจะยังอยู่ที่นี่',
|
||||
},
|
||||
installingRunner: 'กำลังติดตั้ง {{runner}}...',
|
||||
runnerInstallSuccess: 'ติดตั้ง {{runner}} แล้ว พร้อมให้เลือก',
|
||||
selectedRunnerUnavailable: 'รันเนอร์ที่เลือกไม่พร้อมใช้งาน',
|
||||
@@ -2172,6 +2183,10 @@ const thTH = {
|
||||
addPlugin: 'เพิ่มปลั๊กอิน',
|
||||
selectPlugins: 'เลือกปลั๊กอิน',
|
||||
pluginsTitle: 'ปลั๊กอิน',
|
||||
pluginScopeHint:
|
||||
'ควบคุมเฉพาะตัวรับฟังเหตุการณ์ เครื่องมือ และคำสั่งของไปป์ไลน์นี้ ไม่ส่งผลต่อ Runner ที่เลือกในการตั้งค่า AI',
|
||||
runnerScopeHint:
|
||||
'เลือก Runner ได้อย่างอิสระจากสวิตช์ปลั๊กอินส่วนขยาย โดยปลั๊กอินของ Runner ต้องติดตั้งแล้วและพร้อมใช้งาน',
|
||||
mcpServersTitle: 'เซิร์ฟเวอร์ MCP',
|
||||
mcpResourcesTitle: 'ทรัพยากร MCP',
|
||||
noMCPServersSelected: 'ไม่ได้เลือกเซิร์ฟเวอร์ MCP',
|
||||
|
||||
@@ -1601,6 +1601,17 @@ const viVN = {
|
||||
viewMarketplace: 'Xem chợ tiện ích',
|
||||
restoringRunnerInstall: 'Đang khôi phục cài đặt plugin và chờ trình chạy…',
|
||||
noInstalledRunners: 'Chưa cài tiện ích trình chạy nào.',
|
||||
runnerDiagnosis: {
|
||||
missing:
|
||||
'Không tìm thấy Runner đã chọn hoặc tiện ích của nó. Hãy cài đặt hoặc khôi phục tiện ích.',
|
||||
disabled:
|
||||
'Tiện ích Runner đã chọn bị tắt. Hãy bật nó trong trang Tiện ích.',
|
||||
runtime:
|
||||
'Môi trường chạy tiện ích không khả dụng hoặc bị tắt. Hãy khôi phục kết nối trước khi chạy.',
|
||||
repair: 'Quản lý tiện ích và môi trường chạy',
|
||||
draft:
|
||||
'Trang sửa lỗi mở trong thẻ mới. Lựa chọn Runner và bản nháp chưa lưu vẫn được giữ ở đây.',
|
||||
},
|
||||
installingRunner: 'Đang cài {{runner}}...',
|
||||
runnerInstallSuccess: 'Đã cài {{runner}} và có thể chọn',
|
||||
selectedRunnerUnavailable: 'Trình chạy đã chọn không khả dụng',
|
||||
@@ -2205,6 +2216,10 @@ const viVN = {
|
||||
addPlugin: 'Thêm Plugin',
|
||||
selectPlugins: 'Chọn Plugin',
|
||||
pluginsTitle: 'Plugin',
|
||||
pluginScopeHint:
|
||||
'Chỉ kiểm soát trình lắng nghe sự kiện, công cụ và lệnh của pipeline này; không ảnh hưởng đến Runner được chọn trong cài đặt AI.',
|
||||
runnerScopeHint:
|
||||
'Chọn Runner độc lập tại đây, không phụ thuộc vào công tắc plugin tiện ích. Plugin của Runner vẫn phải được cài đặt và khả dụng.',
|
||||
mcpServersTitle: 'Máy chủ MCP',
|
||||
mcpResourcesTitle: 'Tài nguyên MCP',
|
||||
noMCPServersSelected: 'Chưa chọn máy chủ MCP nào',
|
||||
|
||||
@@ -1516,6 +1516,13 @@ const zhHans = {
|
||||
viewMarketplace: '查看市场',
|
||||
restoringRunnerInstall: '正在恢复运行器插件安装并等待运行器就绪…',
|
||||
noInstalledRunners: '尚未安装任何运行器扩展。',
|
||||
runnerDiagnosis: {
|
||||
missing: '所选运行器或其插件不存在,请安装或恢复插件。',
|
||||
disabled: '所选运行器插件已禁用,请在插件页面启用。',
|
||||
runtime: '插件运行时不可用或已禁用,请恢复连接后再运行。',
|
||||
repair: '管理插件和运行时',
|
||||
draft: '修复页面将在新标签页打开,当前运行器选择和未保存的草稿将保留。',
|
||||
},
|
||||
installingRunner: '正在安装 {{runner}}...',
|
||||
runnerInstallSuccess: '{{runner}} 已安装,可从已安装列表中选择',
|
||||
selectedRunnerUnavailable: '所选运行器不可用',
|
||||
@@ -2092,6 +2099,10 @@ const zhHans = {
|
||||
addPlugin: '添加插件',
|
||||
selectPlugins: '选择插件',
|
||||
pluginsTitle: '插件',
|
||||
pluginScopeHint:
|
||||
'此处仅控制本流水线使用的事件监听器、工具和命令组件,不影响 AI 设置中选择的运行器(Runner)。',
|
||||
runnerScopeHint:
|
||||
'运行器在此独立选择,不受扩展集成中的插件开关控制;其所属插件仍需已安装且可用。',
|
||||
mcpServersTitle: 'MCP 服务器',
|
||||
mcpResourcesTitle: 'MCP 资源',
|
||||
noMCPServersSelected: '未选择任何 MCP 服务器',
|
||||
|
||||
@@ -1514,6 +1514,13 @@ const zhHant = {
|
||||
viewMarketplace: '查看市場',
|
||||
restoringRunnerInstall: '正在恢復運行器插件安裝並等待運行器就緒…',
|
||||
noInstalledRunners: '尚未安裝任何運行器擴展。',
|
||||
runnerDiagnosis: {
|
||||
missing: '所選運行器或其外掛不存在,請安裝或還原外掛。',
|
||||
disabled: '所選運行器外掛已停用,請在外掛頁面啟用。',
|
||||
runtime: '外掛執行環境無法使用或已停用,請恢復連線後再執行。',
|
||||
repair: '管理外掛和執行環境',
|
||||
draft: '修復頁面將在新分頁開啟,目前的運行器選擇和未儲存的草稿將保留。',
|
||||
},
|
||||
installingRunner: '正在安裝 {{runner}}...',
|
||||
runnerInstallSuccess: '{{runner}} 已安裝,可從已安裝列表中選擇',
|
||||
selectedRunnerUnavailable: '所選運行器不可用',
|
||||
@@ -2089,6 +2096,10 @@ const zhHant = {
|
||||
addPlugin: '新增插件',
|
||||
selectPlugins: '選擇插件',
|
||||
pluginsTitle: '插件',
|
||||
pluginScopeHint:
|
||||
'此處僅控制本流水線使用的事件監聽器、工具和命令元件,不影響 AI 設定中選擇的執行器(Runner)。',
|
||||
runnerScopeHint:
|
||||
'執行器在此獨立選擇,不受擴充整合中的外掛開關控制;其所屬外掛仍須已安裝且可用。',
|
||||
mcpServersTitle: 'MCP 伺服器',
|
||||
mcpResourcesTitle: 'MCP 資源',
|
||||
noMCPServersSelected: '未選擇任何 MCP 伺服器',
|
||||
|
||||
Reference in New Issue
Block a user