#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 会话管理 API 作者:小白 🐶 """ import os import json 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 = [] # 遍历会话目录 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_id): """获取会话详情""" try: 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:] 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_id): """终止会话(删除会话文件)""" try: 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': True, 'message': '会话已终止' }) except Exception as e: return jsonify({ 'success': False, 'error': str(e) })