Files
1jehuang--jcode/scripts/jcode_monitor.py
T
wehub-resource-sync a789495a98
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:34 +08:00

342 lines
10 KiB
Python
Executable File

#!/usr/bin/env python3
"""
jcode Live Monitor - Real-time activity dashboard
Connects to jcode's debug socket and displays live streaming events.
Run jcode serve in one terminal, then this monitor in another.
Usage: ./jcode_monitor.py [--socket PATH]
"""
import json
import os
import socket
import sys
import time
from dataclasses import dataclass, field
from typing import Optional
# ANSI color codes
class Colors:
RESET = "\033[0m"
BOLD = "\033[1m"
DIM = "\033[2m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
BLUE = "\033[34m"
MAGENTA = "\033[35m"
CYAN = "\033[36m"
WHITE = "\033[37m"
BG_BLUE = "\033[44m"
# Clear screen and move cursor
CLEAR = "\033[2J\033[H"
CLEAR_LINE = "\033[2K"
@dataclass
class MonitorState:
"""Current state of the monitor"""
connected: bool = False
session_id: str = ""
is_processing: bool = False
input_tokens: int = 0
output_tokens: int = 0
current_text: str = ""
active_tools: dict = field(default_factory=dict) # id -> name
tool_history: list = field(default_factory=list) # recent tool completions
events_received: int = 0
last_event_time: float = 0
errors: list = field(default_factory=list)
def get_socket_path() -> str:
"""Get the jcode debug socket path"""
runtime_dir = os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")
return os.path.join(runtime_dir, "jcode-debug.sock")
def connect_to_socket(path: str) -> Optional[socket.socket]:
"""Connect to the Unix socket"""
try:
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(path)
sock.setblocking(False)
return sock
except Exception as e:
return None
def send_request(sock: socket.socket, request: dict) -> bool:
"""Send a JSON request to the socket"""
try:
data = json.dumps(request) + "\n"
sock.send(data.encode())
return True
except:
return False
def read_events(sock: socket.socket) -> list:
"""Read available events from socket (non-blocking)"""
events = []
buffer = ""
try:
while True:
data = sock.recv(4096)
if not data:
break
buffer += data.decode()
while "\n" in buffer:
line, buffer = buffer.split("\n", 1)
if line.strip():
try:
events.append(json.loads(line))
except json.JSONDecodeError:
pass
except BlockingIOError:
pass
except:
pass
return events
def format_tokens(n: int) -> str:
"""Format token count with color based on size"""
if n == 0:
return f"{Colors.DIM}0{Colors.RESET}"
elif n < 1000:
return f"{Colors.GREEN}{n}{Colors.RESET}"
elif n < 10000:
return f"{Colors.YELLOW}{n:,}{Colors.RESET}"
else:
return f"{Colors.RED}{n:,}{Colors.RESET}"
def format_tool(name: str, status: str = "active") -> str:
"""Format a tool name with appropriate color"""
if status == "active":
return f"{Colors.CYAN}{Colors.BOLD}{name}{Colors.RESET}"
elif status == "done":
return f"{Colors.GREEN}{name}{Colors.RESET}"
elif status == "error":
return f"{Colors.RED}{name}{Colors.RESET}"
return name
def truncate(s: str, max_len: int) -> str:
"""Truncate string with ellipsis"""
if len(s) <= max_len:
return s
return s[:max_len-3] + "..."
def render_dashboard(state: MonitorState, width: int = 80):
"""Render the monitoring dashboard"""
lines = []
# Header
header = f" JCODE MONITOR "
padding = (width - len(header)) // 2
lines.append(f"{Colors.BG_BLUE}{Colors.WHITE}{Colors.BOLD}{' ' * padding}{header}{' ' * padding}{Colors.RESET}")
lines.append("")
# Connection status
if state.connected:
status = f"{Colors.GREEN}CONNECTED{Colors.RESET}"
session = f" | Session: {Colors.DIM}{state.session_id[:8]}...{Colors.RESET}" if state.session_id else ""
else:
status = f"{Colors.RED}DISCONNECTED{Colors.RESET}"
session = ""
lines.append(f" Status: {status}{session}")
# Processing state
if state.is_processing:
proc = f"{Colors.YELLOW}{Colors.BOLD}PROCESSING{Colors.RESET}"
else:
proc = f"{Colors.DIM}idle{Colors.RESET}"
lines.append(f" State: {proc}")
lines.append("")
# Token usage
lines.append(f" {Colors.BOLD}Tokens{Colors.RESET}")
lines.append(f" Input: {format_tokens(state.input_tokens)}")
lines.append(f" Output: {format_tokens(state.output_tokens)}")
lines.append("")
# Active tools
lines.append(f" {Colors.BOLD}Active Tools{Colors.RESET}")
if state.active_tools:
for tool_id, tool_name in state.active_tools.items():
lines.append(f" {Colors.CYAN}>{Colors.RESET} {format_tool(tool_name)}")
else:
lines.append(f" {Colors.DIM}(none){Colors.RESET}")
lines.append("")
# Recent tool completions
lines.append(f" {Colors.BOLD}Recent Tools{Colors.RESET}")
if state.tool_history:
for item in state.tool_history[-5:]:
name, success, output = item
status = "done" if success else "error"
output_preview = truncate(output.replace("\n", " "), 40)
lines.append(f" {format_tool(name, status)}: {Colors.DIM}{output_preview}{Colors.RESET}")
else:
lines.append(f" {Colors.DIM}(none){Colors.RESET}")
lines.append("")
# Current streaming text
lines.append(f" {Colors.BOLD}Streaming Text{Colors.RESET}")
if state.current_text:
# Show last few lines of streaming text
text_lines = state.current_text.split("\n")[-4:]
for tl in text_lines:
lines.append(f" {Colors.WHITE}{truncate(tl, width-6)}{Colors.RESET}")
else:
lines.append(f" {Colors.DIM}(waiting...){Colors.RESET}")
lines.append("")
# Stats
lines.append(f" {Colors.DIM}Events: {state.events_received} | Last: {time.time() - state.last_event_time:.1f}s ago{Colors.RESET}")
# Errors
if state.errors:
lines.append("")
lines.append(f" {Colors.RED}{Colors.BOLD}Errors{Colors.RESET}")
for err in state.errors[-3:]:
lines.append(f" {Colors.RED}{truncate(err, width-6)}{Colors.RESET}")
# Print with clear
print(Colors.CLEAR, end="")
print("\n".join(lines))
def process_event(event: dict, state: MonitorState):
"""Process a single event and update state"""
state.events_received += 1
state.last_event_time = time.time()
event_type = event.get("type", "")
if event_type == "ack":
state.is_processing = True
elif event_type == "text_delta":
state.current_text += event.get("text", "")
# Keep last 2000 chars
if len(state.current_text) > 2000:
state.current_text = state.current_text[-2000:]
elif event_type == "tool_start":
tool_id = event.get("id", "")
tool_name = event.get("name", "unknown")
state.active_tools[tool_id] = tool_name
elif event_type == "tool_exec":
pass # Tool is executing, still active
elif event_type == "tool_done":
tool_id = event.get("id", "")
tool_name = event.get("name", "unknown")
output = event.get("output", "")
error = event.get("error")
# Remove from active
state.active_tools.pop(tool_id, None)
# Add to history
state.tool_history.append((tool_name, error is None, output[:100] if output else "(empty)"))
# Keep last 20
state.tool_history = state.tool_history[-20:]
elif event_type == "tokens":
state.input_tokens = event.get("input", 0)
state.output_tokens = event.get("output", 0)
elif event_type == "done":
state.is_processing = False
state.current_text = "" # Clear for next turn
elif event_type == "error":
state.errors.append(event.get("message", "unknown error"))
state.errors = state.errors[-10:]
elif event_type == "pong":
pass # Health check response
elif event_type == "state":
state.session_id = event.get("session_id", "")
state.is_processing = event.get("is_processing", False)
elif event_type == "session":
state.session_id = event.get("session_id", "")
def main():
"""Main monitor loop"""
socket_path = sys.argv[1] if len(sys.argv) > 1 else get_socket_path()
print(f"Connecting to {socket_path}...")
state = MonitorState()
sock = None
request_id = 1
last_ping = 0
while True:
try:
# Connect if needed
if sock is None:
sock = connect_to_socket(socket_path)
if sock:
state.connected = True
# Subscribe to events
send_request(sock, {"type": "subscribe", "id": request_id})
request_id += 1
# Get initial state
send_request(sock, {"type": "state", "id": request_id})
request_id += 1
else:
state.connected = False
# Read events
if sock:
events = read_events(sock)
for event in events:
process_event(event, state)
# Periodic ping
if time.time() - last_ping > 5:
send_request(sock, {"type": "ping", "id": request_id})
request_id += 1
last_ping = time.time()
# Render dashboard
render_dashboard(state)
# Small delay
time.sleep(0.1)
except KeyboardInterrupt:
print(f"\n{Colors.DIM}Exiting...{Colors.RESET}")
break
except BrokenPipeError:
state.connected = False
sock = None
except Exception as e:
state.errors.append(str(e))
time.sleep(1)
if sock:
sock.close()
if __name__ == "__main__":
main()