UNPKG

comfyui-mcp

Version:

Local-first, agent-native control plane for ComfyUI — MCP server + autonomous sidebar agent that drives your live graph in natural language on ANY LLM: Claude/ChatGPT/Gemini on your subscription (no API key), free local models via Ollama (fully offline),

137 lines (116 loc) • 3.87 kB
#!/usr/bin/env node /** * CLI script: npm run generations:stats * * Shows local generation tracking statistics. */ import Database from "better-sqlite3"; import { existsSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { homedir } from "node:os"; const __dirname = dirname(fileURLToPath(import.meta.url)); // Find the generations.db — same logic as generation-tracker.ts function findDb() { const home = homedir(); // Check common ComfyUI data paths const candidates = [ join(home, "Documents", "ComfyUI", "comfyui-mcp", "generations.db"), join(home, "My Documents", "ComfyUI", "comfyui-mcp", "generations.db"), join(home, "AppData", "Local", "Programs", "ComfyUI", "resources", "ComfyUI", "comfyui-mcp", "generations.db"), join(home, "ComfyUI", "comfyui-mcp", "generations.db"), // Fallback: next to the package join(__dirname, "..", "generations.db"), ]; // COMFYUI_PATH env override if (process.env.COMFYUI_PATH) { candidates.unshift( join(process.env.COMFYUI_PATH, "comfyui-mcp", "generations.db"), ); } for (const path of candidates) { if (existsSync(path)) return path; } return null; } function main() { const dbPath = findDb(); if (!dbPath) { console.log("\n No generations.db found. Run some workflows first!\n"); process.exit(0); } const db = new Database(dbPath, { readonly: true }); const totalRow = db .prepare("SELECT COALESCE(SUM(reuse_count), 0) AS total FROM generations") .get(); const uniqueRow = db .prepare("SELECT COUNT(*) AS cnt FROM generations") .get(); console.log("\n === Generation Stats ===\n"); console.log(` Database: ${dbPath}`); console.log(` Total generations: ${totalRow.total}`); console.log(` Unique setting combos: ${uniqueRow.cnt}`); // Breakdown by model family const breakdown = db .prepare( `SELECT model_family, SUM(reuse_count) AS total, COUNT(*) AS combos FROM generations GROUP BY model_family ORDER BY total DESC`, ) .all(); if (breakdown.length > 0) { console.log("\n Model Family Generations Combos"); console.log(" " + "─".repeat(50)); for (const row of breakdown) { const family = row.model_family.padEnd(22); const total = String(row.total).padStart(11); const combos = String(row.combos).padStart(7); console.log(` ${family}${total}${combos}`); } } // Top 10 settings const top = db .prepare( `SELECT model_family, model_name, sampler, scheduler, steps, cfg, shift, lora_name, lora_strength, reuse_count FROM generations ORDER BY reuse_count DESC, created_at DESC LIMIT 10`, ) .all(); if (top.length > 0) { console.log("\n Top Settings:\n"); for (const [i, row] of top.entries()) { const lora = row.lora_name ? ` + ${row.lora_name} (${row.lora_strength})` : ""; const shiftStr = row.shift != null ? `, shift ${row.shift}` : ""; console.log( ` ${i + 1}. ${row.model_family} / ${row.sampler}/${row.scheduler} / ` + `${row.steps} steps / CFG ${row.cfg}${shiftStr}${lora}` + ` — ${row.reuse_count}x`, ); if (row.model_name) { console.log(` Model: ${row.model_name}`); } } } // File hash cache stats try { const hashCount = db .prepare("SELECT COUNT(*) AS cnt FROM file_hashes") .get(); const civitaiCount = db .prepare("SELECT COUNT(*) AS cnt FROM file_hashes WHERE civitai_id IS NOT NULL") .get(); console.log( `\n File hashes cached: ${hashCount.cnt} (${civitaiCount.cnt} matched on CivitAI)`, ); } catch { // file_hashes table may not exist yet } console.log(""); db.close(); } main();