From 7caf77683fad11d5a2633e6ccf5d2bb9ed650af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=85=AC=E6=98=8E?= <83812544+Ed1s0nZ@users.noreply.github.com> Date: Wed, 22 Jul 2026 11:27:34 +0800 Subject: [PATCH] Add files via upload --- web/static/css/style.css | 5 +++++ web/static/i18n/en-US.json | 3 +++ web/static/i18n/zh-CN.json | 3 +++ web/static/js/dashboard.js | 22 +++++++++++++--------- web/static/js/monitor.js | 29 +++++++++++++++++++++-------- 5 files changed, 45 insertions(+), 17 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 9c53e73b..67df23b0 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -10767,6 +10767,11 @@ html[data-theme="dark"] .robot-binding-service-hint-icon { background: rgba(239, 68, 68, 0.1); } +.mcp-stats-kpi__chip.is-neutral { + color: #475569; + background: rgba(100, 116, 139, 0.12); +} + .mcp-stats-kpi__status { font-size: 0.75rem; font-weight: 500; diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 1df1ae23..3d019400 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -210,6 +210,7 @@ "toolsCountLabel_other": "{{count}} tools", "failedNCalls_one": "{{count}} failed", "failedNCalls_other": "{{count}} failed", + "noCompletedYet": "No completed outcomes yet", "noCallYet": "No calls yet", "allClear": "No new risks", "allIdle": "System idle", @@ -2170,6 +2171,7 @@ "lastCall": "Last call", "lastRefreshTime": "Last refresh", "noCallsYet": "No calls yet", + "noCompletedYet": "No completed outcomes yet", "unknownTool": "Unknown tool", "successFailedRate": "Success {{success}} / Failed {{failed}} · {{rate}}% success rate", "topToolsTitle": "Top {{n}} tools by calls", @@ -2192,6 +2194,7 @@ "clearToolFilter": "Clear tool filter", "successCount": "Success {{n}}", "failedCount": "Failed {{n}}", + "neutralCount": "Stopped {{n}}", "rateHealthy": "Running smoothly", "rateWarning": "Some failures detected", "rateCritical": "High failure rate", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index c1926ad6..ea83e556 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -207,6 +207,7 @@ "highCountLabel": "高危 {{count}}", "toolsCountLabel": "{{count}} 个工具", "failedNCalls": "{{count}} 次失败", + "noCompletedYet": "暂无有效完成", "noCallYet": "暂无调用", "allClear": "暂无新增风险", "allIdle": "系统空闲", @@ -2158,6 +2159,7 @@ "lastCall": "最近一次调用", "lastRefreshTime": "最后刷新时间", "noCallsYet": "暂无调用", + "noCompletedYet": "暂无有效完成", "unknownTool": "未知工具", "successFailedRate": "成功 {{success}} / 失败 {{failed}} · 成功率 {{rate}}%", "topToolsTitle": "工具调用 Top {{n}}", @@ -2180,6 +2182,7 @@ "clearToolFilter": "清除工具筛选", "successCount": "成功 {{n}}", "failedCount": "失败 {{n}}", + "neutralCount": "终止 {{n}}", "rateHealthy": "运行平稳", "rateWarning": "存在失败调用", "rateCritical": "失败率偏高", diff --git a/web/static/js/dashboard.js b/web/static/js/dashboard.js index 9d09ef22..8666f2f2 100644 --- a/web/static/js/dashboard.js +++ b/web/static/js/dashboard.js @@ -295,14 +295,18 @@ async function refreshDashboard() { toolsTotalCalls = s.totalCalls || 0; toolsFailedCount = s.failedCalls || 0; const totalSuccess = s.successCalls || 0; + const effectiveToolCalls = totalSuccess + toolsFailedCount; setEl('dashboard-kpi-tools-calls', formatNumber(toolsTotalCalls)); setKpiSubText('dashboard-kpi-tools-sub-text', dt('dashboard.toolsCountLabel', { count: toolsCount }, toolsCount + ' 个工具')); - if (toolsTotalCalls > 0) { - toolsSuccessRate = (totalSuccess / toolsTotalCalls) * 100; + if (effectiveToolCalls > 0) { + toolsSuccessRate = (totalSuccess / effectiveToolCalls) * 100; const rateStr = toolsSuccessRate.toFixed(1) + '%'; setEl('dashboard-kpi-success-rate', rateStr); setKpiRateBadge('dashboard-kpi-rate-sub-text', toolsSuccessRate, toolsFailedCount); + } else if (toolsTotalCalls > 0) { + setEl('dashboard-kpi-success-rate', '-'); + setKpiSubText('dashboard-kpi-rate-sub-text', dt('dashboard.noCompletedYet', null, '暂无有效完成')); } else { setEl('dashboard-kpi-success-rate', '-'); setKpiSubText('dashboard-kpi-rate-sub-text', dt('dashboard.noCallYet', null, '暂无调用')); @@ -1500,19 +1504,19 @@ function renderVulnStatusPanel(byStatus, total) { setEl('dashboard-status-fp', formatNumber(fp)); setEl('dashboard-status-ignored', formatNumber(ignored)); - // 修复率:fixed / total(不计入 false_positive 时也可,按 total 维持一致) - var t = Number(total || 0); - var rate = t > 0 ? (fixed / t) * 100 : 0; - var rateStr = t > 0 ? rate.toFixed(rate >= 100 ? 0 : 1) + '%' : '-'; + // 修复率只按需要处置的有效漏洞计算;误报/已忽略属于中性闭环,不拉低修复率。 + var actionableTotal = open + confirmed + fixed; + var rate = actionableTotal > 0 ? (fixed / actionableTotal) * 100 : 0; + var rateStr = actionableTotal > 0 ? rate.toFixed(rate >= 100 ? 0 : 1) + '%' : '-'; setEl('dashboard-fix-rate', rateStr); var detailEl = document.getElementById('dashboard-fix-detail'); if (detailEl) { - detailEl.textContent = '(' + formatNumber(fixed) + ' / ' + formatNumber(t) + ')'; + detailEl.textContent = '(' + formatNumber(fixed) + ' / ' + formatNumber(actionableTotal) + ')'; } - var fixedPct = t > 0 ? (fixed / t) * 100 : 0; - var confirmedPct = t > 0 ? (confirmed / t) * 100 : 0; + var fixedPct = actionableTotal > 0 ? (fixed / actionableTotal) * 100 : 0; + var confirmedPct = actionableTotal > 0 ? (confirmed / actionableTotal) * 100 : 0; var fixedBar = document.getElementById('dashboard-fix-progress-fixed'); var confirmedBar = document.getElementById('dashboard-fix-progress-confirmed'); if (fixedBar) fixedBar.style.width = fixedPct.toFixed(2) + '%'; diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index b83a791c..1cab7920 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -5293,10 +5293,14 @@ function getMcpMonitorTimelineRange() { function buildMonitorTotals(summary) { const s = summary && typeof summary === 'object' ? summary : {}; + const total = s.totalCalls || 0; + const success = s.successCalls || 0; + const failed = s.failedCalls || 0; return { - total: s.totalCalls || 0, - success: s.successCalls || 0, - failed: s.failedCalls || 0, + total, + success, + failed, + neutral: Math.max(0, total - success - failed), lastCallTime: s.lastCallTime ? new Date(s.lastCallTime) : null, }; } @@ -6193,7 +6197,11 @@ function renderMcpStatsMetricsBar(totals, successRate, rateTone, rateSubText, la const lastCallLabel = mcpMonitorT('lastCall') || monitorFallback('最近一次调用', 'Last call'); const successPill = mcpMonitorT('successCount', { n: totals.success }) || monitorFallback(`成功 ${totals.success}`, `Success ${totals.success}`); const failedPill = mcpMonitorT('failedCount', { n: totals.failed }) || monitorFallback(`失败 ${totals.failed}`, `Failed ${totals.failed}`); + const neutralPill = mcpMonitorT('neutralCount', { n: totals.neutral }) || monitorFallback(`终止 ${totals.neutral}`, `Stopped ${totals.neutral}`); const rateValue = hasCalls ? `${successRate}%` : successRate; + const neutralChip = totals.neutral > 0 + ? `${escapeHtml(neutralPill)}` + : ''; return `
@@ -6205,6 +6213,7 @@ function renderMcpStatsMetricsBar(totals, successRate, rateTone, rateSubText, la
${escapeHtml(successPill)} ${escapeHtml(failedPill)} + ${neutralChip}
@@ -6240,7 +6249,8 @@ function renderMcpStatsToolTable(topTools, totals, activeToolFilter = '') { const total = tool.totalCalls || 0; const success = tool.successCalls || 0; const failed = tool.failedCalls || 0; - const toolRateNum = total > 0 ? (success / total) * 100 : 0; + const effectiveTotal = success + failed; + const toolRateNum = effectiveTotal > 0 ? (success / effectiveTotal) * 100 : 0; const toolRate = toolRateNum.toFixed(1); 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]; @@ -6318,7 +6328,8 @@ function renderMcpStatsToolsPanel(topTools, totals, activeToolFilter = '') { const total = tool.totalCalls || 0; const success = tool.successCalls || 0; const failed = tool.failedCalls || 0; - const toolRateNum = total > 0 ? (success / total) * 100 : 0; + const effectiveTotal = success + failed; + const toolRateNum = effectiveTotal > 0 ? (success / effectiveTotal) * 100 : 0; const toolRate = toolRateNum.toFixed(1); 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]; @@ -6453,17 +6464,19 @@ function renderMonitorStats(summary = null, topTools = [], lastFetchedAt = null) return; } - const hasCalls = totals.total > 0; - const successRateNum = hasCalls ? (totals.success / totals.total) * 100 : 0; + const effectiveTotal = totals.success + totals.failed; + const hasCalls = effectiveTotal > 0; + const successRateNum = hasCalls ? (totals.success / effectiveTotal) * 100 : 0; const successRate = hasCalls ? successRateNum.toFixed(1) : '-'; const locale = (typeof window.__locale === 'string' && window.__locale.startsWith('zh')) ? 'zh-CN' : 'en-US'; const noCallsYet = mcpMonitorT('noCallsYet') || monitorFallback('暂无调用', 'No calls yet'); + const noCompletedYet = mcpMonitorT('noCompletedYet') || monitorFallback('暂无有效完成', 'No completed outcomes yet'); const lastCallText = totals.lastCallTime ? (totals.lastCallTime.toLocaleString ? totals.lastCallTime.toLocaleString(locale) : String(totals.lastCallTime)) : noCallsYet; const rateTone = hasCalls ? getMcpStatsRateTone(successRateNum) : 'is-muted'; - let rateSubText = noCallsYet; + let rateSubText = totals.total > 0 ? noCompletedYet : noCallsYet; if (hasCalls) { rateSubText = mcpMonitorT('rateHealthy') || monitorFallback('运行平稳', 'Running smoothly'); if (successRateNum < 80) rateSubText = mcpMonitorT('rateCritical') || monitorFallback('失败率偏高', 'High failure rate');