mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-12 13:50:59 +00:00
fix: run lint
This commit is contained in:
@@ -115,7 +115,6 @@ export default function BotForm({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBotFormValues();
|
setBotFormValues();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function setBotFormValues() {
|
function setBotFormValues() {
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ export default function HomeSidebar({
|
|||||||
console.error('Failed to fetch GitHub star count:', error);
|
console.error('Failed to fetch GitHub star count:', error);
|
||||||
});
|
});
|
||||||
return () => console.log('sidebar.unmounted');
|
return () => console.log('sidebar.unmounted');
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function handleChildClick(child: SidebarChildVO) {
|
function handleChildClick(child: SidebarChildVO) {
|
||||||
|
|||||||
@@ -2,28 +2,9 @@
|
|||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import styles from '@/app/home/plugins/plugins.module.css';
|
import styles from '@/app/home/plugins/plugins.module.css';
|
||||||
// import { MCPMarketCardVO } from '@/app/home/plugins/mcp-market/mcp-market-card/MCPMarketCardVO';
|
|
||||||
// import MCPMarketCardComponent from '@/app/home/plugins/mcp-market/mcp-market-card/MCPMarketCardComponent';
|
|
||||||
import MCPCardComponent from '@/app/home/plugins/mcp/mcp-card/MCPCardComponent';
|
import MCPCardComponent from '@/app/home/plugins/mcp/mcp-card/MCPCardComponent';
|
||||||
import { MCPCardVO } from '@/app/home/plugins/mcp/MCPCardVO';
|
import { MCPCardVO } from '@/app/home/plugins/mcp/MCPCardVO';
|
||||||
// import { spaceClient } from '@/app/infra/http/HttpClient';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
// import { Input } from '@/components/ui/input';
|
|
||||||
// import {
|
|
||||||
// Pagination,
|
|
||||||
// PaginationContent,
|
|
||||||
// PaginationItem,
|
|
||||||
// PaginationLink,
|
|
||||||
// PaginationNext,
|
|
||||||
// PaginationPrevious,
|
|
||||||
// } from '@/components/ui/pagination';
|
|
||||||
// import {
|
|
||||||
// Select,
|
|
||||||
// SelectContent,
|
|
||||||
// SelectItem,
|
|
||||||
// SelectTrigger,
|
|
||||||
// SelectValue,
|
|
||||||
// } from '@/components/ui/select';
|
|
||||||
|
|
||||||
import { httpClient } from '@/app/infra/http/HttpClient';
|
import { httpClient } from '@/app/infra/http/HttpClient';
|
||||||
|
|
||||||
@@ -36,33 +17,20 @@ export default function MCPMarketComponent({
|
|||||||
toolsCountCache?: Record<string, number>;
|
toolsCountCache?: Record<string, number>;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
// const [marketServerList, setMarketServerList] = useState<MCPMarketCardVO[]>(
|
|
||||||
// [],
|
|
||||||
// );
|
|
||||||
const [installedServers, setInstalledServers] = useState<MCPCardVO[]>([]);
|
const [installedServers, setInstalledServers] = useState<MCPCardVO[]>([]);
|
||||||
// const [totalCount, setTotalCount] = useState(0);
|
|
||||||
// const [nowPage, setNowPage] = useState(1);
|
|
||||||
// const [searchKeyword, setSearchKeyword] = useState('');
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
// const [sortByValue, setSortByValue] = useState<string>('pushed_at');
|
|
||||||
// const [sortOrderValue, setSortOrderValue] = useState<string>('DESC');
|
|
||||||
// const searchTimeout = useRef<NodeJS.Timeout | null>(null);
|
|
||||||
// const pageSize = 12;
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initData();
|
initData();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 当工具数量缓存变化时,重新获取服务器列表
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchInstalledServers();
|
fetchInstalledServers();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [toolsCountCache]);
|
}, [toolsCountCache]);
|
||||||
|
|
||||||
function initData() {
|
function initData() {
|
||||||
fetchInstalledServers();
|
fetchInstalledServers();
|
||||||
// getServerList(); // GitHub 市场功能暂时注释
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchInstalledServers() {
|
function fetchInstalledServers() {
|
||||||
@@ -72,7 +40,7 @@ export default function MCPMarketComponent({
|
|||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const servers = resp.servers.map((server) => {
|
const servers = resp.servers.map((server) => {
|
||||||
const vo = new MCPCardVO(server);
|
const vo = new MCPCardVO(server);
|
||||||
// 如果缓存中有工具数量,使用缓存值覆盖
|
|
||||||
if (toolsCountCache[server.name] !== undefined) {
|
if (toolsCountCache[server.name] !== undefined) {
|
||||||
vo.tools = toolsCountCache[server.name];
|
vo.tools = toolsCountCache[server.name];
|
||||||
}
|
}
|
||||||
@@ -87,75 +55,7 @@ export default function MCPMarketComponent({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// GitHub 市场功能暂时注释
|
|
||||||
// function onInputSearchKeyword(keyword: string) {
|
|
||||||
// setSearchKeyword(keyword);
|
|
||||||
// if (searchTimeout.current) {
|
|
||||||
// clearTimeout(searchTimeout.current);
|
|
||||||
// }
|
|
||||||
// searchTimeout.current = setTimeout(() => {
|
|
||||||
// setNowPage(1);
|
|
||||||
// getServerList(1, keyword);
|
|
||||||
// }, 500);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function getServerList(
|
|
||||||
// page: number = nowPage,
|
|
||||||
// keyword: string = searchKeyword,
|
|
||||||
// sortBy: string = sortByValue,
|
|
||||||
// sortOrder: string = sortOrderValue,
|
|
||||||
// ) {
|
|
||||||
// // GitHub 安装功能暂时注释
|
|
||||||
// // spaceClient
|
|
||||||
// // .getMCPMarketServers(page, pageSize, keyword, sortBy, sortOrder)
|
|
||||||
// // .then((res) => {
|
|
||||||
// // setMarketServerList(
|
|
||||||
// // res.servers.map((marketServer) => {
|
|
||||||
// // let repository = marketServer.repository;
|
|
||||||
// // if (repository.startsWith('https://github.com/')) {
|
|
||||||
// // repository = repository.replace('https://github.com/', '');
|
|
||||||
// // }
|
|
||||||
// // if (repository.startsWith('github.com/')) {
|
|
||||||
// // repository = repository.replace('github.com/', '');
|
|
||||||
// // }
|
|
||||||
// // const author = repository.split('/')[0];
|
|
||||||
// // const name = repository.split('/')[1];
|
|
||||||
// // return new MCPMarketCardVO({
|
|
||||||
// // author: author,
|
|
||||||
// // description: marketServer.description,
|
|
||||||
// // githubURL: `https://github.com/${repository}`,
|
|
||||||
// // name: name,
|
|
||||||
// // serverId: String(marketServer.ID),
|
|
||||||
// // starCount: marketServer.stars,
|
|
||||||
// // version:
|
|
||||||
// // 'version' in marketServer
|
|
||||||
// // ? String(marketServer.version)
|
|
||||||
// // : '1.0.0',
|
|
||||||
// // });
|
|
||||||
// // }),
|
|
||||||
// // );
|
|
||||||
// // setTotalCount(res.total);
|
|
||||||
// // setLoading(false);
|
|
||||||
// // console.log('market servers:', res);
|
|
||||||
// // })
|
|
||||||
// // .catch((error) => {
|
|
||||||
// // console.error(t('mcp.getServerListError'), error);
|
|
||||||
// // setLoading(false);
|
|
||||||
// // });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function handlePageChange(page: number) {
|
|
||||||
// setNowPage(page);
|
|
||||||
// getServerList(page);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// function handleSortChange(value: string) {
|
|
||||||
// const [newSortBy, newSortOrder] = value.split(',').map((s) => s.trim());
|
|
||||||
// setSortByValue(newSortBy);
|
|
||||||
// setSortOrderValue(newSortOrder);
|
|
||||||
// setNowPage(1);
|
|
||||||
// getServerList(1, searchKeyword, newSortBy, newSortOrder);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`${styles.marketComponentBody}`}>
|
<div className={`${styles.marketComponentBody}`}>
|
||||||
@@ -187,126 +87,6 @@ export default function MCPMarketComponent({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* GitHub 市场功能暂时注释 */}
|
|
||||||
{/* <div className="flex items-center justify-start mb-2 mt-2 pl-[0.8rem] pr-[0.8rem]">
|
|
||||||
<Input
|
|
||||||
style={{
|
|
||||||
width: '300px',
|
|
||||||
}}
|
|
||||||
value={searchKeyword}
|
|
||||||
placeholder={t('mcp.searchServer')}
|
|
||||||
onChange={(e) => onInputSearchKeyword(e.target.value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Select
|
|
||||||
value={`${sortByValue},${sortOrderValue}`}
|
|
||||||
onValueChange={handleSortChange}
|
|
||||||
>
|
|
||||||
<SelectTrigger className="w-[180px] ml-2 cursor-pointer">
|
|
||||||
<SelectValue placeholder={t('mcp.sortBy')} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="stars,DESC">{t('mcp.mostStars')}</SelectItem>
|
|
||||||
<SelectItem value="created_at,DESC">
|
|
||||||
{t('mcp.recentlyAdded')}
|
|
||||||
</SelectItem>
|
|
||||||
<SelectItem value="pushed_at,DESC">
|
|
||||||
{t('mcp.recentlyUpdated')}
|
|
||||||
</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-end ml-2">
|
|
||||||
{totalCount > 0 && (
|
|
||||||
<Pagination>
|
|
||||||
<PaginationContent>
|
|
||||||
<PaginationItem className="cursor-pointer">
|
|
||||||
<PaginationPrevious
|
|
||||||
onClick={() => handlePageChange(nowPage - 1)}
|
|
||||||
className={
|
|
||||||
nowPage <= 1 ? 'pointer-events-none opacity-50' : ''
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PaginationItem>
|
|
||||||
|
|
||||||
{(() => {
|
|
||||||
const totalPages = Math.ceil(totalCount / pageSize);
|
|
||||||
const maxVisiblePages = 5;
|
|
||||||
let startPage = Math.max(
|
|
||||||
1,
|
|
||||||
nowPage - Math.floor(maxVisiblePages / 2),
|
|
||||||
);
|
|
||||||
const endPage = Math.min(
|
|
||||||
totalPages,
|
|
||||||
startPage + maxVisiblePages - 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (endPage - startPage + 1 < maxVisiblePages) {
|
|
||||||
startPage = Math.max(1, endPage - maxVisiblePages + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.from(
|
|
||||||
{ length: endPage - startPage + 1 },
|
|
||||||
(_, i) => {
|
|
||||||
const pageNum = startPage + i;
|
|
||||||
return (
|
|
||||||
<PaginationItem
|
|
||||||
key={pageNum}
|
|
||||||
className="cursor-pointer"
|
|
||||||
>
|
|
||||||
<PaginationLink
|
|
||||||
isActive={pageNum === nowPage}
|
|
||||||
onClick={() => handlePageChange(pageNum)}
|
|
||||||
>
|
|
||||||
<span className="text-black select-none">
|
|
||||||
{pageNum}
|
|
||||||
</span>
|
|
||||||
</PaginationLink>
|
|
||||||
</PaginationItem>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
|
|
||||||
<PaginationItem className="cursor-pointer">
|
|
||||||
<PaginationNext
|
|
||||||
onClick={() => handlePageChange(nowPage + 1)}
|
|
||||||
className={
|
|
||||||
nowPage >= Math.ceil(totalCount / pageSize)
|
|
||||||
? 'pointer-events-none opacity-50'
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</PaginationItem>
|
|
||||||
</PaginationContent>
|
|
||||||
</Pagination>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={`${styles.pluginListContainer}`}>
|
|
||||||
{loading ? (
|
|
||||||
<div style={{ textAlign: 'center', padding: '20px' }}>
|
|
||||||
{t('mcp.loading')}
|
|
||||||
</div>
|
|
||||||
) : marketServerList.length === 0 ? (
|
|
||||||
<div style={{ textAlign: 'center', padding: '20px' }}>
|
|
||||||
{t('mcp.noMatchingServers')}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
marketServerList.map((vo, index) => (
|
|
||||||
<div key={`${vo.serverId}-${index}`}>
|
|
||||||
<MCPMarketCardComponent
|
|
||||||
cardVO={vo}
|
|
||||||
installServer={(githubURL) => {
|
|
||||||
askInstallServer(githubURL);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</div> */}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,9 @@ export class MCPCardVO {
|
|||||||
this.name = data.name;
|
this.name = data.name;
|
||||||
this.mode = data.mode;
|
this.mode = data.mode;
|
||||||
this.enable = data.enable;
|
this.enable = data.enable;
|
||||||
// 将后端返回的 "enabled" 状态映射为 "connected"
|
|
||||||
this.status = (data.status as string) === 'enabled'
|
this.status =
|
||||||
? 'connected'
|
(data.status as string) === 'enabled' ? 'connected' : data.status;
|
||||||
: data.status;
|
|
||||||
// tools可能是数组或数字
|
|
||||||
this.tools = Array.isArray(data.tools)
|
this.tools = Array.isArray(data.tools)
|
||||||
? data.tools.length
|
? data.tools.length
|
||||||
: data.tools || 0;
|
: data.tools || 0;
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ export interface MCPComponentRef {
|
|||||||
createServer: () => void;
|
createServer: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react/display-name
|
|
||||||
const MCPComponent = forwardRef<MCPComponentRef>((_props, ref) => {
|
const MCPComponent = forwardRef<MCPComponentRef>((_props, ref) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [serverList, setServerList] = useState<MCPCardVO[]>([]);
|
const [serverList, setServerList] = useState<MCPCardVO[]>([]);
|
||||||
@@ -40,12 +39,10 @@ const MCPComponent = forwardRef<MCPComponentRef>((_props, ref) => {
|
|||||||
const [serverToDelete, setServerToDelete] = useState<MCPCardVO | null>(null);
|
const [serverToDelete, setServerToDelete] = useState<MCPCardVO | null>(null);
|
||||||
const [deleting, setDeleting] = useState<boolean>(false);
|
const [deleting, setDeleting] = useState<boolean>(false);
|
||||||
const [autoTestTriggered, setAutoTestTriggered] = useState<boolean>(false);
|
const [autoTestTriggered, setAutoTestTriggered] = useState<boolean>(false);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
const [testingServers, setTestingServers] = useState<Set<string>>(new Set());
|
const [testingServers, setTestingServers] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initData();
|
initData();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function initData() {
|
function initData() {
|
||||||
@@ -274,7 +271,6 @@ const MCPComponent = forwardRef<MCPComponentRef>((_props, ref) => {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 编辑配置对话框 */}
|
|
||||||
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
<Dialog open={modalOpen} onOpenChange={setModalOpen}>
|
||||||
<DialogContent className="w-[700px] max-h-[80vh] p-0 flex flex-col">
|
<DialogContent className="w-[700px] max-h-[80vh] p-0 flex flex-col">
|
||||||
<DialogHeader className="px-6 pt-6 pb-2">
|
<DialogHeader className="px-6 pt-6 pb-2">
|
||||||
@@ -298,7 +294,7 @@ const MCPComponent = forwardRef<MCPComponentRef>((_props, ref) => {
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
{/* 删除确认对话框 */}
|
|
||||||
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ export default function MCPCardComponent({
|
|||||||
const [status, setStatus] = useState(cardVO.status);
|
const [status, setStatus] = useState(cardVO.status);
|
||||||
const [error, setError] = useState(cardVO.error);
|
const [error, setError] = useState(cardVO.error);
|
||||||
|
|
||||||
// 响应cardVO的变化,更新本地状态
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(`[MCPCard ${cardVO.name}] Status updated:`, {
|
console.log(`[MCPCard ${cardVO.name}] Status updated:`, {
|
||||||
status: cardVO.status,
|
status: cardVO.status,
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export default function MCPForm({
|
|||||||
if (isEdit && serverName) {
|
if (isEdit && serverName) {
|
||||||
loadServerConfig();
|
loadServerConfig();
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [isEdit, serverName]);
|
}, [isEdit, serverName]);
|
||||||
|
|
||||||
async function loadServerConfig() {
|
async function loadServerConfig() {
|
||||||
|
|||||||
+196
-254
@@ -46,7 +46,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
} from "@/components/ui/select"
|
} from '@/components/ui/select';
|
||||||
|
|
||||||
import { Resolver, useForm } from 'react-hook-form';
|
import { Resolver, useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
@@ -88,81 +88,52 @@ export default function PluginConfigPage() {
|
|||||||
const addExtraArg = () => {
|
const addExtraArg = () => {
|
||||||
setExtraArgs([...extraArgs, { key: '', type: 'string', value: '' }]);
|
setExtraArgs([...extraArgs, { key: '', type: 'string', value: '' }]);
|
||||||
};
|
};
|
||||||
const getExtraArgSchema = (t: (key: string) => string) =>
|
|
||||||
z
|
|
||||||
.object({
|
|
||||||
key: z.string().min(1, { message: t('models.keyNameRequired') }),
|
|
||||||
type: z.enum(['string', 'number', 'boolean']),
|
|
||||||
value: z.string(),
|
|
||||||
})
|
|
||||||
.superRefine((data, ctx) => {
|
|
||||||
if (data.type === 'number' && isNaN(Number(data.value))) {
|
|
||||||
ctx.addIssue({
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: t('models.mustBeValidNumber'),
|
|
||||||
path: ['value'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
data.type === 'boolean' &&
|
|
||||||
data.value !== 'true' &&
|
|
||||||
data.value !== 'false'
|
|
||||||
) {
|
|
||||||
ctx.addIssue({
|
|
||||||
code: z.ZodIssueCode.custom,
|
|
||||||
message: t('models.mustBeTrueOrFalse'),
|
|
||||||
path: ['value'],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const removeExtraArg = (index: number) => {
|
const removeExtraArg = (index: number) => {
|
||||||
const newArgs = extraArgs.filter((_, i) => i !== index);
|
const newArgs = extraArgs.filter((_, i) => i !== index);
|
||||||
setExtraArgs(newArgs);
|
setExtraArgs(newArgs);
|
||||||
form.setValue('extra_args', newArgs);
|
form.setValue('extra_args', newArgs);
|
||||||
};
|
};
|
||||||
const getFormSchema = (t: (key: string) => string) =>
|
const getFormSchema = (t: (key: string) => string) =>
|
||||||
z.object({
|
z.object({
|
||||||
name: z.string({ required_error: t('mcp.nameRequired') }),
|
name: z.string({ required_error: t('mcp.nameRequired') }),
|
||||||
timeout: z
|
timeout: z
|
||||||
.number({ invalid_type_error: t('mcp.timeoutMustBeNumber') })
|
.number({ invalid_type_error: t('mcp.timeoutMustBeNumber') })
|
||||||
.nonnegative({ message: t('mcp.timeoutNonNegative') })
|
.nonnegative({ message: t('mcp.timeoutNonNegative') })
|
||||||
.default(30),
|
.default(30),
|
||||||
ssereadtimeout: z
|
ssereadtimeout: z
|
||||||
.number({ invalid_type_error: t('mcp.sseTimeoutMustBeNumber') })
|
.number({ invalid_type_error: t('mcp.sseTimeoutMustBeNumber') })
|
||||||
.nonnegative({ message: t('mcp.sseTimeoutNonNegative') })
|
.nonnegative({ message: t('mcp.sseTimeoutNonNegative') })
|
||||||
.default(300),
|
.default(300),
|
||||||
url: z.string({ required_error: t('models.requestURLRequired') }),
|
url: z.string({ required_error: t('models.requestURLRequired') }),
|
||||||
extra_args: z
|
extra_args: z
|
||||||
.array(
|
.array(
|
||||||
z.object({
|
z.object({
|
||||||
key: z.string(),
|
key: z.string(),
|
||||||
type: z.enum(['string', 'number', 'boolean']),
|
type: z.enum(['string', 'number', 'boolean']),
|
||||||
value: z.string(),
|
value: z.string(),
|
||||||
})
|
}),
|
||||||
)
|
)
|
||||||
.optional(),
|
.optional(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const formSchema = getFormSchema(t);
|
||||||
|
|
||||||
|
type FormValues = z.infer<typeof formSchema> & {
|
||||||
|
timeout: number;
|
||||||
|
ssereadtimeout: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const form = useForm<FormValues>({
|
||||||
|
resolver: zodResolver(formSchema) as unknown as Resolver<FormValues>,
|
||||||
|
defaultValues: {
|
||||||
|
name: '',
|
||||||
|
url: '',
|
||||||
|
timeout: 30,
|
||||||
|
ssereadtimeout: 300,
|
||||||
|
extra_args: [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const formSchema = getFormSchema(t);
|
|
||||||
|
|
||||||
|
|
||||||
type FormValues = z.infer<typeof formSchema> & {
|
|
||||||
timeout: number;
|
|
||||||
ssereadtimeout: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const form = useForm<FormValues>({
|
|
||||||
resolver: zodResolver(formSchema) as unknown as Resolver<FormValues>,
|
|
||||||
defaultValues: {
|
|
||||||
name: '',
|
|
||||||
url: '',
|
|
||||||
timeout: 30,
|
|
||||||
ssereadtimeout: 300,
|
|
||||||
extra_args: [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const [extraArgs, setExtraArgs] = useState<
|
const [extraArgs, setExtraArgs] = useState<
|
||||||
{ key: string; type: 'string' | 'number' | 'boolean'; value: string }[]
|
{ key: string; type: 'string' | 'number' | 'boolean'; value: string }[]
|
||||||
>([]);
|
>([]);
|
||||||
@@ -223,52 +194,8 @@ const form = useForm<FormValues>({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function watchTestMCPTask(taskId: number) {
|
|
||||||
let alreadyHandled = false;
|
|
||||||
console.log('Watching MCP test task:', taskId);
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
httpClient.getAsyncTask(taskId).then((resp) => {
|
|
||||||
console.log('task status:', resp);
|
|
||||||
|
|
||||||
// 若任务已完成
|
|
||||||
if (resp.runtime && resp.runtime.done) {
|
|
||||||
clearInterval(interval);
|
|
||||||
|
|
||||||
if (resp.runtime.exception) {
|
|
||||||
// 任务失败
|
|
||||||
toast.error(`测试失败: ${resp.runtime.exception}`);
|
|
||||||
} else if (resp.runtime.result) {
|
|
||||||
// 任务成功
|
|
||||||
const result = resp.runtime.result as {
|
|
||||||
status?: string;
|
|
||||||
tools_count?: number;
|
|
||||||
tools_names_lists?: string[];
|
|
||||||
error?: string;
|
|
||||||
};
|
|
||||||
const names = result.tools_names_lists || [];
|
|
||||||
|
|
||||||
if (!alreadyHandled) {
|
|
||||||
alreadyHandled = true;
|
|
||||||
const names = result.tools_names_lists || [];
|
|
||||||
toast.success(`连接成功,找到 ${names.length} 个工具`);
|
|
||||||
console.log('工具列表:', names);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 没结果但标记为完成
|
|
||||||
toast.error('测试任务完成但未返回结果');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch((err) => {
|
|
||||||
console.error('任务状态获取失败:', err);
|
|
||||||
toast.error('获取任务状态失败');
|
|
||||||
clearInterval(interval);
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluginInstalledRef = useRef<PluginInstalledComponentRef>(null);
|
const pluginInstalledRef = useRef<PluginInstalledComponentRef>(null);
|
||||||
const mcpComponentRef = useRef<MCPComponentRef>(null);
|
const mcpComponentRef = useRef<MCPComponentRef>(null);
|
||||||
@@ -280,12 +207,16 @@ const form = useForm<FormValues>({
|
|||||||
const [refreshKey, setRefreshKey] = useState(0);
|
const [refreshKey, setRefreshKey] = useState(0);
|
||||||
|
|
||||||
// MCP测试结果状态
|
// MCP测试结果状态
|
||||||
const [mcpTestStatus, setMcpTestStatus] = useState<'idle' | 'testing' | 'success' | 'failed'>('idle');
|
const [mcpTestStatus, setMcpTestStatus] = useState<
|
||||||
|
'idle' | 'testing' | 'success' | 'failed'
|
||||||
|
>('idle');
|
||||||
const [mcpToolNames, setMcpToolNames] = useState<string[]>([]);
|
const [mcpToolNames, setMcpToolNames] = useState<string[]>([]);
|
||||||
const [mcpTestError, setMcpTestError] = useState<string>('');
|
const [mcpTestError, setMcpTestError] = useState<string>('');
|
||||||
|
|
||||||
// 缓存每个服务器测试后的工具数量
|
// 缓存每个服务器测试后的工具数量
|
||||||
const [serverToolsCache, setServerToolsCache] = useState<Record<string, number>>({});
|
const [serverToolsCache, setServerToolsCache] = useState<
|
||||||
|
Record<string, number>
|
||||||
|
>({});
|
||||||
|
|
||||||
// 强制清理 body 样式以修复 Dialog 关闭后点击失效的问题
|
// 强制清理 body 样式以修复 Dialog 关闭后点击失效的问题
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -297,11 +228,9 @@ const form = useForm<FormValues>({
|
|||||||
|
|
||||||
if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) {
|
if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) {
|
||||||
const cleanup = () => {
|
const cleanup = () => {
|
||||||
|
|
||||||
document.body.style.removeProperty('pointer-events');
|
document.body.style.removeProperty('pointer-events');
|
||||||
document.body.style.removeProperty('overflow');
|
document.body.style.removeProperty('overflow');
|
||||||
|
|
||||||
|
|
||||||
if (document.body.style.pointerEvents === 'none') {
|
if (document.body.style.pointerEvents === 'none') {
|
||||||
document.body.style.pointerEvents = '';
|
document.body.style.pointerEvents = '';
|
||||||
}
|
}
|
||||||
@@ -344,7 +273,6 @@ const form = useForm<FormValues>({
|
|||||||
}
|
}
|
||||||
}, [mcpSSEModalOpen, modalOpen, showDeleteConfirmModal]);
|
}, [mcpSSEModalOpen, modalOpen, showDeleteConfirmModal]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) {
|
if (!mcpSSEModalOpen && !modalOpen && !showDeleteConfirmModal) {
|
||||||
@@ -391,51 +319,52 @@ const form = useForm<FormValues>({
|
|||||||
}, [mcpSSEModalOpen, modalOpen, showDeleteConfirmModal]);
|
}, [mcpSSEModalOpen, modalOpen, showDeleteConfirmModal]);
|
||||||
|
|
||||||
function handleModalConfirm() {
|
function handleModalConfirm() {
|
||||||
installPlugin(installSource, installInfo as Record<string, any>); // eslint-disable-line @typescript-eslint/no-explicit-any
|
installPlugin(installSource, installInfo as Record<string, unknown>);
|
||||||
}
|
|
||||||
function installPlugin(
|
|
||||||
installSource: string,
|
|
||||||
installInfo: Record<string, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
||||||
) {
|
|
||||||
setPluginInstallStatus(PluginInstallStatus.INSTALLING);
|
|
||||||
if (installSource === 'github') {
|
|
||||||
httpClient
|
|
||||||
.installPluginFromGithub(installInfo.url)
|
|
||||||
.then((resp) => {
|
|
||||||
const taskId = resp.task_id;
|
|
||||||
watchTask(taskId);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log('error when install plugin:', err);
|
|
||||||
setInstallError(err.message);
|
|
||||||
setPluginInstallStatus(PluginInstallStatus.ERROR);
|
|
||||||
});
|
|
||||||
} else if (installSource === 'local') {
|
|
||||||
httpClient
|
|
||||||
.installPluginFromLocal(installInfo.file)
|
|
||||||
.then((resp) => {
|
|
||||||
const taskId = resp.task_id;
|
|
||||||
watchTask(taskId);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log('error when install plugin:', err);
|
|
||||||
setInstallError(err.message);
|
|
||||||
setPluginInstallStatus(PluginInstallStatus.ERROR);
|
|
||||||
});
|
|
||||||
} else if (installSource === 'marketplace') {
|
|
||||||
httpClient
|
|
||||||
.installPluginFromMarketplace(
|
|
||||||
installInfo.plugin_author,
|
|
||||||
installInfo.plugin_name,
|
|
||||||
installInfo.plugin_version,
|
|
||||||
)
|
|
||||||
.then((resp) => {
|
|
||||||
const taskId = resp.task_id;
|
|
||||||
watchTask(taskId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const installPlugin = useCallback(
|
||||||
|
(installSource: string, installInfo: Record<string, unknown>) => {
|
||||||
|
setPluginInstallStatus(PluginInstallStatus.INSTALLING);
|
||||||
|
if (installSource === 'github') {
|
||||||
|
httpClient
|
||||||
|
.installPluginFromGithub((installInfo as { url: string }).url)
|
||||||
|
.then((resp) => {
|
||||||
|
const taskId = resp.task_id;
|
||||||
|
watchTask(taskId);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('error when install plugin:', err);
|
||||||
|
setInstallError(err.message);
|
||||||
|
setPluginInstallStatus(PluginInstallStatus.ERROR);
|
||||||
|
});
|
||||||
|
} else if (installSource === 'local') {
|
||||||
|
httpClient
|
||||||
|
.installPluginFromLocal((installInfo as { file: File }).file)
|
||||||
|
.then((resp) => {
|
||||||
|
const taskId = resp.task_id;
|
||||||
|
watchTask(taskId);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('error when install plugin:', err);
|
||||||
|
setInstallError(err.message);
|
||||||
|
setPluginInstallStatus(PluginInstallStatus.ERROR);
|
||||||
|
});
|
||||||
|
} else if (installSource === 'marketplace') {
|
||||||
|
httpClient
|
||||||
|
.installPluginFromMarketplace(
|
||||||
|
(installInfo as { plugin_author: string }).plugin_author,
|
||||||
|
(installInfo as { plugin_name: string }).plugin_name,
|
||||||
|
(installInfo as { plugin_version: string }).plugin_version,
|
||||||
|
)
|
||||||
|
.then((resp) => {
|
||||||
|
const taskId = resp.task_id;
|
||||||
|
watchTask(taskId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[watchTask],
|
||||||
|
);
|
||||||
|
|
||||||
async function deleteMCPServer() {
|
async function deleteMCPServer() {
|
||||||
if (!editingServerName) return;
|
if (!editingServerName) return;
|
||||||
|
|
||||||
@@ -467,118 +396,131 @@ const form = useForm<FormValues>({
|
|||||||
const server = resp.server ?? resp;
|
const server = resp.server ?? resp;
|
||||||
console.log('Loaded server for edit:', server);
|
console.log('Loaded server for edit:', server);
|
||||||
|
|
||||||
|
const extraArgs = server.extra_args as
|
||||||
|
| Record<string, unknown>
|
||||||
|
| undefined;
|
||||||
form.setValue('name', server.name);
|
form.setValue('name', server.name);
|
||||||
form.setValue('url', server.extra_args?.url || '');
|
form.setValue('url', (extraArgs?.url as string) || '');
|
||||||
form.setValue('timeout', server.extra_args?.timeout || 30);
|
form.setValue('timeout', (extraArgs?.timeout as number) || 30);
|
||||||
form.setValue('ssereadtimeout', server.extra_args?.ssereadtimeout || 300);
|
form.setValue(
|
||||||
|
'ssereadtimeout',
|
||||||
|
(extraArgs?.ssereadtimeout as number) || 300,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (extraArgs?.headers) {
|
||||||
if (server.extra_args?.headers) {
|
const headers = Object.entries(
|
||||||
const headers = Object.entries(server.extra_args.headers).map(
|
extraArgs.headers as Record<string, unknown>,
|
||||||
([key, value]) => ({
|
).map(([key, value]) => ({
|
||||||
key,
|
key,
|
||||||
type: 'string' as const,
|
type: 'string' as const,
|
||||||
value: String(value),
|
value: String(value),
|
||||||
}),
|
}));
|
||||||
);
|
|
||||||
setExtraArgs(headers);
|
setExtraArgs(headers);
|
||||||
form.setValue('extra_args', headers);
|
form.setValue('extra_args', headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setMcpTestStatus('testing');
|
setMcpTestStatus('testing');
|
||||||
setMcpToolNames([]);
|
setMcpToolNames([]);
|
||||||
setMcpTestError('');
|
setMcpTestError('');
|
||||||
|
|
||||||
|
|
||||||
setEditingServerName(serverName);
|
setEditingServerName(serverName);
|
||||||
setIsEditMode(true);
|
setIsEditMode(true);
|
||||||
setMcpSSEModalOpen(true);
|
setMcpSSEModalOpen(true);
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await httpClient.testMCPServer(server.name);
|
const res = await httpClient.testMCPServer(server.name);
|
||||||
if (res.task_id) {
|
if (res.task_id) {
|
||||||
const taskId = res.task_id;
|
const taskId = res.task_id;
|
||||||
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
httpClient.getAsyncTask(taskId).then((taskResp) => {
|
httpClient
|
||||||
console.log('Task response:', taskResp);
|
.getAsyncTask(taskId)
|
||||||
|
.then((taskResp) => {
|
||||||
|
console.log('Task response:', taskResp);
|
||||||
|
|
||||||
if (taskResp.runtime && taskResp.runtime.done) {
|
if (taskResp.runtime && taskResp.runtime.done) {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
|
|
||||||
console.log('Task completed. Runtime:', taskResp.runtime);
|
console.log('Task completed. Runtime:', taskResp.runtime);
|
||||||
console.log('Result:', taskResp.runtime.result);
|
console.log('Result:', taskResp.runtime.result);
|
||||||
console.log('Exception:', taskResp.runtime.exception);
|
console.log('Exception:', taskResp.runtime.exception);
|
||||||
|
|
||||||
if (taskResp.runtime.exception) {
|
if (taskResp.runtime.exception) {
|
||||||
|
console.log('Test failed with exception');
|
||||||
console.log('Test failed with exception');
|
|
||||||
setMcpTestStatus('failed');
|
|
||||||
setMcpToolNames([]);
|
|
||||||
setMcpTestError(taskResp.runtime.exception || '未知错误');
|
|
||||||
} else if (taskResp.runtime.result) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
let result: {
|
|
||||||
status?: string;
|
|
||||||
tools_count?: number;
|
|
||||||
tools_names_lists?: string[];
|
|
||||||
error?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const rawResult: any = taskResp.runtime.result;
|
|
||||||
if (typeof rawResult === 'string') {
|
|
||||||
console.log('Result is string, parsing...');
|
|
||||||
result = JSON.parse(rawResult.replace(/'/g, '"'));
|
|
||||||
} else {
|
|
||||||
result = rawResult as typeof result;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Parsed result:', result);
|
|
||||||
console.log('tools_names_lists:', result.tools_names_lists);
|
|
||||||
console.log('tools_names_lists length:', result.tools_names_lists?.length);
|
|
||||||
|
|
||||||
if (result.tools_names_lists && result.tools_names_lists.length > 0) {
|
|
||||||
console.log('Test success with', result.tools_names_lists.length, 'tools');
|
|
||||||
setMcpTestStatus('success');
|
|
||||||
setMcpToolNames(result.tools_names_lists);
|
|
||||||
// 保存工具数量到缓存
|
|
||||||
setServerToolsCache(prev => ({
|
|
||||||
...prev,
|
|
||||||
[server.name]: result.tools_names_lists!.length
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
console.log('Test failed: no tools found');
|
|
||||||
setMcpTestStatus('failed');
|
|
||||||
setMcpToolNames([]);
|
|
||||||
setMcpTestError('未找到任何工具');
|
|
||||||
}
|
|
||||||
} catch (parseError) {
|
|
||||||
console.error('Failed to parse result:', parseError);
|
|
||||||
setMcpTestStatus('failed');
|
setMcpTestStatus('failed');
|
||||||
setMcpToolNames([]);
|
setMcpToolNames([]);
|
||||||
setMcpTestError('解析测试结果失败');
|
setMcpTestError(taskResp.runtime.exception || '未知错误');
|
||||||
|
} else if (taskResp.runtime.result) {
|
||||||
|
try {
|
||||||
|
let result: {
|
||||||
|
status?: string;
|
||||||
|
tools_count?: number;
|
||||||
|
tools_names_lists?: string[];
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rawResult: unknown = taskResp.runtime.result;
|
||||||
|
if (typeof rawResult === 'string') {
|
||||||
|
console.log('Result is string, parsing...');
|
||||||
|
result = JSON.parse(rawResult.replace(/'/g, '"'));
|
||||||
|
} else {
|
||||||
|
result = rawResult as typeof result;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Parsed result:', result);
|
||||||
|
console.log(
|
||||||
|
'tools_names_lists:',
|
||||||
|
result.tools_names_lists,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
'tools_names_lists length:',
|
||||||
|
result.tools_names_lists?.length,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
result.tools_names_lists &&
|
||||||
|
result.tools_names_lists.length > 0
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
'Test success with',
|
||||||
|
result.tools_names_lists.length,
|
||||||
|
'tools',
|
||||||
|
);
|
||||||
|
setMcpTestStatus('success');
|
||||||
|
setMcpToolNames(result.tools_names_lists);
|
||||||
|
// 保存工具数量到缓存
|
||||||
|
setServerToolsCache((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[server.name]: result.tools_names_lists!.length,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
console.log('Test failed: no tools found');
|
||||||
|
setMcpTestStatus('failed');
|
||||||
|
setMcpToolNames([]);
|
||||||
|
setMcpTestError('未找到任何工具');
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
console.error('Failed to parse result:', parseError);
|
||||||
|
setMcpTestStatus('failed');
|
||||||
|
setMcpToolNames([]);
|
||||||
|
setMcpTestError('解析测试结果失败');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没结果
|
||||||
|
console.log('Test failed: no result');
|
||||||
|
setMcpTestStatus('failed');
|
||||||
|
setMcpToolNames([]);
|
||||||
|
setMcpTestError('测试未返回结果');
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// 没结果
|
|
||||||
console.log('Test failed: no result');
|
|
||||||
setMcpTestStatus('failed');
|
|
||||||
setMcpToolNames([]);
|
|
||||||
setMcpTestError('测试未返回结果');
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}).catch((err) => {
|
.catch((err) => {
|
||||||
console.error('获取任务状态失败:', err);
|
console.error('获取任务状态失败:', err);
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
setMcpTestStatus('failed');
|
setMcpTestStatus('failed');
|
||||||
setMcpToolNames([]);
|
setMcpToolNames([]);
|
||||||
setMcpTestError(err.message || '获取任务状态失败');
|
setMcpTestError(err.message || '获取任务状态失败');
|
||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
setMcpTestStatus('failed');
|
setMcpTestStatus('failed');
|
||||||
@@ -624,7 +566,6 @@ const form = useForm<FormValues>({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (isEditMode && editingServerName) {
|
if (isEditMode && editingServerName) {
|
||||||
|
|
||||||
await httpClient.updateMCPServer(editingServerName, serverConfig);
|
await httpClient.updateMCPServer(editingServerName, serverConfig);
|
||||||
toast.success(t('mcp.updateSuccess'));
|
toast.success(t('mcp.updateSuccess'));
|
||||||
} else {
|
} else {
|
||||||
@@ -697,7 +638,7 @@ const form = useForm<FormValues>({
|
|||||||
setInstallError(null);
|
setInstallError(null);
|
||||||
installPlugin('local', { file });
|
installPlugin('local', { file });
|
||||||
},
|
},
|
||||||
[t, pluginSystemStatus],
|
[t, pluginSystemStatus, installPlugin],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFileSelect = useCallback(() => {
|
const handleFileSelect = useCallback(() => {
|
||||||
@@ -754,7 +695,6 @@ const form = useForm<FormValues>({
|
|||||||
[uploadPluginFile, isPluginSystemReady, t],
|
[uploadPluginFile, isPluginSystemReady, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const renderPluginDisabledState = () => (
|
const renderPluginDisabledState = () => (
|
||||||
<div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]">
|
<div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]">
|
||||||
<Power className="w-16 h-16 text-gray-400 mb-4" />
|
<Power className="w-16 h-16 text-gray-400 mb-4" />
|
||||||
@@ -767,7 +707,6 @@ const form = useForm<FormValues>({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
const renderPluginConnectionErrorState = () => (
|
const renderPluginConnectionErrorState = () => (
|
||||||
<div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]">
|
<div className="flex flex-col items-center justify-center h-[60vh] text-center pt-[10vh]">
|
||||||
<svg
|
<svg
|
||||||
@@ -1058,7 +997,6 @@ const form = useForm<FormValues>({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>
|
<DialogTitle>
|
||||||
@@ -1113,7 +1051,8 @@ const form = useForm<FormValues>({
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<span className="font-medium">
|
<span className="font-medium">
|
||||||
{t('mcp.connectionSuccess')} - {mcpToolNames.length} {t('mcp.toolsFound')}
|
{t('mcp.connectionSuccess')} - {mcpToolNames.length}{' '}
|
||||||
|
{t('mcp.toolsFound')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap gap-1 mt-2">
|
<div className="flex flex-wrap gap-1 mt-2">
|
||||||
@@ -1146,7 +1085,9 @@ const form = useForm<FormValues>({
|
|||||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<span className="font-medium">{t('mcp.connectionFailed')}</span>
|
<span className="font-medium">
|
||||||
|
{t('mcp.connectionFailed')}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{mcpTestError && (
|
{mcpTestError && (
|
||||||
<div className="text-sm text-red-500 pl-7">
|
<div className="text-sm text-red-500 pl-7">
|
||||||
@@ -1217,7 +1158,9 @@ const form = useForm<FormValues>({
|
|||||||
type="number"
|
type="number"
|
||||||
placeholder={t('mcp.sseTimeoutDescription')}
|
placeholder={t('mcp.sseTimeoutDescription')}
|
||||||
{...field}
|
{...field}
|
||||||
onChange={(e) => field.onChange(Number(e.target.value))}
|
onChange={(e) =>
|
||||||
|
field.onChange(Number(e.target.value))
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -1225,7 +1168,6 @@ const form = useForm<FormValues>({
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel>{t('models.extraParameters')}</FormLabel>
|
<FormLabel>{t('models.extraParameters')}</FormLabel>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ const PluginInstalledComponent = forwardRef<PluginInstalledComponentRef>(
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initData();
|
initData();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function initData() {
|
function initData() {
|
||||||
|
|||||||
@@ -172,7 +172,6 @@ function MarketPageContent({
|
|||||||
// 初始加载
|
// 初始加载
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchPlugins(1, false, true);
|
fetchPlugins(1, false, true);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 搜索功能
|
// 搜索功能
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ export interface ApiRespMCPServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface MCPServer {
|
export interface MCPServer {
|
||||||
extra_args: any;
|
extra_args: Record<string, unknown>;
|
||||||
name: string;
|
name: string;
|
||||||
mode: 'stdio' | 'sse';
|
mode: 'stdio' | 'sse';
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ const zhHans = {
|
|||||||
sseTimeout: 'SSE超时时间',
|
sseTimeout: 'SSE超时时间',
|
||||||
sseTimeoutDescription: '用于建立SSE连接的超时时间',
|
sseTimeoutDescription: '用于建立SSE连接的超时时间',
|
||||||
extraParametersDescription: '额外参数,用于配置MCP服务器的特定行为',
|
extraParametersDescription: '额外参数,用于配置MCP服务器的特定行为',
|
||||||
updateSuccess:'更新成功',
|
updateSuccess: '更新成功',
|
||||||
},
|
},
|
||||||
pipelines: {
|
pipelines: {
|
||||||
title: '流水线',
|
title: '流水线',
|
||||||
|
|||||||
Reference in New Issue
Block a user