Monorepo for Aesthetic.Computer aesthetic.computer
at main 122 lines 4.1 kB view raw
1#!/usr/bin/env node 2// ants/refresh-score.mjs 3// Refreshes the Front Door stats in SCORE.md with live data from /api/metrics 4// and local file counts. Runs once per day (skips if already refreshed today). 5// 6// Usage: 7// node ants/refresh-score.mjs # uses production API 8// node ants/refresh-score.mjs http://localhost:8888 # uses local dev server 9// node ants/refresh-score.mjs --force # bypass daily check 10// 11// Called from: .git/hooks/pre-commit (once per day on first commit) 12 13import { readFileSync, writeFileSync, existsSync, readdirSync } from "fs"; 14import { join, dirname } from "path"; 15import { fileURLToPath } from "url"; 16 17const __dirname = dirname(fileURLToPath(import.meta.url)); 18const ROOT = join(__dirname, ".."); 19const SCORE_PATH = join(ROOT, "SCORE.md"); 20const DISKS_DIR = join(ROOT, "system/public/aesthetic.computer/disks"); 21const FUNCTIONS_DIR = join(ROOT, "system/netlify/functions"); 22const MARKER = "/tmp/ac-score-refreshed"; 23 24const args = process.argv.slice(2); 25const force = args.includes("--force"); 26const apiArg = args.find((a) => a.startsWith("http")); 27const API_BASE = apiArg || "https://aesthetic.computer"; 28 29// Once-per-day check 30const today = new Date().toISOString().slice(0, 10); 31if (!force && existsSync(MARKER)) { 32 const markerDate = readFileSync(MARKER, "utf8").trim(); 33 if (markerDate === today) { 34 process.exit(0); // Already refreshed today 35 } 36} 37 38async function fetchMetrics() { 39 try { 40 const res = await fetch(`${API_BASE}/api/metrics?fresh=true`); 41 if (!res.ok) throw new Error(`HTTP ${res.status}`); 42 return await res.json(); 43 } catch (err) { 44 process.stderr.write(`refresh-score: could not fetch metrics: ${err.message}\n`); 45 return null; 46 } 47} 48 49function countFiles(dir, ext) { 50 try { 51 return readdirSync(dir).filter((f) => f.endsWith(ext)).length; 52 } catch { 53 return 0; 54 } 55} 56 57function formatDate() { 58 return new Date().toLocaleDateString("en-US", { 59 year: "numeric", 60 month: "short", 61 day: "numeric", 62 }); 63} 64 65async function main() { 66 const metrics = await fetchMetrics(); 67 const mjsCount = countFiles(DISKS_DIR, ".mjs"); 68 const lispCount = countFiles(DISKS_DIR, ".lisp"); 69 const builtInPieces = mjsCount + lispCount; 70 const apiEndpoints = countFiles(FUNCTIONS_DIR, ".mjs"); 71 72 const lines = []; 73 lines.push( 74 `${builtInPieces} built-in pieces (${mjsCount} JS + ${lispCount} KidLisp), ~${apiEndpoints} API endpoints.` 75 ); 76 77 if (metrics) { 78 const parts = []; 79 if (metrics.handles) parts.push(`${metrics.handles} registered handles`); 80 if (metrics.pieces) parts.push(`${metrics.pieces} user-published pieces`); 81 if (metrics.paintings) parts.push(`${metrics.paintings} paintings`); 82 if (metrics.kidlisp) parts.push(`${metrics.kidlisp} KidLisp programs`); 83 if (metrics.chatMessages) parts.push(`${metrics.chatMessages} chat messages`); 84 if (metrics.printsOrdered) parts.push(`${metrics.printsOrdered} prints ordered`); 85 if (parts.length) lines.push(parts.join(", ") + "."); 86 } 87 88 lines.push(`*Last refreshed: ${formatDate()}*`); 89 90 const statsBlock = lines.join("<br>\n"); 91 92 const score = readFileSync(SCORE_PATH, "utf8"); 93 94 const startMarker = "<!-- stats:start -->"; 95 const endMarker = "<!-- stats:end -->"; 96 97 if (!score.includes(startMarker) || !score.includes(endMarker)) { 98 process.stderr.write( 99 `refresh-score: missing stats markers in SCORE.md\n` 100 ); 101 process.exit(1); 102 } 103 104 const before = score.slice( 105 0, 106 score.indexOf(startMarker) + startMarker.length 107 ); 108 const after = score.slice(score.indexOf(endMarker)); 109 const updated = before + "\n" + statsBlock + "\n" + after; 110 111 if (updated !== score) { 112 writeFileSync(SCORE_PATH, updated); 113 process.stderr.write(`refresh-score: ✓ updated SCORE.md\n`); 114 } else { 115 process.stderr.write(`refresh-score: no changes needed\n`); 116 } 117 118 // Mark today as done 119 writeFileSync(MARKER, today); 120} 121 122main();