每日备份 2026-06-10 12:57:13
This commit is contained in:
1 parent
fda2ccb8c2
commit
3d2e638c6a
244 files changed
+58890
-3
No files matched your search
+509
@@ -0,0 +1,509 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
OpenClaw 问题诊断工具
|
||||
仅识别问题,不自动修复
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import psutil
|
||||
import socket
|
||||
import requests
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
class OpenClawDiagnoser:
|
||||
def __init__(self):
|
||||
self.problems = []
|
||||
self.config_path = Path.home() / ".openclaw" / "openclaw.json"
|
||||
self.log_dir = Path.home() / ".openclaw"
|
||||
self.tmp_dir = Path("/tmp")
|
||||
|
||||
def log_problem(self, severity, category, message, evidence=None):
|
||||
"""记录发现的问题"""
|
||||
problem = {
|
||||
"severity": severity, # CRITICAL, HIGH, MEDIUM, LOW, INFO
|
||||
"category": category,
|
||||
"message": message,
|
||||
"evidence": evidence,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}
|
||||
self.problems.append(problem)
|
||||
print(f"[{severity}] {category}: {message}")
|
||||
if evidence:
|
||||
print(f" 证据: {evidence}")
|
||||
|
||||
def check_gateway_process(self):
|
||||
"""检查网关进程状态"""
|
||||
print("\n🔍 检查网关进程...")
|
||||
gateway_processes = []
|
||||
|
||||
for proc in psutil.process_iter(['pid', 'name', 'cmdline', 'status']):
|
||||
try:
|
||||
if proc.info['cmdline']:
|
||||
cmdline = ' '.join(proc.info['cmdline'])
|
||||
if 'openclaw' in cmdline.lower() and 'gateway' in cmdline.lower():
|
||||
gateway_processes.append({
|
||||
'pid': proc.info['pid'],
|
||||
'cmdline': cmdline,
|
||||
'status': proc.info['status']
|
||||
})
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
continue
|
||||
|
||||
if not gateway_processes:
|
||||
self.log_problem("CRITICAL", "进程", "未找到OpenClaw网关进程")
|
||||
return False
|
||||
|
||||
print(f"找到 {len(gateway_processes)} 个网关进程:")
|
||||
for proc in gateway_processes:
|
||||
print(f" PID: {proc['pid']}, 状态: {proc['status']}")
|
||||
|
||||
# 检查进程状态
|
||||
if proc['status'] not in ['running', 'sleeping']:
|
||||
self.log_problem("HIGH", "进程", f"进程状态异常: {proc['status']}",
|
||||
f"PID: {proc['pid']}")
|
||||
|
||||
return True
|
||||
|
||||
def check_port_listening(self):
|
||||
"""检查端口监听状态"""
|
||||
print("\n🔍 检查端口监听...")
|
||||
port = 18789
|
||||
|
||||
try:
|
||||
# 检查端口是否被占用
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(1)
|
||||
result = sock.connect_ex(('127.0.0.1', port))
|
||||
sock.close()
|
||||
|
||||
if result == 0:
|
||||
print(f"✅ 端口 {port} 正在监听")
|
||||
|
||||
# 查看哪个进程在监听
|
||||
try:
|
||||
output = subprocess.check_output(
|
||||
f"ss -tulpn | grep :{port}",
|
||||
shell=True,
|
||||
text=True
|
||||
).strip()
|
||||
if output:
|
||||
print(f" 监听进程: {output}")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
self.log_problem("HIGH", "网络", f"端口 {port} 未监听",
|
||||
"网关可能未启动或绑定失败")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
self.log_problem("MEDIUM", "网络", f"检查端口时出错: {str(e)}")
|
||||
|
||||
return True
|
||||
|
||||
def check_web_access(self):
|
||||
"""检查Web服务访问"""
|
||||
print("\n🔍 检查Web服务...")
|
||||
urls = [
|
||||
("http://localhost:18789/", "本地访问"),
|
||||
("http://127.0.0.1:18789/", "本地回环"),
|
||||
]
|
||||
|
||||
accessible = False
|
||||
for url, desc in urls:
|
||||
try:
|
||||
response = requests.get(url, timeout=5)
|
||||
if response.status_code == 200:
|
||||
print(f"✅ {desc}: 成功 (HTTP {response.status_code})")
|
||||
accessible = True
|
||||
|
||||
# 检查返回内容
|
||||
if "openclaw" in response.text.lower():
|
||||
print(" 页面包含 'openclaw'")
|
||||
else:
|
||||
self.log_problem("MEDIUM", "服务", "返回页面可能不是OpenClaw控制台")
|
||||
else:
|
||||
print(f"⚠️ {desc}: HTTP {response.status_code}")
|
||||
self.log_problem("MEDIUM", "服务", f"{desc}返回异常状态码",
|
||||
f"URL: {url}, 状态码: {response.status_code}")
|
||||
except requests.ConnectionError:
|
||||
print(f"❌ {desc}: 连接失败")
|
||||
if desc == "本地访问":
|
||||
self.log_problem("HIGH", "服务", "无法连接到本地网关", url)
|
||||
except requests.Timeout:
|
||||
print(f"❌ {desc}: 连接超时")
|
||||
self.log_problem("MEDIUM", "服务", "连接网关超时", url)
|
||||
except Exception as e:
|
||||
print(f"❌ {desc}: 错误 {str(e)}")
|
||||
|
||||
return accessible
|
||||
|
||||
def check_lock_files(self):
|
||||
"""检查锁文件"""
|
||||
print("\n🔍 检查锁文件...")
|
||||
lock_files = []
|
||||
|
||||
# 检查配置目录
|
||||
if self.log_dir.exists():
|
||||
lock_patterns = ['*.lock', '*lock*']
|
||||
for pattern in lock_patterns:
|
||||
for lock_file in self.log_dir.rglob(pattern):
|
||||
if lock_file.is_file():
|
||||
lock_files.append(str(lock_file))
|
||||
|
||||
# 检查/tmp目录
|
||||
for pattern in ['openclaw*lock*', '*openclaw*lock*']:
|
||||
for lock_file in self.tmp_dir.glob(pattern):
|
||||
if lock_file.is_file():
|
||||
lock_files.append(str(lock_file))
|
||||
|
||||
if lock_files:
|
||||
print(f"⚠️ 发现 {len(lock_files)} 个锁文件:")
|
||||
for lock in sorted(lock_files)[:10]: # 只显示前10个
|
||||
print(f" {lock}")
|
||||
|
||||
if len(lock_files) > 10:
|
||||
print(f" ... 还有 {len(lock_files)-10} 个未显示")
|
||||
|
||||
if len(lock_files) > 5:
|
||||
self.log_problem("HIGH", "文件锁", f"发现 {len(lock_files)} 个锁文件",
|
||||
"过多的锁文件可能导致死锁")
|
||||
elif len(lock_files) > 0:
|
||||
self.log_problem("MEDIUM", "文件锁", f"发现 {len(lock_files)} 个锁文件")
|
||||
else:
|
||||
print("✅ 未发现锁文件")
|
||||
|
||||
return len(lock_files)
|
||||
|
||||
def check_config_file(self):
|
||||
"""检查配置文件"""
|
||||
print("\n🔍 检查配置文件...")
|
||||
|
||||
if not self.config_path.exists():
|
||||
self.log_problem("CRITICAL", "配置", "配置文件不存在", str(self.config_path))
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(self.config_path, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
print(f"✅ 配置文件存在: {self.config_path}")
|
||||
print(f" 文件大小: {os.path.getsize(self.config_path)} 字节")
|
||||
|
||||
# 检查关键配置项
|
||||
required_sections = ['models', 'agents']
|
||||
for section in required_sections:
|
||||
if section in config:
|
||||
print(f" ✅ 包含 {section} 配置")
|
||||
else:
|
||||
self.log_problem("HIGH", "配置", f"缺少关键配置项: {section}")
|
||||
|
||||
# 检查模型配置
|
||||
if 'models' in config and 'providers' in config['models']:
|
||||
providers = config['models']['providers']
|
||||
print(f" 配置了 {len(providers)} 个模型提供商")
|
||||
|
||||
for provider, details in providers.items():
|
||||
if provider == 'deepseek':
|
||||
if 'apiKey' in details:
|
||||
key_len = len(details['apiKey'])
|
||||
masked = details['apiKey'][:4] + '...' + details['apiKey'][-4:] if key_len > 8 else '***'
|
||||
print(f" DeepSeek API密钥: {masked}")
|
||||
else:
|
||||
self.log_problem("HIGH", "配置", "DeepSeek缺少API密钥")
|
||||
|
||||
return True
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
self.log_problem("CRITICAL", "配置", "配置文件JSON格式错误", str(e))
|
||||
return False
|
||||
except Exception as e:
|
||||
self.log_problem("HIGH", "配置", "读取配置文件时出错", str(e))
|
||||
return False
|
||||
|
||||
def check_model_connectivity(self):
|
||||
"""检查模型连接性"""
|
||||
print("\n🔍 检查模型连接性...")
|
||||
|
||||
# 从配置文件读取API密钥
|
||||
api_key = None
|
||||
try:
|
||||
if self.config_path.exists():
|
||||
with open(self.config_path, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
if 'models' in config and 'providers' in config['models']:
|
||||
providers = config['models']['providers']
|
||||
if 'deepseek' in providers and 'apiKey' in providers['deepseek']:
|
||||
api_key = providers['deepseek']['apiKey']
|
||||
except:
|
||||
pass
|
||||
|
||||
if not api_key:
|
||||
print("⚠️ 未找到DeepSeek API密钥,跳过连接测试")
|
||||
return
|
||||
|
||||
# 测试DeepSeek API
|
||||
print("测试DeepSeek API连接...")
|
||||
try:
|
||||
headers = {
|
||||
'Authorization': f'Bearer {api_key}',
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
data = {
|
||||
'model': 'deepseek-chat',
|
||||
'messages': [{'role': 'user', 'content': 'ping'}],
|
||||
'max_tokens': 5
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
'https://api.deepseek.com/v1/chat/completions',
|
||||
headers=headers,
|
||||
json=data,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
print(f"✅ DeepSeek API: 连接成功 (HTTP 200)")
|
||||
try:
|
||||
result = response.json()
|
||||
if 'choices' in result and len(result['choices']) > 0:
|
||||
print(f" 模型响应正常")
|
||||
else:
|
||||
self.log_problem("MEDIUM", "模型", "API返回异常格式")
|
||||
except:
|
||||
print(f" API返回: {response.text[:100]}...")
|
||||
elif response.status_code == 401:
|
||||
self.log_problem("CRITICAL", "模型", "DeepSeek API密钥无效或过期", "HTTP 401")
|
||||
elif response.status_code == 429:
|
||||
self.log_problem("HIGH", "模型", "DeepSeek API调用频率限制", "HTTP 429")
|
||||
elif response.status_code == 500:
|
||||
self.log_problem("MEDIUM", "模型", "DeepSeek服务器内部错误", "HTTP 500")
|
||||
else:
|
||||
self.log_problem("MEDIUM", "模型", f"DeepSeek API返回异常状态码",
|
||||
f"HTTP {response.status_code}")
|
||||
|
||||
except requests.Timeout:
|
||||
self.log_problem("HIGH", "模型", "DeepSeek API连接超时", "10秒超时")
|
||||
except requests.ConnectionError:
|
||||
self.log_problem("HIGH", "模型", "无法连接到DeepSeek API", "网络连接失败")
|
||||
except Exception as e:
|
||||
self.log_problem("MEDIUM", "模型", f"测试DeepSeek API时出错: {str(e)}")
|
||||
|
||||
def check_system_resources(self):
|
||||
"""检查系统资源"""
|
||||
print("\n🔍 检查系统资源...")
|
||||
|
||||
# 内存使用
|
||||
memory = psutil.virtual_memory()
|
||||
print(f"内存: {memory.percent}% 已使用 ({memory.used//1024//1024}MB/{memory.total//1024//1024}MB)")
|
||||
|
||||
if memory.percent > 90:
|
||||
self.log_problem("HIGH", "资源", "系统内存使用过高", f"{memory.percent}% 已使用")
|
||||
elif memory.percent > 80:
|
||||
self.log_problem("MEDIUM", "资源", "系统内存使用较高", f"{memory.percent}% 已使用")
|
||||
|
||||
# 查找OpenClaw进程内存使用
|
||||
openclaw_memory = 0
|
||||
for proc in psutil.process_iter(['pid', 'name', 'memory_info']):
|
||||
try:
|
||||
if 'openclaw' in proc.info['name'].lower():
|
||||
mem = proc.info['memory_info']
|
||||
if mem:
|
||||
openclaw_memory += mem.rss
|
||||
except:
|
||||
continue
|
||||
|
||||
if openclaw_memory > 0:
|
||||
print(f"OpenClaw进程内存: {openclaw_memory//1024//1024}MB")
|
||||
if openclaw_memory > 1024*1024*1024: # 超过1GB
|
||||
self.log_problem("HIGH", "资源", "OpenClaw内存使用过高",
|
||||
f"{openclaw_memory//1024//1024}MB")
|
||||
|
||||
def check_log_files(self):
|
||||
"""检查日志文件"""
|
||||
print("\n🔍 检查日志文件...")
|
||||
|
||||
log_files = []
|
||||
error_patterns = ['error', 'fail', 'timeout', 'locked', 'exception', 'crash']
|
||||
|
||||
# 查找日志文件
|
||||
log_patterns = ['*.log', 'gateway*.log', 'openclaw*.log']
|
||||
for pattern in log_patterns:
|
||||
# 检查 ~/.openclaw
|
||||
for log_file in self.log_dir.rglob(pattern):
|
||||
if log_file.is_file():
|
||||
log_files.append(log_file)
|
||||
|
||||
# 检查 /tmp/openclaw
|
||||
tmp_log_dir = self.tmp_dir / "openclaw"
|
||||
if tmp_log_dir.exists():
|
||||
for log_file in tmp_log_dir.rglob(pattern):
|
||||
if log_file.is_file():
|
||||
log_files.append(log_file)
|
||||
|
||||
if log_files:
|
||||
print(f"找到 {len(log_files)} 个日志文件")
|
||||
recent_errors = []
|
||||
|
||||
for log_file in sorted(log_files, key=lambda x: x.stat().st_mtime, reverse=True)[:3]:
|
||||
print(f" {log_file} (修改时间: {datetime.fromtimestamp(log_file.stat().st_mtime)})")
|
||||
|
||||
# 检查最近错误
|
||||
try:
|
||||
with open(log_file, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
lines = f.readlines()[-100:] # 最后100行
|
||||
|
||||
errors = []
|
||||
for i, line in enumerate(lines):
|
||||
line_lower = line.lower()
|
||||
if any(pattern in line_lower for pattern in error_patterns):
|
||||
# 获取上下文
|
||||
context_start = max(0, i-1)
|
||||
context_end = min(len(lines), i+2)
|
||||
context = ''.join(lines[context_start:context_end])
|
||||
errors.append(context.strip())
|
||||
|
||||
if errors:
|
||||
recent_errors.extend(errors[-3:]) # 最多记录3个错误
|
||||
|
||||
except Exception as e:
|
||||
print(f" 读取日志失败: {str(e)}")
|
||||
|
||||
if recent_errors:
|
||||
print(f" 发现 {len(recent_errors)} 个最近错误")
|
||||
for err in recent_errors[:3]:
|
||||
print(f" - {err[:200]}...")
|
||||
self.log_problem("MEDIUM", "日志", "日志中发现错误信息",
|
||||
f"共 {len(recent_errors)} 个错误")
|
||||
else:
|
||||
print("未找到日志文件")
|
||||
|
||||
def run_diagnostics(self):
|
||||
"""运行所有诊断"""
|
||||
print("="*60)
|
||||
print("OpenClaw 问题诊断工具")
|
||||
print(f"运行时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
print("="*60)
|
||||
|
||||
# 运行所有检查
|
||||
checks = [
|
||||
("系统资源", self.check_system_resources),
|
||||
("配置文件", self.check_config_file),
|
||||
("网关进程", self.check_gateway_process),
|
||||
("端口监听", self.check_port_listening),
|
||||
("Web服务", self.check_web_access),
|
||||
("文件锁", self.check_lock_files),
|
||||
("模型连接", self.check_model_connectivity),
|
||||
("日志文件", self.check_log_files),
|
||||
]
|
||||
|
||||
for name, check_func in checks:
|
||||
try:
|
||||
check_func()
|
||||
except Exception as e:
|
||||
print(f"❌ 检查 {name} 时出错: {str(e)}")
|
||||
|
||||
# 生成报告
|
||||
self.generate_report()
|
||||
|
||||
def generate_report(self):
|
||||
"""生成诊断报告"""
|
||||
print("\n" + "="*60)
|
||||
print("诊断报告摘要")
|
||||
print("="*60)
|
||||
|
||||
if not self.problems:
|
||||
print("✅ 未发现严重问题")
|
||||
return
|
||||
|
||||
# 按严重程度分组
|
||||
severity_order = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO']
|
||||
problems_by_severity = {severity: [] for severity in severity_order}
|
||||
|
||||
for problem in self.problems:
|
||||
problems_by_severity[problem['severity']].append(problem)
|
||||
|
||||
# 输出摘要
|
||||
for severity in severity_order:
|
||||
problems = problems_by_severity[severity]
|
||||
if problems:
|
||||
print(f"\n{severity}级别问题 ({len(problems)}个):")
|
||||
for i, problem in enumerate(problems, 1):
|
||||
print(f" {i}. {problem['category']}: {problem['message']}")
|
||||
|
||||
# 保存详细报告
|
||||
report_path = Path.home() / ".openclaw" / f"diagnosis_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
||||
try:
|
||||
report = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"summary": {
|
||||
"total_problems": len(self.problems),
|
||||
"critical": len(problems_by_severity['CRITICAL']),
|
||||
"high": len(problems_by_severity['HIGH']),
|
||||
"medium": len(problems_by_severity['MEDIUM']),
|
||||
"low": len(problems_by_severity['LOW']),
|
||||
},
|
||||
"problems": self.problems
|
||||
}
|
||||
|
||||
with open(report_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(report, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"\n📄 详细报告已保存到: {report_path}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"保存报告失败: {str(e)}")
|
||||
|
||||
def get_recommendations(self):
|
||||
"""根据问题提供建议"""
|
||||
recommendations = []
|
||||
|
||||
for problem in self.problems:
|
||||
if problem['severity'] in ['CRITICAL', 'HIGH']:
|
||||
if "未找到OpenClaw网关进程" in problem['message']:
|
||||
recommendations.append("建议: 启动网关 - `openclaw gateway --force &`")
|
||||
elif "端口未监听" in problem['message']:
|
||||
recommendations.append("建议: 检查网关是否启动,或尝试不同端口")
|
||||
elif "DeepSeek API密钥无效" in problem['message']:
|
||||
recommendations.append("建议: 更新DeepSeek API密钥 - `openclaw config set provider.deepseek.api_key '新密钥'`")
|
||||
elif "文件锁" in problem['message'] and "过多" in problem['message']:
|
||||
recommendations.append("建议: 清理锁文件 - `find ~/.openclaw -name '*.lock' -delete`")
|
||||
|
||||
if recommendations:
|
||||
print("\n💡 建议操作:")
|
||||
for i, rec in enumerate(recommendations, 1):
|
||||
print(f" {i}. {rec}")
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
diagnoser = OpenClawDiagnoser()
|
||||
|
||||
try:
|
||||
diagnoser.run_diagnostics()
|
||||
diagnoser.get_recommendations()
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n诊断被用户中断")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"诊断过程中发生错误: {str(e)}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 检查Python版本和依赖
|
||||
if sys.version_info < (3, 6):
|
||||
print("需要Python 3.6或更高版本")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
import psutil
|
||||
import requests
|
||||
except ImportError as e:
|
||||
print(f"缺少依赖: {e}")
|
||||
print("请安装: pip install psutil requests")
|
||||
sys.exit(1)
|
||||
|
||||
main()
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
echo "=== OpenClaw快速修复 ==="
|
||||
openclaw doctor --fix
|
||||
|
||||
# 1. 清理锁文件
|
||||
echo "清理锁文件..."
|
||||
find /root/.openclaw -name "*.lock" -delete 2>/dev/null
|
||||
LOCK_COUNT=$(find /root/.openclaw -name "*.lock" 2>/dev/null | wc -l)
|
||||
echo "剩余锁文件: $LOCK_COUNT"
|
||||
|
||||
# 2. 重启网关
|
||||
echo "重启网关..."
|
||||
openclaw gateway stop 2>/dev/null || true
|
||||
sleep 2
|
||||
openclaw gateway --force > /tmp/openclaw_restart.log 2>&1 &
|
||||
|
||||
# 3. 等待启动
|
||||
echo "等待启动..."
|
||||
for i in {1..10}; do
|
||||
if curl -s http://localhost:18789/ > /dev/null 2>&1; then
|
||||
echo "✅ 第${i}秒: 网关启动成功"
|
||||
break
|
||||
fi
|
||||
echo "⏳ 第${i}秒: 启动中..."
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# 4. 测试
|
||||
echo "测试对话..."
|
||||
openclaw agent --to "fix_test_$(date +%s)" --message "你刚刚从无法运行中被我手动恢复,所以你之前的操作可能导致了灾难的后果,现在停止一切对你身体的修改和任务。" --local --timeout 20
|
||||
|
||||
echo "修复完成!"
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
echo "=== OpenClaw 加强修复 (2026-02-26 版) ==="
|
||||
|
||||
# 0. 先杀干净所有可能残留(最重要,避免鬼进程)
|
||||
echo "强制杀死所有 OpenClaw / node 相关进程..."
|
||||
pkill -9 -f "openclaw|clawdbot|gateway|molt" 2>/dev/null
|
||||
pkill -9 -f node 2>/dev/null # 如果你确定没其他 node 程序在跑才安全,否则注释掉这行
|
||||
sleep 2
|
||||
|
||||
# 检查端口是否真释放
|
||||
echo "检查 18789 端口占用:"
|
||||
sudo lsof -i :18789 || sudo netstat -tuln | grep 18789 || echo "端口已释放"
|
||||
|
||||
# 1. 运行 doctor --fix(官方推荐修复工具)
|
||||
echo "运行 openclaw doctor --fix ..."
|
||||
openclaw doctor --fix || openclaw doctor --repair --force
|
||||
|
||||
# 2. 清理更多可能导致崩溃的东西
|
||||
echo "清理锁、旧 session、缓存..."
|
||||
find ~/.openclaw -name "*.lock" -delete 2>/dev/null
|
||||
rm -rf ~/.openclaw/sessions/* 2>/dev/null # 如果你不怕丢对话历史
|
||||
openclaw system clean --cache 2>/dev/null || true
|
||||
|
||||
# 3. 以后台 + verbose + force 方式启动(方便看崩溃原因)
|
||||
echo "启动 gateway(前台 verbose + force)..."
|
||||
openclaw gateway stop 2>/dev/null || true
|
||||
sleep 2
|
||||
openclaw gateway --force --verbose --port 18789 > /tmp/gateway_crash.log 2>&1 &
|
||||
|
||||
# 4. 等待并监控(延长到 30 秒,看是否秒崩)
|
||||
echo "等待 gateway 稳定启动(最多 30 秒)..."
|
||||
for i in {1..30}; do
|
||||
if curl -s --connect-timeout 2 http://127.0.0.1:18789/ > /dev/null; then
|
||||
echo "✅ 端口已监听(第 ${i} 秒)"
|
||||
# 再等 8 秒看会不会掉
|
||||
sleep 8
|
||||
if curl -s http://127.0.0.1:18789/ > /dev/null; then
|
||||
echo "✅ 稳定!(没在 8 秒内崩溃)"
|
||||
break
|
||||
else
|
||||
echo "❌ 端口又消失了 → 崩溃了"
|
||||
fi
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# 5. 如果起来了,测试
|
||||
if curl -s http://127.0.0.1:18789/ > /dev/null; then
|
||||
echo "测试 agent 对话..."
|
||||
openclaw agent --to "fix_test_$(date +%s)" --message "测试:你现在状态正常吗?请回复 '爪子已复活,汪!'" --local --timeout 25
|
||||
else
|
||||
echo "❌ Gateway 没起来,看日志:"
|
||||
echo "tail -n 80 /tmp/gateway_crash.log"
|
||||
echo "请把上面日志贴给我,我帮你看具体崩溃原因。"
|
||||
fi
|
||||
|
||||
echo "修复结束。如果还是不行,请提供:"
|
||||
echo "1. /tmp/gateway_crash.log 的最后 30 行"
|
||||
echo "2. openclaw status --all 的输出"
|
||||
echo "3. journalctl --user -u openclaw-gateway -n 50 (如果用了 systemd)"
|
||||
Executable
+242
@@ -0,0 +1,242 @@
|
||||
#!/bin/bash
|
||||
echo "=== OpenClaw增强修复 ==="
|
||||
echo "修复时间: $(date)"
|
||||
echo ""
|
||||
|
||||
# 1. 备份当前状态
|
||||
echo "1. 📊 备份当前状态..."
|
||||
CONFIG_FILE="/root/.openclaw/openclaw.json"
|
||||
BACKUP_DIR="/root/.openclaw/backup"
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
|
||||
# 备份配置文件
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
BACKUP_FILE="${BACKUP_DIR}/openclaw.json.backup_$(date +%Y%m%d_%H%M%S)"
|
||||
cp "$CONFIG_FILE" "$BACKUP_FILE"
|
||||
echo " 配置文件备份到: $BACKUP_FILE"
|
||||
fi
|
||||
|
||||
# 备份进程信息
|
||||
echo " 当前进程:"
|
||||
ps aux | grep -E "(openclaw|claw)" | grep -v grep || echo " 无相关进程"
|
||||
|
||||
# 2. 清理无效配置字段
|
||||
echo ""
|
||||
echo "2. 🧹 清理无效配置字段..."
|
||||
if [ -f "$CONFIG_FILE" ]; then
|
||||
echo " 运行配置修复..."
|
||||
openclaw doctor --fix 2>&1 | grep -v "autoSelectFamily" | head -20
|
||||
|
||||
# 如果doctor失败,手动清理
|
||||
if openclaw config validate 2>&1 | grep -q "invalid"; then
|
||||
echo " 手动清理无效字段..."
|
||||
python3 << 'PYEOF'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
config_file = '/root/.openclaw/openclaw.json'
|
||||
if not os.path.exists(config_file):
|
||||
print("配置文件不存在")
|
||||
sys.exit(0)
|
||||
|
||||
with open(config_file, 'r') as f:
|
||||
config = json.load(f)
|
||||
|
||||
# 清理已知无效字段
|
||||
invalid_fields = []
|
||||
|
||||
def clean_invalid(obj, path=''):
|
||||
if isinstance(obj, dict):
|
||||
keys_to_remove = []
|
||||
for key in list(obj.keys()):
|
||||
current_path = f'{path}.{key}' if path else key
|
||||
|
||||
# 检查Telegram相关无效字段
|
||||
if 'telegram' in str(key).lower():
|
||||
if 'capabilities' in str(key).lower():
|
||||
if isinstance(obj[key], dict):
|
||||
# 只保留有效的capabilities
|
||||
valid_caps = {}
|
||||
if 'inlineButtons' in obj[key]:
|
||||
valid_caps['inlineButtons'] = obj[key]['inlineButtons']
|
||||
obj[key] = valid_caps
|
||||
elif 'defaultTarget' in str(key).lower():
|
||||
# 记录但暂时保留
|
||||
invalid_fields.append(current_path)
|
||||
elif 'polling' in str(key).lower():
|
||||
invalid_fields.append(current_path)
|
||||
|
||||
# 递归清理
|
||||
if isinstance(obj[key], (dict, list)):
|
||||
clean_invalid(obj[key], current_path)
|
||||
|
||||
# 移除明确无效的字段
|
||||
for key in ['defaultTarget', 'polling']:
|
||||
if key in obj:
|
||||
del obj[key]
|
||||
invalid_fields.append(f'{path}.{key}' if path else key)
|
||||
|
||||
elif isinstance(obj, list):
|
||||
for i, item in enumerate(obj):
|
||||
clean_invalid(item, f'{path}[{i}]')
|
||||
|
||||
clean_invalid(config)
|
||||
|
||||
if invalid_fields:
|
||||
print(f" 清理了 {len(invalid_fields)} 个无效字段:")
|
||||
for field in invalid_fields[:10]: # 只显示前10个
|
||||
print(f" - {field}")
|
||||
|
||||
# 确保有基本的Telegram配置
|
||||
if 'channels' not in config:
|
||||
config['channels'] = {}
|
||||
|
||||
if 'telegram' not in config['channels']:
|
||||
config['channels']['telegram'] = {
|
||||
'enabled': True,
|
||||
'token': '8529719199:AAGjLDflvMXAEdgrBTSBEczOoSMybLU4gcs'
|
||||
}
|
||||
elif 'enabled' not in config['channels']['telegram']:
|
||||
config['channels']['telegram']['enabled'] = True
|
||||
|
||||
# 保存清理后的配置
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(config, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print("✅ 配置清理完成")
|
||||
PYEOF
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. 停止所有相关进程
|
||||
echo ""
|
||||
echo "3. 🛑 停止所有OpenClaw进程..."
|
||||
pkill -f "openclaw" 2>/dev/null
|
||||
sleep 2
|
||||
|
||||
# 确保进程停止
|
||||
if pgrep -f "openclaw" > /dev/null; then
|
||||
echo " 强制停止剩余进程..."
|
||||
pkill -9 -f "openclaw" 2>/dev/null
|
||||
sleep 1
|
||||
fi
|
||||
|
||||
# 4. 清理锁文件和临时文件
|
||||
echo ""
|
||||
echo "4. 🗑️ 清理临时文件..."
|
||||
find /root/.openclaw -name "*.lock" -delete 2>/dev/null
|
||||
find /tmp -name "*openclaw*" -type f -delete 2>/dev/null 2>/dev/null
|
||||
LOCK_COUNT=$(find /root/.openclaw -name "*.lock" 2>/dev/null | wc -l)
|
||||
echo " 剩余锁文件: $LOCK_COUNT"
|
||||
|
||||
# 5. 启动网关
|
||||
echo ""
|
||||
echo "5. 🚀 启动网关..."
|
||||
LOG_FILE="/tmp/openclaw_restart_$(date +%Y%m%d_%H%M%S).log"
|
||||
echo " 日志文件: $LOG_FILE"
|
||||
|
||||
# 启动网关并记录日志
|
||||
openclaw gateway --force --port 18789 > "$LOG_FILE" 2>&1 &
|
||||
GATEWAY_PID=$!
|
||||
echo " 网关PID: $GATEWAY_PID"
|
||||
|
||||
# 6. 等待启动
|
||||
echo ""
|
||||
echo "6. ⏳ 等待网关启动..."
|
||||
START_SUCCESS=false
|
||||
for i in {1..20}; do
|
||||
# 检查进程是否存在
|
||||
if ! ps -p "$GATEWAY_PID" > /dev/null 2>&1; then
|
||||
echo " ❌ 第${i}秒: 网关进程已退出"
|
||||
echo " 查看日志:"
|
||||
tail -20 "$LOG_FILE"
|
||||
break
|
||||
fi
|
||||
|
||||
# 检查端口监听
|
||||
if netstat -tlnp 2>/dev/null | grep -q ":18789 "; then
|
||||
# 检查Web服务
|
||||
if curl -s http://localhost:18789/ > /dev/null 2>&1; then
|
||||
echo " ✅ 第${i}秒: 网关启动成功"
|
||||
START_SUCCESS=true
|
||||
break
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $i -le 10 ]; then
|
||||
echo " ⏳ 第${i}秒: 启动中..."
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
# 7. 验证修复
|
||||
echo ""
|
||||
echo "7. ✅ 验证修复..."
|
||||
|
||||
if [ "$START_SUCCESS" = true ]; then
|
||||
echo " a) 检查网关状态:"
|
||||
echo " 进程: $(ps -p $GATEWAY_PID -o pid,cmd --no-headers 2>/dev/null || echo '无')"
|
||||
echo " 端口: $(netstat -tlnp 2>/dev/null | grep 18789 | wc -l)个监听"
|
||||
|
||||
echo ""
|
||||
echo " b) 测试基本功能:"
|
||||
# 测试Web访问
|
||||
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:18789/ 2>/dev/null || echo "无法连接")
|
||||
echo " Web服务: HTTP $HTTP_STATUS"
|
||||
|
||||
echo ""
|
||||
echo " c) 测试Telegram发送:"
|
||||
echo " 测试发送消息到Telegram..."
|
||||
TELEGRAM_TEST=$(timeout 15 openclaw message send --channel telegram --target "5117446326" --message "修复测试 $(date '+%H:%M:%S')" 2>&1 | tail -5)
|
||||
if echo "$TELEGRAM_TEST" | grep -q "✅\|Sent"; then
|
||||
echo " ✅ Telegram测试成功"
|
||||
else
|
||||
echo " ℹ️ Telegram测试结果:"
|
||||
echo "$TELEGRAM_TEST" | sed 's/^/ /'
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo " d) 系统状态:"
|
||||
openclaw channels status 2>&1 | grep -A 5 -B 2 "Telegram" || echo " Telegram状态检查失败"
|
||||
|
||||
else
|
||||
echo " ❌ 网关启动失败"
|
||||
echo " 最后20行日志:"
|
||||
tail -20 "$LOG_FILE"
|
||||
|
||||
# 尝试备用启动方式
|
||||
echo ""
|
||||
echo " 尝试备用启动方式..."
|
||||
openclaw gateway stop 2>/dev/null || true
|
||||
sleep 2
|
||||
nohup openclaw gateway --port 18789 > /tmp/openclaw_fallback.log 2>&1 &
|
||||
sleep 5
|
||||
|
||||
if curl -s http://localhost:18789/ > /dev/null 2>&1; then
|
||||
echo " ✅ 备用启动方式成功"
|
||||
else
|
||||
echo " ❌ 所有启动方式都失败"
|
||||
fi
|
||||
fi
|
||||
|
||||
# 8. 生成修复报告
|
||||
echo ""
|
||||
echo "8. 📋 修复报告:"
|
||||
echo "========================================"
|
||||
echo "修复时间: $(date)"
|
||||
echo "配置文件: $CONFIG_FILE"
|
||||
echo "网关状态: $([ "$START_SUCCESS" = true ] && echo "✅ 运行中" || echo "❌ 失败")"
|
||||
echo "Telegram: $(openclaw channels status 2>&1 | grep -q "Telegram.*enabled" && echo "✅ 已启用" || echo "❌ 未启用")"
|
||||
echo "锁文件数: $LOCK_COUNT"
|
||||
echo "========================================"
|
||||
|
||||
echo ""
|
||||
echo "🔧 后续操作建议:"
|
||||
echo " 1. 发送消息: openclaw message send --channel telegram --target 5117446326 --message '内容'"
|
||||
echo " 2. 查看状态: openclaw channels status"
|
||||
echo " 3. 查看日志: tail -f $LOG_FILE"
|
||||
echo " 4. 如果还有问题,查看备份配置: $BACKUP_FILE"
|
||||
|
||||
echo ""
|
||||
echo "🎉 修复完成!"
|
||||
Reference in new issue
Block a user