#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Auto-Repair Library v1.0 一键修复脚本库 — 根据诊断上下文自动匹配修复方案 用法: from auto_repair import match_repair, RepairAction ctx = {"status": "stopped", "source": "cli"} action = match_repair(ctx) if action: print(action.description) action.execute() # 执行修复 """ import subprocess import logging from typing import Optional, Dict, List from dataclasses import dataclass, field logger = logging.getLogger(__name__) # ============================================================ # 修复动作定义 # ============================================================ @dataclass class RepairAction: """一个可执行的修复动作""" issue: str # 问题标识 description: str # 人类可读描述 risk: str # low / medium / high auto_approve: bool = False # 是否可自动执行(无需用户确认) commands: List[str] = field(default_factory=list) # PowerShell 命令列表 def execute(self) -> bool: """执行修复命令,返回是否成功""" for cmd in self.commands: try: logger.info(f"执行修复: {self.issue} → {cmd}") result = subprocess.run(["powershell", "-Command", cmd], capture_output=True, text=True, timeout=30) if result.returncode != 0: logger.warning(f"修复命令失败: {result.stderr[:100]}") return False except Exception as e: logger.error(f"修复异常: {e}") return False logger.info(f"修复完成: {self.issue}") return True def summary(self) -> str: return f"[{self.risk}] {self.description}" # ============================================================ # 修复方案库 # ============================================================ REPAIR_PLANS: List[Dict] = [ # ---- Gateway 停止 ---- { "match": {"status": "stopped"}, "actions": [ RepairAction( issue="gateway_stopped", description="Gateway 进程未运行,尝试重启", risk="low", auto_approve=False, commands=["openclaw gateway restart"] ), ] }, # ---- RPC 连接失败 ---- { "match": {"rpc_ok": False}, "actions": [ RepairAction( issue="rpc_failed", description="Gateway RPC 连接异常,尝试重启", risk="medium", auto_approve=False, commands=["openclaw gateway restart"] ), ] }, # ---- CLI 检查失败 ---- { "match": {"source": "cli", "status": "cli_error"}, "actions": [ RepairAction( issue="cli_unavailable", description="openclaw CLI 不可用,检查安装路径", risk="low", auto_approve=True, commands=[ "Get-Command openclaw -ErrorAction SilentlyContinue | Select-Object Source" ] ), ] }, # ---- HTTP API 不可达(Gateway 未响应)---- { "match": {"source": "http", "status": "unreachable"}, "actions": [ RepairAction( issue="gateway_unreachable", description="Gateway HTTP API 不可达,检查端口并尝试重启", risk="medium", auto_approve=False, commands=[ "Test-NetConnection -ComputerName 127.0.0.1 -Port 18788 -WarningAction SilentlyContinue | Select-Object TcpTestSucceeded", "openclaw gateway restart" ] ), ] }, # ---- 配置错误 ---- { "match": {"source": "sim", "status": "healthy"}, "actions": [] # 模拟健康检查,无修复必要 }, ] # ============================================================ # 匹配引擎 # ============================================================ def match_repair(ctx: Dict[str, any]) -> Optional[List[RepairAction]]: """ 根据诊断上下文匹配修复方案。 返回匹配的 RepairAction 列表,无匹配则返回 None。 """ if not ctx: return None best_score = 0 best_actions = None for plan in REPAIR_PLANS: match = plan["match"] score = 0 total = len(match) for key, expected in match.items(): actual = ctx.get(key) if actual == expected: score += 1 elif isinstance(expected, (list, tuple)) and actual in expected: score += 1 elif actual and str(expected).lower() in str(actual).lower(): score += 0.5 # 匹配度:至少有一半字段匹配 if total > 0 and score / total >= 0.5 and score > best_score: best_score = score best_actions = plan["actions"] return best_actions if best_actions else None def format_repair_suggestion(actions: List[RepairAction]) -> str: """将修复方案格式化为可读文本""" if not actions: return "未找到匹配的自动修复方案,需要人工诊断。" lines = ["**🔧 自动修复方案**\n"] for action in actions: risk_emoji = {"low": "✅", "medium": "⚠️", "high": "🔴"}.get(action.risk, "❓") auto = "可自动执行" if action.auto_approve else "需要确认" lines.append(f"- {risk_emoji} **{action.description}** ({auto})") lines.append(f"\n共有 {len(actions)} 个修复步骤") return "\n".join(lines) # ============================================================ # 快速测试 # ============================================================ if __name__ == "__main__": # 测试样例 test_cases = [ {"status": "stopped", "source": "cli"}, {"rpc_ok": False, "source": "cli"}, {"status": "unreachable", "source": "http"}, {"status": "running", "rpc_ok": True, "source": "cli"}, ] for ctx in test_cases: actions = match_repair(ctx) print(f"\n诊断上下文: {ctx}") print(f"匹配结果: {format_repair_suggestion(actions) if actions else '无匹配'}")