= ({ appType, onClose, onS
/>
- {/* Phase 4.1: 依赖声明 */}
-
-
-
-
- {/* 能力分类(AI验证后自动识别,可手动修改) */}
- {appType !== 'agent' && (
-
-
-
-
- )}
-
{/* MCP-specific: Transport Type (修改5) */}
{appType === 'mcp' && (
@@ -892,17 +759,13 @@ const CreateToolModal: React.FC
= ({ appType, onClose, onS
@@ -1212,55 +1012,16 @@ const HubImportModal: React.FC = ({ appType, platforms, onC
- {/* Name & Description */}
-
-
- setHubName(e.target.value)}
- />
-
-
-
-
-
- {/* 能力分类(AI验证后自动识别,可手动修改) */}
-
-
-
-
-
{/* MCP Hub Import: Code Editor + AI验证 */}
{appType === 'mcp' && (
)}
- {/* MCP 独立导入按钮:直接导入(不依赖选择平台卡片) */}
- {appType === 'mcp' && hubCodeVerifyResult === 'pass' && (
-
-
- {!hubName.trim() &&
请先输入名称
}
-
- 直接从传输代码导入,无需选择平台
-
-
- )}
-
{platforms.map(p => (
-
-
-
-
-
-
- {p.name}
- {p.url}
-
+
+
+
+
+ {p.name}
+ {p.url}
+
+
+
-
- {appType === 'skill' ? (
- <>
+ {appType === 'skill' ? (
-
setImportUrl(prev => ({ ...prev, [p.id]: e.target.value }))}
- disabled={importingId === p.id}
- />
-
- {importUrl[p.id]?.trim() && (
-
- 检测名称: {extractSkillName(importUrl[p.id], p.name)}
-
- )}
+
+
setImportUrl(prev => ({ ...prev, [p.id]: e.target.value }))}
+ disabled={importingId === p.id}
+ />
-
+
+
+ {statusMsg}
+
+ ) : '导入'}
+
- >
- ) : (
-
-
+ {importUrl[p.id]?.trim() && (
+
+ 检测名称: {extractSkillName(importUrl[p.id], p.name)}
+
+ )}
+
+ ) : (
+
-
- )}
- {appType === 'mcp' && (
-
-
-
- )}
+ )}
+
))}
@@ -1457,174 +1145,122 @@ const HubImportModal: React.FC
= ({ appType, platforms, onC
);
};
-// ===== Agent 单接入 Modal(修改8:以单个Agent方式接入,含名称/介绍) =====
+// ===== Agent Platform Connect Modal (修改4) =====
interface AgentConnectModalProps {
onClose: () => void;
- onConnect: (platformId: string, config: Record, price?: number, name?: string, description?: string) => void;
+ onConnect: (platformId: string, config: Record, price?: number) => void;
}
const AgentConnectModal: React.FC = ({ onClose, onConnect }) => {
- const [platform, setPlatform] = useState('dify');
- // 能力分类(可选,未指定时由后端 AI 自动归类)
- const [capability, setCapability] = useState('');
- const [name, setName] = useState('');
- const [intro, setIntro] = useState('');
+ const [selectedPlatform, setSelectedPlatform] = useState(null);
const [apiKey, setApiKey] = useState('');
const [endpoint, setEndpoint] = useState('');
const [price, setPrice] = useState(0);
const [connecting, setConnecting] = useState(false);
- const [verifying, setVerifying] = useState(false);
- const [verifyMsg, setVerifyMsg] = useState('');
- const [verifyOk, setVerifyOk] = useState(null);
+ const [connected, setConnected] = useState(null);
- const runVerify = async (): Promise => {
- if (!endpoint.trim() || !apiKey.trim()) {
- setVerifyMsg('请先填写 API Endpoint 与 API Key');
- setVerifyOk(false);
- return false;
- }
- setVerifying(true);
- setVerifyMsg('正在校验连通性与密钥...');
- setVerifyOk(null);
- try {
- const res: any = await marketAPI.verifyAgent({
- platform,
- endpoint: endpoint.trim(),
- api_key: apiKey.trim(),
- });
- if (res && res.code === 0 && res.data && res.data.ok) {
- setVerifyOk(true);
- setVerifyMsg('✓ ' + (res.data.detail || '校验通过'));
- return true;
- }
- setVerifyOk(false);
- setVerifyMsg('✗ ' + ((res && res.data && res.data.detail) || '校验未通过'));
- return false;
- } catch (e: any) {
- setVerifyOk(false);
- setVerifyMsg('✗ 校验请求失败:' + (e?.message || '网络错误'));
- return false;
- } finally {
- setVerifying(false);
- }
- };
-
- const handleConnect = async () => {
- if (!name.trim() || !endpoint.trim() || !apiKey.trim()) return;
- // 接入前必须先通过连通性 / 鉴权校验
- const ok = await runVerify();
- if (!ok) return;
+ const handleConnect = () => {
+ if (!selectedPlatform || !apiKey.trim()) return;
setConnecting(true);
- try {
- await onConnect(platform, { apiKey: apiKey.trim(), endpoint: endpoint.trim() }, price, name.trim(), intro.trim(), capability);
- } finally {
+ setTimeout(() => {
setConnecting(false);
- }
+ setConnected(selectedPlatform);
+ onConnect(selectedPlatform, { apiKey: apiKey.trim(), endpoint: endpoint.trim() }, price);
+ }, 1500);
};
return (
e.stopPropagation()}>
-
接入单个 Agent
+ 对接外部Agent平台
- 以单个 Agent 方式接入(例如 Dify 中每个 Agent 都有独立的访问地址与密钥)。请填写该 Agent 的名称、介绍及连接信息。
+ 将外部平台的Agent接入AIFlow应用市场,实现跨平台的Agent管理与调度。
-
-
-
+ ) : (
+
+
+
+
+ p.id === selectedPlatform)?.name || ''} disabled />
+
+
+
+ setApiKey(e.target.value)}
+ />
+
+
+
+ setEndpoint(e.target.value)}
+ />
+
+
+
+ setPrice(parseFloat(e.target.value) || 0)}
+ />
+ 用户对接的Agent:是否收费由您决定;管理员导入默认免费且审核通过。
+
+
+ {connecting && (
+
+
+
正在验证连接并同步Agent列表...
+
+ )}
+
+ )}
@@ -1639,8 +1275,6 @@ const MarketPage: React.FC = () => {
const [localApps, setLocalApps] = useState([]);
const [loading, setLoading] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
- const searchTimerRef = useRef(null);
- const [capabilityFilter, setCapabilityFilter] = useState(null);
const [installToast, setInstallToast] = useState(null);
// Install toast
@@ -1652,13 +1286,12 @@ const MarketPage: React.FC = () => {
// 从后端加载应用列表
const loadApps = (search?: string) => {
setLoading(true);
- marketAPI.getApps({ ...(search ? { search } : {}), ...(capabilityFilter ? { capability: capabilityFilter } : {}), page_size: 100 }).then((res: any) => {
+ marketAPI.getApps(search ? { search } : {}).then((res: any) => {
if (res.code === 0 && res.data) {
const items = (res.data.items || []).map((item: any) => ({
id: item.id,
name: item.name,
type: item.type,
- capability: item.capability || null,
source: item.source,
publisherId: item.publisher_id,
publisherName: item.publisher_name || '未知',
@@ -1673,10 +1306,6 @@ const MarketPage: React.FC = () => {
installCount: item.install_count || 0,
installed: item.installed || false,
createdAt: item.created_at || new Date().toISOString(),
- version: item.version || '1.0.0',
- verified: item.verified || false,
- securityScan: item.security_scan || null,
- dependencies: item.dependencies || null,
}));
setLocalApps(items);
}
@@ -1695,23 +1324,20 @@ const MarketPage: React.FC = () => {
let base = activeTab === 'all'
? localApps
: localApps.filter(a => a.type === activeTab);
- if (capabilityFilter) {
- base = base.filter(a => a.capability === capabilityFilter);
- }
return base;
- }, [activeTab, localApps, capabilityFilter]);
+ }, [activeTab, localApps]);
const tabs: { key: AppType | 'all'; label: string }[] = [
{ key: 'all', label: '全部' },
{ key: 'skill', label: 'Skills工具' },
{ key: 'agent', label: '智能Agent' },
- { key: 'mcp', label: 'MCP应用' },
+ { key: 'mcp', label: 'MCP协议应用' },
];
// Handle create tool - 调用后端 API 创建应用
const handleCreateApp = async (data: Partial) => {
try {
- const payload: any = {
+ await marketAPI.createApp({
name: data.name,
type: data.type || 'skill',
description: data.description || '',
@@ -1719,53 +1345,27 @@ const MarketPage: React.FC = () => {
is_member_free: data.isMemberFree ?? true,
join_promotion: data.joinPromotion ?? false,
source: 'user',
- dependencies: (data as any).dependencies || undefined,
- };
- // 关键修复:把粘贴的 MCP 传输代码作为 download_url 传给后端,
- // 后端会据此解析出 url / headers / transport 并自动注册到 mcp_servers 表做一次连通性测试
- const mcpCode = (data as any).mcpCode as string | undefined;
- if (data.type === 'mcp' && mcpCode) {
- payload.download_url = mcpCode;
- payload.mcp_transport = (data as any).mcpTransport || 'streamable_http';
- }
- // 关键修复:手动创建的 skill 把真实 SKILL 内容作为 skill_content 传给后端落库
- const skillContent = (data as any).skillContent as string | undefined;
- if (data.type === 'skill' && skillContent) {
- payload.skill_content = skillContent;
- }
- await marketAPI.createApp(payload);
+ });
// 重新加载列表
loadApps();
} catch (e: any) {
console.error('创建应用失败:', e);
- alert('创建应用失败:' + (e?.detail || e?.message || '未知错误'));
}
setShowCreateModal(null);
};
// Handle hub import - 调用后端 API 创建应用
- const handleHubImport = async (platformName: string, url: string, skillName: string, price?: number, description?: string, capability?: string) => {
+ const handleHubImport = async (platformName: string, url: string, skillName: string, price?: number) => {
try {
- const isMcp = showHubImport === 'mcp';
- const desc = description || `从 ${platformName} 导入的${isMcp ? 'MCP应用' : 'Skills工具'}${!isMcp ? ` (来源: ${url})` : ''}`;
- const payload: any = {
+ await marketAPI.createApp({
name: skillName,
type: showHubImport || 'skill',
- description: desc,
+ description: `从 ${platformName} 导入的${showHubImport === 'skill' ? 'Skills工具' : 'MCP应用'} (来源: ${url})`,
price: price || 0,
is_member_free: (price || 0) === 0,
join_promotion: false,
source: 'user',
- };
- if (capability) payload.capability = capability;
- if (isMcp && url) {
- payload.download_url = url;
- }
- // skill Hub 导入:把目录 URL 作为 download_url 发后端,后端自动抓取真实 SKILL.md 回填 skill_content
- if (!isMcp && url) {
- payload.download_url = url;
- }
- await marketAPI.createApp(payload);
+ });
// 重新加载列表
loadApps();
} catch (e: any) {
@@ -1774,28 +1374,21 @@ const MarketPage: React.FC = () => {
setShowHubImport(null);
};
- // Handle agent connect - 调用后端 API 创建应用(单个Agent方式接入)
- const handleAgentConnect = async (platformId: string, config: Record, price?: number, name?: string, description?: string, capability?: string) => {
+ // Handle agent connect - 调用后端 API 创建应用
+ const handleAgentConnect = async (platformId: string, config: Record, price?: number) => {
const platform = agentPlatforms.find(p => p.id === platformId);
- const finalName = (name && name.trim()) ? name.trim() : `${platform?.name || '外部'} Agent`;
- const finalDesc = (description && description.trim())
- ? description.trim()
- : `从 ${platform?.name || '外部平台'} 对接导入的智能Agent`;
+ if (!platform) return;
try {
- const payload: any = {
- name: finalName,
+ await marketAPI.createApp({
+ name: `${platform.name} Agent`,
type: 'agent',
- description: finalDesc,
+ description: `从 ${platform.name} 对接导入的智能Agent`,
price: price || 0,
is_member_free: (price || 0) === 0,
join_promotion: false,
source: 'user',
agent_endpoint: config.endpoint || '',
- api_key: config.apiKey || '',
- platform: platformId || 'dify',
- };
- if (capability) payload.capability = capability;
- await marketAPI.createApp(payload);
+ });
// 重新加载列表
loadApps();
} catch (e: any) {
@@ -1911,22 +1504,6 @@ const MarketPage: React.FC = () => {
)}
- {/* 业务场景筛选(岗位/职能视角,扁平) */}
-
-
- {SCENES.map(c => (
-
- ))}
-
-
{/* Search Bar */}
@@ -1992,9 +1565,7 @@ const MarketPage: React.FC = () => {
- {app.source === 'official' && app.status === 'published' ? (
- 官方内置
- ) : app.installed ? (
+ {app.installed ? (