diff --git a/api/sessions.py b/api/sessions.py index cd7fc16..38ad1f3 100644 --- a/api/sessions.py +++ b/api/sessions.py @@ -5,85 +5,166 @@ 作者:小白 🐶 """ +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 = [] - if result.returncode == 0: - data = json.loads(result.stdout) - return jsonify({ - 'success': True, - 'data': data - }) - else: - return jsonify({ - 'success': False, - 'error': result.stderr + # 遍历会话目录 + 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) + + return jsonify({ + 'success': True, + 'data': { + 'count': len(sessions), + 'sessions': sessions + } + }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }) -@api.route('/sessions/') -def get_session_detail(session_key): +@api.route('/sessions/') +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 result.returncode == 0: - data = json.loads(result.stdout) - return jsonify({ - 'success': True, - 'data': data - }) - else: + if not os.path.exists(filepath): return jsonify({ 'success': False, - 'error': result.stderr + '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:] + + return jsonify({ + 'success': True, + 'data': { + 'id': session_id, + 'messagesCount': len(messages), + 'messages': messages + } + }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) }) -@api.route('/sessions//kill', methods=['POST']) -def kill_session(session_key): - """终止会话""" +@api.route('/sessions//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({