Skip to main content
Glama
app.py5.93 kB
"""Claude Code conversation history viewer web server""" import os from pathlib import Path from flask import Flask, jsonify, render_template, request from flask_cors import CORS from ..models import ClaudeHistoryParser app = Flask(__name__) CORS(app) parser = ClaudeHistoryParser() def get_version(): """Get version from pyproject.toml""" try: pyproject_path = Path(__file__).parent.parent.parent.parent / "pyproject.toml" with open(pyproject_path, 'r') as f: for line in f: if line.startswith('version = '): return line.split('=')[1].strip().strip('"').strip("'") except: pass return "unknown" @app.route('/') def index(): """Main page""" return render_template('index.html') @app.route('/api/version') def get_version_info(): """Get version API""" return jsonify({'version': get_version()}) @app.route('/api/projects') def get_projects(): """List projects API""" projects = parser.get_projects() return jsonify([{ 'name': p.name, 'display_name': p.display_name, 'path': p.path } for p in projects]) @app.route('/api/projects/<path:project_name>/sessions') def get_sessions(project_name: str): """List sessions API""" sessions = parser.get_sessions(project_name) return jsonify([{ 'session_id': s.session_id, 'title': s.title, 'message_count': s.message_count, 'created_at': s.created_at.isoformat() if s.created_at else None, 'updated_at': s.updated_at.isoformat() if s.updated_at else None, 'cwd': s.cwd, 'version': s.version, 'git_branch': s.git_branch } for s in sessions]) @app.route('/api/projects/<path:project_name>/sessions/<session_id>') def get_session(project_name: str, session_id: str): """Get session details API""" session = parser.get_session(project_name, session_id) if not session: return jsonify({'error': 'Session not found'}), 404 return jsonify({ 'session_id': session.session_id, 'title': session.title, 'cwd': session.cwd, 'version': session.version, 'git_branch': session.git_branch, 'created_at': session.created_at.isoformat() if session.created_at else None, 'updated_at': session.updated_at.isoformat() if session.updated_at else None, 'messages': [{ 'uuid': m.uuid, 'role': m.role, 'content': m.content, 'timestamp': m.timestamp.isoformat(), 'model': m.model, 'tool_use': m.tool_use } for m in session.messages] }) @app.route('/api/search') def search(): """Search API""" query = request.args.get('q', '') project_name = request.args.get('project', None) if not query: return jsonify([]) sessions = parser.search_sessions(query, project_name) return jsonify([{ 'session_id': s.session_id, 'project_path': s.project_path, 'title': s.title, 'message_count': s.message_count, 'updated_at': s.updated_at.isoformat() if s.updated_at else None } for s in sessions[:50]]) # max 50 results @app.route('/api/projects/<path:project_name>/sessions/<session_id>', methods=['DELETE']) def delete_session(project_name: str, session_id: str): """Delete session API""" success = parser.delete_session(project_name, session_id) if success: return jsonify({'success': True}) else: return jsonify({'error': 'Session not found'}), 404 @app.route('/api/projects/<path:project_name>/sessions/<session_id>/rename', methods=['POST']) def rename_session(project_name: str, session_id: str): """Rename session API""" data = request.get_json() or {} new_title = data.get('title', '').strip() if not new_title: return jsonify({'error': 'Title is required'}), 400 success = parser.rename_session(project_name, session_id, new_title) if success: return jsonify({'success': True}) else: return jsonify({'error': 'Failed to rename session'}), 500 @app.route('/api/projects/<path:project_name>/sessions/<session_id>/messages/<message_uuid>', methods=['DELETE']) def delete_message(project_name: str, session_id: str, message_uuid: str): """Delete message API""" success = parser.delete_message(project_name, session_id, message_uuid) if success: return jsonify({'success': True}) else: return jsonify({'error': 'Failed to delete message'}), 500 @app.route('/api/clear/preview') def clear_preview(): """Preview cleanable sessions API""" project_name = request.args.get('project', None) result = parser.find_cleanable_sessions(project_name) return jsonify(result) @app.route('/api/clear', methods=['POST']) def clear_sessions(): """Clear sessions API""" data = request.get_json() or {} project_name = data.get('project', None) clear_empty = data.get('clear_empty', True) clear_invalid_api_key = data.get('clear_invalid_api_key', True) result = parser.clear_sessions(project_name, clear_empty, clear_invalid_api_key) return jsonify(result) @app.route('/api/projects/<path:project_name>/sessions/<session_id>/move', methods=['POST']) def move_session(project_name: str, session_id: str): """Move session to another project API""" data = request.get_json() or {} target_project = data.get('target_project', '').strip() if not target_project: return jsonify({'error': 'Target project is required'}), 400 success = parser.move_session(project_name, session_id, target_project) if success: return jsonify({'success': True}) else: return jsonify({'error': 'Failed to move session'}), 500 if __name__ == '__main__': port = int(os.environ.get('PORT', 5050)) app.run(host='0.0.0.0', port=port, debug=True)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/DrumRobot/claude-session-manager'

If you have feedback or need assistance with the MCP directory API, please join our Discord server