Add daily dashboard, wisdom, and system monitor - today.py: Comprehensive daily overview combining all tools - wisdom.py: Quotes and wisdom collection with daily feature - sysmon.py: System resource monitor (CPU, memory, disk) - Updated ws CLI with new commands
This commit is contained in:
237
tools/sysmon.py
Executable file
237
tools/sysmon.py
Executable file
@ -0,0 +1,237 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
sysmon - System monitor
|
||||
|
||||
Quick system status and resource tracking.
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
WORKSPACE = Path("/home/wdjones/.openclaw/workspace")
|
||||
HISTORY_FILE = WORKSPACE / "data" / "sysmon_history.json"
|
||||
|
||||
def get_cpu_usage():
|
||||
"""Get CPU usage percentage."""
|
||||
try:
|
||||
# Read /proc/stat for CPU usage
|
||||
with open('/proc/stat') as f:
|
||||
line = f.readline()
|
||||
parts = line.split()
|
||||
idle = int(parts[4])
|
||||
total = sum(int(p) for p in parts[1:])
|
||||
|
||||
# Read again after a moment
|
||||
import time
|
||||
time.sleep(0.1)
|
||||
|
||||
with open('/proc/stat') as f:
|
||||
line = f.readline()
|
||||
parts = line.split()
|
||||
idle2 = int(parts[4])
|
||||
total2 = sum(int(p) for p in parts[1:])
|
||||
|
||||
idle_delta = idle2 - idle
|
||||
total_delta = total2 - total
|
||||
|
||||
if total_delta == 0:
|
||||
return 0
|
||||
|
||||
usage = 100 * (1 - idle_delta / total_delta)
|
||||
return round(usage, 1)
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_memory():
|
||||
"""Get memory usage."""
|
||||
try:
|
||||
with open('/proc/meminfo') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
mem = {}
|
||||
for line in lines:
|
||||
parts = line.split()
|
||||
key = parts[0].rstrip(':')
|
||||
value = int(parts[1]) # in KB
|
||||
mem[key] = value
|
||||
|
||||
total = mem.get('MemTotal', 0) / 1024 / 1024 # GB
|
||||
available = mem.get('MemAvailable', 0) / 1024 / 1024 # GB
|
||||
used = total - available
|
||||
pct = (used / total * 100) if total > 0 else 0
|
||||
|
||||
return {
|
||||
'total_gb': round(total, 1),
|
||||
'used_gb': round(used, 1),
|
||||
'available_gb': round(available, 1),
|
||||
'percent': round(pct, 1),
|
||||
}
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_disk():
|
||||
"""Get disk usage."""
|
||||
try:
|
||||
stat = os.statvfs('/')
|
||||
total = stat.f_blocks * stat.f_frsize / 1024 / 1024 / 1024 # GB
|
||||
free = stat.f_bavail * stat.f_frsize / 1024 / 1024 / 1024 # GB
|
||||
used = total - free
|
||||
pct = (used / total * 100) if total > 0 else 0
|
||||
|
||||
return {
|
||||
'total_gb': round(total, 1),
|
||||
'used_gb': round(used, 1),
|
||||
'free_gb': round(free, 1),
|
||||
'percent': round(pct, 1),
|
||||
}
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_load():
|
||||
"""Get system load average."""
|
||||
try:
|
||||
with open('/proc/loadavg') as f:
|
||||
parts = f.read().split()
|
||||
return {
|
||||
'load1': float(parts[0]),
|
||||
'load5': float(parts[1]),
|
||||
'load15': float(parts[2]),
|
||||
}
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_uptime():
|
||||
"""Get system uptime."""
|
||||
try:
|
||||
with open('/proc/uptime') as f:
|
||||
uptime_seconds = float(f.read().split()[0])
|
||||
|
||||
days = int(uptime_seconds // 86400)
|
||||
hours = int((uptime_seconds % 86400) // 3600)
|
||||
mins = int((uptime_seconds % 3600) // 60)
|
||||
|
||||
if days > 0:
|
||||
return f"{days}d {hours}h {mins}m"
|
||||
elif hours > 0:
|
||||
return f"{hours}h {mins}m"
|
||||
else:
|
||||
return f"{mins}m"
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_processes():
|
||||
"""Get top processes by memory/CPU."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['ps', 'aux', '--sort=-rss'],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
lines = result.stdout.strip().split('\n')[1:6] # Skip header, top 5
|
||||
|
||||
processes = []
|
||||
for line in lines:
|
||||
parts = line.split(None, 10)
|
||||
if len(parts) >= 11:
|
||||
processes.append({
|
||||
'user': parts[0],
|
||||
'cpu': float(parts[2]),
|
||||
'mem': float(parts[3]),
|
||||
'cmd': parts[10][:30],
|
||||
})
|
||||
return processes
|
||||
except:
|
||||
return []
|
||||
|
||||
def bar(pct: float, width: int = 20) -> str:
|
||||
"""Create a progress bar."""
|
||||
filled = int(pct / 100 * width)
|
||||
return "█" * filled + "░" * (width - filled)
|
||||
|
||||
def show_status():
|
||||
"""Show current system status."""
|
||||
print()
|
||||
print("╔" + "═" * 40 + "╗")
|
||||
print(f"║ 🖥️ System Monitor {' ' * 20}║")
|
||||
print("╚" + "═" * 40 + "╝")
|
||||
|
||||
# Uptime
|
||||
uptime = get_uptime()
|
||||
if uptime:
|
||||
print(f"\n⏰ Uptime: {uptime}")
|
||||
|
||||
# CPU
|
||||
cpu = get_cpu_usage()
|
||||
if cpu is not None:
|
||||
print(f"\n🔲 CPU: {bar(cpu)} {cpu}%")
|
||||
|
||||
# Memory
|
||||
mem = get_memory()
|
||||
if mem:
|
||||
print(f"🔲 Memory: {bar(mem['percent'])} {mem['percent']}%")
|
||||
print(f" {mem['used_gb']:.1f}GB / {mem['total_gb']:.1f}GB")
|
||||
|
||||
# Disk
|
||||
disk = get_disk()
|
||||
if disk:
|
||||
print(f"🔲 Disk: {bar(disk['percent'])} {disk['percent']}%")
|
||||
print(f" {disk['used_gb']:.0f}GB / {disk['total_gb']:.0f}GB")
|
||||
|
||||
# Load
|
||||
load = get_load()
|
||||
if load:
|
||||
print(f"\n📊 Load: {load['load1']:.2f} / {load['load5']:.2f} / {load['load15']:.2f}")
|
||||
|
||||
# Top processes
|
||||
procs = get_processes()
|
||||
if procs:
|
||||
print(f"\n🔝 Top Processes:")
|
||||
for p in procs[:3]:
|
||||
print(f" {p['mem']:.1f}% {p['cmd']}")
|
||||
|
||||
print()
|
||||
|
||||
def record():
|
||||
"""Record current stats to history."""
|
||||
HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
if HISTORY_FILE.exists():
|
||||
with open(HISTORY_FILE) as f:
|
||||
history = json.load(f)
|
||||
else:
|
||||
history = []
|
||||
|
||||
entry = {
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'cpu': get_cpu_usage(),
|
||||
'memory': get_memory(),
|
||||
'disk': get_disk(),
|
||||
'load': get_load(),
|
||||
}
|
||||
|
||||
history.append(entry)
|
||||
|
||||
# Keep last 1000 entries
|
||||
history = history[-1000:]
|
||||
|
||||
with open(HISTORY_FILE, 'w') as f:
|
||||
json.dump(history, f)
|
||||
|
||||
print(f"✓ Recorded at {entry['timestamp'][:19]}")
|
||||
|
||||
def main():
|
||||
import sys
|
||||
|
||||
if len(sys.argv) > 1:
|
||||
cmd = sys.argv[1]
|
||||
if cmd == 'record':
|
||||
record()
|
||||
else:
|
||||
print("Usage: sysmon [record]")
|
||||
else:
|
||||
show_status()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user