fix: 修复会话管理 API

- 改用直接读取会话文件方式替代命令调用
- 修复 sessions_list 命令不存在的错误
- 实现会话列表、详情、终止功能
This commit is contained in:
小白
2026-03-12 21:01:59 +08:00
parent 2f620fa4d0
commit a0cf95a960

View File

@@ -5,33 +5,79 @@
作者:小白 🐶
"""
import os
import json
import subprocess
import glob
from datetime import datetime
from flask import jsonify, request
from . import api
SESSIONS_DIR = '/root/.openclaw/agents/main/sessions'
@api.route('/sessions')
def get_sessions():
"""获取会话列表"""
try:
# 调用 sessions_list 命令
result = subprocess.run(
['sessions_list'],
capture_output=True,
text=True,
timeout=10
)
sessions = []
# 遍历会话目录
for filepath in glob.glob(os.path.join(SESSIONS_DIR, '*.jsonl')):
if filepath.endswith('.lock'):
continue
session_id = os.path.basename(filepath).replace('.jsonl', '')
# 获取文件信息
stat = os.stat(filepath)
updated_at = stat.st_mtime
size = stat.st_size
# 读取最后几行获取会话信息
try:
with open(filepath, 'r') as f:
lines = f.readlines()
total_tokens = 0
channel = 'unknown'
# 解析最后一条消息获取信息
if lines:
try:
last_line = lines[-1].strip()
if last_line:
data = json.loads(last_line)
if 'usage' in data:
total_tokens = data['usage'].get('total_tokens', 0)
except:
pass
messages_count = len([l for l in lines if l.strip()])
except:
messages_count = 0
total_tokens = 0
# 判断是否活跃5分钟内更新
is_active = (datetime.now().timestamp() - updated_at) < 300
sessions.append({
'id': session_id,
'key': f'agent:main:session:{session_id}',
'updatedAt': int(updated_at * 1000),
'updatedTime': datetime.fromtimestamp(updated_at).strftime('%Y-%m-%d %H:%M:%S'),
'size': size,
'messagesCount': messages_count,
'totalTokens': total_tokens,
'status': 'active' if is_active else 'inactive'
})
# 按更新时间排序
sessions.sort(key=lambda x: x['updatedAt'], reverse=True)
if result.returncode == 0:
data = json.loads(result.stdout)
return jsonify({
'success': True,
'data': data
})
else:
return jsonify({
'success': False,
'error': result.stderr
'data': {
'count': len(sessions),
'sessions': sessions
}
})
except Exception as e:
return jsonify({
@@ -39,28 +85,55 @@ def get_sessions():
'error': str(e)
})
@api.route('/sessions/<session_key>')
def get_session_detail(session_key):
@api.route('/sessions/<session_id>')
def get_session_detail(session_id):
"""获取会话详情"""
try:
# 获取会话历史
result = subprocess.run(
['sessions_history', '--session-key', session_key, '--limit', '20'],
capture_output=True,
text=True,
timeout=10
)
filepath = os.path.join(SESSIONS_DIR, f'{session_id}.jsonl')
if not os.path.exists(filepath):
return jsonify({
'success': False,
'error': '会话不存在'
})
messages = []
with open(filepath, 'r') as f:
for line in f:
line = line.strip()
if not line:
continue
try:
data = json.loads(line)
# 提取消息
if 'messages' in data:
for msg in data['messages']:
messages.append({
'role': msg.get('role', 'unknown'),
'content': msg.get('content', '')[:500], # 截断内容
'timestamp': data.get('created', 0)
})
elif 'content' in data:
messages.append({
'role': data.get('role', 'unknown'),
'content': data.get('content', '')[:500]
})
except:
continue
# 只保留最近 20 条消息
messages = messages[-20:]
if result.returncode == 0:
data = json.loads(result.stdout)
return jsonify({
'success': True,
'data': data
})
else:
return jsonify({
'success': False,
'error': result.stderr
'data': {
'id': session_id,
'messagesCount': len(messages),
'messages': messages
}
})
except Exception as e:
return jsonify({
@@ -68,22 +141,30 @@ def get_session_detail(session_key):
'error': str(e)
})
@api.route('/sessions/<session_key>/kill', methods=['POST'])
def kill_session(session_key):
"""终止会话"""
@api.route('/sessions/<session_id>/kill', methods=['POST'])
def kill_session(session_id):
"""终止会话(删除会话文件)"""
try:
# 调用 subagents kill 命令
result = subprocess.run(
['subagents', 'kill', '--target', session_key],
capture_output=True,
text=True,
timeout=10
)
filepath = os.path.join(SESSIONS_DIR, f'{session_id}.jsonl')
lockfile = filepath + '.lock'
if not os.path.exists(filepath):
return jsonify({
'success': False,
'error': '会话不存在'
})
# 重命名为备份文件而不是直接删除
backup_path = filepath + f'.reset.{datetime.now().isoformat()}'
os.rename(filepath, backup_path)
# 删除锁文件
if os.path.exists(lockfile):
os.remove(lockfile)
return jsonify({
'success': result.returncode == 0,
'message': '会话已终止' if result.returncode == 0 else '终止失败',
'output': result.stdout or result.stderr
'success': True,
'message': '会话已终止'
})
except Exception as e:
return jsonify({