diff --git a/.wrangler/cache/pages.json b/.wrangler/cache/pages.json new file mode 100644 index 0000000..0212643 --- /dev/null +++ b/.wrangler/cache/pages.json @@ -0,0 +1,3 @@ +{ + "account_id": "848cf0b18d51e0170e0d1537aec3505a" +} \ No newline at end of file diff --git a/.wrangler/cache/wrangler-account.json b/.wrangler/cache/wrangler-account.json new file mode 100644 index 0000000..124f33c --- /dev/null +++ b/.wrangler/cache/wrangler-account.json @@ -0,0 +1,6 @@ +{ + "account": { + "id": "848cf0b18d51e0170e0d1537aec3505a", + "name": "Amundsonalexa@gmail.com's Account" + } +} \ No newline at end of file diff --git a/collectors/cloudflare.sh b/collectors/cloudflare.sh new file mode 100644 index 0000000..d9ebd4b --- /dev/null +++ b/collectors/cloudflare.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Collect Cloudflare KPIs: D1, KV, R2, Workers, Pages, Tunnels + +source "$(dirname "$0")/../lib/common.sh" + +log "Collecting Cloudflare KPIs..." + +OUT=$(snapshot_file cloudflare) + +CF_ACCOUNT="848cf0b18d51e0170e0d1537aec3505a" + +# D1 databases +d1_count=0 +d1_total_size=0 +d1_json="[]" +d1_raw=$(npx wrangler d1 list --json 2>/dev/null || echo '[]') +if [ -n "$d1_raw" ]; then + d1_count=$(echo "$d1_raw" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d))" 2>/dev/null || echo 0) + d1_json=$(echo "$d1_raw" | python3 -c " +import json, sys +dbs = json.load(sys.stdin) +print(json.dumps([{'name': d.get('name',''), 'uuid': d.get('uuid','')} for d in dbs])) +" 2>/dev/null || echo '[]') +fi +ok "D1: $d1_count databases" + +# KV namespaces +kv_count=0 +kv_raw=$(npx wrangler kv namespace list 2>/dev/null || echo '[]') +if [ -n "$kv_raw" ]; then + kv_count=$(echo "$kv_raw" | python3 -c "import json,sys; print(len(json.load(sys.stdin)))" 2>/dev/null || echo 0) +fi +ok "KV: $kv_count namespaces" + +# R2 buckets +r2_count=0 +# R2 outputs text, not JSON — count "name:" lines +r2_count=$(npx wrangler r2 bucket list 2>/dev/null | grep -c "^name:" || echo 0) +ok "R2: $r2_count buckets" + +# Pages projects (table output, count data rows) +pages_count=$(npx wrangler pages project list 2>/dev/null | grep -c "│" || echo 0) +# Subtract header rows (2 per table: header + separator) +pages_count=$((pages_count > 2 ? pages_count - 2 : 0)) +ok "Pages: $pages_count projects" + +# D1 total size +d1_total_size_kb=0 +if [ -n "$d1_raw" ] && [ "$d1_raw" != "[]" ]; then + d1_total_size_kb=$(echo "$d1_raw" | python3 -c " +import json, sys +dbs = json.load(sys.stdin) +total = sum(d.get('file_size', 0) for d in dbs) +print(total // 1024) +" 2>/dev/null || echo 0) +fi + +cat > "$OUT" << ENDJSON +{ + "source": "cloudflare", + "collected_at": "$TIMESTAMP", + "date": "$TODAY", + "d1": { + "count": $d1_count, + "total_size_kb": $d1_total_size_kb, + "databases": $d1_json + }, + "kv": { + "count": $kv_count + }, + "r2": { + "count": $r2_count + }, + "pages": { + "count": $pages_count + }, + "account_id": "$CF_ACCOUNT" +} +ENDJSON + +ok "Cloudflare: ${d1_count} D1, ${kv_count} KV, ${r2_count} R2, ${pages_count} Pages" diff --git a/collectors/collect-all.sh b/collectors/collect-all.sh index 0aa0b74..c91ab62 100644 --- a/collectors/collect-all.sh +++ b/collectors/collect-all.sh @@ -11,7 +11,7 @@ log "═════════════════════════ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # Run all collectors -for collector in github gitea fleet autonomy loc; do +for collector in github github-deep gitea fleet services autonomy loc local cloudflare; do log "Running $collector collector..." bash "$SCRIPT_DIR/$collector.sh" 2>&1 || err "Collector $collector failed" echo @@ -37,31 +37,109 @@ for f in glob.glob(f'{data_dir}/snapshots/{today}-*.json'): # Build daily summary gh = snapshots.get('github', {}) +ghd = snapshots.get('github-deep', {}) gt = snapshots.get('gitea', {}) fl = snapshots.get('fleet', {}) +sv = snapshots.get('services', {}) au = snapshots.get('autonomy', {}) lc = snapshots.get('loc', {}) +lo = snapshots.get('local', {}) +cf = snapshots.get('cloudflare', {}) daily = { 'date': today, 'collected_at': '$TIMESTAMP', 'summary': { + # Code velocity 'commits_today': gh.get('commits', {}).get('today', 0) + gt.get('commits', {}).get('today', 0), + 'push_events_today': gh.get('commits', {}).get('push_events_today', 0), 'prs_open': gh.get('pull_requests', {}).get('open', 0), 'prs_merged_today': gh.get('pull_requests', {}).get('merged_today', 0), 'prs_merged_total': gh.get('pull_requests', {}).get('merged_total', 0), + 'github_events_today': gh.get('activity', {}).get('events_today', 0), + + # Repos 'repos_github': gh.get('repos', {}).get('total', 0), 'repos_gitea': gt.get('repos', {}).get('total', 0), 'repos_total': gh.get('repos', {}).get('total', 0) + gt.get('repos', {}).get('total', 0), + 'repos_active': ghd.get('repos', {}).get('active', 0), + 'repos_archived': ghd.get('repos', {}).get('archived', 0), + + # GitHub profile + 'github_stars': ghd.get('repos', {}).get('total_stars', 0), + 'github_forks': ghd.get('repos', {}).get('total_forks', 0), + 'github_followers': ghd.get('profile', {}).get('followers', 0), + 'github_following': ghd.get('profile', {}).get('following', 0), + 'github_open_issues': ghd.get('repos', {}).get('total_open_issues', 0), + 'github_size_mb': ghd.get('repos', {}).get('total_size_mb', 0), + 'github_languages': ghd.get('repos', {}).get('languages', {}), + + # Fleet 'fleet_online': fl.get('fleet', {}).get('online', 0), 'fleet_total': fl.get('fleet', {}).get('total_nodes', 4), - 'autonomy_score': au.get('autonomy_score', 0), - 'total_loc': lc.get('total_estimated_loc', 0), - 'docker_containers': fl.get('totals', {}).get('docker_containers', 0), - 'ollama_models': fl.get('totals', {}).get('ollama_models', 0), + 'fleet_offline': fl.get('fleet', {}).get('offline_nodes', []), 'avg_temp_c': fl.get('totals', {}).get('cpu_avg_temp_c', 0), - 'failed_units': fl.get('totals', {}).get('systemd_failed', 0), - 'throttled_nodes': fl.get('totals', {}).get('throttled_nodes', []) + 'throttled_nodes': fl.get('totals', {}).get('throttled_nodes', []), + 'fleet_mem_used_mb': fl.get('totals', {}).get('mem_used_mb', 0), + 'fleet_mem_total_mb': fl.get('totals', {}).get('mem_total_mb', 0), + 'fleet_disk_used_gb': fl.get('totals', {}).get('disk_used_gb', 0), + 'fleet_disk_total_gb': fl.get('totals', {}).get('disk_total_gb', 0), + + # Services + 'docker_containers': sv.get('totals', {}).get('docker_containers', fl.get('totals', {}).get('docker_containers', 0)), + 'docker_images': sv.get('totals', {}).get('docker_images', 0), + 'ollama_models': sv.get('totals', {}).get('ollama_models', fl.get('totals', {}).get('ollama_models', 0)), + 'ollama_size_gb': sv.get('totals', {}).get('ollama_size_gb', 0), + 'postgres_dbs': sv.get('totals', {}).get('postgres_dbs', 0), + 'nginx_sites': sv.get('totals', {}).get('nginx_sites', 0), + 'systemd_services': sv.get('totals', {}).get('systemd_services', 0), + 'systemd_timers': sv.get('totals', {}).get('systemd_timers', 0), + 'failed_units': sv.get('totals', {}).get('systemd_failed', fl.get('totals', {}).get('systemd_failed', 0)), + 'fleet_processes': sv.get('totals', {}).get('processes', 0), + 'fleet_connections': sv.get('totals', {}).get('network_connections', 0), + 'fleet_swap_used_mb': sv.get('totals', {}).get('swap_used_mb', 0), + 'fleet_swap_total_mb': sv.get('totals', {}).get('swap_total_mb', 0), + 'tailscale_peers': sv.get('totals', {}).get('tailscale_peers', 0), + + # Autonomy + 'autonomy_score': au.get('autonomy_score', 0), + 'heal_events_today': au.get('totals', {}).get('heal_events_today', 0), + 'service_restarts_today': au.get('totals', {}).get('service_restarts_today', 0), + 'fleet_cron_jobs': au.get('totals', {}).get('total_cron_jobs', 0), + 'fleet_timers': au.get('totals', {}).get('active_timers', 0), + 'max_uptime_days': au.get('totals', {}).get('max_uptime_days', 0), + + # LOC + 'total_loc': lc.get('total_estimated_loc', 0), + 'local_repos': lc.get('local', {}).get('repos', 0), + 'local_files': lc.get('local', {}).get('files', 0), + 'local_scripts': lc.get('local', {}).get('scripts', 0), + 'local_script_lines': lc.get('local', {}).get('script_lines', 0), + + # Local Mac + 'bin_tools': lo.get('scripts', {}).get('bin_tools', 0), + 'bin_size_mb': lo.get('scripts', {}).get('bin_size_mb', 0), + 'home_scripts': lo.get('scripts', {}).get('home_scripts', 0), + 'templates': lo.get('scripts', {}).get('templates', 0), + 'sqlite_dbs': lo.get('databases', {}).get('sqlite_count', 0), + 'blackroad_dir_mb': lo.get('databases', {}).get('blackroad_dir_mb', 0), + 'fts5_entries': lo.get('databases', {}).get('fts5_entries', 0), + 'systems_registered': lo.get('databases', {}).get('systems_registered', 0), + 'brew_packages': lo.get('packages', {}).get('homebrew', 0), + 'pip_packages': lo.get('packages', {}).get('pip3', 0), + 'npm_global_packages': lo.get('packages', {}).get('npm_global', 0), + 'mac_cron_jobs': lo.get('automation', {}).get('cron_jobs', 0), + 'local_git_repos': lo.get('automation', {}).get('local_git_repos', 0), + 'mac_disk_pct': lo.get('disk', {}).get('pct', 0), + 'mac_disk_used_gb': lo.get('disk', {}).get('used_gb', 0), + 'mac_processes': lo.get('system', {}).get('processes', 0), + + # Cloudflare + 'cf_d1_databases': cf.get('d1', {}).get('count', 0), + 'cf_kv_namespaces': cf.get('kv', {}).get('count', 0), + 'cf_r2_buckets': cf.get('r2', {}).get('count', 0), + 'cf_pages': cf.get('pages', {}).get('count', 0), + 'cf_d1_size_kb': cf.get('d1', {}).get('total_size_kb', 0), }, 'sources': snapshots } diff --git a/collectors/github-deep.sh b/collectors/github-deep.sh new file mode 100644 index 0000000..ca09d15 --- /dev/null +++ b/collectors/github-deep.sh @@ -0,0 +1,108 @@ +#!/bin/bash +# Deep GitHub metrics: stars, forks, traffic, languages, profile stats + +source "$(dirname "$0")/../lib/common.sh" + +log "Collecting deep GitHub metrics..." + +OUT=$(snapshot_file github-deep) + +# Profile stats +profile=$(gh api users/$GITHUB_USER 2>/dev/null || echo '{}') +followers=$(echo "$profile" | python3 -c "import json,sys; print(json.load(sys.stdin).get('followers',0))" 2>/dev/null || echo 0) +following=$(echo "$profile" | python3 -c "import json,sys; print(json.load(sys.stdin).get('following',0))" 2>/dev/null || echo 0) +public_repos=$(echo "$profile" | python3 -c "import json,sys; print(json.load(sys.stdin).get('public_repos',0))" 2>/dev/null || echo 0) +public_gists=$(echo "$profile" | python3 -c "import json,sys; print(json.load(sys.stdin).get('public_gists',0))" 2>/dev/null || echo 0) + +ok "Profile: $public_repos repos, $followers followers" + +# Aggregate stars, forks, sizes across all repos +repo_stats=$(gh api "users/$GITHUB_USER/repos?per_page=100&type=owner" --paginate 2>/dev/null | python3 -c " +import json, sys + +repos = [] +for line in sys.stdin: + try: + repos.extend(json.loads(line)) + except: + pass + +total_stars = sum(r.get('stargazers_count', 0) for r in repos) +total_forks = sum(r.get('forks_count', 0) for r in repos) +total_watchers = sum(r.get('watchers_count', 0) for r in repos) +total_size_kb = sum(r.get('size', 0) for r in repos) +total_open_issues = sum(r.get('open_issues_count', 0) for r in repos) +archived = sum(1 for r in repos if r.get('archived')) +active = len(repos) - archived + +# Languages +langs = {} +for r in repos: + l = r.get('language') + if l: + langs[l] = langs.get(l, 0) + 1 + +# Most recently updated +recent = sorted(repos, key=lambda r: r.get('updated_at', ''), reverse=True)[:10] +recent_names = [r['full_name'] for r in recent] + +# Largest repos +largest = sorted(repos, key=lambda r: r.get('size', 0), reverse=True)[:10] +largest_info = [{r['full_name']: round(r['size']/1024, 1)} for r in largest] + +print(json.dumps({ + 'total_stars': total_stars, + 'total_forks': total_forks, + 'total_watchers': total_watchers, + 'total_size_mb': round(total_size_kb / 1024, 1), + 'total_open_issues': total_open_issues, + 'archived': archived, + 'active': active, + 'languages': langs, + 'top_10_recent': recent_names, + 'top_10_largest_mb': largest_info +})) +" 2>/dev/null || echo '{}') + +# Org stats +org_stats='{' +first=true +for org in $GITHUB_ORGS; do + org_repos=$(gh api "orgs/$org/repos?per_page=100" --paginate --jq 'length' 2>/dev/null || echo 0) + org_members=$(gh api "orgs/$org/members?per_page=100" --jq 'length' 2>/dev/null || echo 0) + if [ "$first" = true ]; then + org_stats="$org_stats\"$org\": {\"repos\": $org_repos, \"members\": $org_members}" + first=false + else + org_stats="$org_stats, \"$org\": {\"repos\": $org_repos, \"members\": $org_members}" + fi +done +org_stats="$org_stats}" + +ok "Orgs: $org_stats" + +python3 -c " +import json + +repo_stats = json.loads('''$repo_stats''') +org_stats = json.loads('''$org_stats''') + +output = { + 'source': 'github-deep', + 'collected_at': '$TIMESTAMP', + 'date': '$TODAY', + 'profile': { + 'followers': $followers, + 'following': $following, + 'public_repos': $public_repos, + 'public_gists': $public_gists + }, + 'repos': repo_stats, + 'orgs': org_stats +} + +with open('$OUT', 'w') as f: + json.dump(output, f, indent=2) +" 2>/dev/null + +ok "Deep GitHub metrics collected" diff --git a/collectors/local.sh b/collectors/local.sh new file mode 100644 index 0000000..b9f798b --- /dev/null +++ b/collectors/local.sh @@ -0,0 +1,120 @@ +#!/bin/bash +# Collect local Mac KPIs: scripts, databases, packages, disk, processes + +source "$(dirname "$0")/../lib/common.sh" + +log "Collecting local Mac KPIs..." + +OUT=$(snapshot_file local) + +# Scripts in ~/bin +bin_count=$(ls -1 ~/bin/ 2>/dev/null | wc -l | tr -d ' ') +bin_size_mb=$(du -sm ~/bin/ 2>/dev/null | cut -f1 || echo 0) + +# Shell scripts in home +home_scripts=$(ls -1 ~/*.sh 2>/dev/null | wc -l | tr -d ' ') + +# Templates +template_count=$(ls -1 ~/Desktop/templates/ 2>/dev/null | wc -l | tr -d ' ') + +# SQLite databases +db_count=$(find ~/.blackroad -name "*.db" -type f 2>/dev/null | wc -l | tr -d ' ') +db_size_mb=$(du -sm ~/.blackroad/ 2>/dev/null | cut -f1 || echo 0) + +# Package managers +brew_count=$(brew list 2>/dev/null | wc -l | tr -d ' ' || echo 0) +pip_count=$(pip3 list 2>/dev/null | tail -n +3 | wc -l | tr -d ' ' || echo 0) +npm_global=$(npm list -g --depth=0 2>/dev/null | tail -n +2 | wc -l | tr -d ' ' || echo 0) + +# Cron jobs +cron_count=$(crontab -l 2>/dev/null | grep -cv '^#\|^$' || echo 0) + +# Git repos (local) +git_repos=$(find ~/ -maxdepth 3 -name ".git" -type d 2>/dev/null | wc -l | tr -d ' ') + +# Disk usage +disk_used=$(df -g / | tail -1 | awk '{print $3}') +disk_total=$(df -g / | tail -1 | awk '{print $2}') +disk_pct=$(df / | tail -1 | awk '{print $5}' | tr -d '%') + +# Processes +process_count=$(ps aux | wc -l | tr -d ' ') + +# Network connections +net_connections=$(netstat -an 2>/dev/null | grep ESTABLISHED | wc -l | tr -d ' ' || echo 0) + +# Downloads & Documents +downloads_count=$(ls -1 ~/Downloads/ 2>/dev/null | wc -l | tr -d ' ') +documents_count=$(ls -1 ~/Documents/ 2>/dev/null | wc -l | tr -d ' ') + +# FTS5 memory index +fts_entries=0 +if [ -f ~/.blackroad/markdown.db ]; then + fts_entries=$(python3 -c " +import sqlite3 +c = sqlite3.connect('$HOME/.blackroad/markdown.db') +try: + r = c.execute('SELECT count(*) FROM markdown_fts').fetchone() + print(r[0]) +except: + print(0) +" 2>/dev/null || echo 0) +fi + +# Systems.db count +systems_count=0 +if [ -f ~/.blackroad/systems.db ]; then + systems_count=$(python3 -c " +import sqlite3 +c = sqlite3.connect('$HOME/.blackroad/systems.db') +try: + r = c.execute('SELECT count(*) FROM systems').fetchone() + print(r[0]) +except: + print(0) +" 2>/dev/null || echo 0) +fi + +cat > "$OUT" << ENDJSON +{ + "source": "local", + "collected_at": "$TIMESTAMP", + "date": "$TODAY", + "scripts": { + "bin_tools": $bin_count, + "bin_size_mb": $bin_size_mb, + "home_scripts": $home_scripts, + "templates": $template_count + }, + "databases": { + "sqlite_count": $db_count, + "blackroad_dir_mb": $db_size_mb, + "fts5_entries": $fts_entries, + "systems_registered": $systems_count + }, + "packages": { + "homebrew": $brew_count, + "pip3": $pip_count, + "npm_global": $npm_global + }, + "automation": { + "cron_jobs": $cron_count, + "local_git_repos": $git_repos + }, + "disk": { + "used_gb": $disk_used, + "total_gb": $disk_total, + "pct": $disk_pct + }, + "system": { + "processes": $process_count, + "net_connections": $net_connections + }, + "files": { + "downloads": $downloads_count, + "documents": $documents_count + } +} +ENDJSON + +ok "Local: ${bin_count} tools, ${db_count} DBs, ${brew_count} brew, ${cron_count} crons, ${disk_pct}% disk" diff --git a/collectors/services-probe.py b/collectors/services-probe.py new file mode 100644 index 0000000..19c09e1 --- /dev/null +++ b/collectors/services-probe.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Deep service probe — runs on each Pi via SSH""" +import json, subprocess, os + +def run(cmd): + try: + return subprocess.check_output(cmd, shell=True, stderr=subprocess.DEVNULL, timeout=10).decode().strip() + except: + return "" + +# Ollama +ollama = {"count": 0, "size_gb": 0, "models": []} +try: + import urllib.request + r = urllib.request.urlopen("http://localhost:11434/api/tags", timeout=3) + data = json.loads(r.read()) + models = data.get("models", []) + ollama["count"] = len(models) + ollama["size_gb"] = round(sum(m.get("size", 0) for m in models) / 1e9, 1) + ollama["models"] = [m.get("name", "") for m in models] +except: + pass + +# Docker +docker = {"running": 0, "images": 0, "containers_total": 0, "names": []} +docker["running"] = int(run("docker ps -q 2>/dev/null | wc -l").strip() or "0") +docker["images"] = int(run("docker images -q 2>/dev/null | wc -l").strip() or "0") +docker["containers_total"] = int(run("docker ps -aq 2>/dev/null | wc -l").strip() or "0") +names = run("docker ps --format '{{.Names}}' 2>/dev/null") +docker["names"] = names.split("\n") if names else [] + +# PostgreSQL +postgres = {"databases": 0} +pg_count = run("sudo -u postgres psql -tc 'SELECT count(*) FROM pg_database' 2>/dev/null").strip() +if pg_count and pg_count.isdigit(): + postgres["databases"] = int(pg_count) + +# Nginx +nginx = {"sites": 0, "active": False} +nginx["sites"] = int(run("ls /etc/nginx/sites-enabled/ 2>/dev/null | wc -l").strip() or "0") +nginx["active"] = run("systemctl is-active nginx 2>/dev/null") == "active" + +# Systemd +systemd = {"services": 0, "timers": 0, "failed": 0} +systemd["services"] = int(run("systemctl list-units --type=service --no-legend 2>/dev/null | wc -l").strip() or "0") +systemd["timers"] = int(run("systemctl list-timers --no-legend 2>/dev/null | wc -l").strip() or "0") +systemd["failed"] = int(run("systemctl --failed --no-legend 2>/dev/null | wc -l").strip() or "0") + +# Processes & connections +processes = int(run("ps aux 2>/dev/null | wc -l").strip() or "0") +connections = int(run("ss -tunp 2>/dev/null | wc -l").strip() or "0") + +# Swap +swap = {"used_mb": 0, "total_mb": 0} +swap_line = run("free -m | grep Swap") +if swap_line: + parts = swap_line.split() + if len(parts) >= 3: + swap["total_mb"] = int(parts[1]) + swap["used_mb"] = int(parts[2]) + +# Cloudflared +cloudflared = run("systemctl is-active cloudflared 2>/dev/null") == "active" + +# Tailscale peers +tailscale_peers = int(run("tailscale status 2>/dev/null | wc -l").strip() or "0") + +# Hailo +hailo = os.path.exists("/dev/hailo0") + +# Crons (all users) +cron_root = int(run("crontab -l 2>/dev/null | grep -cv '^#\\|^$'") or "0") +cron_users = 0 +for u in run("ls /home/ 2>/dev/null").split(): + c = run(f"sudo crontab -u {u} -l 2>/dev/null | grep -cv '^#\\|^$'") + cron_users += int(c) if c and c.isdigit() else 0 + +d = { + "ollama": ollama, + "docker": docker, + "postgres": postgres, + "nginx": nginx, + "systemd": systemd, + "processes": processes, + "connections": connections, + "swap": swap, + "cloudflared": cloudflared, + "tailscale_peers": tailscale_peers, + "hailo": hailo, + "crons": {"root": cron_root, "users": cron_users, "total": cron_root + cron_users}, +} +print(json.dumps(d)) diff --git a/collectors/services.sh b/collectors/services.sh new file mode 100644 index 0000000..57e9ec0 --- /dev/null +++ b/collectors/services.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Collect service-level KPIs from fleet: Ollama, Docker, PostgreSQL, Nginx, systemd + +source "$(dirname "$0")/../lib/common.sh" +set +e + +log "Collecting service KPIs..." + +OUT=$(snapshot_file services) +PROBE_SCRIPT="$(dirname "$0")/services-probe.py" + +nodes_json='{' +first=true + +for entry in $FLEET_NODES; do + node=$(echo "$entry" | cut -d: -f1) + ip=$(echo "$entry" | cut -d: -f2) + user=$(get_ssh_user "$node") + + log "Probing services on $node..." + + result=$(ssh -o ConnectTimeout=3 -o ServerAliveInterval=3 -o ServerAliveCountMax=2 \ + -o StrictHostKeyChecking=no -o BatchMode=yes \ + "$user@$ip" "python3 -" < "$PROBE_SCRIPT" 2>/dev/null || echo '') + + if [ -n "$result" ]; then + if [ "$first" = true ]; then + nodes_json="$nodes_json\"$node\": $result" + first=false + else + nodes_json="$nodes_json, \"$node\": $result" + fi + ok "$node: services probed" + else + if [ "$first" = true ]; then + nodes_json="$nodes_json\"$node\": {\"status\": \"offline\"}" + first=false + else + nodes_json="$nodes_json, \"$node\": {\"status\": \"offline\"}" + fi + err "$node: offline" + fi +done + +nodes_json="$nodes_json}" + +# Aggregate +python3 -c " +import json + +nodes = json.loads('''$nodes_json''') +online = {k: v for k, v in nodes.items() if v.get('status') != 'offline'} + +output = { + 'source': 'services', + 'collected_at': '$TIMESTAMP', + 'date': '$TODAY', + 'totals': { + 'ollama_models': sum(v.get('ollama', {}).get('count', 0) for v in online.values()), + 'ollama_size_gb': round(sum(v.get('ollama', {}).get('size_gb', 0) for v in online.values()), 1), + 'docker_containers': sum(v.get('docker', {}).get('running', 0) for v in online.values()), + 'docker_images': sum(v.get('docker', {}).get('images', 0) for v in online.values()), + 'postgres_dbs': sum(v.get('postgres', {}).get('databases', 0) for v in online.values()), + 'nginx_sites': sum(v.get('nginx', {}).get('sites', 0) for v in online.values()), + 'systemd_services': sum(v.get('systemd', {}).get('services', 0) for v in online.values()), + 'systemd_timers': sum(v.get('systemd', {}).get('timers', 0) for v in online.values()), + 'systemd_failed': sum(v.get('systemd', {}).get('failed', 0) for v in online.values()), + 'processes': sum(v.get('processes', 0) for v in online.values()), + 'network_connections': sum(v.get('connections', 0) for v in online.values()), + 'swap_used_mb': sum(v.get('swap', {}).get('used_mb', 0) for v in online.values()), + 'swap_total_mb': sum(v.get('swap', {}).get('total_mb', 0) for v in online.values()), + 'tailscale_peers': max((v.get('tailscale_peers', 0) for v in online.values()), default=0) + }, + 'nodes': nodes +} + +with open('$OUT', 'w') as f: + json.dump(output, f, indent=2) +" 2>/dev/null + +ok "Services collected" diff --git a/data/daily/2026-03-12.json b/data/daily/2026-03-12.json index a244214..ac80197 100644 --- a/data/daily/2026-03-12.json +++ b/data/daily/2026-03-12.json @@ -1,38 +1,108 @@ { "date": "2026-03-12", - "collected_at": "2026-03-13T03:46:49Z", + "collected_at": "2026-03-13T04:43:38Z", "summary": { - "commits_today": 349, + "commits_today": 326, + "push_events_today": 67, "prs_open": 16, "prs_merged_today": 0, "prs_merged_total": 4019, - "repos_github": 101, + "github_events_today": 100, + "repos_github": 102, "repos_gitea": 0, - "repos_total": 101, + "repos_total": 102, + "repos_active": 73, + "repos_archived": 85, + "github_stars": 4, + "github_forks": 0, + "github_followers": 2, + "github_following": 22, + "github_open_issues": 160, + "github_size_mb": 3748.7, + "github_languages": { + "Python": 26, + "HTML": 21, + "Dockerfile": 7, + "Shell": 10, + "JavaScript": 32, + "TypeScript": 14, + "Go": 2, + "MDX": 1, + "C": 2, + "CSS": 1 + }, "fleet_online": 3, - "fleet_total": 0, - "autonomy_score": 50, - "total_loc": 7212320, + "fleet_total": 4, + "fleet_offline": [ + "octavia" + ], + "avg_temp_c": 44.8, + "throttled_nodes": [], + "fleet_mem_used_mb": 13050, + "fleet_mem_total_mb": 19915, + "fleet_disk_used_gb": 159, + "fleet_disk_total_gb": 707, "docker_containers": 14, + "docker_images": 15, "ollama_models": 27, - "avg_temp_c": 43.3, + "ollama_size_gb": 48.1, + "postgres_dbs": 11, + "nginx_sites": 48, + "systemd_services": 256, + "systemd_timers": 35, "failed_units": 12, - "throttled_nodes": [] + "fleet_processes": 867, + "fleet_connections": 106, + "fleet_swap_used_mb": 4704, + "fleet_swap_total_mb": 10849, + "tailscale_peers": 9, + "autonomy_score": 50, + "heal_events_today": 0, + "service_restarts_today": 0, + "fleet_cron_jobs": 0, + "fleet_timers": 0, + "max_uptime_days": 0, + "total_loc": 7212320, + "local_repos": 73, + "local_files": 1723, + "local_scripts": 300, + "local_script_lines": 576887, + "bin_tools": 212, + "bin_size_mb": 121, + "home_scripts": 91, + "templates": 75, + "sqlite_dbs": 230, + "blackroad_dir_mb": 1390, + "fts5_entries": 0, + "systems_registered": 111, + "brew_packages": 293, + "pip_packages": 35, + "npm_global_packages": 14, + "mac_cron_jobs": 17, + "local_git_repos": 12, + "mac_disk_pct": 16, + "mac_disk_used_gb": 12, + "mac_processes": 526, + "cf_d1_databases": 22, + "cf_kv_namespaces": 46, + "cf_r2_buckets": 11, + "cf_pages": 99, + "cf_d1_size_kb": 40708 }, "sources": { "loc": { "source": "loc", - "collected_at": "2026-03-13T03:47:07Z", + "collected_at": "2026-03-13T04:44:06Z", "date": "2026-03-12", "local": { "total_code_lines": 4608051, "repos": 73, "files": 1723, - "scripts": 298, - "script_lines": 575777 + "scripts": 300, + "script_lines": 576887 }, "projects": { - "blackroad-os-kpis": 1226, + "blackroad-os-kpis": 2953, "images-blackroad": 2562, "roadnet": 5064 }, @@ -46,15 +116,15 @@ }, "github": { "source": "github", - "collected_at": "2026-03-13T03:46:49Z", + "collected_at": "2026-03-13T04:43:38Z", "date": "2026-03-12", "repos": { - "total": 101, + "total": 102, "total_size_mb": 2650.9, "languages": { "Python": 23, "HTML": 22, - "Shell": 9, + "Shell": 10, "TypeScript": 8, "JavaScript": 7, "Dockerfile": 6, @@ -63,8 +133,8 @@ } }, "commits": { - "today": 349, - "push_events_today": 68 + "today": 326, + "push_events_today": 67 }, "pull_requests": { "open": 16, @@ -77,7 +147,7 @@ }, "autonomy": { "source": "autonomy", - "collected_at": "2026-03-13T03:47:03Z", + "collected_at": "2026-03-13T04:44:03Z", "date": "2026-03-12", "autonomy_score": 50, "totals": { @@ -94,9 +164,431 @@ } } }, + "services": { + "source": "services", + "collected_at": "2026-03-13T04:43:55Z", + "date": "2026-03-12", + "totals": { + "ollama_models": 27, + "ollama_size_gb": 48.1, + "docker_containers": 14, + "docker_images": 15, + "postgres_dbs": 11, + "nginx_sites": 48, + "systemd_services": 256, + "systemd_timers": 35, + "systemd_failed": 12, + "processes": 867, + "network_connections": 106, + "swap_used_mb": 4704, + "swap_total_mb": 10849, + "tailscale_peers": 9 + }, + "nodes": { + "alice": { + "ollama": { + "count": 6, + "size_gb": 10.1, + "models": [ + "qwen2.5:3b", + "nomic-embed-text:latest", + "lucidia:latest", + "llama3.2:1b", + "tinyllama:latest", + "qwen2.5:1.5b" + ] + }, + "docker": { + "running": 0, + "images": 0, + "containers_total": 0, + "names": [] + }, + "postgres": { + "databases": 4 + }, + "nginx": { + "sites": 30, + "active": true + }, + "systemd": { + "services": 87, + "timers": 11, + "failed": 3 + }, + "processes": 226, + "connections": 13, + "swap": { + "used_mb": 0, + "total_mb": 99 + }, + "cloudflared": true, + "tailscale_peers": 0, + "hailo": false, + "crons": { + "root": 5, + "users": 8, + "total": 13 + } + }, + "cecilia": { + "ollama": { + "count": 15, + "size_gb": 27.9, + "models": [ + "deepseek-r1:1.5b", + "nomic-embed-text:latest", + "hf.co/mradermacher/OpenELM-1_1B-Instruct-GGUF:Q4_K_M", + "hf.co/mradermacher/OpenELM-3B-Instruct-GGUF:Q4_K_M", + "cece2:latest", + "qwen3:8b", + "llama3:8b-instruct-q4_K_M", + "cece:latest", + "deepseek-coder:1.3b", + "qwen2.5-coder:3b", + "llama3.2:3b", + "tinyllama:latest", + "llama3.2:latest", + "codellama:7b", + "llama3.2:1b" + ] + }, + "docker": { + "running": 0, + "images": 0, + "containers_total": 0, + "names": [] + }, + "postgres": { + "databases": 3 + }, + "nginx": { + "sites": 15, + "active": false + }, + "systemd": { + "services": 80, + "timers": 11, + "failed": 5 + }, + "processes": 254, + "connections": 34, + "swap": { + "used_mb": 2039, + "total_mb": 2047 + }, + "cloudflared": true, + "tailscale_peers": 0, + "hailo": true, + "crons": { + "root": 12, + "users": 14, + "total": 26 + } + }, + "octavia": { + "status": "offline" + }, + "lucidia": { + "ollama": { + "count": 6, + "size_gb": 10.1, + "models": [ + "qwen2.5:3b", + "nomic-embed-text:latest", + "lucidia:latest", + "llama3.2:1b", + "tinyllama:latest", + "qwen2.5:1.5b" + ] + }, + "docker": { + "running": 14, + "images": 15, + "containers_total": 15, + "names": [ + "blackroad-gitea", + "road-pdns-admin", + "road-pdns", + "road-dns-db", + "roadauth", + "roadapi", + "blackroad-edge-agent", + "blackroad.systems", + "blackroadai.com", + "blackroad-auth-gateway", + "blackroad-metaverse", + "blackroad-os", + "blackroad-os-carpool", + "pi-my-agent-1" + ] + }, + "postgres": { + "databases": 4 + }, + "nginx": { + "sites": 3, + "active": true + }, + "systemd": { + "services": 89, + "timers": 13, + "failed": 4 + }, + "processes": 387, + "connections": 59, + "swap": { + "used_mb": 2665, + "total_mb": 8703 + }, + "cloudflared": true, + "tailscale_peers": 9, + "hailo": false, + "crons": { + "root": 11, + "users": 21, + "total": 32 + } + } + } + }, + "cloudflare": { + "source": "cloudflare", + "collected_at": "2026-03-13T04:44:34Z", + "date": "2026-03-12", + "d1": { + "count": 22, + "total_size_kb": 40708, + "databases": [ + { + "name": "blackroad-auth", + "uuid": "761ccc7a-f2b5-43d5-b0df-aeedc212d382" + }, + { + "name": "images-blackroad", + "uuid": "98ddb91d-4705-4531-969e-a0b3fb8a4c57" + }, + { + "name": "index-blackroad", + "uuid": "5599c9ac-5bb9-430a-9743-c32d873b9e78" + }, + { + "name": "blackboard", + "uuid": "bcc78f33-b052-4cb2-bcfb-db5cd3c08472" + }, + { + "name": "openapi-template-db", + "uuid": "4c14af7a-9ddf-44fd-a67a-21585d5e2101" + }, + { + "name": "blackroad-verification", + "uuid": "e781869d-962c-459d-91c3-f0edbf111815" + }, + { + "name": "blackroad-db", + "uuid": "b768c5de-b9a3-4337-8c26-b2e9ff230829" + }, + { + "name": "blackroad-billing", + "uuid": "147604cc-6c0e-45e4-985b-3b3fd19f688b" + }, + { + "name": "blackroad-api-gateway", + "uuid": "16353ffd-17fa-4c5b-95b7-b02a4cd3d228" + }, + { + "name": "blackroad-analytics-db", + "uuid": "3175e028-d45a-4d98-9675-8de70c49fc7f" + }, + { + "name": "blackroad-memory-db", + "uuid": "1f981017-629b-41cb-a4fb-42cebee9f17e" + }, + { + "name": "blackroad-agents-db", + "uuid": "2b17625c-4eb3-4c2e-8134-cb617a41ec7d" + }, + { + "name": "blackroad-dialer", + "uuid": "38337a08-6de3-4688-8daf-f2b0c0d40131" + }, + { + "name": "blackroad-repos", + "uuid": "324e793e-20c6-4917-b034-ef0ee86e6760" + }, + { + "name": "blackroad-registry", + "uuid": "9acf402f-6338-41e9-b909-a0317ea5e8bc" + }, + { + "name": "blackroad-continuity", + "uuid": "f0721506-cb52-41ee-b587-38f7b42b97d9" + }, + { + "name": "lucidia-world", + "uuid": "aa8ac8d2-cc7f-4718-a15b-e7e39586a0ce" + }, + { + "name": "blackroad-saas", + "uuid": "c7bec6d8-42fa-49fb-9d8c-57d626dde6b9" + }, + { + "name": "apollo-agent-registry", + "uuid": "79f8b80d-3bb5-4dd4-beee-a77a1084b574" + }, + { + "name": "blackroad_revenue", + "uuid": "8744905a-cf6c-4e16-9661-4c67d340813f" + }, + { + "name": "blackroad-logs", + "uuid": "2bea6826-d4cb-4877-8d78-aa7a8fd3c1b0" + }, + { + "name": "blackroad-os-main", + "uuid": "e2c6dcd9-c21a-48ac-8807-7b3a6881c4f7" + } + ] + }, + "kv": { + "count": 46 + }, + "r2": { + "count": 11 + }, + "pages": { + "count": 99 + }, + "account_id": "848cf0b18d51e0170e0d1537aec3505a" + }, + "github-deep": { + "source": "github-deep", + "collected_at": "2026-03-13T04:43:45Z", + "date": "2026-03-12", + "profile": { + "followers": 2, + "following": 22, + "public_repos": 158, + "public_gists": 1 + }, + "repos": { + "total_stars": 4, + "total_forks": 0, + "total_watchers": 4, + "total_size_mb": 3748.7, + "total_open_issues": 160, + "archived": 85, + "active": 73, + "languages": { + "Python": 26, + "HTML": 21, + "Dockerfile": 7, + "Shell": 10, + "JavaScript": 32, + "TypeScript": 14, + "Go": 2, + "MDX": 1, + "C": 2, + "CSS": 1 + }, + "top_10_recent": [ + "blackboxprogramming/blackroad-os-kpis", + "blackboxprogramming/pi-mono", + "blackboxprogramming/system-prompts-and-models-of-ai-tools", + "blackboxprogramming/openclaw", + "blackboxprogramming/skills", + "blackboxprogramming/OpenSandbox", + "blackboxprogramming/OpenViking", + "blackboxprogramming/rowboat", + "blackboxprogramming/learn-claude-code", + "blackboxprogramming/superpowers" + ], + "top_10_largest_mb": [ + { + "blackboxprogramming/openapi-generator": 876.1 + }, + { + "blackboxprogramming/docs": 694.2 + }, + { + "blackboxprogramming/blackroad": 385.9 + }, + { + "blackboxprogramming/hindsight": 344.7 + }, + { + "blackboxprogramming/git": 276.8 + }, + { + "blackboxprogramming/openclaw": 276.6 + }, + { + "blackboxprogramming/A2UI": 122.8 + }, + { + "blackboxprogramming/clerk-docs": 91.1 + }, + { + "blackboxprogramming/OpenSandbox": 78.2 + }, + { + "blackboxprogramming/rowboat": 76.1 + } + ] + }, + "orgs": { + "Blackbox-Enterprises": { + "repos": 8, + "members": 1 + }, + "blackroad-os-inc": { + "repos": 22, + "members": 1 + } + } + }, + "local": { + "source": "local", + "collected_at": "2026-03-13T04:44:10Z", + "date": "2026-03-12", + "scripts": { + "bin_tools": 212, + "bin_size_mb": 121, + "home_scripts": 91, + "templates": 75 + }, + "databases": { + "sqlite_count": 230, + "blackroad_dir_mb": 1390, + "fts5_entries": 0, + "systems_registered": 111 + }, + "packages": { + "homebrew": 293, + "pip3": 35, + "npm_global": 14 + }, + "automation": { + "cron_jobs": 17, + "local_git_repos": 12 + }, + "disk": { + "used_gb": 12, + "total_gb": 460, + "pct": 16 + }, + "system": { + "processes": 526, + "net_connections": 40 + }, + "files": { + "downloads": 94, + "documents": 4 + } + }, "fleet": { "source": "fleet", - "collected_at": "2026-03-13T03:47:00Z", + "collected_at": "2026-03-13T04:43:53Z", "date": "2026-03-12", "fleet": { "total_nodes": 4, @@ -107,8 +599,8 @@ ] }, "totals": { - "cpu_avg_temp_c": 43.3, - "mem_used_mb": 13130, + "cpu_avg_temp_c": 44.8, + "mem_used_mb": 13050, "mem_total_mb": 19915, "disk_used_gb": 159, "disk_total_gb": 707, @@ -120,12 +612,12 @@ "nodes": [ { "hostname": "alice", - "uptime_seconds": 113325, - "load_1m": 0.84, - "load_5m": 1.88, + "uptime_seconds": 116737, + "load_1m": 0.82, + "load_5m": 1.13, "cpu_temp": 34563, "mem_total_mb": 3794, - "mem_used_mb": 360, + "mem_used_mb": 363, "disk_total_gb": 15, "disk_used_gb": 11, "disk_pct": 76, @@ -136,19 +628,19 @@ "throttle_hex": "0x0", "governor": "ondemand", "cpu_temp_c": 34.6, - "mem_pct": 9.5, + "mem_pct": 9.6, "status": "online", "node": "alice", "ip": "192.168.4.49" }, { "hostname": "cecilia", - "uptime_seconds": 113411, - "load_1m": 0.29, - "load_5m": 0.39, + "uptime_seconds": 116823, + "load_1m": 1.11, + "load_5m": 0.63, "cpu_temp": 38050, "mem_total_mb": 8062, - "mem_used_mb": 7306, + "mem_used_mb": 7310, "disk_total_gb": 457, "disk_used_gb": 76, "disk_pct": 18, @@ -159,7 +651,7 @@ "throttle_hex": "unknown", "governor": "conservative", "cpu_temp_c": 38.0, - "mem_pct": 90.6, + "mem_pct": 90.7, "status": "online", "node": "cecilia", "ip": "192.168.4.96" @@ -171,12 +663,12 @@ }, { "hostname": "octavia", - "uptime_seconds": 113380, - "load_1m": 14.11, - "load_5m": 7.17, - "cpu_temp": 57300, + "uptime_seconds": 116791, + "load_1m": 3.58, + "load_5m": 2.87, + "cpu_temp": 61700, "mem_total_mb": 8059, - "mem_used_mb": 5464, + "mem_used_mb": 5377, "disk_total_gb": 235, "disk_used_gb": 72, "disk_pct": 33, @@ -186,8 +678,8 @@ "ollama_models": 6, "throttle_hex": "unknown", "governor": "ondemand", - "cpu_temp_c": 57.3, - "mem_pct": 67.8, + "cpu_temp_c": 61.7, + "mem_pct": 66.7, "status": "online", "node": "lucidia", "ip": "192.168.4.38" @@ -196,7 +688,7 @@ }, "gitea": { "source": "gitea", - "collected_at": "2026-03-13T03:46:55Z", + "collected_at": "2026-03-13T04:43:48Z", "date": "2026-03-12", "status": "unreachable", "repos": { diff --git a/data/snapshots/2026-03-12-autonomy.json b/data/snapshots/2026-03-12-autonomy.json index 3eacbe8..75fc28c 100644 --- a/data/snapshots/2026-03-12-autonomy.json +++ b/data/snapshots/2026-03-12-autonomy.json @@ -1,6 +1,6 @@ { "source": "autonomy", - "collected_at": "2026-03-13T03:47:03Z", + "collected_at": "2026-03-13T04:44:03Z", "date": "2026-03-12", "autonomy_score": 50, "totals": { diff --git a/data/snapshots/2026-03-12-cloudflare.json b/data/snapshots/2026-03-12-cloudflare.json new file mode 100644 index 0000000..26e6d2d --- /dev/null +++ b/data/snapshots/2026-03-12-cloudflare.json @@ -0,0 +1,20 @@ +{ + "source": "cloudflare", + "collected_at": "2026-03-13T04:44:34Z", + "date": "2026-03-12", + "d1": { + "count": 22, + "total_size_kb": 40708, + "databases": [{"name": "blackroad-auth", "uuid": "761ccc7a-f2b5-43d5-b0df-aeedc212d382"}, {"name": "images-blackroad", "uuid": "98ddb91d-4705-4531-969e-a0b3fb8a4c57"}, {"name": "index-blackroad", "uuid": "5599c9ac-5bb9-430a-9743-c32d873b9e78"}, {"name": "blackboard", "uuid": "bcc78f33-b052-4cb2-bcfb-db5cd3c08472"}, {"name": "openapi-template-db", "uuid": "4c14af7a-9ddf-44fd-a67a-21585d5e2101"}, {"name": "blackroad-verification", "uuid": "e781869d-962c-459d-91c3-f0edbf111815"}, {"name": "blackroad-db", "uuid": "b768c5de-b9a3-4337-8c26-b2e9ff230829"}, {"name": "blackroad-billing", "uuid": "147604cc-6c0e-45e4-985b-3b3fd19f688b"}, {"name": "blackroad-api-gateway", "uuid": "16353ffd-17fa-4c5b-95b7-b02a4cd3d228"}, {"name": "blackroad-analytics-db", "uuid": "3175e028-d45a-4d98-9675-8de70c49fc7f"}, {"name": "blackroad-memory-db", "uuid": "1f981017-629b-41cb-a4fb-42cebee9f17e"}, {"name": "blackroad-agents-db", "uuid": "2b17625c-4eb3-4c2e-8134-cb617a41ec7d"}, {"name": "blackroad-dialer", "uuid": "38337a08-6de3-4688-8daf-f2b0c0d40131"}, {"name": "blackroad-repos", "uuid": "324e793e-20c6-4917-b034-ef0ee86e6760"}, {"name": "blackroad-registry", "uuid": "9acf402f-6338-41e9-b909-a0317ea5e8bc"}, {"name": "blackroad-continuity", "uuid": "f0721506-cb52-41ee-b587-38f7b42b97d9"}, {"name": "lucidia-world", "uuid": "aa8ac8d2-cc7f-4718-a15b-e7e39586a0ce"}, {"name": "blackroad-saas", "uuid": "c7bec6d8-42fa-49fb-9d8c-57d626dde6b9"}, {"name": "apollo-agent-registry", "uuid": "79f8b80d-3bb5-4dd4-beee-a77a1084b574"}, {"name": "blackroad_revenue", "uuid": "8744905a-cf6c-4e16-9661-4c67d340813f"}, {"name": "blackroad-logs", "uuid": "2bea6826-d4cb-4877-8d78-aa7a8fd3c1b0"}, {"name": "blackroad-os-main", "uuid": "e2c6dcd9-c21a-48ac-8807-7b3a6881c4f7"}] + }, + "kv": { + "count": 46 + }, + "r2": { + "count": 11 + }, + "pages": { + "count": 99 + }, + "account_id": "848cf0b18d51e0170e0d1537aec3505a" +} diff --git a/data/snapshots/2026-03-12-fleet.json b/data/snapshots/2026-03-12-fleet.json index 175fc89..2d333af 100644 --- a/data/snapshots/2026-03-12-fleet.json +++ b/data/snapshots/2026-03-12-fleet.json @@ -1,6 +1,6 @@ { "source": "fleet", - "collected_at": "2026-03-13T03:47:00Z", + "collected_at": "2026-03-13T04:43:53Z", "date": "2026-03-12", "fleet": { "total_nodes": 4, @@ -11,8 +11,8 @@ ] }, "totals": { - "cpu_avg_temp_c": 43.3, - "mem_used_mb": 13130, + "cpu_avg_temp_c": 44.8, + "mem_used_mb": 13050, "mem_total_mb": 19915, "disk_used_gb": 159, "disk_total_gb": 707, @@ -24,12 +24,12 @@ "nodes": [ { "hostname": "alice", - "uptime_seconds": 113325, - "load_1m": 0.84, - "load_5m": 1.88, + "uptime_seconds": 116737, + "load_1m": 0.82, + "load_5m": 1.13, "cpu_temp": 34563, "mem_total_mb": 3794, - "mem_used_mb": 360, + "mem_used_mb": 363, "disk_total_gb": 15, "disk_used_gb": 11, "disk_pct": 76, @@ -40,19 +40,19 @@ "throttle_hex": "0x0", "governor": "ondemand", "cpu_temp_c": 34.6, - "mem_pct": 9.5, + "mem_pct": 9.6, "status": "online", "node": "alice", "ip": "192.168.4.49" }, { "hostname": "cecilia", - "uptime_seconds": 113411, - "load_1m": 0.29, - "load_5m": 0.39, + "uptime_seconds": 116823, + "load_1m": 1.11, + "load_5m": 0.63, "cpu_temp": 38050, "mem_total_mb": 8062, - "mem_used_mb": 7306, + "mem_used_mb": 7310, "disk_total_gb": 457, "disk_used_gb": 76, "disk_pct": 18, @@ -63,7 +63,7 @@ "throttle_hex": "unknown", "governor": "conservative", "cpu_temp_c": 38.0, - "mem_pct": 90.6, + "mem_pct": 90.7, "status": "online", "node": "cecilia", "ip": "192.168.4.96" @@ -75,12 +75,12 @@ }, { "hostname": "octavia", - "uptime_seconds": 113380, - "load_1m": 14.11, - "load_5m": 7.17, - "cpu_temp": 57300, + "uptime_seconds": 116791, + "load_1m": 3.58, + "load_5m": 2.87, + "cpu_temp": 61700, "mem_total_mb": 8059, - "mem_used_mb": 5464, + "mem_used_mb": 5377, "disk_total_gb": 235, "disk_used_gb": 72, "disk_pct": 33, @@ -90,8 +90,8 @@ "ollama_models": 6, "throttle_hex": "unknown", "governor": "ondemand", - "cpu_temp_c": 57.3, - "mem_pct": 67.8, + "cpu_temp_c": 61.7, + "mem_pct": 66.7, "status": "online", "node": "lucidia", "ip": "192.168.4.38" diff --git a/data/snapshots/2026-03-12-gitea.json b/data/snapshots/2026-03-12-gitea.json index 870e5da..b6bd882 100644 --- a/data/snapshots/2026-03-12-gitea.json +++ b/data/snapshots/2026-03-12-gitea.json @@ -1,6 +1,6 @@ { "source": "gitea", - "collected_at": "2026-03-13T03:46:55Z", + "collected_at": "2026-03-13T04:43:48Z", "date": "2026-03-12", "status": "unreachable", "repos": { "total": 0 }, diff --git a/data/snapshots/2026-03-12-github-deep.json b/data/snapshots/2026-03-12-github-deep.json new file mode 100644 index 0000000..2d9897b --- /dev/null +++ b/data/snapshots/2026-03-12-github-deep.json @@ -0,0 +1,86 @@ +{ + "source": "github-deep", + "collected_at": "2026-03-13T04:43:45Z", + "date": "2026-03-12", + "profile": { + "followers": 2, + "following": 22, + "public_repos": 158, + "public_gists": 1 + }, + "repos": { + "total_stars": 4, + "total_forks": 0, + "total_watchers": 4, + "total_size_mb": 3748.7, + "total_open_issues": 160, + "archived": 85, + "active": 73, + "languages": { + "Python": 26, + "HTML": 21, + "Dockerfile": 7, + "Shell": 10, + "JavaScript": 32, + "TypeScript": 14, + "Go": 2, + "MDX": 1, + "C": 2, + "CSS": 1 + }, + "top_10_recent": [ + "blackboxprogramming/blackroad-os-kpis", + "blackboxprogramming/pi-mono", + "blackboxprogramming/system-prompts-and-models-of-ai-tools", + "blackboxprogramming/openclaw", + "blackboxprogramming/skills", + "blackboxprogramming/OpenSandbox", + "blackboxprogramming/OpenViking", + "blackboxprogramming/rowboat", + "blackboxprogramming/learn-claude-code", + "blackboxprogramming/superpowers" + ], + "top_10_largest_mb": [ + { + "blackboxprogramming/openapi-generator": 876.1 + }, + { + "blackboxprogramming/docs": 694.2 + }, + { + "blackboxprogramming/blackroad": 385.9 + }, + { + "blackboxprogramming/hindsight": 344.7 + }, + { + "blackboxprogramming/git": 276.8 + }, + { + "blackboxprogramming/openclaw": 276.6 + }, + { + "blackboxprogramming/A2UI": 122.8 + }, + { + "blackboxprogramming/clerk-docs": 91.1 + }, + { + "blackboxprogramming/OpenSandbox": 78.2 + }, + { + "blackboxprogramming/rowboat": 76.1 + } + ] + }, + "orgs": { + "Blackbox-Enterprises": { + "repos": 8, + "members": 1 + }, + "blackroad-os-inc": { + "repos": 22, + "members": 1 + } + } +} \ No newline at end of file diff --git a/data/snapshots/2026-03-12-github.json b/data/snapshots/2026-03-12-github.json index 51dd204..535eabc 100644 --- a/data/snapshots/2026-03-12-github.json +++ b/data/snapshots/2026-03-12-github.json @@ -1,15 +1,15 @@ { "source": "github", - "collected_at": "2026-03-13T03:46:49Z", + "collected_at": "2026-03-13T04:43:38Z", "date": "2026-03-12", "repos": { - "total": 101, + "total": 102, "total_size_mb": 2650.9, - "languages": {"Python": 23, "HTML": 22, "Shell": 9, "TypeScript": 8, "JavaScript": 7, "Dockerfile": 6, "Go": 2, "CSS": 1} + "languages": {"Python": 23, "HTML": 22, "Shell": 10, "TypeScript": 8, "JavaScript": 7, "Dockerfile": 6, "Go": 2, "CSS": 1} }, "commits": { - "today": 349, - "push_events_today": 68 + "today": 326, + "push_events_today": 67 }, "pull_requests": { "open": 16, diff --git a/data/snapshots/2026-03-12-loc.json b/data/snapshots/2026-03-12-loc.json index c255203..d7eb8ca 100644 --- a/data/snapshots/2026-03-12-loc.json +++ b/data/snapshots/2026-03-12-loc.json @@ -1,16 +1,16 @@ { "source": "loc", - "collected_at": "2026-03-13T03:47:07Z", + "collected_at": "2026-03-13T04:44:06Z", "date": "2026-03-12", "local": { "total_code_lines": 4608051, "repos": 73, "files": 1723, - "scripts": 298, - "script_lines": 575777 + "scripts": 300, + "script_lines": 576887 }, "projects": { - "blackroad-os-kpis": 1226, + "blackroad-os-kpis": 2953, "images-blackroad": 2562, "roadnet": 5064 }, diff --git a/data/snapshots/2026-03-12-local.json b/data/snapshots/2026-03-12-local.json new file mode 100644 index 0000000..e9aa1f2 --- /dev/null +++ b/data/snapshots/2026-03-12-local.json @@ -0,0 +1,39 @@ +{ + "source": "local", + "collected_at": "2026-03-13T04:44:10Z", + "date": "2026-03-12", + "scripts": { + "bin_tools": 212, + "bin_size_mb": 121, + "home_scripts": 91, + "templates": 75 + }, + "databases": { + "sqlite_count": 230, + "blackroad_dir_mb": 1390, + "fts5_entries": 0, + "systems_registered": 111 + }, + "packages": { + "homebrew": 293, + "pip3": 35, + "npm_global": 14 + }, + "automation": { + "cron_jobs": 17, + "local_git_repos": 12 + }, + "disk": { + "used_gb": 12, + "total_gb": 460, + "pct": 16 + }, + "system": { + "processes": 526, + "net_connections": 40 + }, + "files": { + "downloads": 94, + "documents": 4 + } +} diff --git a/data/snapshots/2026-03-12-services.json b/data/snapshots/2026-03-12-services.json new file mode 100644 index 0000000..96af254 --- /dev/null +++ b/data/snapshots/2026-03-12-services.json @@ -0,0 +1,188 @@ +{ + "source": "services", + "collected_at": "2026-03-13T04:43:55Z", + "date": "2026-03-12", + "totals": { + "ollama_models": 27, + "ollama_size_gb": 48.1, + "docker_containers": 14, + "docker_images": 15, + "postgres_dbs": 11, + "nginx_sites": 48, + "systemd_services": 256, + "systemd_timers": 35, + "systemd_failed": 12, + "processes": 867, + "network_connections": 106, + "swap_used_mb": 4704, + "swap_total_mb": 10849, + "tailscale_peers": 9 + }, + "nodes": { + "alice": { + "ollama": { + "count": 6, + "size_gb": 10.1, + "models": [ + "qwen2.5:3b", + "nomic-embed-text:latest", + "lucidia:latest", + "llama3.2:1b", + "tinyllama:latest", + "qwen2.5:1.5b" + ] + }, + "docker": { + "running": 0, + "images": 0, + "containers_total": 0, + "names": [] + }, + "postgres": { + "databases": 4 + }, + "nginx": { + "sites": 30, + "active": true + }, + "systemd": { + "services": 87, + "timers": 11, + "failed": 3 + }, + "processes": 226, + "connections": 13, + "swap": { + "used_mb": 0, + "total_mb": 99 + }, + "cloudflared": true, + "tailscale_peers": 0, + "hailo": false, + "crons": { + "root": 5, + "users": 8, + "total": 13 + } + }, + "cecilia": { + "ollama": { + "count": 15, + "size_gb": 27.9, + "models": [ + "deepseek-r1:1.5b", + "nomic-embed-text:latest", + "hf.co/mradermacher/OpenELM-1_1B-Instruct-GGUF:Q4_K_M", + "hf.co/mradermacher/OpenELM-3B-Instruct-GGUF:Q4_K_M", + "cece2:latest", + "qwen3:8b", + "llama3:8b-instruct-q4_K_M", + "cece:latest", + "deepseek-coder:1.3b", + "qwen2.5-coder:3b", + "llama3.2:3b", + "tinyllama:latest", + "llama3.2:latest", + "codellama:7b", + "llama3.2:1b" + ] + }, + "docker": { + "running": 0, + "images": 0, + "containers_total": 0, + "names": [] + }, + "postgres": { + "databases": 3 + }, + "nginx": { + "sites": 15, + "active": false + }, + "systemd": { + "services": 80, + "timers": 11, + "failed": 5 + }, + "processes": 254, + "connections": 34, + "swap": { + "used_mb": 2039, + "total_mb": 2047 + }, + "cloudflared": true, + "tailscale_peers": 0, + "hailo": true, + "crons": { + "root": 12, + "users": 14, + "total": 26 + } + }, + "octavia": { + "status": "offline" + }, + "lucidia": { + "ollama": { + "count": 6, + "size_gb": 10.1, + "models": [ + "qwen2.5:3b", + "nomic-embed-text:latest", + "lucidia:latest", + "llama3.2:1b", + "tinyllama:latest", + "qwen2.5:1.5b" + ] + }, + "docker": { + "running": 14, + "images": 15, + "containers_total": 15, + "names": [ + "blackroad-gitea", + "road-pdns-admin", + "road-pdns", + "road-dns-db", + "roadauth", + "roadapi", + "blackroad-edge-agent", + "blackroad.systems", + "blackroadai.com", + "blackroad-auth-gateway", + "blackroad-metaverse", + "blackroad-os", + "blackroad-os-carpool", + "pi-my-agent-1" + ] + }, + "postgres": { + "databases": 4 + }, + "nginx": { + "sites": 3, + "active": true + }, + "systemd": { + "services": 89, + "timers": 13, + "failed": 4 + }, + "processes": 387, + "connections": 59, + "swap": { + "used_mb": 2665, + "total_mb": 8703 + }, + "cloudflared": true, + "tailscale_peers": 9, + "hailo": false, + "crons": { + "root": 11, + "users": 21, + "total": 32 + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index 557d500..564f8c9 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,21 @@ { "name": "blackroad-os-kpis", - "version": "1.0.0", - "description": "BlackRoad OS KPI tracking — commits, PRs, autonomy, LOC, fleet health", + "version": "2.0.0", + "description": "BlackRoad OS KPI tracking — 60+ metrics across code, fleet, services, autonomy, Cloudflare, LOC", "scripts": { "collect": "bash collectors/collect-all.sh", "collect:github": "bash collectors/github.sh", + "collect:github-deep": "bash collectors/github-deep.sh", "collect:gitea": "bash collectors/gitea.sh", "collect:fleet": "bash collectors/fleet.sh", + "collect:services": "bash collectors/services.sh", "collect:autonomy": "bash collectors/autonomy.sh", + "collect:loc": "bash collectors/loc.sh", + "collect:local": "bash collectors/local.sh", + "collect:cloudflare": "bash collectors/cloudflare.sh", "report": "bash reports/daily-report.sh", "report:slack": "bash reports/slack-notify.sh", - "dashboard": "node dashboards/serve.js" + "report:md": "bash reports/markdown-report.sh" }, "private": true } diff --git a/reports/daily-report.sh b/reports/daily-report.sh index e9ad52e..db73652 100644 --- a/reports/daily-report.sh +++ b/reports/daily-report.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Generate formatted daily KPI report +# Generate formatted daily KPI report — FULL NUMBERS source "$(dirname "$0")/../lib/common.sh" @@ -10,76 +10,164 @@ if [ ! -f "$DAILY" ]; then exit 1 fi -# Get yesterday's data for deltas YESTERDAY=$(date -v-1d +%Y-%m-%d 2>/dev/null || date -d '1 day ago' +%Y-%m-%d) YESTERDAY_FILE="$DATA_DIR/daily/${YESTERDAY}.json" export DAILY YESTERDAY_FILE python3 << 'PYEOF' -import json, os, sys +import json, os -today_file = os.environ.get('DAILY', '') -yesterday_file = os.environ.get('YESTERDAY_FILE', '') - -with open(today_file) as f: +with open(os.environ['DAILY']) as f: data = json.load(f) yesterday = {} -if os.path.exists(yesterday_file or ''): - with open(yesterday_file) as f: +yf = os.environ.get('YESTERDAY_FILE', '') +if yf and os.path.exists(yf): + with open(yf) as f: yesterday = json.load(f) s = data['summary'] ys = yesterday.get('summary', {}) -def delta(key): +def d(key): + """Delta indicator""" curr = s.get(key, 0) prev = ys.get(key, 0) - if not prev: + if not prev or not isinstance(curr, (int, float)): return '' diff = curr - prev if diff > 0: return f' \033[38;5;82m(+{diff})\033[0m' elif diff < 0: return f' \033[38;5;196m({diff})\033[0m' - return ' (=)' + return ' \033[38;5;240m(=)\033[0m' + +def pct(used, total): + if not total: + return '0%' + return f"{round(used/total*100)}%" + +def bar(value, max_val, width=20): + if not max_val: + return '░' * width + filled = int(value / max_val * width) + return '█' * filled + '░' * (width - filled) P = '\033[38;5;205m' A = '\033[38;5;214m' B = '\033[38;5;69m' G = '\033[38;5;82m' V = '\033[38;5;135m' +W = '\033[1;37m' +DIM = '\033[38;5;240m' R = '\033[0m' +# Compute some derived metrics +fleet_mem_pct = pct(s.get('fleet_mem_used_mb', 0), s.get('fleet_mem_total_mb', 1)) +fleet_disk_pct = pct(s.get('fleet_disk_used_gb', 0), s.get('fleet_disk_total_gb', 1)) +fleet_swap_pct = pct(s.get('fleet_swap_used_mb', 0), s.get('fleet_swap_total_mb', 1)) + +# Total infrastructure count +total_infra = ( + s.get('cf_d1_databases', 0) + + s.get('cf_kv_namespaces', 0) + + s.get('cf_r2_buckets', 0) + + s.get('cf_pages', 0) + + s.get('docker_containers', 0) + + s.get('postgres_dbs', 0) + + s.get('sqlite_dbs', 0) +) + +# Total automation +total_automation = ( + s.get('mac_cron_jobs', 0) + + s.get('fleet_cron_jobs', 0) + + s.get('systemd_timers', 0) +) + print(f""" -{P}╔══════════════════════════════════════════════════╗{R} -{P}║{R} {A}BlackRoad OS — Daily KPIs{R} {P}║{R} -{P}║{R} {B}{data['date']}{R} {P}║{R} -{P}╠══════════════════════════════════════════════════╣{R} +{P}╔══════════════════════════════════════════════════════════════╗{R} +{P}║{R} {W}B L A C K R O A D O S{R} — {A}Daily KPIs{R} {P}║{R} +{P}║{R} {B}{data['date']}{R} {DIM}collected {data['collected_at']}{R} {P}║{R} +{P}╠══════════════════════════════════════════════════════════════╣{R} -{A}📊 CODE{R} - Commits today: {G}{s['commits_today']}{R}{delta('commits_today')} - PRs open: {s['prs_open']}{delta('prs_open')} - PRs merged today: {s['prs_merged_today']}{delta('prs_merged_today')} - PRs merged total: {s['prs_merged_total']}{delta('prs_merged_total')} - Total LOC: {s['total_loc']:,}{delta('total_loc')} +{A}═══ CODE VELOCITY ══════════════════════════════════════════════{R} + Commits today {G}{s['commits_today']:>6}{R}{d('commits_today')} + Push events {s.get('push_events_today', 0):>6}{d('push_events_today')} + GitHub events {s.get('github_events_today', 0):>6}{d('github_events_today')} + PRs open {s['prs_open']:>6}{d('prs_open')} + PRs merged today {s['prs_merged_today']:>6}{d('prs_merged_today')} + PRs merged total {s['prs_merged_total']:>6}{d('prs_merged_total')} + Open issues {s.get('github_open_issues', 0):>6}{d('github_open_issues')} + Total LOC {G}{s['total_loc']:>10,}{R}{d('total_loc')} -{A}📦 REPOS{R} - GitHub: {s['repos_github']}{delta('repos_github')} - Gitea: {s['repos_gitea']}{delta('repos_gitea')} - Total: {s['repos_total']}{delta('repos_total')} +{A}═══ REPOSITORIES ══════════════════════════════════════════════{R} + GitHub repos {s['repos_github']:>6}{d('repos_github')} + Gitea repos {s['repos_gitea']:>6}{d('repos_gitea')} + Total repos {W}{s['repos_total']:>6}{R}{d('repos_total')} + Active {s.get('repos_active', 0):>6} + Archived {s.get('repos_archived', 0):>6} + Total size {s.get('github_size_mb', 0):>7.0f} MB{d('github_size_mb')} + Stars {s.get('github_stars', 0):>6} Forks {s.get('github_forks', 0)} + Followers {s.get('github_followers', 0):>6} Following {s.get('github_following', 0)} -{A}🖥 FLEET{R} - Nodes online: {s['fleet_online']}/{s['fleet_total']} - Docker containers: {s['docker_containers']}{delta('docker_containers')} - Ollama models: {s['ollama_models']}{delta('ollama_models')} - Avg temp: {s['avg_temp_c']}°C - Failed units: {s['failed_units']}{delta('failed_units')} - Throttled: {', '.join(s.get('throttled_nodes', [])) or 'none'} +{A}═══ FLEET ({s['fleet_online']}/{s['fleet_total']} online) ══════════════════════════════════════{R} + Nodes online {G}{s['fleet_online']:>6}{R}/{s['fleet_total']}{d('fleet_online')} + Offline {', '.join(s.get('fleet_offline', [])) or 'none'} + Avg temp {s['avg_temp_c']:>7.1f}°C + Throttled {', '.join(s.get('throttled_nodes', [])) or 'none'} + Memory {s.get('fleet_mem_used_mb', 0):>5} / {s.get('fleet_mem_total_mb', 0)} MB {DIM}({fleet_mem_pct}){R} + Disk {s.get('fleet_disk_used_gb', 0):>5} / {s.get('fleet_disk_total_gb', 0)} GB {DIM}({fleet_disk_pct}){R} + Swap {s.get('fleet_swap_used_mb', 0):>5} / {s.get('fleet_swap_total_mb', 0)} MB {DIM}({fleet_swap_pct}){R} + Processes {s.get('fleet_processes', 0):>6} + Net connections {s.get('fleet_connections', 0):>6} + Tailscale peers {s.get('tailscale_peers', 0):>6} -{A}🤖 AUTONOMY{R} - Score: {V}{s['autonomy_score']}/100{R}{delta('autonomy_score')} +{A}═══ SERVICES ═══════════════════════════════════════════════════{R} + Ollama models {V}{s.get('ollama_models', 0):>6}{R} {DIM}({s.get('ollama_size_gb', 0):.1f} GB){R}{d('ollama_models')} + Docker containers {s.get('docker_containers', 0):>6}{d('docker_containers')} + Docker images {s.get('docker_images', 0):>6}{d('docker_images')} + PostgreSQL DBs {s.get('postgres_dbs', 0):>6}{d('postgres_dbs')} + Nginx sites {s.get('nginx_sites', 0):>6}{d('nginx_sites')} + Systemd services {s.get('systemd_services', 0):>6}{d('systemd_services')} + Systemd timers {s.get('systemd_timers', 0):>6}{d('systemd_timers')} + Failed units {s.get('failed_units', 0):>6}{d('failed_units')} -{P}╚══════════════════════════════════════════════════╝{R} +{A}═══ AUTONOMY ═══════════════════════════════════════════════════{R} + Score {V}{s.get('autonomy_score', 0):>5}/100{R} {bar(s.get('autonomy_score', 0), 100, 30)}{d('autonomy_score')} + Heal events today {s.get('heal_events_today', 0):>6} + Service restarts {s.get('service_restarts_today', 0):>6} + Fleet cron jobs {s.get('fleet_cron_jobs', 0):>6} + Max uptime {s.get('max_uptime_days', 0):>5} days + +{A}═══ LOCAL MAC ═══════════════════════════════════════════════════{R} + CLI tools (~/bin) {s.get('bin_tools', 0):>6} {DIM}({s.get('bin_size_mb', 0)} MB){R}{d('bin_tools')} + Home scripts {s.get('home_scripts', 0):>6} + Templates {s.get('templates', 0):>6} + Local git repos {s.get('local_git_repos', 0):>6} + SQLite databases {s.get('sqlite_dbs', 0):>6} {DIM}({s.get('blackroad_dir_mb', 0)} MB){R} + FTS5 entries {s.get('fts5_entries', 0):>8,} + Systems registered {s.get('systems_registered', 0):>6} + Cron jobs {s.get('mac_cron_jobs', 0):>6} + Mac disk {s.get('mac_disk_used_gb', 0):>5} GB {DIM}({s.get('mac_disk_pct', 0)}%){R} + Mac processes {s.get('mac_processes', 0):>6} + Brew packages {s.get('brew_packages', 0):>6} + pip packages {s.get('pip_packages', 0):>6} + npm global {s.get('npm_global_packages', 0):>6} + +{A}═══ CLOUDFLARE ═════════════════════════════════════════════════{R} + Pages projects {s.get('cf_pages', 0):>6}{d('cf_pages')} + D1 databases {s.get('cf_d1_databases', 0):>6} {DIM}({s.get('cf_d1_size_kb', 0)} KB){R}{d('cf_d1_databases')} + KV namespaces {s.get('cf_kv_namespaces', 0):>6}{d('cf_kv_namespaces')} + R2 buckets {s.get('cf_r2_buckets', 0):>6}{d('cf_r2_buckets')} + +{P}═══ TOTALS ═════════════════════════════════════════════════════{R} + {W}Total repos {s['repos_total']:>6}{R} + {W}Total LOC {s['total_loc']:>10,}{R} + {W}Total databases {total_infra:>6}{R} {DIM}(D1+KV+R2+Pages+SQLite+PG+Docker){R} + {W}Total automation {total_automation:>5}{R} {DIM}(crons+timers){R} + {W}Total AI models {s.get('ollama_models', 0):>6}{R} {DIM}({s.get('ollama_size_gb', 0):.1f} GB){R} + +{P}╚══════════════════════════════════════════════════════════════╝{R} """) PYEOF