mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-26 11:21:18 +02:00
Add files via upload
This commit is contained in:
+40
-16
@@ -31,6 +31,25 @@ function shouldSkipTaskEventReplayAttach(conversationId) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/** 监控页展示:内部 mcp::tool → 模型侧 mcp__tool */
|
||||||
|
function formatMonitorToolName(name) {
|
||||||
|
if (!name || typeof name !== 'string') return name || '';
|
||||||
|
return name.includes('::') ? name.replace('::', '__') : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 筛选/API:mcp__tool → 内部 mcp::tool(与库存一致) */
|
||||||
|
function canonicalMonitorToolName(name) {
|
||||||
|
if (!name || typeof name !== 'string') return name || '';
|
||||||
|
if (name.includes('::')) return name;
|
||||||
|
const idx = name.indexOf('__');
|
||||||
|
if (idx > 0) return `${name.slice(0, idx)}::${name.slice(idx + 2)}`;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function monitorToolNamesEqual(a, b) {
|
||||||
|
return canonicalMonitorToolName(a) === canonicalMonitorToolName(b);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
window.shouldSkipTaskEventReplayAttach = shouldSkipTaskEventReplayAttach;
|
window.shouldSkipTaskEventReplayAttach = shouldSkipTaskEventReplayAttach;
|
||||||
}
|
}
|
||||||
@@ -3632,9 +3651,10 @@ async function applyMonitorFilters() {
|
|||||||
const statusFilter = document.getElementById('monitor-status-filter');
|
const statusFilter = document.getElementById('monitor-status-filter');
|
||||||
const toolFilter = document.getElementById('monitor-tool-filter');
|
const toolFilter = document.getElementById('monitor-tool-filter');
|
||||||
const status = statusFilter ? statusFilter.value : 'all';
|
const status = statusFilter ? statusFilter.value : 'all';
|
||||||
const tool = toolFilter ? (toolFilter.value.trim() || 'all') : 'all';
|
const toolRaw = toolFilter ? (toolFilter.value.trim() || 'all') : 'all';
|
||||||
|
const tool = toolRaw === 'all' ? 'all' : canonicalMonitorToolName(toolRaw);
|
||||||
if (toolFilter) {
|
if (toolFilter) {
|
||||||
toolFilter.classList.toggle('is-filter-active', tool !== 'all');
|
toolFilter.classList.toggle('is-filter-active', toolRaw !== 'all');
|
||||||
}
|
}
|
||||||
// 当筛选条件改变时,从后端重新获取数据
|
// 当筛选条件改变时,从后端重新获取数据
|
||||||
await refreshMonitorPanelWithFilter(status, tool);
|
await refreshMonitorPanelWithFilter(status, tool);
|
||||||
@@ -4041,9 +4061,10 @@ function renderMcpStatsCombinedSection(topTools, totals, activeToolFilter, timel
|
|||||||
|
|
||||||
if (!hasTools && !showTimeline) return '';
|
if (!hasTools && !showTimeline) return '';
|
||||||
|
|
||||||
|
const filterChipLabel = activeToolFilter ? formatMonitorToolName(activeToolFilter) : '';
|
||||||
const filterChip = activeToolFilter
|
const filterChip = activeToolFilter
|
||||||
? `<span class="mcp-stats-filter-chip" title="${escapeHtml(mcpMonitorT('filterByToolTitle', { tool: activeToolFilter }) || activeToolFilter)}">
|
? `<span class="mcp-stats-filter-chip" title="${escapeHtml(mcpMonitorT('filterByToolTitle', { tool: filterChipLabel }) || filterChipLabel)}">
|
||||||
<span class="mcp-stats-filter-chip__label">${escapeHtml(mcpMonitorT('filterActive', { tool: activeToolFilter }) || `已筛选:${activeToolFilter}`)}</span>
|
<span class="mcp-stats-filter-chip__label">${escapeHtml(mcpMonitorT('filterActive', { tool: filterChipLabel }) || `已筛选:${filterChipLabel}`)}</span>
|
||||||
<button type="button" class="mcp-stats-filter-chip__clear mcp-stats-clear-filter" aria-label="${escapeHtml(mcpMonitorT('clearToolFilter') || '清除工具筛选')}">×</button>
|
<button type="button" class="mcp-stats-filter-chip__clear mcp-stats-clear-filter" aria-label="${escapeHtml(mcpMonitorT('clearToolFilter') || '清除工具筛选')}">×</button>
|
||||||
</span>`
|
</span>`
|
||||||
: '';
|
: '';
|
||||||
@@ -4483,7 +4504,7 @@ function updateMonitorStatsSubtitle(lastFetchedAt, toolCount) {
|
|||||||
function filterMonitorByTool(toolName) {
|
function filterMonitorByTool(toolName) {
|
||||||
const toolFilter = document.getElementById('monitor-tool-filter');
|
const toolFilter = document.getElementById('monitor-tool-filter');
|
||||||
if (!toolFilter || !toolName) return;
|
if (!toolFilter || !toolName) return;
|
||||||
toolFilter.value = toolName;
|
toolFilter.value = formatMonitorToolName(toolName);
|
||||||
toolFilter.classList.add('is-filter-active');
|
toolFilter.classList.add('is-filter-active');
|
||||||
applyMonitorFilters();
|
applyMonitorFilters();
|
||||||
const execSection = document.querySelector('.monitor-executions');
|
const execSection = document.querySelector('.monitor-executions');
|
||||||
@@ -4615,7 +4636,8 @@ function renderMcpStatsToolTable(topTools, totals, activeToolFilter = '') {
|
|||||||
|
|
||||||
let rowsHtml = '';
|
let rowsHtml = '';
|
||||||
topTools.forEach((tool, index) => {
|
topTools.forEach((tool, index) => {
|
||||||
const name = tool.toolName || unknownToolLabel;
|
const rawName = tool.toolName || unknownToolLabel;
|
||||||
|
const name = formatMonitorToolName(rawName);
|
||||||
const total = tool.totalCalls || 0;
|
const total = tool.totalCalls || 0;
|
||||||
const success = tool.successCalls || 0;
|
const success = tool.successCalls || 0;
|
||||||
const failed = tool.failedCalls || 0;
|
const failed = tool.failedCalls || 0;
|
||||||
@@ -4623,14 +4645,14 @@ function renderMcpStatsToolTable(topTools, totals, activeToolFilter = '') {
|
|||||||
const toolRate = toolRateNum.toFixed(1);
|
const toolRate = toolRateNum.toFixed(1);
|
||||||
const sharePct = totals.total > 0 ? ((total / totals.total) * 100).toFixed(1) : '0.0';
|
const sharePct = totals.total > 0 ? ((total / totals.total) * 100).toFixed(1) : '0.0';
|
||||||
const dotColor = MCP_STATS_DIST_COLORS[index % MCP_STATS_DIST_COLORS.length];
|
const dotColor = MCP_STATS_DIST_COLORS[index % MCP_STATS_DIST_COLORS.length];
|
||||||
const isActive = activeToolFilter && activeToolFilter === name;
|
const isActive = activeToolFilter && monitorToolNamesEqual(activeToolFilter, rawName);
|
||||||
const rateClass = getMcpToolRateClass(toolRateNum);
|
const rateClass = getMcpToolRateClass(toolRateNum);
|
||||||
const rankClass = index === 0 ? ' rank-1' : index === 1 ? ' rank-2' : index === 2 ? ' rank-3' : '';
|
const rankClass = index === 0 ? ' rank-1' : index === 1 ? ' rank-2' : index === 2 ? ' rank-3' : '';
|
||||||
const rowAria = mcpMonitorT('toolRowAriaLabel', { name, total, rate: toolRate })
|
const rowAria = mcpMonitorT('toolRowAriaLabel', { name, total, rate: toolRate })
|
||||||
|| `${name},${total} 次调用,成功率 ${toolRate}%`;
|
|| `${name},${total} 次调用,成功率 ${toolRate}%`;
|
||||||
rowsHtml += `
|
rowsHtml += `
|
||||||
<tr class="mcp-stats-tool-row${isActive ? ' is-active' : ''}"
|
<tr class="mcp-stats-tool-row${isActive ? ' is-active' : ''}"
|
||||||
data-tool-name="${escapeHtml(name)}"
|
data-tool-name="${escapeHtml(rawName)}"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="${escapeHtml(rowAria)}"
|
aria-label="${escapeHtml(rowAria)}"
|
||||||
@@ -4679,14 +4701,15 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') {
|
|||||||
const distAria = mcpMonitorT('distTitle') || '调用分布';
|
const distAria = mcpMonitorT('distTitle') || '调用分布';
|
||||||
|
|
||||||
const stackedHtml = segments.map((s) => {
|
const stackedHtml = segments.map((s) => {
|
||||||
const isActive = !s.isOthers && activeToolFilter && activeToolFilter === s.name;
|
const isActive = !s.isOthers && activeToolFilter && monitorToolNamesEqual(activeToolFilter, s.name);
|
||||||
const title = `${s.name} · ${s.pct}% · ${s.calls}`;
|
const displayName = s.isOthers ? s.name : formatMonitorToolName(s.name);
|
||||||
|
const title = `${displayName} · ${s.pct}% · ${s.calls}`;
|
||||||
if (s.isOthers) {
|
if (s.isOthers) {
|
||||||
return `<span class="mcp-stats-proportion-seg is-others" data-is-others="1" role="presentation"
|
return `<span class="mcp-stats-proportion-seg is-others" data-is-others="1" role="presentation"
|
||||||
style="flex:${s.pctNum} 1 0;background:${s.color}" title="${escapeHtml(title)}"></span>`;
|
style="flex:${s.pctNum} 1 0;background:${s.color}" title="${escapeHtml(title)}"></span>`;
|
||||||
}
|
}
|
||||||
const segAria = mcpMonitorT('distSegmentAria', { name: s.name, pct: s.pct, calls: s.calls })
|
const segAria = mcpMonitorT('distSegmentAria', { name: displayName, pct: s.pct, calls: s.calls })
|
||||||
|| `${s.name},占 ${s.pct}%,${s.calls} 次`;
|
|| `${displayName},占 ${s.pct}%,${s.calls} 次`;
|
||||||
return `<span class="mcp-stats-proportion-seg${isActive ? ' is-active' : ''}"
|
return `<span class="mcp-stats-proportion-seg${isActive ? ' is-active' : ''}"
|
||||||
data-tool-name="${escapeHtml(s.name)}" data-pct="${s.pct}" data-calls="${s.calls}" data-is-others="0"
|
data-tool-name="${escapeHtml(s.name)}" data-pct="${s.pct}" data-calls="${s.calls}" data-is-others="0"
|
||||||
role="button" tabindex="0" aria-label="${escapeHtml(segAria)}"
|
role="button" tabindex="0" aria-label="${escapeHtml(segAria)}"
|
||||||
@@ -4695,7 +4718,8 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') {
|
|||||||
|
|
||||||
const maxCalls = Math.max(1, ...topTools.map((t) => t.totalCalls || 0));
|
const maxCalls = Math.max(1, ...topTools.map((t) => t.totalCalls || 0));
|
||||||
const listHtml = topTools.map((tool, index) => {
|
const listHtml = topTools.map((tool, index) => {
|
||||||
const name = tool.toolName || unknownToolLabel;
|
const rawName = tool.toolName || unknownToolLabel;
|
||||||
|
const name = formatMonitorToolName(rawName);
|
||||||
const total = tool.totalCalls || 0;
|
const total = tool.totalCalls || 0;
|
||||||
const success = tool.successCalls || 0;
|
const success = tool.successCalls || 0;
|
||||||
const failed = tool.failedCalls || 0;
|
const failed = tool.failedCalls || 0;
|
||||||
@@ -4704,7 +4728,7 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') {
|
|||||||
const sharePct = totals.total > 0 ? ((total / totals.total) * 100).toFixed(1) : '0.0';
|
const sharePct = totals.total > 0 ? ((total / totals.total) * 100).toFixed(1) : '0.0';
|
||||||
const color = MCP_STATS_DIST_COLORS[index % MCP_STATS_DIST_COLORS.length];
|
const color = MCP_STATS_DIST_COLORS[index % MCP_STATS_DIST_COLORS.length];
|
||||||
const barPct = maxCalls > 0 ? ((total / maxCalls) * 100).toFixed(1) : '0';
|
const barPct = maxCalls > 0 ? ((total / maxCalls) * 100).toFixed(1) : '0';
|
||||||
const isActive = activeToolFilter && activeToolFilter === name;
|
const isActive = activeToolFilter && monitorToolNamesEqual(activeToolFilter, rawName);
|
||||||
const rateClass = getMcpToolRateClass(toolRateNum);
|
const rateClass = getMcpToolRateClass(toolRateNum);
|
||||||
const rankClass = index === 0 ? ' rank-1' : index === 1 ? ' rank-2' : index === 2 ? ' rank-3' : '';
|
const rankClass = index === 0 ? ' rank-1' : index === 1 ? ' rank-2' : index === 2 ? ' rank-3' : '';
|
||||||
const rowAria = mcpMonitorT('toolRowAriaLabel', { name, total, rate: toolRate })
|
const rowAria = mcpMonitorT('toolRowAriaLabel', { name, total, rate: toolRate })
|
||||||
@@ -4713,7 +4737,7 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') {
|
|||||||
? `<span class="mcp-stats-tool-item__fail">${escapeHtml(mcpMonitorT('failedCount', { n: failed }) || `失败 ${failed}`)}</span>`
|
? `<span class="mcp-stats-tool-item__fail">${escapeHtml(mcpMonitorT('failedCount', { n: failed }) || `失败 ${failed}`)}</span>`
|
||||||
: '';
|
: '';
|
||||||
return `<li class="mcp-stats-tool-item${isActive ? ' is-active' : ''}"
|
return `<li class="mcp-stats-tool-item${isActive ? ' is-active' : ''}"
|
||||||
data-tool-name="${escapeHtml(name)}" tabindex="0" role="button"
|
data-tool-name="${escapeHtml(rawName)}" tabindex="0" role="button"
|
||||||
aria-label="${escapeHtml(rowAria)}" aria-pressed="${isActive ? 'true' : 'false'}">
|
aria-label="${escapeHtml(rowAria)}" aria-pressed="${isActive ? 'true' : 'false'}">
|
||||||
<span class="mcp-stats-tool-item__rank mcp-stats-rank${rankClass}">${index + 1}</span>
|
<span class="mcp-stats-tool-item__rank mcp-stats-rank${rankClass}">${index + 1}</span>
|
||||||
<span class="mcp-stats-tool-item__dot" style="background:${color}" aria-hidden="true"></span>
|
<span class="mcp-stats-tool-item__dot" style="background:${color}" aria-hidden="true"></span>
|
||||||
@@ -4947,7 +4971,7 @@ function renderMonitorExecutions(executions = [], statusFilter = 'all') {
|
|||||||
const statusLabel = (typeof window.t === 'function' && statusKey) ? window.t('mcpMonitor.' + statusKey) : getStatusText(status);
|
const statusLabel = (typeof window.t === 'function' && statusKey) ? window.t('mcpMonitor.' + statusKey) : getStatusText(status);
|
||||||
const startTime = exec.startTime ? (new Date(exec.startTime).toLocaleString ? new Date(exec.startTime).toLocaleString(locale || 'en-US') : String(exec.startTime)) : unknownLabel;
|
const startTime = exec.startTime ? (new Date(exec.startTime).toLocaleString ? new Date(exec.startTime).toLocaleString(locale || 'en-US') : String(exec.startTime)) : unknownLabel;
|
||||||
const duration = formatExecutionDuration(exec.startTime, exec.endTime);
|
const duration = formatExecutionDuration(exec.startTime, exec.endTime);
|
||||||
const toolName = escapeHtml(exec.toolName || unknownToolLabel);
|
const toolName = escapeHtml(formatMonitorToolName(exec.toolName) || unknownToolLabel);
|
||||||
const rawExecId = exec.id || '';
|
const rawExecId = exec.id || '';
|
||||||
const executionId = escapeHtml(rawExecId);
|
const executionId = escapeHtml(rawExecId);
|
||||||
const terminateBtn = status === 'running'
|
const terminateBtn = status === 'running'
|
||||||
|
|||||||
+18
-10
@@ -315,6 +315,9 @@ function showSubmenuPopup(navItem, menuId) {
|
|||||||
async function initPage(pageId) {
|
async function initPage(pageId) {
|
||||||
// 等待 i18n 就绪,避免快速刷新时翻译函数未初始化导致页面显示原始占位符 key
|
// 等待 i18n 就绪,避免快速刷新时翻译函数未初始化导致页面显示原始占位符 key
|
||||||
if (window.i18nReady) await window.i18nReady;
|
if (window.i18nReady) await window.i18nReady;
|
||||||
|
if (typeof stopExternalMcpPoll === 'function') {
|
||||||
|
stopExternalMcpPoll();
|
||||||
|
}
|
||||||
switch(pageId) {
|
switch(pageId) {
|
||||||
case 'dashboard':
|
case 'dashboard':
|
||||||
if (typeof refreshDashboard === 'function') {
|
if (typeof refreshDashboard === 'function') {
|
||||||
@@ -372,21 +375,26 @@ async function initPage(pageId) {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// 先拉取全局配置,确保 tool_search 常驻状态按后端生效集合展示
|
const afterMcpConfigReady = () => {
|
||||||
|
startLoadMcpTools();
|
||||||
|
if (typeof loadExternalMCPs === 'function') {
|
||||||
|
loadExternalMCPs().catch(err => {
|
||||||
|
console.warn('加载外部MCP列表失败:', err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof startExternalMcpPoll === 'function') {
|
||||||
|
startExternalMcpPoll();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 先拉取配置(含 tool_search 常驻列表),再加载工具与外部 MCP
|
||||||
if (typeof loadConfig === 'function') {
|
if (typeof loadConfig === 'function') {
|
||||||
loadConfig(false)
|
loadConfig(false)
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.warn('加载配置失败(将继续加载工具列表):', err);
|
console.warn('加载配置失败(将继续加载 MCP 列表):', err);
|
||||||
})
|
})
|
||||||
.finally(startLoadMcpTools);
|
.finally(afterMcpConfigReady);
|
||||||
} else {
|
} else {
|
||||||
startLoadMcpTools();
|
afterMcpConfigReady();
|
||||||
}
|
|
||||||
// 先加载外部MCP列表(快速),然后加载工具列表
|
|
||||||
if (typeof loadExternalMCPs === 'function') {
|
|
||||||
loadExternalMCPs().catch(err => {
|
|
||||||
console.warn('加载外部MCP列表失败:', err);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'projects':
|
case 'projects':
|
||||||
|
|||||||
+144
-20
@@ -16,6 +16,96 @@ function getToolKey(tool) {
|
|||||||
}
|
}
|
||||||
return tool.name;
|
return tool.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 常驻工具配置存储键(外部工具用 mcp::tool,与后端 tool_search 白名单一致)
|
||||||
|
function getAlwaysVisibleStorageKey(tool) {
|
||||||
|
return getToolKey(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAlwaysVisibleAliases(name) {
|
||||||
|
const n = (name || '').trim();
|
||||||
|
if (!n) return;
|
||||||
|
alwaysVisibleToolNames.add(n);
|
||||||
|
if (n.includes('::')) {
|
||||||
|
const sep = n.indexOf('::');
|
||||||
|
const mcp = n.slice(0, sep);
|
||||||
|
const tool = n.slice(sep + 2);
|
||||||
|
if (mcp && tool) {
|
||||||
|
alwaysVisibleToolNames.add(`${mcp}__${tool}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (n.includes('__')) {
|
||||||
|
const sep = n.lastIndexOf('__');
|
||||||
|
const mcp = n.slice(0, sep);
|
||||||
|
const tool = n.slice(sep + 2);
|
||||||
|
if (mcp && tool) {
|
||||||
|
alwaysVisibleToolNames.add(`${mcp}::${tool}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeAlwaysVisibleAliases(name) {
|
||||||
|
const n = (name || '').trim();
|
||||||
|
if (!n) return;
|
||||||
|
alwaysVisibleToolNames.delete(n);
|
||||||
|
if (n.includes('::')) {
|
||||||
|
const sep = n.indexOf('::');
|
||||||
|
const mcp = n.slice(0, sep);
|
||||||
|
const tool = n.slice(sep + 2);
|
||||||
|
if (mcp && tool) {
|
||||||
|
alwaysVisibleToolNames.delete(`${mcp}__${tool}`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (n.includes('__')) {
|
||||||
|
const sep = n.lastIndexOf('__');
|
||||||
|
const mcp = n.slice(0, sep);
|
||||||
|
const tool = n.slice(sep + 2);
|
||||||
|
if (mcp && tool) {
|
||||||
|
alwaysVisibleToolNames.delete(`${mcp}::${tool}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isToolAlwaysVisible(tool) {
|
||||||
|
const key = getAlwaysVisibleStorageKey(tool);
|
||||||
|
if (alwaysVisibleToolNames.has(key)) return true;
|
||||||
|
if (alwaysVisibleToolNames.has(tool.name)) return true;
|
||||||
|
if (tool.is_external && tool.external_mcp) {
|
||||||
|
if (alwaysVisibleToolNames.has(`${tool.external_mcp}__${tool.name}`)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isToolAlwaysVisibleBuiltin(tool) {
|
||||||
|
if (alwaysVisibleBuiltinToolNames.has(tool.name)) return true;
|
||||||
|
return alwaysVisibleBuiltinToolNames.has(getAlwaysVisibleStorageKey(tool));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAlwaysVisibleForSave() {
|
||||||
|
const out = new Set();
|
||||||
|
for (const name of alwaysVisibleToolNames) {
|
||||||
|
if (alwaysVisibleBuiltinToolNames.has(name)) continue;
|
||||||
|
if (name.includes('::')) {
|
||||||
|
out.add(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (name.includes('__')) {
|
||||||
|
const sep = name.lastIndexOf('__');
|
||||||
|
const mcp = name.slice(0, sep);
|
||||||
|
const tool = name.slice(sep + 2);
|
||||||
|
if (mcp && tool) out.add(`${mcp}::${tool}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.add(name);
|
||||||
|
}
|
||||||
|
return Array.from(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
function countUserAlwaysVisibleTools() {
|
||||||
|
return getAlwaysVisibleForSave().length;
|
||||||
|
}
|
||||||
// 从localStorage读取每页显示数量,默认为20
|
// 从localStorage读取每页显示数量,默认为20
|
||||||
const getToolsPageSize = () => {
|
const getToolsPageSize = () => {
|
||||||
const saved = localStorage.getItem('toolsPageSize');
|
const saved = localStorage.getItem('toolsPageSize');
|
||||||
@@ -158,14 +248,21 @@ async function loadConfig(loadTools = true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentConfig = await response.json();
|
currentConfig = await response.json();
|
||||||
const alwaysVisibleList = currentConfig?.multi_agent?.tool_search_always_visible_effective_tools;
|
|
||||||
const alwaysVisibleConfigured = currentConfig?.multi_agent?.tool_search_always_visible_tools;
|
const alwaysVisibleConfigured = currentConfig?.multi_agent?.tool_search_always_visible_tools;
|
||||||
alwaysVisibleToolNames = new Set(Array.isArray(alwaysVisibleList) ? alwaysVisibleList.filter(Boolean) : []);
|
const alwaysVisibleEffective = currentConfig?.multi_agent?.tool_search_always_visible_effective_tools;
|
||||||
alwaysVisibleBuiltinToolNames = new Set(
|
alwaysVisibleToolNames = new Set();
|
||||||
alwaysVisibleToolNames.size > 0 && Array.isArray(alwaysVisibleConfigured)
|
if (Array.isArray(alwaysVisibleConfigured)) {
|
||||||
? Array.from(alwaysVisibleToolNames).filter(name => !alwaysVisibleConfigured.includes(name))
|
alwaysVisibleConfigured.filter(Boolean).forEach(addAlwaysVisibleAliases);
|
||||||
: []
|
}
|
||||||
);
|
alwaysVisibleBuiltinToolNames = new Set();
|
||||||
|
if (Array.isArray(alwaysVisibleEffective)) {
|
||||||
|
const configuredSet = new Set(Array.isArray(alwaysVisibleConfigured) ? alwaysVisibleConfigured : []);
|
||||||
|
alwaysVisibleEffective.filter(Boolean).forEach(name => {
|
||||||
|
if (!configuredSet.has(name)) {
|
||||||
|
alwaysVisibleBuiltinToolNames.add(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 填充OpenAI配置
|
// 填充OpenAI配置
|
||||||
const providerEl = document.getElementById('openai-provider');
|
const providerEl = document.getElementById('openai-provider');
|
||||||
@@ -634,8 +731,8 @@ function renderToolsList() {
|
|||||||
is_external: tool.is_external || false,
|
is_external: tool.is_external || false,
|
||||||
external_mcp: tool.external_mcp || ''
|
external_mcp: tool.external_mcp || ''
|
||||||
};
|
};
|
||||||
const alwaysVisibleChecked = alwaysVisibleToolNames.has(tool.name);
|
const alwaysVisibleChecked = isToolAlwaysVisible(tool);
|
||||||
const alwaysVisibleLocked = alwaysVisibleBuiltinToolNames.has(tool.name);
|
const alwaysVisibleLocked = isToolAlwaysVisibleBuiltin(tool);
|
||||||
|
|
||||||
// 外部工具标签,显示来源信息(可点击跳转到对应 MCP 卡片)
|
// 外部工具标签,显示来源信息(可点击跳转到对应 MCP 卡片)
|
||||||
let externalBadge = '';
|
let externalBadge = '';
|
||||||
@@ -660,7 +757,7 @@ function renderToolsList() {
|
|||||||
${escapeHtml(tool.name)}
|
${escapeHtml(tool.name)}
|
||||||
${externalBadge}
|
${externalBadge}
|
||||||
<label class="tool-resident-toggle" title="${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleHint') : '始终常驻在 Tool Search 可见列表'}" onclick="event.stopPropagation()">
|
<label class="tool-resident-toggle" title="${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleHint') : '始终常驻在 Tool Search 可见列表'}" onclick="event.stopPropagation()">
|
||||||
<input type="checkbox" ${alwaysVisibleChecked ? 'checked' : ''} ${alwaysVisibleLocked ? 'disabled' : ''} onchange="handleToolAlwaysVisibleChange('${escapeHtml(tool.name)}', this.checked)" />
|
<input type="checkbox" ${alwaysVisibleChecked ? 'checked' : ''} ${alwaysVisibleLocked ? 'disabled' : ''} onchange="handleToolAlwaysVisibleChange('${escapeHtml(toolKey)}', this.checked)" />
|
||||||
<span>${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleLabel') : '常驻'}</span>
|
<span>${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleLabel') : '常驻'}</span>
|
||||||
</label>
|
</label>
|
||||||
${alwaysVisibleLocked ? `<span class="external-tool-badge" title="${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleBuiltinHint') : '后端内置工具默认常驻,不可关闭'}">${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleBuiltinLabel') : '内置默认'}</span>` : ''}
|
${alwaysVisibleLocked ? `<span class="external-tool-badge" title="${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleBuiltinHint') : '后端内置工具默认常驻,不可关闭'}">${typeof window.t === 'function' ? window.t('mcp.alwaysVisibleBuiltinLabel') : '内置默认'}</span>` : ''}
|
||||||
@@ -946,14 +1043,15 @@ function handleToolCheckboxChange(toolKey, enabled) {
|
|||||||
updateToolsStats();
|
updateToolsStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToolAlwaysVisibleChange(toolName, alwaysVisible) {
|
function handleToolAlwaysVisibleChange(toolKey, alwaysVisible) {
|
||||||
const name = (toolName || '').trim();
|
const key = (toolKey || '').trim();
|
||||||
if (!name) return;
|
if (!key) return;
|
||||||
if (alwaysVisible) {
|
if (alwaysVisible) {
|
||||||
alwaysVisibleToolNames.add(name);
|
addAlwaysVisibleAliases(key);
|
||||||
} else {
|
} else {
|
||||||
alwaysVisibleToolNames.delete(name);
|
removeAlwaysVisibleAliases(key);
|
||||||
}
|
}
|
||||||
|
updateToolsStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 全选工具
|
// 全选工具
|
||||||
@@ -1088,7 +1186,7 @@ async function updateToolsStats() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tStats = typeof window.t === 'function' ? window.t : (k) => k;
|
const tStats = typeof window.t === 'function' ? window.t : (k) => k;
|
||||||
const pinnedCount = alwaysVisibleToolNames.size;
|
const pinnedCount = countUserAlwaysVisibleTools();
|
||||||
statsEl.innerHTML = `
|
statsEl.innerHTML = `
|
||||||
<span title="${tStats('mcp.currentPageEnabled')}">✅ ${tStats('mcp.currentPageEnabled')}: <strong>${currentPageEnabled}</strong> / ${currentPageTotal}</span>
|
<span title="${tStats('mcp.currentPageEnabled')}">✅ ${tStats('mcp.currentPageEnabled')}: <strong>${currentPageEnabled}</strong> / ${currentPageTotal}</span>
|
||||||
<span title="${tStats('mcp.totalEnabled')}">📊 ${tStats('mcp.totalEnabled')}: <strong>${totalEnabled}</strong> / ${totalTools}</span>
|
<span title="${tStats('mcp.totalEnabled')}">📊 ${tStats('mcp.totalEnabled')}: <strong>${totalEnabled}</strong> / ${totalTools}</span>
|
||||||
@@ -1596,7 +1694,7 @@ async function saveToolsConfig() {
|
|||||||
robot_default_agent_mode: currentConfig?.multi_agent?.robot_default_agent_mode || 'eino_single',
|
robot_default_agent_mode: currentConfig?.multi_agent?.robot_default_agent_mode || 'eino_single',
|
||||||
batch_use_multi_agent: currentConfig?.multi_agent?.batch_use_multi_agent === true,
|
batch_use_multi_agent: currentConfig?.multi_agent?.batch_use_multi_agent === true,
|
||||||
plan_execute_loop_max_iterations: Number(currentConfig?.multi_agent?.plan_execute_loop_max_iterations || 0),
|
plan_execute_loop_max_iterations: Number(currentConfig?.multi_agent?.plan_execute_loop_max_iterations || 0),
|
||||||
tool_search_always_visible_tools: Array.from(alwaysVisibleToolNames).filter(name => !alwaysVisibleBuiltinToolNames.has(name))
|
tool_search_always_visible_tools: getAlwaysVisibleForSave()
|
||||||
},
|
},
|
||||||
tools: []
|
tools: []
|
||||||
};
|
};
|
||||||
@@ -1793,6 +1891,32 @@ async function fetchExternalMCPs() {
|
|||||||
return response.json();
|
return response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MCP 管理页定时刷新外部 MCP 状态(感知后台断连/自动重连)
|
||||||
|
let externalMcpPollTimer = null;
|
||||||
|
const EXTERNAL_MCP_POLL_INTERVAL_MS = 8000;
|
||||||
|
|
||||||
|
function startExternalMcpPoll() {
|
||||||
|
stopExternalMcpPoll();
|
||||||
|
externalMcpPollTimer = setInterval(function () {
|
||||||
|
const mcpPage = document.getElementById('page-mcp-management');
|
||||||
|
if (!mcpPage || !mcpPage.classList.contains('active')) {
|
||||||
|
stopExternalMcpPoll();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (document.hidden) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadExternalMCPs().catch(function () { /* ignore */ });
|
||||||
|
}, EXTERNAL_MCP_POLL_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopExternalMcpPoll() {
|
||||||
|
if (externalMcpPollTimer) {
|
||||||
|
clearInterval(externalMcpPollTimer);
|
||||||
|
externalMcpPollTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 加载外部MCP列表并渲染
|
// 加载外部MCP列表并渲染
|
||||||
async function loadExternalMCPs() {
|
async function loadExternalMCPs() {
|
||||||
try {
|
try {
|
||||||
@@ -1898,9 +2022,9 @@ function renderExternalMCPList(servers) {
|
|||||||
<button class="btn-small btn-danger" onclick="deleteExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.deleteConfig')}" ${status === 'connecting' ? 'disabled' : ''}>🗑 ${statusT('common.delete')}</button>
|
<button class="btn-small btn-danger" onclick="deleteExternalMCP('${escapeHtml(name)}')" title="${statusT('mcp.deleteConfig')}" ${status === 'connecting' ? 'disabled' : ''}>🗑 ${statusT('common.delete')}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${status === 'error' && server.error ? `
|
${(status === 'error' || status === 'disconnected') && server.error ? `
|
||||||
<div class="external-mcp-error" style="margin: 12px 0; padding: 12px; background: #fee; border-left: 3px solid #f44; border-radius: 4px; color: #c33; font-size: 0.875rem;">
|
<div class="external-mcp-error" style="margin: 12px 0; padding: 12px; background: ${status === 'error' ? '#fee' : '#fff8e6'}; border-left: 3px solid ${status === 'error' ? '#f44' : '#e6a700'}; border-radius: 4px; color: ${status === 'error' ? '#c33' : '#8a6d00'}; font-size: 0.875rem;">
|
||||||
<strong>❌ ${statusT('mcp.connectionErrorLabel')}</strong>${escapeHtml(server.error)}
|
<strong>${status === 'error' ? '❌' : '⚠️'} ${statusT('mcp.connectionErrorLabel')}</strong>${escapeHtml(server.error)}
|
||||||
</div>` : ''}
|
</div>` : ''}
|
||||||
<div class="external-mcp-item-details">
|
<div class="external-mcp-item-details">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user