diff --git a/apps/web/src/components/ai/openclaw-panel.tsx b/apps/web/src/components/ai/openclaw-panel.tsx
index 8feb010c..e72a3426 100644
--- a/apps/web/src/components/ai/openclaw-panel.tsx
+++ b/apps/web/src/components/ai/openclaw-panel.tsx
@@ -192,9 +192,95 @@ export function OpenClawPanel({
)}
+
+ {/* Sprint 5R S6: 狀態數據 + AI Terminal */}
+
+
+
+ 模型: openclaw_nemo
+
+
● 運行中
+
+
+
+
)
}
+/** AI 即時統計 */
+function OpenClawStats() {
+ const [stats, setStats] = useState<{ analyses: number; successRate: string; mttr: string }>({ analyses: 0, successRate: '--', mttr: '--' })
+ const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? ''
+
+ useEffect(() => {
+ Promise.all([
+ fetch(`${API_BASE}/api/v1/stats/disposition`).then(r => r.ok ? r.json() : null).catch(() => null),
+ fetch(`${API_BASE}/api/v1/incidents`).then(r => r.ok ? r.json() : null).catch(() => null),
+ ]).then(([disp, inc]) => {
+ const total = disp?.summary?.total ?? 0
+ const rate = disp?.summary?.auto_rate != null ? `${Math.round(disp.summary.auto_rate * 100)}%` : '--'
+ const incidents = inc?.incidents ?? inc ?? []
+ const resolved = incidents.filter((i: any) => i.updated_at && (i.status === 'resolved' || i.status === 'closed'))
+ let mttr = '--'
+ if (resolved.length > 0) {
+ const avg = resolved.reduce((sum: number, i: any) => sum + (new Date(i.updated_at).getTime() - new Date(i.created_at).getTime()), 0) / resolved.length
+ mttr = `${(avg / 60000).toFixed(1)}m`
+ }
+ setStats({ analyses: total, successRate: rate, mttr })
+ })
+ }, [API_BASE])
+
+ return (
+
+ 今日分析: {stats.analyses}
+ 成功率: {stats.successRate}
+ MTTR: {stats.mttr}
+
+ )
+}
+
+/** AI 推理終端 */
+function OpenClawTerminal() {
+ const [logs, setLogs] = useState([])
+ const API_BASE = process.env.NEXT_PUBLIC_API_URL ?? ''
+
+ useEffect(() => {
+ fetch(`${API_BASE}/api/v1/alert-operation-logs?limit=4`)
+ .then(r => r.ok ? r.json() : { items: [] })
+ .then(d => {
+ const items = (d.items ?? []).reverse()
+ setLogs(items.map((item: any) => {
+ const time = (() => { try { return new Date(item.created_at).toLocaleTimeString('zh-TW', { timeZone: 'Asia/Taipei', hour: '2-digit', minute: '2-digit' }) } catch { return '--:--' } })()
+ return `[${time}] ${item.action_detail || item.event_type.replace(/_/g, ' ')}`
+ }))
+ })
+ .catch(() => {})
+ }, [API_BASE])
+
+ return (
+
+
+ {logs.length === 0 ? (
+
[--:--] Scanning cluster state...
+ ) : (
+ logs.map((line, i) => (
+
+ {i < logs.length - 1 ? (
+ <>{line.slice(0, 7)}{line.slice(7)}>
+ ) : (
+ {line} ▎
+ )}
+
+ ))
+ )}
+
+ )
+}
+
export default OpenClawPanel