The file /Users/neog/Services/servers/log-server.py has been updated. Here's the result of running `cat -n` on a snippet of the edited file: 2433→ 'gates': health, 2434→ 'timestamp': datetime.now().isoformat() 2435→ }) 2436→ 2437→ except Exception as e: 2438→ return jsonify({'status': 'error', 'message': str(e)}), 500 2439→ 2440→# ========== LIVE SESSION ENDPOINTS ========== 2441→ 2442→@app.route('/api/cc/session/update', methods=['POST']) 2443→def cc_session_update(): 2444→ """Receive real-time session updates from cc-live-tracker.py""" 2445→ global CURRENT_LIVE_SESSION 2446→ try: 2447→ data = request.json or {} 2448→ 2449→ session_id = data.get('session_id') 2450→ if not session_id: 2451→ return jsonify({'status': 'error', 'message': 'session_id required'}), 400 2452→ 2453→ # Update or create session 2454→ if CURRENT_LIVE_SESSION['session_id'] != session_id: 2455→ # New session 2456→ CURRENT_LIVE_SESSION = { 2457→ 'session_id': session_id, 2458→ 'started_at': data.get('timestamp'), 2459→ 'last_update': data.get('timestamp'), 2460→ 'cumulative_cost': 0.0, 2461→ 'cumulative_tokens': { 2462→ 'input': 0, 2463→ 'output': 0, 2464→ 'cacheRead': 0, 2465→ 'cacheCreation': 0 2466→ }, 2467→ 'duration_seconds': 0, 2468→ 'updates': [] 2469→ } 2470→ print(f"[SESSION] New live session: {session_id}") 2471→ 2472→ # Update with cumulative data 2473→ cumulative = data.get('cumulative', {}) 2474→ CURRENT_LIVE_SESSION['last_update'] = data.get('timestamp') 2475→ CURRENT_LIVE_SESSION['cumulative_cost'] = cumulative.get('cost', 0) 2476→ CURRENT_LIVE_SESSION['cumulative_tokens'] = cumulative.get('tokens', {}) 2477→ CURRENT_LIVE_SESSION['duration_seconds'] = cumulative.get('duration_seconds', 0) 2478→ 2479→ # Track deltas for rate calculation (keep last 10) 2480→ deltas = data.get('deltas', {}) 2481→ if deltas.get('total_cost', 0) > 0: 2482→ CURRENT_LIVE_SESSION['updates'].append({ 2483→ 'timestamp': data.get('timestamp'), 2484→ 'cost': deltas.get('total_cost', 0), 2485→ 'tokens': deltas.get('total_tokens', {}) 2486→ }) 2487→ # Keep only last 10 updates 2488→ if len(CURRENT_LIVE_SESSION['updates']) > 10: 2489→ CURRENT_LIVE_SESSION['updates'] = CURRENT_LIVE_SESSION['updates'][-10:] 2490→ 2491→ # Broadcast to connected clients via WebSocket 2492→ socketio.emit('cc_session_update', { 2493→ 'session_id': session_id, 2494→ 'cumulative_cost': CURRENT_LIVE_SESSION['cumulative_cost'], 2495→ 'cumulative_tokens': CURRENT_LIVE_SESSION['cumulative_tokens'], 2496→ 'duration_seconds': CURRENT_LIVE_SESSION['duration_seconds'], 2497→ 'deltas': deltas, 2498→ 'timestamp': data.get('timestamp') 2499→ }) 2500→ 2501→ return jsonify({'status': 'ok', 'session_id': session_id}) 2502→ 2503→ except Exception as e: 2504→ print(f"[SESSION] Error: {e}") 2505→ return jsonify({'status': 'error', 'message': str(e)}), 500 2506→ 2507→ 2508→@app.route('/api/cc/session/current', methods=['GET']) 2509→def cc_session_current(): 2510→ """Get current live session data""" 2511→ global CURRENT_LIVE_SESSION 2512→ 2513→ if not CURRENT_LIVE_SESSION['session_id']: 2514→ return jsonify({ 2515→ 'active': False, 2516→ 'message': 'No active session' 2517→ }) 2518→ 2519→ # Calculate cost rate ($/hour) from recent updates 2520→ cost_rate = 0.0 2521→ if len(CURRENT_LIVE_SESSION['updates']) >= 2: 2522→ recent = CURRENT_LIVE_SESSION['updates'][-5:] # Last 5 updates 2523→ total_cost = sum(u['cost'] for u in recent) 2524→ if len(recent) >= 2: 2525→ try: 2526→ first_time = datetime.fromisoformat(recent[0]['timestamp']) 2527→ last_time = datetime.fromisoformat(recent[-1]['timestamp']) 2528→ time_diff = (last_time - first_time).total_seconds() 2529→ if time_diff > 0: 2530→ cost_rate = (total_cost / time_diff) * 3600 # Per hour 2531→ except: 2532→ pass 2533→ 2534→ return jsonify({ 2535→ 'active': True, 2536→ 'session_id': CURRENT_LIVE_SESSION['session_id'], 2537→ 'started_at': CURRENT_LIVE_SESSION['started_at'], 2538→ 'last_update': CURRENT_LIVE_SESSION['last_update'], 2539→ 'cumulative_cost': round(CURRENT_LIVE_SESSION['cumulative_cost'], 4), 2540→ 'cumulative_tokens': CURRENT_LIVE_SESSION['cumulative_tokens'], 2541→ 'duration_seconds': CURRENT_LIVE_SESSION['duration_seconds'], 2542→ 'cost_rate_per_hour': round(cost_rate, 2) 2543→ }) 2544→ 2545→ 2546→@app.route('/api/cc/session/end', methods=['POST']) 2547→def cc_session_end(): 2548→ """End the current live session""" 2549→ global CURRENT_LIVE_SESSION 2550→ 2551→ if not CURRENT_LIVE_SESSION['session_id']: 2552→ return jsonify({'status': 'ok', 'message': 'No active session'}) 2553→ 2554→ session_id = CURRENT_LIVE_SESSION['session_id'] 2555→ final_cost = CURRENT_LIVE_SESSION['cumulative_cost'] 2556→ final_tokens = CURRENT_LIVE_SESSION['cumulative_tokens'] 2557→ 2558→ # Broadcast session end 2559→ socketio.emit('cc_session_ended', { 2560→ 'session_id': session_id, 2561→ 'final_cost': final_cost, 2562→ 'final_tokens': final_tokens 2563→ }) 2564→ 2565→ print(f"[SESSION] Session ended: {session_id} (${final_cost:.4f})") 2566→ 2567→ # Reset session 2568→ CURRENT_LIVE_SESSION = { 2569→ 'session_id': None, 2570→ 'started_at': None, 2571→ 'last_update': None, 2572→ 'cumulative_cost': 0.0, 2573→ 'cumulative_tokens': {'input': 0, 'output': 0, 'cacheRead': 0, 'cacheCreation': 0}, 2574→ 'duration_seconds': 0, 2575→ 'updates': [] 2576→ } 2577→ 2578→ return jsonify({ 2579→ 'status': 'ok', 2580→ 'session_id': session_id, 2581→ 'final_cost': final_cost, 2582→ 'final_tokens': final_tokens 2583→ }) 2584→ 2585→ 2586→# Analytics endpoints 2587→@app.route('/api/analytics', methods=['POST']) 2588→def receive_analytics(): 2589→ """Receive analytics data from clients""" 2590→ try: 2591→ analytics_data = request.json or {}