mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 23:07:14 +00:00
feat(runner): unify plugin execution across agents and event processors
This commit is contained in:
@@ -23,12 +23,12 @@ import {
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import PipelineDetailContent from '@/app/home/pipelines/PipelineDetailContent';
|
||||
import EventProcessorDetailContent from './EventProcessorDetailContent';
|
||||
import PluginProcessorDetailContent from './PluginProcessorDetailContent';
|
||||
import AgentCreateContent from './components/AgentCreateContent';
|
||||
import AgentDebugPanel from './components/AgentDebugPanel';
|
||||
import AgentFormComponent, {
|
||||
AgentFormHandle,
|
||||
AgentRunnerStatus,
|
||||
RunnerStatus,
|
||||
} from './components/AgentFormComponent';
|
||||
|
||||
export default function AgentDetailContent({ id }: { id: string }) {
|
||||
@@ -49,9 +49,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
||||
const [basicInfoOpen, setBasicInfoOpen] = useState(false);
|
||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
const [runnerStatus, setRunnerStatus] = useState<AgentRunnerStatus | null>(
|
||||
null,
|
||||
);
|
||||
const [runnerStatus, setRunnerStatus] = useState<RunnerStatus | null>(null);
|
||||
const [availableEventTypes, setAvailableEventTypes] = useState<string[]>([
|
||||
'message.received',
|
||||
]);
|
||||
@@ -169,7 +167,7 @@ export default function AgentDetailContent({ id }: { id: string }) {
|
||||
return (
|
||||
<>
|
||||
{agent.kind === 'event_processor' ? (
|
||||
<EventProcessorDetailContent
|
||||
<PluginProcessorDetailContent
|
||||
key={id}
|
||||
id={id}
|
||||
agent={agent}
|
||||
|
||||
+8
-8
@@ -8,7 +8,7 @@ import { toast } from 'sonner';
|
||||
import type {
|
||||
Agent,
|
||||
AgentPlatformTool,
|
||||
EventProcessorDescriptor,
|
||||
RunnerDescriptor,
|
||||
ProcessorRun,
|
||||
ProcessorRunEvent,
|
||||
} from '@/app/infra/entities/api';
|
||||
@@ -21,14 +21,14 @@ import { ScrollArea } from '@/components/ui/scroll-area';
|
||||
import ProcessorDetailWorkbench from '@/app/home/components/processor-detail/ProcessorDetailWorkbench';
|
||||
import EntityTitleEditButton from '@/app/home/components/entity-basic-info/EntityTitleEditButton';
|
||||
import AgentDebugPanel from './components/AgentDebugPanel';
|
||||
import EventProcessorTrace, {
|
||||
import PluginProcessorTrace, {
|
||||
ProcessorPayload,
|
||||
} from './components/EventProcessorTrace';
|
||||
} from './components/PluginProcessorTrace';
|
||||
import ProcessorRunList from './components/ProcessorRunList';
|
||||
import EventProcessorSettings from './components/EventProcessorSettings';
|
||||
import PluginProcessorSettings from './components/PluginProcessorSettings';
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
|
||||
export default function EventProcessorDetailContent({
|
||||
export default function PluginProcessorDetailContent({
|
||||
agent,
|
||||
id,
|
||||
canManage,
|
||||
@@ -53,7 +53,7 @@ export default function EventProcessorDetailContent({
|
||||
const toolLabels = Object.fromEntries(
|
||||
platformTools.map((tool) => [tool.name, extractI18nObject(tool.label)]),
|
||||
);
|
||||
const [components, setComponents] = useState<EventProcessorDescriptor[]>([]);
|
||||
const [components, setComponents] = useState<RunnerDescriptor[]>([]);
|
||||
const [componentRef, setComponentRef] = useState(agent.component_ref ?? '');
|
||||
const initialParameters =
|
||||
(
|
||||
@@ -358,7 +358,7 @@ export default function EventProcessorDetailContent({
|
||||
value={selected.metadata.delivery}
|
||||
/>
|
||||
)}
|
||||
<EventProcessorTrace events={events} toolLabels={toolLabels} />
|
||||
<PluginProcessorTrace events={events} toolLabels={toolLabels} />
|
||||
{selected.status === 'failed' && selected.status_reason && (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription className="break-words">
|
||||
@@ -389,7 +389,7 @@ export default function EventProcessorDetailContent({
|
||||
canManage ? <EntityTitleEditButton onClick={onEdit} /> : undefined
|
||||
}
|
||||
titleControls={
|
||||
<EventProcessorSettings
|
||||
<PluginProcessorSettings
|
||||
components={components}
|
||||
value={componentRef}
|
||||
disabled={!canManage || saving || loading}
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
groupEventPatterns,
|
||||
} from '@/app/home/components/event-patterns/event-pattern-groups';
|
||||
import EventSelectOptionContent from '@/app/home/components/event-patterns/EventSelectOptionContent';
|
||||
import EventProcessorTrace from './EventProcessorTrace';
|
||||
import PluginProcessorTrace from './PluginProcessorTrace';
|
||||
import AgentExecutionTrace from './AgentExecutionTrace';
|
||||
import AgentEventDataEditor from './AgentEventDataEditor';
|
||||
import {
|
||||
@@ -455,7 +455,7 @@ export default function AgentDebugPanel({
|
||||
</div>
|
||||
{entry.events &&
|
||||
(processor ? (
|
||||
<EventProcessorTrace
|
||||
<PluginProcessorTrace
|
||||
events={entry.events}
|
||||
toolLabels={toolLabels}
|
||||
/>
|
||||
|
||||
@@ -28,10 +28,10 @@ import {
|
||||
import DynamicFormComponent from '@/app/home/components/dynamic-form/DynamicFormComponent';
|
||||
import {
|
||||
getErrorMessage,
|
||||
readPendingAgentRunnerInstall,
|
||||
resumePendingAgentRunnerInstall,
|
||||
type InstalledAgentRunner,
|
||||
} from '@/app/home/agents/agent-runner-marketplace';
|
||||
readPendingRunnerInstall,
|
||||
resumePendingRunnerInstall,
|
||||
type InstalledRunner,
|
||||
} from '@/app/home/agents/runner-marketplace';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import {
|
||||
@@ -43,7 +43,7 @@ import {
|
||||
} from '@/components/ui/card';
|
||||
import { Form, FormField, FormItem, FormMessage } from '@/components/ui/form';
|
||||
import AgentEventPatternPicker from './AgentEventPatternPicker';
|
||||
import AgentRunnerSelect from './AgentRunnerSelect';
|
||||
import RunnerSelect from './RunnerSelect';
|
||||
import AgentApiToolPicker from './AgentApiToolPicker';
|
||||
|
||||
const OTHER_TOOL_SCOPES = [
|
||||
@@ -54,7 +54,7 @@ const OTHER_TOOL_SCOPES = [
|
||||
'skill',
|
||||
] as const;
|
||||
|
||||
export interface AgentRunnerStatus {
|
||||
export interface RunnerStatus {
|
||||
label: string;
|
||||
description?: string;
|
||||
tone: 'neutral' | 'success' | 'warning' | 'error';
|
||||
@@ -66,7 +66,7 @@ interface AgentFormComponentProps {
|
||||
onFinish: (agent?: Partial<Agent>) => void;
|
||||
onDirtyChange?: (dirty: boolean) => void;
|
||||
onSavingChange?: (saving: boolean) => void;
|
||||
onRunnerStatusChange?: (status: AgentRunnerStatus) => void;
|
||||
onRunnerStatusChange?: (status: RunnerStatus) => void;
|
||||
onSupportedEventPatternsChange?: (patterns: string[]) => void;
|
||||
onPlatformToolsChange?: (tools: AgentPlatformTool[]) => void;
|
||||
}
|
||||
@@ -184,12 +184,9 @@ function AgentFormComponent(
|
||||
});
|
||||
const runnerInstallScope = `agent:${agentId}`;
|
||||
|
||||
const applyInstalledRunner = useCallback(
|
||||
(installed: InstalledAgentRunner) => {
|
||||
setRunnerConfigSchema(installed.configTab);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const applyInstalledRunner = useCallback((installed: InstalledRunner) => {
|
||||
setRunnerConfigSchema(installed.configTab);
|
||||
}, []);
|
||||
|
||||
const savedSnapshotRef = useRef('');
|
||||
const initializedStagesRef = useRef<Set<string>>(new Set());
|
||||
@@ -285,15 +282,12 @@ function AgentFormComponent(
|
||||
}, [agentId, form, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!initialDataLoaded ||
|
||||
!readPendingAgentRunnerInstall(runnerInstallScope)
|
||||
) {
|
||||
if (!initialDataLoaded || !readPendingRunnerInstall(runnerInstallScope)) {
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setRunnerInstallRecovering(true);
|
||||
void resumePendingAgentRunnerInstall(runnerInstallScope)
|
||||
void resumePendingRunnerInstall(runnerInstallScope)
|
||||
.then((installed) => {
|
||||
if (cancelled || !installed) return;
|
||||
applyInstalledRunner(installed);
|
||||
@@ -395,7 +389,7 @@ function AgentFormComponent(
|
||||
},
|
||||
];
|
||||
|
||||
const runnerStatus = useMemo<AgentRunnerStatus>(() => {
|
||||
const runnerStatus = useMemo<RunnerStatus>(() => {
|
||||
if (pluginStatusLoading) {
|
||||
return {
|
||||
label: t('agents.runnerStatusLoading'),
|
||||
@@ -540,7 +534,7 @@ function AgentFormComponent(
|
||||
isRunnerSelector
|
||||
? ({ config, field }) =>
|
||||
config.name === 'id' ? (
|
||||
<AgentRunnerSelect
|
||||
<RunnerSelect
|
||||
options={config.options ?? []}
|
||||
label={extractI18nObject(config.label)}
|
||||
value={String(field.value ?? '')}
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
import { Puzzle } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { EventProcessorDescriptor } from '@/app/infra/entities/api';
|
||||
import type { RunnerDescriptor } from '@/app/infra/entities/api';
|
||||
import { httpClient } from '@/app/infra/http';
|
||||
import { extractI18nObject } from '@/i18n/I18nProvider';
|
||||
import {
|
||||
@@ -18,7 +18,7 @@ function ProcessorComponentContent({
|
||||
component,
|
||||
option = false,
|
||||
}: {
|
||||
component: EventProcessorDescriptor;
|
||||
component: RunnerDescriptor;
|
||||
option?: boolean;
|
||||
}) {
|
||||
const label = extractI18nObject({
|
||||
@@ -62,13 +62,13 @@ function ProcessorComponentContent({
|
||||
);
|
||||
}
|
||||
|
||||
export default function EventProcessorSettings({
|
||||
export default function PluginProcessorSettings({
|
||||
components,
|
||||
value,
|
||||
onChange,
|
||||
disabled = false,
|
||||
}: {
|
||||
components: EventProcessorDescriptor[];
|
||||
components: RunnerDescriptor[];
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
disabled?: boolean;
|
||||
+1
-1
@@ -38,7 +38,7 @@ export function ProcessorPayload({
|
||||
);
|
||||
}
|
||||
|
||||
export default function EventProcessorTrace({
|
||||
export default function PluginProcessorTrace({
|
||||
events,
|
||||
toolLabels = {},
|
||||
}: {
|
||||
@@ -373,7 +373,7 @@ function PipelineDiagram() {
|
||||
);
|
||||
}
|
||||
|
||||
function EventProcessorDiagram() {
|
||||
function RunnerDiagram() {
|
||||
const { t } = useTranslation();
|
||||
const codeLines = [
|
||||
<>
|
||||
@@ -459,6 +459,6 @@ function EventProcessorDiagram() {
|
||||
}
|
||||
|
||||
export default function ProcessorTypeDiagram({ kind }: { kind: AgentKind }) {
|
||||
if (kind === 'event_processor') return <EventProcessorDiagram />;
|
||||
if (kind === 'event_processor') return <RunnerDiagram />;
|
||||
return kind === 'agent' ? <AgentDiagram /> : <PipelineDiagram />;
|
||||
}
|
||||
|
||||
+21
-21
@@ -7,17 +7,17 @@ import { getCloudServiceClientSync, httpClient } from '@/app/infra/http';
|
||||
import type { IDynamicFormItemOption } from '@/app/infra/entities/form/dynamic';
|
||||
import type { PluginV4 } from '@/app/infra/entities/plugin';
|
||||
import {
|
||||
AgentRunnerMarketplaceError,
|
||||
RunnerMarketplaceError,
|
||||
getErrorMessage,
|
||||
installMarketplaceAgentRunner,
|
||||
loadAgentRunnerCatalog,
|
||||
installMarketplaceRunner,
|
||||
loadRunnerCatalog,
|
||||
marketplacePluginId,
|
||||
runnerPluginPrefix,
|
||||
readPendingAgentRunnerInstall,
|
||||
subscribePendingAgentRunnerInstall,
|
||||
type AgentRunnerCatalog,
|
||||
type InstalledAgentRunner,
|
||||
} from '@/app/home/agents/agent-runner-marketplace';
|
||||
readPendingRunnerInstall,
|
||||
subscribePendingRunnerInstall,
|
||||
type RunnerCatalog,
|
||||
type InstalledRunner,
|
||||
} from '@/app/home/agents/runner-marketplace';
|
||||
import {
|
||||
InstallStage,
|
||||
usePluginInstallTasks,
|
||||
@@ -39,7 +39,7 @@ function installErrorMessage(
|
||||
error: unknown,
|
||||
t: ReturnType<typeof useTranslation>['t'],
|
||||
) {
|
||||
if (error instanceof AgentRunnerMarketplaceError) {
|
||||
if (error instanceof RunnerMarketplaceError) {
|
||||
if (error.code === 'version-unavailable') {
|
||||
return t('wizard.aiEngine.versionUnavailable');
|
||||
}
|
||||
@@ -123,7 +123,7 @@ function runnerPluginId(optionName: string) {
|
||||
function installedRunnerDescription(
|
||||
option: IDynamicFormItemOption,
|
||||
marketplaceRunners: PluginV4[],
|
||||
installedPluginDescriptions: AgentRunnerCatalog['installedPluginDescriptions'],
|
||||
installedPluginDescriptions: RunnerCatalog['installedPluginDescriptions'],
|
||||
) {
|
||||
const pluginId = runnerPluginId(option.name);
|
||||
if (!pluginId) return option.name;
|
||||
@@ -192,7 +192,7 @@ function MarketplaceRunnerContent({
|
||||
);
|
||||
}
|
||||
|
||||
export default function AgentRunnerSelect({
|
||||
export default function RunnerSelect({
|
||||
options,
|
||||
label,
|
||||
value,
|
||||
@@ -205,18 +205,18 @@ export default function AgentRunnerSelect({
|
||||
value: string;
|
||||
onValueChange: (value: string) => void;
|
||||
installScope: string;
|
||||
onInstalled: (installed: InstalledAgentRunner) => void;
|
||||
onInstalled: (installed: InstalledRunner) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const { addTask, tasks } = usePluginInstallTasks();
|
||||
const [marketplaceRunners, setMarketplaceRunners] = useState<PluginV4[]>([]);
|
||||
const [installedPluginIds, setInstalledPluginIds] = useState<string[]>([]);
|
||||
const [installedPluginDescriptions, setInstalledPluginDescriptions] =
|
||||
useState<AgentRunnerCatalog['installedPluginDescriptions']>({});
|
||||
useState<RunnerCatalog['installedPluginDescriptions']>({});
|
||||
const [catalogLoading, setCatalogLoading] = useState(true);
|
||||
const [catalogError, setCatalogError] = useState(false);
|
||||
const [pendingInstall, setPendingInstall] = useState(() =>
|
||||
readPendingAgentRunnerInstall(installScope),
|
||||
readPendingRunnerInstall(installScope),
|
||||
);
|
||||
const [installError, setInstallError] = useState<string | null>(null);
|
||||
const [installingPluginId, setInstallingPluginId] = useState<string | null>(
|
||||
@@ -227,12 +227,12 @@ export default function AgentRunnerSelect({
|
||||
setCatalogLoading(true);
|
||||
setCatalogError(false);
|
||||
try {
|
||||
const catalog = await loadAgentRunnerCatalog();
|
||||
const catalog = await loadRunnerCatalog();
|
||||
setMarketplaceRunners(catalog.marketplaceRunners);
|
||||
setInstalledPluginIds(catalog.installedPluginIds);
|
||||
setInstalledPluginDescriptions(catalog.installedPluginDescriptions);
|
||||
} catch (error) {
|
||||
console.error('Failed to load AgentRunner catalog', error);
|
||||
console.error('Failed to load Runner catalog', error);
|
||||
setCatalogError(true);
|
||||
} finally {
|
||||
setCatalogLoading(false);
|
||||
@@ -245,9 +245,9 @@ export default function AgentRunnerSelect({
|
||||
|
||||
useEffect(() => {
|
||||
const syncPendingInstall = () =>
|
||||
setPendingInstall(readPendingAgentRunnerInstall(installScope));
|
||||
setPendingInstall(readPendingRunnerInstall(installScope));
|
||||
syncPendingInstall();
|
||||
return subscribePendingAgentRunnerInstall(installScope, syncPendingInstall);
|
||||
return subscribePendingRunnerInstall(installScope, syncPendingInstall);
|
||||
}, [installScope]);
|
||||
|
||||
const marketplaceOptions = useMemo(
|
||||
@@ -291,7 +291,7 @@ export default function AgentRunnerSelect({
|
||||
setInstallingPluginId(pluginId);
|
||||
setInstallError(null);
|
||||
try {
|
||||
const installed = await installMarketplaceAgentRunner(plugin, {
|
||||
const installed = await installMarketplaceRunner(plugin, {
|
||||
scope: installScope,
|
||||
onTaskCreated: (taskId) =>
|
||||
addTask({
|
||||
@@ -313,7 +313,7 @@ export default function AgentRunnerSelect({
|
||||
setInstallError(message);
|
||||
toast.error(message);
|
||||
} finally {
|
||||
const current = readPendingAgentRunnerInstall(installScope);
|
||||
const current = readPendingRunnerInstall(installScope);
|
||||
setPendingInstall(current);
|
||||
if (!current) setInstallingPluginId(null);
|
||||
}
|
||||
@@ -392,7 +392,7 @@ export default function AgentRunnerSelect({
|
||||
{t('agents.marketplaceRunners')}
|
||||
</span>
|
||||
<a
|
||||
href="https://space.langbot.app/market?type=plugin&component=AgentRunner"
|
||||
href="https://space.langbot.app/market?type=plugin&component=Runner"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex shrink-0 items-center gap-1 rounded border px-1.5 py-0.5 text-[10px] font-medium text-foreground hover:bg-accent"
|
||||
+39
-39
@@ -6,38 +6,38 @@ import type { PipelineConfigTab } from '@/app/infra/entities/pipeline';
|
||||
import type { PluginV4 } from '@/app/infra/entities/plugin';
|
||||
import type { I18nObject } from '@/app/infra/entities/common';
|
||||
|
||||
export const RUNNER_COMPONENT_FILTER = 'AgentRunner';
|
||||
export const RUNNER_COMPONENT_FILTER = 'Runner';
|
||||
|
||||
const RUNNER_CATALOG_PAGE_SIZE = 100;
|
||||
const RUNNER_INSTALL_TIMEOUT_MS = 120_000;
|
||||
const RUNNER_REGISTRATION_TIMEOUT_MS = 60_000;
|
||||
const RUNNER_INSTALL_INTENT_KEY_PREFIX = 'langbot-agent-runner-install';
|
||||
const RUNNER_INSTALL_INTENT_EVENT = 'langbot-agent-runner-install-change';
|
||||
const RUNNER_INSTALL_INTENT_KEY_PREFIX = 'langbot-runner-install';
|
||||
const RUNNER_INSTALL_INTENT_EVENT = 'langbot-runner-install-change';
|
||||
|
||||
export type AgentRunnerMarketplaceErrorCode =
|
||||
export type RunnerMarketplaceErrorCode =
|
||||
| 'version-unavailable'
|
||||
| 'install-timeout'
|
||||
| 'registration-timeout';
|
||||
|
||||
export class AgentRunnerMarketplaceError extends Error {
|
||||
constructor(public readonly code: AgentRunnerMarketplaceErrorCode) {
|
||||
export class RunnerMarketplaceError extends Error {
|
||||
constructor(public readonly code: RunnerMarketplaceErrorCode) {
|
||||
super(code);
|
||||
this.name = 'AgentRunnerMarketplaceError';
|
||||
this.name = 'RunnerMarketplaceError';
|
||||
}
|
||||
}
|
||||
|
||||
export interface AgentRunnerCatalog {
|
||||
export interface RunnerCatalog {
|
||||
marketplaceRunners: PluginV4[];
|
||||
installedPluginIds: string[];
|
||||
installedPluginDescriptions: Record<string, I18nObject>;
|
||||
}
|
||||
|
||||
export interface InstalledAgentRunner {
|
||||
export interface InstalledRunner {
|
||||
configTab: PipelineConfigTab;
|
||||
runner: IDynamicFormItemOption;
|
||||
}
|
||||
|
||||
export interface PendingAgentRunnerInstall {
|
||||
export interface PendingRunnerInstall {
|
||||
taskId: number;
|
||||
pluginId: string;
|
||||
pluginAuthor: string;
|
||||
@@ -47,7 +47,7 @@ export interface PendingAgentRunnerInstall {
|
||||
startedAt: number;
|
||||
}
|
||||
|
||||
interface InstallAgentRunnerOptions {
|
||||
interface InstallRunnerOptions {
|
||||
scope: string;
|
||||
onTaskCreated?: (taskId: number) => void;
|
||||
}
|
||||
@@ -83,14 +83,14 @@ function emitInstallIntentChange(scope: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export function readPendingAgentRunnerInstall(
|
||||
export function readPendingRunnerInstall(
|
||||
scope: string,
|
||||
): PendingAgentRunnerInstall | null {
|
||||
): PendingRunnerInstall | null {
|
||||
if (typeof window === 'undefined') return null;
|
||||
try {
|
||||
const raw = sessionStorage.getItem(installIntentStorageKey(scope));
|
||||
if (!raw) return null;
|
||||
const value = JSON.parse(raw) as Partial<PendingAgentRunnerInstall>;
|
||||
const value = JSON.parse(raw) as Partial<PendingRunnerInstall>;
|
||||
if (
|
||||
value.scope !== scope ||
|
||||
typeof value.taskId !== 'number' ||
|
||||
@@ -103,13 +103,13 @@ export function readPendingAgentRunnerInstall(
|
||||
sessionStorage.removeItem(installIntentStorageKey(scope));
|
||||
return null;
|
||||
}
|
||||
return value as PendingAgentRunnerInstall;
|
||||
return value as PendingRunnerInstall;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function writePendingAgentRunnerInstall(intent: PendingAgentRunnerInstall) {
|
||||
function writePendingRunnerInstall(intent: PendingRunnerInstall) {
|
||||
if (typeof window === 'undefined') return;
|
||||
sessionStorage.setItem(
|
||||
installIntentStorageKey(intent.scope),
|
||||
@@ -118,15 +118,15 @@ function writePendingAgentRunnerInstall(intent: PendingAgentRunnerInstall) {
|
||||
emitInstallIntentChange(intent.scope);
|
||||
}
|
||||
|
||||
export function clearPendingAgentRunnerInstall(scope: string, taskId?: number) {
|
||||
export function clearPendingRunnerInstall(scope: string, taskId?: number) {
|
||||
if (typeof window === 'undefined') return;
|
||||
const current = readPendingAgentRunnerInstall(scope);
|
||||
const current = readPendingRunnerInstall(scope);
|
||||
if (taskId !== undefined && current?.taskId !== taskId) return;
|
||||
sessionStorage.removeItem(installIntentStorageKey(scope));
|
||||
emitInstallIntentChange(scope);
|
||||
}
|
||||
|
||||
export function subscribePendingAgentRunnerInstall(
|
||||
export function subscribePendingRunnerInstall(
|
||||
scope: string,
|
||||
listener: () => void,
|
||||
) {
|
||||
@@ -140,7 +140,7 @@ export function subscribePendingAgentRunnerInstall(
|
||||
window.removeEventListener(RUNNER_INSTALL_INTENT_EVENT, handleChange);
|
||||
}
|
||||
|
||||
export async function loadAgentRunnerCatalog(): Promise<AgentRunnerCatalog> {
|
||||
export async function loadRunnerCatalog(): Promise<RunnerCatalog> {
|
||||
const cloudClient = await getCloudServiceClient();
|
||||
const [firstSearchResult, recommendationResult, installedResult] =
|
||||
await Promise.all([
|
||||
@@ -218,12 +218,12 @@ export async function loadAgentRunnerCatalog(): Promise<AgentRunnerCatalog> {
|
||||
};
|
||||
}
|
||||
|
||||
export async function installMarketplaceAgentRunner(
|
||||
export async function installMarketplaceRunner(
|
||||
plugin: PluginV4,
|
||||
options: InstallAgentRunnerOptions,
|
||||
): Promise<InstalledAgentRunner> {
|
||||
options: InstallRunnerOptions,
|
||||
): Promise<InstalledRunner> {
|
||||
if (!plugin.latest_version) {
|
||||
throw new AgentRunnerMarketplaceError('version-unavailable');
|
||||
throw new RunnerMarketplaceError('version-unavailable');
|
||||
}
|
||||
|
||||
const { task_id: taskId } = await httpClient.installPluginFromMarketplace(
|
||||
@@ -231,7 +231,7 @@ export async function installMarketplaceAgentRunner(
|
||||
plugin.name,
|
||||
plugin.latest_version,
|
||||
);
|
||||
const pending: PendingAgentRunnerInstall = {
|
||||
const pending: PendingRunnerInstall = {
|
||||
taskId,
|
||||
pluginId: marketplacePluginId(plugin),
|
||||
pluginAuthor: plugin.author,
|
||||
@@ -240,9 +240,9 @@ export async function installMarketplaceAgentRunner(
|
||||
scope: options.scope,
|
||||
startedAt: Date.now(),
|
||||
};
|
||||
writePendingAgentRunnerInstall(pending);
|
||||
writePendingRunnerInstall(pending);
|
||||
options.onTaskCreated?.(taskId);
|
||||
return finishAgentRunnerInstall(pending);
|
||||
return finishRunnerInstall(pending);
|
||||
}
|
||||
|
||||
function extractPluginLabel(plugin: PluginV4) {
|
||||
@@ -257,9 +257,9 @@ function extractPluginLabel(plugin: PluginV4) {
|
||||
return plugin.name;
|
||||
}
|
||||
|
||||
async function finishAgentRunnerInstall(
|
||||
pending: PendingAgentRunnerInstall,
|
||||
): Promise<InstalledAgentRunner> {
|
||||
async function finishRunnerInstall(
|
||||
pending: PendingRunnerInstall,
|
||||
): Promise<InstalledRunner> {
|
||||
// A refreshed page receives a fresh observation window. The backend task is
|
||||
// authoritative; `startedAt` is display metadata, not a reason to abandon a
|
||||
// still-running installation immediately after recovery.
|
||||
@@ -269,7 +269,7 @@ async function finishAgentRunnerInstall(
|
||||
const task = await httpClient.getAsyncTask(pending.taskId);
|
||||
if (task.runtime.done) {
|
||||
if (task.runtime.exception) {
|
||||
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
|
||||
clearPendingRunnerInstall(pending.scope, pending.taskId);
|
||||
throw new Error(task.runtime.exception);
|
||||
}
|
||||
installCompleted = true;
|
||||
@@ -279,7 +279,7 @@ async function finishAgentRunnerInstall(
|
||||
await wait(1000);
|
||||
}
|
||||
if (!installCompleted) {
|
||||
throw new AgentRunnerMarketplaceError('install-timeout');
|
||||
throw new RunnerMarketplaceError('install-timeout');
|
||||
}
|
||||
|
||||
const registrationDeadline = Date.now() + RUNNER_REGISTRATION_TIMEOUT_MS;
|
||||
@@ -303,20 +303,20 @@ async function finishAgentRunnerInstall(
|
||||
pluginRunnerOptions[0];
|
||||
|
||||
if (configTab && runner) {
|
||||
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
|
||||
clearPendingRunnerInstall(pending.scope, pending.taskId);
|
||||
return { configTab, runner };
|
||||
}
|
||||
await wait(1000);
|
||||
}
|
||||
|
||||
clearPendingAgentRunnerInstall(pending.scope, pending.taskId);
|
||||
throw new AgentRunnerMarketplaceError('registration-timeout');
|
||||
clearPendingRunnerInstall(pending.scope, pending.taskId);
|
||||
throw new RunnerMarketplaceError('registration-timeout');
|
||||
}
|
||||
|
||||
export async function resumePendingAgentRunnerInstall(
|
||||
export async function resumePendingRunnerInstall(
|
||||
scope: string,
|
||||
): Promise<InstalledAgentRunner | null> {
|
||||
const pending = readPendingAgentRunnerInstall(scope);
|
||||
): Promise<InstalledRunner | null> {
|
||||
const pending = readPendingRunnerInstall(scope);
|
||||
if (!pending) return null;
|
||||
return finishAgentRunnerInstall(pending);
|
||||
return finishRunnerInstall(pending);
|
||||
}
|
||||
@@ -582,10 +582,10 @@ export default function ToolResourceSelectors({
|
||||
|
||||
const sourceLabels = useMemo<Record<string, string>>(
|
||||
() => ({
|
||||
builtin: t('pipelines.agentRunner.builtinTools'),
|
||||
plugin: t('pipelines.agentRunner.pluginTools'),
|
||||
mcp: t('pipelines.agentRunner.mcpTools'),
|
||||
skill: t('pipelines.agentRunner.skillTools'),
|
||||
builtin: t('pipelines.runner.builtinTools'),
|
||||
plugin: t('pipelines.runner.pluginTools'),
|
||||
mcp: t('pipelines.runner.mcpTools'),
|
||||
skill: t('pipelines.runner.skillTools'),
|
||||
}),
|
||||
[t],
|
||||
);
|
||||
@@ -653,27 +653,27 @@ export default function ToolResourceSelectors({
|
||||
<div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('pipelines.agentRunner.toolsTitle')}
|
||||
{t('pipelines.runner.toolsTitle')}
|
||||
</h3>
|
||||
{pipelineId && (
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.toolsScopeTooltip')}
|
||||
label={t('pipelines.runner.toolsScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.toolsDescription')}
|
||||
{t('pipelines.runner.toolsDescription')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<Label
|
||||
htmlFor="agent-runner-enable-all-tools"
|
||||
htmlFor="runner-enable-all-tools"
|
||||
className="cursor-pointer text-sm font-normal"
|
||||
>
|
||||
{t('pipelines.agentRunner.enableAllTools')}
|
||||
{t('pipelines.runner.enableAllTools')}
|
||||
</Label>
|
||||
<Switch
|
||||
id="agent-runner-enable-all-tools"
|
||||
id="runner-enable-all-tools"
|
||||
checked={enableAllTools}
|
||||
onCheckedChange={handleToggleToolMode}
|
||||
/>
|
||||
@@ -683,13 +683,13 @@ export default function ToolResourceSelectors({
|
||||
{enableAllTools ? (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border bg-muted/30">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.allToolsEnabled')}
|
||||
{t('pipelines.runner.allToolsEnabled')}
|
||||
</p>
|
||||
</div>
|
||||
) : selectedTools.length === 0 ? (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.noToolsSelected')}
|
||||
{t('pipelines.runner.noToolsSelected')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -764,7 +764,7 @@ export default function ToolResourceSelectors({
|
||||
}}
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t('pipelines.agentRunner.editTools')}
|
||||
{t('pipelines.runner.editTools')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
@@ -773,10 +773,10 @@ export default function ToolResourceSelectors({
|
||||
<div className="space-y-4 rounded-lg border p-4">
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold">
|
||||
{t('pipelines.agentRunner.resourcesTitle')}
|
||||
{t('pipelines.runner.resourcesTitle')}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.resourcesDescription')}
|
||||
{t('pipelines.runner.resourcesDescription')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -785,7 +785,7 @@ export default function ToolResourceSelectors({
|
||||
<div className="flex items-center gap-2">
|
||||
<Database className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('pipelines.agentRunner.knowledgeBases')}
|
||||
{t('pipelines.runner.knowledgeBases')}
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
@@ -855,26 +855,26 @@ export default function ToolResourceSelectors({
|
||||
<div className="flex items-center gap-2">
|
||||
<Server className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-sm font-medium">
|
||||
{t('pipelines.agentRunner.mcpResources')}
|
||||
{t('pipelines.runner.mcpResources')}
|
||||
</span>
|
||||
{pipelineId && (
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.mcpResourcesScopeTooltip')}
|
||||
label={t('pipelines.runner.mcpResourcesScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Label
|
||||
htmlFor="agent-runner-mcp-resource-read"
|
||||
htmlFor="runner-mcp-resource-read"
|
||||
className="cursor-pointer text-sm font-normal"
|
||||
>
|
||||
{t('pipelines.agentRunner.enableMCPResourceRead')}
|
||||
{t('pipelines.runner.enableMCPResourceRead')}
|
||||
</Label>
|
||||
<InfoTooltip
|
||||
label={t('pipelines.agentRunner.mcpResourceReadTooltip')}
|
||||
label={t('pipelines.runner.mcpResourceReadTooltip')}
|
||||
/>
|
||||
<Switch
|
||||
id="agent-runner-mcp-resource-read"
|
||||
id="runner-mcp-resource-read"
|
||||
checked={mcpResourceReadEnabled}
|
||||
onCheckedChange={(checked) =>
|
||||
onChange({ 'mcp-resource-agent-read-enabled': checked })
|
||||
@@ -886,7 +886,7 @@ export default function ToolResourceSelectors({
|
||||
{resourceServers.length === 0 ? (
|
||||
<div className="flex h-20 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.noMCPResourcesAvailable')}
|
||||
{t('pipelines.runner.noMCPResourcesAvailable')}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
@@ -956,9 +956,7 @@ export default function ToolResourceSelectors({
|
||||
<Dialog open={toolsDialogOpen} onOpenChange={setToolsDialogOpen}>
|
||||
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('pipelines.agentRunner.selectTools')}
|
||||
</DialogTitle>
|
||||
<DialogTitle>{t('pipelines.runner.selectTools')}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 space-y-5 overflow-y-auto pr-2">
|
||||
{availableToolGroups.map((sourceGroup) => {
|
||||
@@ -970,16 +968,12 @@ export default function ToolResourceSelectors({
|
||||
</span>
|
||||
{pipelineId && sourceGroup.key === 'mcp' && (
|
||||
<InfoTooltip
|
||||
label={t(
|
||||
'pipelines.agentRunner.mcpToolsScopeTooltip',
|
||||
)}
|
||||
label={t('pipelines.runner.mcpToolsScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
{sourceGroup.key === 'skill' && (
|
||||
<InfoTooltip
|
||||
label={t(
|
||||
'pipelines.agentRunner.skillToolsScopeTooltip',
|
||||
)}
|
||||
label={t('pipelines.runner.skillToolsScopeTooltip')}
|
||||
/>
|
||||
)}
|
||||
<Badge variant="outline" className="ml-auto">
|
||||
@@ -1047,7 +1041,7 @@ export default function ToolResourceSelectors({
|
||||
{availableToolGroups.length === 0 && (
|
||||
<div className="flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-border">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('pipelines.agentRunner.noToolsSelected')}
|
||||
{t('pipelines.runner.noToolsSelected')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -1072,7 +1066,7 @@ export default function ToolResourceSelectors({
|
||||
<DialogContent className="flex max-h-[80vh] max-w-2xl flex-col overflow-hidden">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{t('pipelines.agentRunner.selectKnowledgeBases')}
|
||||
{t('pipelines.runner.selectKnowledgeBases')}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex-1 space-y-2 overflow-y-auto pr-2">
|
||||
|
||||
@@ -58,13 +58,13 @@ import {
|
||||
Copy,
|
||||
} from 'lucide-react';
|
||||
import PipelineExtension from '@/app/home/pipelines/components/pipeline-extensions/PipelineExtension';
|
||||
import AgentRunnerSelect from '@/app/home/agents/components/AgentRunnerSelect';
|
||||
import RunnerSelect from '@/app/home/agents/components/RunnerSelect';
|
||||
import {
|
||||
getErrorMessage,
|
||||
readPendingAgentRunnerInstall,
|
||||
resumePendingAgentRunnerInstall,
|
||||
type InstalledAgentRunner,
|
||||
} from '@/app/home/agents/agent-runner-marketplace';
|
||||
readPendingRunnerInstall,
|
||||
resumePendingRunnerInstall,
|
||||
type InstalledRunner,
|
||||
} from '@/app/home/agents/runner-marketplace';
|
||||
|
||||
interface PipelineFormComponentProps {
|
||||
pipelineId?: string;
|
||||
@@ -229,12 +229,9 @@ const PipelineFormComponent = forwardRef<
|
||||
},
|
||||
});
|
||||
const runnerInstallScope = `pipeline:${pipelineId || 'new'}`;
|
||||
const applyInstalledRunner = useCallback(
|
||||
(installed: InstalledAgentRunner) => {
|
||||
setAIConfigTabSchema(installed.configTab);
|
||||
},
|
||||
[],
|
||||
);
|
||||
const applyInstalledRunner = useCallback((installed: InstalledRunner) => {
|
||||
setAIConfigTabSchema(installed.configTab);
|
||||
}, []);
|
||||
const dynamicFormSystemContext = useMemo(
|
||||
() => ({ pipeline_id: pipelineId }),
|
||||
[pipelineId],
|
||||
@@ -307,12 +304,12 @@ const PipelineFormComponent = forwardRef<
|
||||
if (
|
||||
!metadataLoaded ||
|
||||
!pipelineLoaded ||
|
||||
!readPendingAgentRunnerInstall(runnerInstallScope)
|
||||
!readPendingRunnerInstall(runnerInstallScope)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
void resumePendingAgentRunnerInstall(runnerInstallScope)
|
||||
void resumePendingRunnerInstall(runnerInstallScope)
|
||||
.then((installed) => {
|
||||
if (cancelled || !installed) return;
|
||||
applyInstalledRunner(installed);
|
||||
@@ -575,7 +572,7 @@ const PipelineFormComponent = forwardRef<
|
||||
systemContext={dynamicFormSystemContext}
|
||||
renderItem={({ config, field }) =>
|
||||
config.name === 'id' ? (
|
||||
<AgentRunnerSelect
|
||||
<RunnerSelect
|
||||
options={config.options ?? []}
|
||||
label={extractI18nObject(config.label)}
|
||||
value={String(field.value ?? '')}
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
FileText,
|
||||
PanelTop,
|
||||
Bot,
|
||||
Zap,
|
||||
} from 'lucide-react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
|
||||
@@ -33,8 +32,7 @@ export default function PluginComponentList({
|
||||
KnowledgeEngine: <Book className="w-5 h-5" />,
|
||||
Parser: <FileText className="w-5 h-5" />,
|
||||
Page: <PanelTop className="w-5 h-5" />,
|
||||
AgentRunner: <Bot className="w-5 h-5" />,
|
||||
EventProcessor: <Zap className="w-5 h-5" />,
|
||||
Runner: <Bot className="w-5 h-5" />,
|
||||
};
|
||||
|
||||
const componentKindList = Object.keys(components || {});
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
Book,
|
||||
FileText,
|
||||
Hash,
|
||||
Zap,
|
||||
Wrench,
|
||||
type LucideIcon,
|
||||
} from 'lucide-react';
|
||||
@@ -17,6 +16,5 @@ export const pluginComponentIconMap: Record<string, LucideIcon> = {
|
||||
KnowledgeEngine: Book,
|
||||
Parser: FileText,
|
||||
Page: AppWindow,
|
||||
AgentRunner: Bot,
|
||||
EventProcessor: Zap,
|
||||
Runner: Bot,
|
||||
};
|
||||
|
||||
@@ -18,7 +18,6 @@ import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import {
|
||||
Search,
|
||||
Puzzle,
|
||||
Zap,
|
||||
Server,
|
||||
Sparkles,
|
||||
Wrench,
|
||||
@@ -72,8 +71,7 @@ const MARKET_COMPONENT_VALUES = [
|
||||
'KnowledgeEngine',
|
||||
'Parser',
|
||||
'Page',
|
||||
'AgentRunner',
|
||||
'EventProcessor',
|
||||
'Runner',
|
||||
];
|
||||
|
||||
function getComponentFilterFromQuery(
|
||||
@@ -242,15 +240,10 @@ function MarketPageContent({
|
||||
icon: AppWindow,
|
||||
},
|
||||
{
|
||||
value: 'AgentRunner',
|
||||
label: t('market.componentName.AgentRunner'),
|
||||
value: 'Runner',
|
||||
label: t('market.componentName.Runner'),
|
||||
icon: Bot,
|
||||
},
|
||||
{
|
||||
value: 'EventProcessor',
|
||||
label: t('market.componentName.EventProcessor'),
|
||||
icon: Zap,
|
||||
},
|
||||
];
|
||||
|
||||
// 获取当前排序参数
|
||||
|
||||
@@ -164,7 +164,7 @@ export interface ApiRespPipelines {
|
||||
|
||||
export type AgentKind = 'agent' | 'pipeline' | 'event_processor';
|
||||
|
||||
export interface EventProcessorDescriptor {
|
||||
export interface RunnerDescriptor {
|
||||
id: string;
|
||||
label: Record<string, string>;
|
||||
plugin_author: string;
|
||||
@@ -233,7 +233,7 @@ export interface ApiRespAgent {
|
||||
}
|
||||
|
||||
export interface GetAgentMetadataResponseData {
|
||||
event_processors?: EventProcessorDescriptor[];
|
||||
event_processors?: RunnerDescriptor[];
|
||||
runner_config?: PipelineConfigTab;
|
||||
platform_tools: AgentPlatformTool[];
|
||||
host_tools?: PluginTool[] | null;
|
||||
|
||||
+13
-13
@@ -65,15 +65,15 @@ import { getAdapterDocUrl } from '@/app/infra/entities/adapter-docs';
|
||||
import i18n from 'i18next';
|
||||
import { PluginV4 } from '@/app/infra/entities/plugin';
|
||||
import {
|
||||
AgentRunnerMarketplaceError,
|
||||
RunnerMarketplaceError,
|
||||
getErrorMessage,
|
||||
installMarketplaceAgentRunner,
|
||||
loadAgentRunnerCatalog,
|
||||
installMarketplaceRunner,
|
||||
loadRunnerCatalog as fetchRunnerCatalog,
|
||||
marketplacePluginId,
|
||||
readPendingAgentRunnerInstall,
|
||||
resumePendingAgentRunnerInstall,
|
||||
readPendingRunnerInstall,
|
||||
resumePendingRunnerInstall,
|
||||
runnerPluginPrefix,
|
||||
} from '@/app/home/agents/agent-runner-marketplace';
|
||||
} from '@/app/home/agents/runner-marketplace';
|
||||
import {
|
||||
ensureHttpBotSigningSecret,
|
||||
isRequiredRunnerConfigComplete,
|
||||
@@ -225,11 +225,11 @@ export default function WizardPage() {
|
||||
setIsRunnerCatalogLoading(true);
|
||||
setRunnerCatalogError(false);
|
||||
try {
|
||||
const catalog = await loadAgentRunnerCatalog();
|
||||
const catalog = await fetchRunnerCatalog();
|
||||
setMarketplaceRunners(catalog.marketplaceRunners);
|
||||
setInstalledPluginIds(catalog.installedPluginIds);
|
||||
} catch (error) {
|
||||
console.error('Failed to load AgentRunner catalog', error);
|
||||
console.error('Failed to load Runner catalog', error);
|
||||
setRunnerCatalogError(true);
|
||||
} finally {
|
||||
setIsRunnerCatalogLoading(false);
|
||||
@@ -500,7 +500,7 @@ export default function WizardPage() {
|
||||
setRunnerInstallError(null);
|
||||
|
||||
try {
|
||||
const installed = await installMarketplaceAgentRunner(plugin, {
|
||||
const installed = await installMarketplaceRunner(plugin, {
|
||||
scope: WIZARD_RUNNER_INSTALL_SCOPE,
|
||||
});
|
||||
setAiConfigTab(installed.configTab);
|
||||
@@ -515,7 +515,7 @@ export default function WizardPage() {
|
||||
);
|
||||
} catch (error) {
|
||||
let message = getErrorMessage(error);
|
||||
if (error instanceof AgentRunnerMarketplaceError) {
|
||||
if (error instanceof RunnerMarketplaceError) {
|
||||
const key =
|
||||
error.code === 'version-unavailable'
|
||||
? 'wizard.aiEngine.versionUnavailable'
|
||||
@@ -536,13 +536,13 @@ export default function WizardPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) return;
|
||||
const pending = readPendingAgentRunnerInstall(WIZARD_RUNNER_INSTALL_SCOPE);
|
||||
const pending = readPendingRunnerInstall(WIZARD_RUNNER_INSTALL_SCOPE);
|
||||
if (!pending) return;
|
||||
|
||||
let cancelled = false;
|
||||
setInstallingRunnerPluginId(pending.pluginId);
|
||||
setRunnerInstallError(null);
|
||||
void resumePendingAgentRunnerInstall(WIZARD_RUNNER_INSTALL_SCOPE)
|
||||
void resumePendingRunnerInstall(WIZARD_RUNNER_INSTALL_SCOPE)
|
||||
.then((installed) => {
|
||||
if (cancelled || !installed) return;
|
||||
setAiConfigTab(installed.configTab);
|
||||
@@ -2087,7 +2087,7 @@ function StepAIEngine({
|
||||
|
||||
<div className="flex justify-center">
|
||||
<Button variant="outline" size="sm" asChild>
|
||||
<Link to="/home/extensions?type=plugin&component=AgentRunner">
|
||||
<Link to="/home/extensions?type=plugin&component=Runner">
|
||||
{t('wizard.aiEngine.browseRunners')}
|
||||
<ExternalLink className="size-4" />
|
||||
</Link>
|
||||
|
||||
@@ -901,20 +901,20 @@ const enUS = {
|
||||
deleteAgentAction: 'Delete this Agent',
|
||||
deleteAgentHint:
|
||||
'Once deleted, events bound to it can no longer be executed.',
|
||||
noRunnerMetadata: 'No AgentRunner metadata is currently available.',
|
||||
noRunnerMetadata: 'No Runner metadata is currently available.',
|
||||
runnerStatusLoading: 'Checking runner status',
|
||||
runnerStatusCheckFailed: 'Runner status could not be checked',
|
||||
runnerStatusCheckFailedDescription:
|
||||
'Retry the check. If it still fails, verify the backend and plugin runtime.',
|
||||
noRunnersAvailable: 'No runners are available',
|
||||
noRunnersAvailableDescription:
|
||||
'Install and enable an AgentRunner extension before configuring this Agent.',
|
||||
installedRunners: 'Installed AgentRunners',
|
||||
marketplaceRunners: 'AgentRunner plugins in Marketplace',
|
||||
'Install and enable an Runner extension before configuring this Agent.',
|
||||
installedRunners: 'Installed Runners',
|
||||
marketplaceRunners: 'Runner plugins in Marketplace',
|
||||
viewMarketplace: 'View market',
|
||||
restoringRunnerInstall:
|
||||
'Restoring the AgentRunner plugin installation and waiting for the runner…',
|
||||
noInstalledRunners: 'No AgentRunner extension is installed yet.',
|
||||
'Restoring the Runner plugin installation and waiting for the runner…',
|
||||
noInstalledRunners: 'No Runner extension is installed yet.',
|
||||
installingRunner: 'Installing {{runner}}...',
|
||||
runnerInstallSuccess: '{{runner}} is installed and ready to select',
|
||||
selectedRunnerUnavailable: 'Selected runner is unavailable',
|
||||
@@ -1092,8 +1092,7 @@ const enUS = {
|
||||
KnowledgeEngine: 'Knowledge Engine',
|
||||
Parser: 'Parser',
|
||||
Page: 'Page',
|
||||
AgentRunner: 'Agent Runner',
|
||||
EventProcessor: 'Event Processor',
|
||||
Runner: 'Runner',
|
||||
},
|
||||
uploadLocal: 'Upload Local',
|
||||
debugging: 'Debugging',
|
||||
@@ -1280,8 +1279,7 @@ const enUS = {
|
||||
KnowledgeEngine: 'Knowledge Engine',
|
||||
Parser: 'Parser',
|
||||
Page: 'Page',
|
||||
AgentRunner: 'Agent Runner',
|
||||
EventProcessor: 'Event Processor',
|
||||
Runner: 'Runner',
|
||||
},
|
||||
filterByType: 'Type',
|
||||
allTypes: 'All Types',
|
||||
@@ -1525,14 +1523,14 @@ const enUS = {
|
||||
selectSkills: 'Select Skills',
|
||||
noSkillsAvailable: 'No skills available',
|
||||
mcpServersScopeTooltip:
|
||||
'This only controls which MCP servers are bound to the pipeline. Choose exact MCP tools and resources in AI Feature > Agent Runner.',
|
||||
'This only controls which MCP servers are bound to the pipeline. Choose exact MCP tools and resources in AI Feature > Runner.',
|
||||
enableAllMCPServersTooltip:
|
||||
'When enabled, all configured and enabled MCP servers become candidates for MCP tools and resources in AI Feature.',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: 'Tools',
|
||||
toolsDescription:
|
||||
'Select plugin, MCP, skill, and built-in tools available to this Agent Runner.',
|
||||
'Select plugin, MCP, skill, and built-in tools available to this Runner.',
|
||||
toolsScopeTooltip:
|
||||
'MCP tools only come from MCP servers bound in Extensions. Bind another MCP server there to make its tools selectable here.',
|
||||
enableAllTools: 'Enable all tools',
|
||||
@@ -1550,7 +1548,7 @@ const enUS = {
|
||||
selectTools: 'Select tools',
|
||||
resourcesTitle: 'Resources',
|
||||
resourcesDescription:
|
||||
'Select MCP resources and knowledge bases available to this Agent Runner.',
|
||||
'Select MCP resources and knowledge bases available to this Runner.',
|
||||
knowledgeBases: 'Knowledge bases',
|
||||
mcpResources: 'MCP resources',
|
||||
mcpResourcesScopeTooltip:
|
||||
@@ -2527,11 +2525,11 @@ const enUS = {
|
||||
createExternal: 'Create and Bind',
|
||||
finishWithModel: 'Use Selected Model & Finish',
|
||||
openWorkbench: 'Open Workbench',
|
||||
loadingCatalog: 'Loading AgentRunner extensions...',
|
||||
loadingCatalog: 'Loading Runner extensions...',
|
||||
catalogUnavailable: 'Runner catalog is unavailable',
|
||||
catalogUnavailableDescription:
|
||||
'Installed runners are still available. Retry the catalog or browse Extensions.',
|
||||
noMarketplaceRunners: 'No AgentRunner extensions are published yet',
|
||||
noMarketplaceRunners: 'No Runner extensions are published yet',
|
||||
noMarketplaceRunnersDescription:
|
||||
'Retry after runner extensions are published to the configured Marketplace.',
|
||||
browseRunners: 'Browse Runner Extensions',
|
||||
|
||||
@@ -635,8 +635,7 @@ const esES = {
|
||||
deleteAgentAction: 'Eliminar este Agent',
|
||||
deleteAgentHint:
|
||||
'Una vez eliminado, los eventos vinculados a él ya no podrán ejecutarse.',
|
||||
noRunnerMetadata:
|
||||
'No hay metadatos de AgentRunner disponibles actualmente.',
|
||||
noRunnerMetadata: 'No hay metadatos de Runner disponibles actualmente.',
|
||||
},
|
||||
plugins: {
|
||||
title: 'Extensiones',
|
||||
@@ -734,8 +733,7 @@ const esES = {
|
||||
KnowledgeEngine: 'Motor de conocimiento',
|
||||
Parser: 'Analizador',
|
||||
Page: 'Página',
|
||||
AgentRunner: 'Ejecutor de agentes',
|
||||
EventProcessor: 'Procesador de eventos',
|
||||
Runner: 'Ejecutor de agentes',
|
||||
},
|
||||
uploadLocal: 'Subir local',
|
||||
debugging: 'Depuración',
|
||||
@@ -917,8 +915,7 @@ const esES = {
|
||||
KnowledgeEngine: 'Motor de conocimiento',
|
||||
Parser: 'Analizador',
|
||||
Page: 'Página',
|
||||
AgentRunner: 'Ejecutor de agentes',
|
||||
EventProcessor: 'Procesador de eventos',
|
||||
Runner: 'Ejecutor de agentes',
|
||||
},
|
||||
filterByType: 'Tipo',
|
||||
allTypes: 'Todos los tipos',
|
||||
@@ -1166,14 +1163,14 @@ const esES = {
|
||||
selectSkills: 'Seleccionar skills',
|
||||
noSkillsAvailable: 'No hay skills disponibles',
|
||||
mcpServersScopeTooltip:
|
||||
'Aquí solo se controla qué servidores MCP se vinculan al Pipeline. Las herramientas y recursos MCP concretos se eligen en AI Feature > Agent Runner.',
|
||||
'Aquí solo se controla qué servidores MCP se vinculan al Pipeline. Las herramientas y recursos MCP concretos se eligen en AI Feature > Runner.',
|
||||
enableAllMCPServersTooltip:
|
||||
'Al activarlo, todos los servidores MCP configurados y habilitados serán candidatos para herramientas y recursos MCP en AI Feature.',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: 'Herramientas',
|
||||
toolsDescription:
|
||||
'Selecciona las herramientas de plugins, MCP e integradas disponibles para este Agent Runner.',
|
||||
'Selecciona las herramientas de plugins, MCP e integradas disponibles para este Runner.',
|
||||
toolsScopeTooltip:
|
||||
'Las herramientas MCP solo provienen de servidores MCP vinculados en Extensiones. Vincula allí otro servidor para poder seleccionarlo aquí.',
|
||||
enableAllTools: 'Activar todas las herramientas',
|
||||
@@ -1191,7 +1188,7 @@ const esES = {
|
||||
selectTools: 'Seleccionar herramientas',
|
||||
resourcesTitle: 'Recursos',
|
||||
resourcesDescription:
|
||||
'Selecciona los recursos MCP y bases de conocimiento disponibles para este Agent Runner.',
|
||||
'Selecciona los recursos MCP y bases de conocimiento disponibles para este Runner.',
|
||||
knowledgeBases: 'Bases de conocimiento',
|
||||
mcpResources: 'Recursos MCP',
|
||||
mcpResourcesScopeTooltip:
|
||||
|
||||
@@ -512,7 +512,7 @@ const jaJP = {
|
||||
processor_incompatible:
|
||||
'選択したプロセッサーはこのイベントを処理できません。',
|
||||
processor_not_found: '選択したプロセッサーを利用できません。',
|
||||
runner_failed: 'Agent Runner がイベント処理中に失敗しました。',
|
||||
runner_failed: 'Runner がイベント処理中に失敗しました。',
|
||||
delivery_failed: '処理は完了しましたが、結果の配信に失敗しました。',
|
||||
},
|
||||
dryRunTitle: 'イベントルートを確認',
|
||||
@@ -892,21 +892,20 @@ const jaJP = {
|
||||
dangerZoneDescription: '元に戻せない操作',
|
||||
deleteAgentAction: 'この Agent を削除',
|
||||
deleteAgentHint: '削除すると、紐付けられたイベントは実行できなくなります。',
|
||||
noRunnerMetadata: '現在利用可能な AgentRunner メタデータはありません。',
|
||||
noRunnerMetadata: '現在利用可能な Runner メタデータはありません。',
|
||||
runnerStatusLoading: 'Runner の状態を確認しています',
|
||||
runnerStatusCheckFailed: 'Runner の状態を確認できませんでした',
|
||||
runnerStatusCheckFailedDescription:
|
||||
'再試行してください。失敗が続く場合は、バックエンドとプラグインランタイムを確認してください。',
|
||||
noRunnersAvailable: '利用可能な Runner がありません',
|
||||
noRunnersAvailableDescription:
|
||||
'この Agent を設定する前に AgentRunner 拡張機能をインストールして有効にしてください。',
|
||||
installedRunners: 'インストール済み AgentRunner',
|
||||
marketplaceRunners: 'マーケットプレイスの AgentRunner プラグイン',
|
||||
'この Agent を設定する前に Runner 拡張機能をインストールして有効にしてください。',
|
||||
installedRunners: 'インストール済み Runner',
|
||||
marketplaceRunners: 'マーケットプレイスの Runner プラグイン',
|
||||
viewMarketplace: '市場を見る',
|
||||
restoringRunnerInstall:
|
||||
'AgentRunner プラグインのインストールを復元し、ランナーを待機しています…',
|
||||
noInstalledRunners:
|
||||
'AgentRunner 拡張機能はまだインストールされていません。',
|
||||
'Runner プラグインのインストールを復元し、ランナーを待機しています…',
|
||||
noInstalledRunners: 'Runner 拡張機能はまだインストールされていません。',
|
||||
installingRunner: '{{runner}} をインストールしています...',
|
||||
runnerInstallSuccess:
|
||||
'{{runner}} をインストールしました。インストール済み一覧から選択できます',
|
||||
@@ -1012,8 +1011,7 @@ const jaJP = {
|
||||
KnowledgeEngine: '知識エンジン',
|
||||
Parser: 'パーサー',
|
||||
Page: 'ページ',
|
||||
AgentRunner: 'Agent Runner',
|
||||
EventProcessor: 'イベントプロセッサー',
|
||||
Runner: 'Runner',
|
||||
},
|
||||
uploadLocal: 'ローカルアップロード',
|
||||
debugging: 'デバッグ中',
|
||||
@@ -1201,8 +1199,7 @@ const jaJP = {
|
||||
KnowledgeEngine: '知識エンジン',
|
||||
Parser: 'パーサー',
|
||||
Page: 'ページ',
|
||||
AgentRunner: 'Agent Runner',
|
||||
EventProcessor: 'イベントプロセッサー',
|
||||
Runner: 'Runner',
|
||||
},
|
||||
filterByType: 'タイプ',
|
||||
allTypes: '全部',
|
||||
@@ -1444,14 +1441,14 @@ const jaJP = {
|
||||
selectSkills: 'スキルを選択',
|
||||
noSkillsAvailable: '利用可能なスキルがありません',
|
||||
mcpServersScopeTooltip:
|
||||
'ここでは、このパイプラインに紐付ける MCP サーバーだけを管理します。個別の MCP ツールとリソースは AI 機能の Agent Runner で選択します。',
|
||||
'ここでは、このパイプラインに紐付ける MCP サーバーだけを管理します。個別の MCP ツールとリソースは AI 機能の Runner で選択します。',
|
||||
enableAllMCPServersTooltip:
|
||||
'有効にすると、設定済みで有効なすべての MCP サーバーが AI 機能の MCP ツールとリソース候補になります。',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: 'ツール',
|
||||
toolsDescription:
|
||||
'この Agent Runner が使用できるプラグイン、MCP、組み込みツールを選択します。',
|
||||
'この Runner が使用できるプラグイン、MCP、組み込みツールを選択します。',
|
||||
toolsScopeTooltip:
|
||||
'MCP ツールは拡張機能で紐付けられた MCP サーバーからのみ表示されます。追加するには先に拡張機能でサーバーを紐付けてください。',
|
||||
enableAllTools: 'すべてのツールを有効化',
|
||||
@@ -1469,7 +1466,7 @@ const jaJP = {
|
||||
selectTools: 'ツールを選択',
|
||||
resourcesTitle: 'リソース',
|
||||
resourcesDescription:
|
||||
'この Agent Runner が読み取れる MCP リソースとナレッジベースを選択します。',
|
||||
'この Runner が読み取れる MCP リソースとナレッジベースを選択します。',
|
||||
knowledgeBases: 'ナレッジベース',
|
||||
mcpResources: 'MCP リソース',
|
||||
mcpResourcesScopeTooltip:
|
||||
@@ -2308,11 +2305,11 @@ const jaJP = {
|
||||
createExternal: '作成して関連付ける',
|
||||
finishWithModel: '選択したモデルを使用して完了',
|
||||
openWorkbench: 'ワークベンチを開く',
|
||||
loadingCatalog: 'AgentRunner 拡張機能を読み込んでいます...',
|
||||
loadingCatalog: 'Runner 拡張機能を読み込んでいます...',
|
||||
catalogUnavailable: 'Runner カタログを読み込めません',
|
||||
catalogUnavailableDescription:
|
||||
'インストール済みの Runner は引き続き使用できます。再試行するか、拡張機能を確認してください。',
|
||||
noMarketplaceRunners: 'AgentRunner 拡張機能はまだ公開されていません',
|
||||
noMarketplaceRunners: 'Runner 拡張機能はまだ公開されていません',
|
||||
noMarketplaceRunnersDescription:
|
||||
'設定済みのマーケットプレイスに Runner 拡張機能が公開された後、再試行してください。',
|
||||
browseRunners: 'Runner 拡張機能を見る',
|
||||
|
||||
@@ -630,7 +630,7 @@ const ruRU = {
|
||||
deleteAgentAction: 'Удалить этот Agent',
|
||||
deleteAgentHint:
|
||||
'После удаления события, привязанные к ней, больше не смогут выполняться.',
|
||||
noRunnerMetadata: 'Метаданные AgentRunner в данный момент недоступны.',
|
||||
noRunnerMetadata: 'Метаданные Runner в данный момент недоступны.',
|
||||
},
|
||||
plugins: {
|
||||
title: 'Расширения',
|
||||
@@ -727,8 +727,7 @@ const ruRU = {
|
||||
KnowledgeEngine: 'Движок знаний',
|
||||
Parser: 'Парсер',
|
||||
Page: 'Страница',
|
||||
AgentRunner: 'Исполнитель агента',
|
||||
EventProcessor: 'Обработчик событий',
|
||||
Runner: 'Исполнитель агента',
|
||||
},
|
||||
uploadLocal: 'Загрузить локально',
|
||||
debugging: 'Отладка',
|
||||
@@ -909,8 +908,7 @@ const ruRU = {
|
||||
KnowledgeEngine: 'Движок знаний',
|
||||
Parser: 'Парсер',
|
||||
Page: 'Страница',
|
||||
AgentRunner: 'Исполнитель агента',
|
||||
EventProcessor: 'Обработчик событий',
|
||||
Runner: 'Исполнитель агента',
|
||||
},
|
||||
filterByType: 'Тип',
|
||||
allTypes: 'Все типы',
|
||||
@@ -1153,14 +1151,14 @@ const ruRU = {
|
||||
selectSkills: 'Выбрать навыки',
|
||||
noSkillsAvailable: 'Нет доступных навыков',
|
||||
mcpServersScopeTooltip:
|
||||
'Здесь задаётся только привязка MCP-серверов к конвейеру. Конкретные MCP-инструменты и ресурсы выбираются в AI Feature > Agent Runner.',
|
||||
'Здесь задаётся только привязка MCP-серверов к конвейеру. Конкретные MCP-инструменты и ресурсы выбираются в AI Feature > Runner.',
|
||||
enableAllMCPServersTooltip:
|
||||
'Если включено, все настроенные и включённые MCP-серверы станут кандидатами для инструментов и ресурсов MCP в AI Feature.',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: 'Инструменты',
|
||||
toolsDescription:
|
||||
'Выберите инструменты плагинов, MCP и встроенные инструменты для этого Agent Runner.',
|
||||
'Выберите инструменты плагинов, MCP и встроенные инструменты для этого Runner.',
|
||||
toolsScopeTooltip:
|
||||
'MCP-инструменты берутся только из MCP-серверов, привязанных в Расширениях. Чтобы добавить источник, сначала привяжите там сервер.',
|
||||
enableAllTools: 'Включить все инструменты',
|
||||
@@ -1178,7 +1176,7 @@ const ruRU = {
|
||||
selectTools: 'Выбрать инструменты',
|
||||
resourcesTitle: 'Ресурсы',
|
||||
resourcesDescription:
|
||||
'Выберите MCP-ресурсы и базы знаний для этого Agent Runner.',
|
||||
'Выберите MCP-ресурсы и базы знаний для этого Runner.',
|
||||
knowledgeBases: 'Базы знаний',
|
||||
mcpResources: 'MCP-ресурсы',
|
||||
mcpResourcesScopeTooltip:
|
||||
|
||||
@@ -615,7 +615,7 @@ const thTH = {
|
||||
deleteAgentAction: 'ลบ Agent นี้',
|
||||
deleteAgentHint:
|
||||
'เมื่อลบแล้ว เหตุการณ์ที่เชื่อมกับมันจะไม่สามารถดำเนินการต่อได้',
|
||||
noRunnerMetadata: 'ขณะนี้ไม่มีข้อมูลเมตา AgentRunner ที่พร้อมใช้งาน',
|
||||
noRunnerMetadata: 'ขณะนี้ไม่มีข้อมูลเมตา Runner ที่พร้อมใช้งาน',
|
||||
},
|
||||
plugins: {
|
||||
title: 'ส่วนขยาย',
|
||||
@@ -709,8 +709,7 @@ const thTH = {
|
||||
KnowledgeEngine: 'เครื่องมือความรู้',
|
||||
Parser: 'ตัวแยกวิเคราะห์',
|
||||
Page: 'หน้า',
|
||||
AgentRunner: 'Agent Runner',
|
||||
EventProcessor: 'ตัวประมวลผลเหตุการณ์',
|
||||
Runner: 'Runner',
|
||||
},
|
||||
uploadLocal: 'อัปโหลดจากเครื่อง',
|
||||
debugging: 'ดีบัก',
|
||||
@@ -887,8 +886,7 @@ const thTH = {
|
||||
KnowledgeEngine: 'เครื่องมือความรู้',
|
||||
Parser: 'ตัวแยกวิเคราะห์',
|
||||
Page: 'หน้า',
|
||||
AgentRunner: 'Agent Runner',
|
||||
EventProcessor: 'ตัวประมวลผลเหตุการณ์',
|
||||
Runner: 'Runner',
|
||||
},
|
||||
filterByType: 'ประเภท',
|
||||
allTypes: 'ทุกประเภท',
|
||||
@@ -1127,14 +1125,14 @@ const thTH = {
|
||||
selectSkills: 'เลือกสกิล',
|
||||
noSkillsAvailable: 'ไม่มีสกิลที่พร้อมใช้งาน',
|
||||
mcpServersScopeTooltip:
|
||||
'ส่วนนี้ใช้ควบคุมว่า Pipeline ผูกกับเซิร์ฟเวอร์ MCP ใดเท่านั้น ส่วนเครื่องมือและทรัพยากร MCP รายตัวให้เลือกใน AI Feature > Agent Runner',
|
||||
'ส่วนนี้ใช้ควบคุมว่า Pipeline ผูกกับเซิร์ฟเวอร์ MCP ใดเท่านั้น ส่วนเครื่องมือและทรัพยากร MCP รายตัวให้เลือกใน AI Feature > Runner',
|
||||
enableAllMCPServersTooltip:
|
||||
'เมื่อเปิดใช้ เซิร์ฟเวอร์ MCP ที่ตั้งค่าและเปิดใช้งานทั้งหมดจะเป็นตัวเลือกสำหรับเครื่องมือและทรัพยากร MCP ใน AI Feature',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: 'เครื่องมือ',
|
||||
toolsDescription:
|
||||
'เลือกเครื่องมือจากปลั๊กอิน MCP และเครื่องมือในตัวสำหรับ Agent Runner นี้',
|
||||
'เลือกเครื่องมือจากปลั๊กอิน MCP และเครื่องมือในตัวสำหรับ Runner นี้',
|
||||
toolsScopeTooltip:
|
||||
'เครื่องมือ MCP จะแสดงจากเซิร์ฟเวอร์ MCP ที่ผูกไว้ในส่วนขยายเท่านั้น หากต้องการเพิ่มแหล่งเครื่องมือ ให้ไปผูกเซิร์ฟเวอร์ที่นั่นก่อน',
|
||||
enableAllTools: 'เปิดใช้เครื่องมือทั้งหมด',
|
||||
@@ -1151,8 +1149,7 @@ const thTH = {
|
||||
'เครื่องมือสกิลจะแสดงเมื่อบริการสกิลของ LangBot และแบ็กเอนด์แซนด์บ็อกซ์ Box พร้อมใช้งาน เพื่อให้ Agent เปิดใช้หรือลงทะเบียนสกิลได้',
|
||||
selectTools: 'เลือกเครื่องมือ',
|
||||
resourcesTitle: 'ทรัพยากร',
|
||||
resourcesDescription:
|
||||
'เลือกทรัพยากร MCP และคลังความรู้สำหรับ Agent Runner นี้',
|
||||
resourcesDescription: 'เลือกทรัพยากร MCP และคลังความรู้สำหรับ Runner นี้',
|
||||
knowledgeBases: 'คลังความรู้',
|
||||
mcpResources: 'ทรัพยากร MCP',
|
||||
mcpResourcesScopeTooltip:
|
||||
|
||||
@@ -625,7 +625,7 @@ const viVN = {
|
||||
deleteAgentAction: 'Xóa Agent này',
|
||||
deleteAgentHint:
|
||||
'Sau khi xóa, các sự kiện đã gắn vào nó sẽ không thể thực thi được nữa.',
|
||||
noRunnerMetadata: 'Hiện chưa có siêu dữ liệu AgentRunner khả dụng.',
|
||||
noRunnerMetadata: 'Hiện chưa có siêu dữ liệu Runner khả dụng.',
|
||||
},
|
||||
plugins: {
|
||||
title: 'Tiện ích mở rộng',
|
||||
@@ -720,8 +720,7 @@ const viVN = {
|
||||
KnowledgeEngine: 'Công cụ tri thức',
|
||||
Parser: 'Trình phân tích',
|
||||
Page: 'Trang',
|
||||
AgentRunner: 'Trình chạy tác tử',
|
||||
EventProcessor: 'Bộ xử lý sự kiện',
|
||||
Runner: 'Trình chạy tác tử',
|
||||
},
|
||||
uploadLocal: 'Tải lên cục bộ',
|
||||
debugging: 'Gỡ lỗi',
|
||||
@@ -902,8 +901,7 @@ const viVN = {
|
||||
KnowledgeEngine: 'Công cụ tri thức',
|
||||
Parser: 'Trình phân tích',
|
||||
Page: 'Trang',
|
||||
AgentRunner: 'Trình chạy tác tử',
|
||||
EventProcessor: 'Bộ xử lý sự kiện',
|
||||
Runner: 'Trình chạy tác tử',
|
||||
},
|
||||
filterByType: 'Loại',
|
||||
allTypes: 'Tất cả loại',
|
||||
@@ -1144,14 +1142,14 @@ const viVN = {
|
||||
selectSkills: 'Chọn kỹ năng',
|
||||
noSkillsAvailable: 'Không có kỹ năng khả dụng',
|
||||
mcpServersScopeTooltip:
|
||||
'Tại đây chỉ kiểm soát máy chủ MCP được liên kết với Pipeline. Công cụ và tài nguyên MCP cụ thể được chọn trong AI Feature > Agent Runner.',
|
||||
'Tại đây chỉ kiểm soát máy chủ MCP được liên kết với Pipeline. Công cụ và tài nguyên MCP cụ thể được chọn trong AI Feature > Runner.',
|
||||
enableAllMCPServersTooltip:
|
||||
'Khi bật, mọi máy chủ MCP đã cấu hình và bật sẽ trở thành ứng viên cho công cụ và tài nguyên MCP trong AI Feature.',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: 'Công cụ',
|
||||
toolsDescription:
|
||||
'Chọn công cụ plugin, MCP và công cụ tích hợp sẵn cho Agent Runner này.',
|
||||
'Chọn công cụ plugin, MCP và công cụ tích hợp sẵn cho Runner này.',
|
||||
toolsScopeTooltip:
|
||||
'Công cụ MCP chỉ đến từ máy chủ MCP đã liên kết trong Tiện ích mở rộng. Hãy liên kết máy chủ tại đó trước nếu muốn chọn thêm tại đây.',
|
||||
enableAllTools: 'Bật tất cả công cụ',
|
||||
@@ -1169,7 +1167,7 @@ const viVN = {
|
||||
selectTools: 'Chọn công cụ',
|
||||
resourcesTitle: 'Tài nguyên',
|
||||
resourcesDescription:
|
||||
'Chọn tài nguyên MCP và kho tri thức cho Agent Runner này.',
|
||||
'Chọn tài nguyên MCP và kho tri thức cho Runner này.',
|
||||
knowledgeBases: 'Kho tri thức',
|
||||
mcpResources: 'Tài nguyên MCP',
|
||||
mcpResourcesScopeTooltip:
|
||||
|
||||
@@ -478,7 +478,7 @@ const zhHans = {
|
||||
route_not_found: '没有路由命中此事件。',
|
||||
processor_incompatible: '所选处理器无法处理此事件。',
|
||||
processor_not_found: '所选处理器不可用。',
|
||||
runner_failed: 'Agent Runner 处理事件时失败。',
|
||||
runner_failed: 'Runner 处理事件时失败。',
|
||||
delivery_failed: '处理器已完成,但结果投递失败。',
|
||||
},
|
||||
dryRunTitle: '检查事件路由',
|
||||
@@ -859,19 +859,19 @@ const zhHans = {
|
||||
dangerZoneDescription: '不可逆的操作',
|
||||
deleteAgentAction: '删除此 Agent',
|
||||
deleteAgentHint: '删除后,绑定到它的事件将无法继续执行。',
|
||||
noRunnerMetadata: '当前没有可用的 AgentRunner 元数据。',
|
||||
noRunnerMetadata: '当前没有可用的 Runner 元数据。',
|
||||
runnerStatusLoading: '正在检查运行器状态',
|
||||
runnerStatusCheckFailed: '无法检查运行器状态',
|
||||
runnerStatusCheckFailedDescription:
|
||||
'请重试检查;如果仍然失败,请确认后端和插件运行时正常。',
|
||||
noRunnersAvailable: '没有可用的运行器',
|
||||
noRunnersAvailableDescription:
|
||||
'请先安装并启用 AgentRunner 扩展,再配置此 Agent。',
|
||||
installedRunners: '已安装的 AgentRunner',
|
||||
marketplaceRunners: '插件市场中的 AgentRunner 插件',
|
||||
'请先安装并启用 Runner 扩展,再配置此 Agent。',
|
||||
installedRunners: '已安装的 Runner',
|
||||
marketplaceRunners: '插件市场中的 Runner 插件',
|
||||
viewMarketplace: '查看市场',
|
||||
restoringRunnerInstall: '正在恢复 AgentRunner 插件安装并等待运行器就绪…',
|
||||
noInstalledRunners: '尚未安装任何 AgentRunner 扩展。',
|
||||
restoringRunnerInstall: '正在恢复 Runner 插件安装并等待运行器就绪…',
|
||||
noInstalledRunners: '尚未安装任何 Runner 扩展。',
|
||||
installingRunner: '正在安装 {{runner}}...',
|
||||
runnerInstallSuccess: '{{runner}} 已安装,可从已安装列表中选择',
|
||||
selectedRunnerUnavailable: '所选运行器不可用',
|
||||
@@ -1034,8 +1034,7 @@ const zhHans = {
|
||||
KnowledgeEngine: '知识引擎',
|
||||
Parser: '解析器',
|
||||
Page: '页面',
|
||||
AgentRunner: 'Agent 运行器',
|
||||
EventProcessor: '事件处理器',
|
||||
Runner: 'Agent 运行器',
|
||||
},
|
||||
uploadLocal: '本地上传',
|
||||
debugging: '调试中',
|
||||
@@ -1216,8 +1215,7 @@ const zhHans = {
|
||||
KnowledgeEngine: '知识引擎',
|
||||
Parser: '解析器',
|
||||
Page: '页面',
|
||||
AgentRunner: 'Agent 运行器',
|
||||
EventProcessor: '事件处理器',
|
||||
Runner: 'Agent 运行器',
|
||||
},
|
||||
filterByType: '类型',
|
||||
allTypes: '全部类型',
|
||||
@@ -1455,10 +1453,9 @@ const zhHans = {
|
||||
enableAllMCPServersTooltip:
|
||||
'开启后,所有已配置且启用的 MCP 服务器都会进入 AI 能力里的 MCP 工具和资源候选范围。',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: '工具',
|
||||
toolsDescription:
|
||||
'选择当前 AgentRunner 可以调用的插件、MCP、技能和内置工具。',
|
||||
toolsDescription: '选择当前 Runner 可以调用的插件、MCP、技能和内置工具。',
|
||||
toolsScopeTooltip:
|
||||
'MCP 工具只会从扩展集成中已绑定的 MCP 服务器里出现;如需增加 MCP 工具来源,请先到扩展集成绑定对应服务器。',
|
||||
enableAllTools: '启用所有工具',
|
||||
@@ -1475,8 +1472,7 @@ const zhHans = {
|
||||
'技能工具会在 LangBot 技能服务和 Box 沙箱后端可用时出现,用于让 Agent 激活或注册技能。',
|
||||
selectTools: '选择工具',
|
||||
resourcesTitle: '资源',
|
||||
resourcesDescription:
|
||||
'选择当前 AgentRunner 可以读取的 MCP 资源和知识库。',
|
||||
resourcesDescription: '选择当前 Runner 可以读取的 MCP 资源和知识库。',
|
||||
knowledgeBases: '知识库',
|
||||
mcpResources: 'MCP 资源',
|
||||
mcpResourcesScopeTooltip:
|
||||
@@ -2391,11 +2387,11 @@ const zhHans = {
|
||||
createExternal: '创建并绑定',
|
||||
finishWithModel: '使用所选模型并完成',
|
||||
openWorkbench: '进入工作台',
|
||||
loadingCatalog: '正在加载 AgentRunner 扩展...',
|
||||
loadingCatalog: '正在加载 Runner 扩展...',
|
||||
catalogUnavailable: '无法加载运行器目录',
|
||||
catalogUnavailableDescription:
|
||||
'已安装的运行器仍可使用。你可以重试,或前往扩展页面查看。',
|
||||
noMarketplaceRunners: '市场暂未发布 AgentRunner 扩展',
|
||||
noMarketplaceRunners: '市场暂未发布 Runner 扩展',
|
||||
noMarketplaceRunnersDescription:
|
||||
'请在运行器扩展发布到当前配置的市场后重试。',
|
||||
browseRunners: '浏览运行器扩展',
|
||||
|
||||
@@ -595,7 +595,7 @@ const zhHant = {
|
||||
dangerZoneDescription: '不可逆的操作',
|
||||
deleteAgentAction: '刪除此 Agent',
|
||||
deleteAgentHint: '刪除後,綁定到它的事件將無法繼續執行。',
|
||||
noRunnerMetadata: '目前沒有可用的 AgentRunner 中繼資料。',
|
||||
noRunnerMetadata: '目前沒有可用的 Runner 中繼資料。',
|
||||
},
|
||||
plugins: {
|
||||
title: '外掛擴展',
|
||||
@@ -686,8 +686,7 @@ const zhHant = {
|
||||
KnowledgeEngine: '知識引擎',
|
||||
Parser: '解析器',
|
||||
Page: '擴展頁',
|
||||
AgentRunner: 'Agent 執行器',
|
||||
EventProcessor: '事件處理器',
|
||||
Runner: 'Agent 執行器',
|
||||
},
|
||||
uploadLocal: '本地上傳',
|
||||
debugging: '調試中',
|
||||
@@ -861,8 +860,7 @@ const zhHant = {
|
||||
KnowledgeEngine: '知識引擎',
|
||||
Parser: '解析器',
|
||||
Page: '擴展頁',
|
||||
AgentRunner: 'Agent 執行器',
|
||||
EventProcessor: '事件處理器',
|
||||
Runner: 'Agent 執行器',
|
||||
},
|
||||
filterByType: '類型',
|
||||
allTypes: '全部類型',
|
||||
@@ -1098,7 +1096,7 @@ const zhHant = {
|
||||
enableAllMCPServersTooltip:
|
||||
'啟用後,所有已配置且啟用的 MCP 伺服器都會進入 AI 能力中的 MCP 工具和資源候選範圍。',
|
||||
},
|
||||
agentRunner: {
|
||||
runner: {
|
||||
toolsTitle: '工具',
|
||||
toolsDescription: '選擇此內建 Agent 可以調用的插件、MCP 和內建工具。',
|
||||
toolsScopeTooltip:
|
||||
|
||||
@@ -842,7 +842,7 @@ test.describe('pipeline advanced flows', () => {
|
||||
});
|
||||
|
||||
test.describe('agent runner resource selectors', () => {
|
||||
test('installs an AgentRunner from the grouped empty selector and refreshes it', async ({
|
||||
test('installs an Runner from the grouped empty selector and refreshes it', async ({
|
||||
page,
|
||||
}) => {
|
||||
await installLangBotApiMocks(page, { authenticated: true });
|
||||
@@ -1011,7 +1011,7 @@ test.describe('agent runner resource selectors', () => {
|
||||
tags: [],
|
||||
install_count: 12,
|
||||
latest_version: '1.0.0',
|
||||
components: { AgentRunner: 1 },
|
||||
components: { Runner: 1 },
|
||||
status: 'live',
|
||||
type: 'plugin',
|
||||
created_at: '2026-01-01T00:00:00Z',
|
||||
@@ -1030,11 +1030,11 @@ test.describe('agent runner resource selectors', () => {
|
||||
const runnerSelect = page.getByRole('combobox', { name: 'Runner' });
|
||||
const triggerBox = await runnerSelect.boundingBox();
|
||||
await runnerSelect.click();
|
||||
await expect(page.getByText('Installed AgentRunners')).toBeVisible();
|
||||
await expect(page.getByText('Installed Runners')).toBeVisible();
|
||||
await expect(
|
||||
page.getByText('No AgentRunner extension is installed yet.'),
|
||||
page.getByText('No Runner extension is installed yet.'),
|
||||
).toBeVisible();
|
||||
await expect(page.getByText('AgentRunner Marketplace')).toBeVisible();
|
||||
await expect(page.getByText('Runner Marketplace')).toBeVisible();
|
||||
const selectorPopup = page.locator('[data-slot="select-content"]');
|
||||
await expect(
|
||||
selectorPopup.getByText('Runner used by the grouped selector test.', {
|
||||
@@ -1050,7 +1050,7 @@ test.describe('agent runner resource selectors', () => {
|
||||
await expect
|
||||
.poll(() => marketplaceSearchBody)
|
||||
.toMatchObject({
|
||||
component_filter: 'AgentRunner',
|
||||
component_filter: 'Runner',
|
||||
type_filter: 'plugin',
|
||||
});
|
||||
|
||||
@@ -1074,7 +1074,7 @@ test.describe('agent runner resource selectors', () => {
|
||||
).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('uses the compact AgentRunner marketplace selector in pipeline AI settings', async ({
|
||||
test('uses the compact Runner marketplace selector in pipeline AI settings', async ({
|
||||
page,
|
||||
}) => {
|
||||
await installLangBotApiMocks(page, { authenticated: true });
|
||||
@@ -1112,7 +1112,7 @@ test.describe('agent runner resource selectors', () => {
|
||||
tags: [],
|
||||
install_count: 9,
|
||||
latest_version: '1.0.0',
|
||||
components: { AgentRunner: 1 },
|
||||
components: { Runner: 1 },
|
||||
status: 'live',
|
||||
type: 'plugin',
|
||||
created_at: '2026-01-01T00:00:00Z',
|
||||
@@ -1129,8 +1129,8 @@ test.describe('agent runner resource selectors', () => {
|
||||
|
||||
const runnerSelect = page.getByRole('combobox', { name: 'Runner' });
|
||||
await runnerSelect.click();
|
||||
await expect(page.getByText('Installed AgentRunners')).toBeVisible();
|
||||
await expect(page.getByText('AgentRunner Marketplace')).toBeVisible();
|
||||
await expect(page.getByText('Installed Runners')).toBeVisible();
|
||||
await expect(page.getByText('Runner Marketplace')).toBeVisible();
|
||||
await expect(
|
||||
page
|
||||
.locator('[data-slot="select-content"]')
|
||||
|
||||
@@ -5,7 +5,7 @@ test('create first, select a plugin in the header, debug beside scrollable logs'
|
||||
page,
|
||||
}) => {
|
||||
await installLangBotApiMocks(page, { authenticated: true });
|
||||
const ref = 'event_processor:qa/welcome/default';
|
||||
const ref = 'plugin:qa/welcome/default';
|
||||
const processor = {
|
||||
uuid: 'processor-qa',
|
||||
kind: 'event_processor',
|
||||
|
||||
@@ -344,7 +344,7 @@ test.describe('wizard and QR platform regressions', () => {
|
||||
await expect.poll(() => inboundTestCount).toBe(1);
|
||||
});
|
||||
|
||||
test('blocks deployment until required AgentRunner configuration is real', async ({
|
||||
test('blocks deployment until required Runner configuration is real', async ({
|
||||
page,
|
||||
}) => {
|
||||
await installLangBotApiMocks(page, {
|
||||
|
||||
@@ -17,8 +17,8 @@ const kbFormSource = readSource(
|
||||
const selectSource = readSource(
|
||||
'src/app/home/knowledge/components/kb-form/KnowledgeEngineSelect.tsx',
|
||||
);
|
||||
const agentRunnerSelectSource = readSource(
|
||||
'src/app/home/agents/components/AgentRunnerSelect.tsx',
|
||||
const runnerSelectSource = readSource(
|
||||
'src/app/home/agents/components/RunnerSelect.tsx',
|
||||
);
|
||||
const agentFormSource = readSource(
|
||||
'src/app/home/agents/components/AgentFormComponent.tsx',
|
||||
@@ -26,8 +26,8 @@ const agentFormSource = readSource(
|
||||
const pipelineFormSource = readSource(
|
||||
'src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx',
|
||||
);
|
||||
const agentRunnerMarketplaceSource = readSource(
|
||||
'src/app/home/agents/agent-runner-marketplace.ts',
|
||||
const runnerMarketplaceSource = readSource(
|
||||
'src/app/home/agents/runner-marketplace.ts',
|
||||
);
|
||||
const marketplaceInstallButtonSource = readSource(
|
||||
'src/app/home/components/MarketplaceInstallButton.tsx',
|
||||
@@ -89,7 +89,7 @@ test('tracks plugin upgrades as recoverable multistep async tasks', () => {
|
||||
});
|
||||
|
||||
test('keeps marketplace install actions on one fixed vertical column', () => {
|
||||
for (const source of [selectSource, agentRunnerSelectSource]) {
|
||||
for (const source of [selectSource, runnerSelectSource]) {
|
||||
assert.match(source, /grid-cols-\[1\.75rem_minmax\(0,1fr\)_4rem\]/);
|
||||
assert.match(source, /<MarketplaceInstallButton/);
|
||||
}
|
||||
@@ -112,7 +112,7 @@ test('uses a compact selected engine layout and clears stale required errors', (
|
||||
});
|
||||
|
||||
test('installs marketplace components from inline progress buttons without selecting them', () => {
|
||||
for (const source of [selectSource, agentRunnerSelectSource]) {
|
||||
for (const source of [selectSource, runnerSelectSource]) {
|
||||
assert.match(source, /const handleInstall = useCallback/);
|
||||
assert.match(source, /installing=\{activePluginId === pluginId\}/);
|
||||
assert.match(source, /progress=\{installProgress\}/);
|
||||
@@ -134,18 +134,12 @@ test('installs marketplace components from inline progress buttons without selec
|
||||
}
|
||||
});
|
||||
|
||||
test('shows plugin descriptions for installed AgentRunner entries', () => {
|
||||
assert.match(agentRunnerMarketplaceSource, /installedPluginDescriptions/);
|
||||
assert.match(agentRunnerMarketplaceSource, /metadata\.description/);
|
||||
assert.match(agentRunnerSelectSource, /installedRunnerDescription\(/);
|
||||
assert.match(
|
||||
agentRunnerSelectSource,
|
||||
/function InstalledRunnerOptionContent/,
|
||||
);
|
||||
assert.match(agentRunnerSelectSource, /marketplacePlugin\?\.description/);
|
||||
assert.match(
|
||||
agentRunnerSelectSource,
|
||||
/grid-cols-\[1\.75rem_minmax\(0,1fr\)\]/,
|
||||
);
|
||||
assert.doesNotMatch(agentRunnerSelectSource, /description=\{option\.name\}/);
|
||||
test('shows plugin descriptions for installed Runner entries', () => {
|
||||
assert.match(runnerMarketplaceSource, /installedPluginDescriptions/);
|
||||
assert.match(runnerMarketplaceSource, /metadata\.description/);
|
||||
assert.match(runnerSelectSource, /installedRunnerDescription\(/);
|
||||
assert.match(runnerSelectSource, /function InstalledRunnerOptionContent/);
|
||||
assert.match(runnerSelectSource, /marketplacePlugin\?\.description/);
|
||||
assert.match(runnerSelectSource, /grid-cols-\[1\.75rem_minmax\(0,1fr\)\]/);
|
||||
assert.doesNotMatch(runnerSelectSource, /description=\{option\.name\}/);
|
||||
});
|
||||
|
||||
@@ -40,10 +40,10 @@ test('hides the entire workspace switcher slot for a singleton local workspace',
|
||||
);
|
||||
});
|
||||
|
||||
test('keeps bot cards at the same vertical spacing as knowledge-base cards', () => {
|
||||
test('keeps compact bot forms vertically spaced while detail forms support columns', () => {
|
||||
assert.match(
|
||||
botFormSource,
|
||||
/<fieldset\s+className="[^"]*\bspace-y-6\b[^"]*"\s+disabled=\{isLoading\}/,
|
||||
/<fieldset[\s\S]*?'space-y-6'[\s\S]*?disabled=\{isLoading\}/,
|
||||
);
|
||||
assert.match(kbFormSource, /<form[\s\S]*?className="space-y-6"/);
|
||||
});
|
||||
|
||||
@@ -31,7 +31,8 @@ test('agent and pipeline details share the split processor workbench', () => {
|
||||
/lg:grid-cols-\[minmax\(20rem,0\.72fr\)_minmax\(0,1\.28fr\)\]/,
|
||||
);
|
||||
assert.ok(
|
||||
workbench.indexOf('{debugContent}') < workbench.indexOf('{configContent}'),
|
||||
workbench.indexOf('{debugContent}') <
|
||||
workbench.lastIndexOf('{configPanel}'),
|
||||
);
|
||||
assert.match(agentDetail, /<ProcessorDetailWorkbench/);
|
||||
assert.match(agentDetail, /debugContent=/);
|
||||
|
||||
@@ -12,7 +12,7 @@ const wizardSource = fs.readFileSync(
|
||||
const runnerMarketplaceSource = fs.readFileSync(
|
||||
path.resolve(
|
||||
currentDirectory,
|
||||
'../../src/app/home/agents/agent-runner-marketplace.ts',
|
||||
'../../src/app/home/agents/runner-marketplace.ts',
|
||||
),
|
||||
'utf8',
|
||||
);
|
||||
@@ -66,25 +66,25 @@ test('binds every message-reply bot to its provisional pipeline before verificat
|
||||
);
|
||||
});
|
||||
|
||||
test('keeps the 4.11 AgentRunner marketplace installation flow', () => {
|
||||
assert.match(wizardSource, /loadAgentRunnerCatalog\(\)/);
|
||||
test('keeps the 4.11 Runner marketplace installation flow', () => {
|
||||
assert.match(wizardSource, /loadRunnerCatalog\(\)/);
|
||||
assert.match(
|
||||
wizardSource,
|
||||
/installMarketplaceAgentRunner\(plugin, \{[\s\S]*?scope: WIZARD_RUNNER_INSTALL_SCOPE/,
|
||||
/installMarketplaceRunner\(plugin, \{[\s\S]*?scope: WIZARD_RUNNER_INSTALL_SCOPE/,
|
||||
);
|
||||
assert.match(wizardSource, /resumePendingAgentRunnerInstall\(/);
|
||||
assert.match(wizardSource, /resumePendingRunnerInstall\(/);
|
||||
assert.match(runnerMarketplaceSource, /RUNNER_COMPONENT_FILTER = 'Runner'/);
|
||||
assert.match(runnerMarketplaceSource, /installPluginFromMarketplace\(/);
|
||||
assert.match(
|
||||
runnerMarketplaceSource,
|
||||
/RUNNER_COMPONENT_FILTER = 'AgentRunner'/,
|
||||
/const prefix = runnerPluginPrefix\(\{/,
|
||||
);
|
||||
assert.match(runnerMarketplaceSource, /installPluginFromMarketplace\(/);
|
||||
assert.match(runnerMarketplaceSource, /const prefix = runnerPluginPrefix\(\{/);
|
||||
assert.match(runnerMarketplaceSource, /option\.name\.startsWith\(prefix\)/);
|
||||
assert.match(runnerMarketplaceSource, /registrationDeadline/);
|
||||
assert.match(runnerMarketplaceSource, /sessionStorage\.setItem\(/);
|
||||
});
|
||||
|
||||
test('requires the selected AgentRunner mandatory configuration before finishing', () => {
|
||||
test('requires the selected Runner mandatory configuration before finishing', () => {
|
||||
assert.match(
|
||||
wizardSource,
|
||||
/isRequiredRunnerConfigComplete\(selectedRunnerConfigItems, runnerConfig\)/,
|
||||
|
||||
Reference in New Issue
Block a user