Spaces:
Paused
Paused
File size: 17,696 Bytes
ee826ee | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 | /**
* ============================================
* 🐛 debugify.js — Diagnóstico Completo Total
* ============================================
*/
import { createWriteStream, mkdirSync, readFileSync, existsSync } from 'fs';
import { join } from 'path';
import archiver from 'archiver';
import * as db from './db.js';
const LOG_BUFFER_SIZE = 1000;
const logBuffer = [];
const errorBuffer = [];
export function initDebugify() {
const origLog = console.log.bind(console);
const origErr = console.error.bind(console);
const origWarn = console.warn.bind(console);
console.log = (...args) => {
const line = `[${new Date().toISOString()}] [LOG] ${args.join(' ')}`;
logBuffer.push(line);
if (logBuffer.length > LOG_BUFFER_SIZE) logBuffer.shift();
origLog(...args);
};
console.error = (...args) => {
const line = `[${new Date().toISOString()}] [ERR] ${args.join(' ')}`;
logBuffer.push(line); errorBuffer.push(line);
if (logBuffer.length > LOG_BUFFER_SIZE) logBuffer.shift();
if (errorBuffer.length > 500) errorBuffer.shift();
origErr(...args);
};
console.warn = (...args) => {
const line = `[${new Date().toISOString()}] [WARN] ${args.join(' ')}`;
logBuffer.push(line);
if (logBuffer.length > LOG_BUFFER_SIZE) logBuffer.shift();
origWarn(...args);
};
process.on('uncaughtException', (err) => {
errorBuffer.push(`[${new Date().toISOString()}] [UNCAUGHT] ${err.stack || err.message}`);
});
process.on('unhandledRejection', (reason) => {
errorBuffer.push(`[${new Date().toISOString()}] [UNHANDLED] ${reason?.stack || reason}`);
});
console.log('[Debugify] ✅ Sistema de debug iniciado');
}
async function q(sql, args = []) {
try {
const r = await db.db.execute({ sql, args });
return r.rows ?? [];
} catch (e) {
return [{ _error: e.message }];
}
}
export async function generateDebugZip(client, config) {
const now = new Date();
const ts = now.toISOString().replace(/[:.]/g, '-').slice(0, 19);
const zipName = `debugify-${ts}.zip`;
const zipPath = join('./debugify', zipName);
mkdirSync('./debugify', { recursive: true });
const output = createWriteStream(zipPath);
const archive = archiver('zip', { zlib: { level: 9 } });
return new Promise(async (resolve, reject) => {
output.on('close', () => resolve({ path: zipPath, name: zipName, size: archive.pointer() }));
archive.on('error', reject);
archive.pipe(output);
// 01 — LOGS
archive.append(logBuffer.join('\n') || '(sin logs)', { name: '01-logs/console-completo.txt' });
archive.append(errorBuffer.join('\n') || '(sin errores)', { name: '01-logs/solo-errores.txt' });
archive.append(logBuffer.slice(-100).join('\n') || '(vacío)', { name: '01-logs/ultimas-100-lineas.txt' });
// 02 — CONVERSACIONES USUARIO + ZELIN
try {
const convs = await q(`SELECT channel_id, role, content, username, created_at FROM conversation_context ORDER BY channel_id, created_at DESC LIMIT 2000`);
const byChannel = new Map();
for (const row of convs) {
const ch = row.channel_id ?? 'unknown';
if (!byChannel.has(ch)) byChannel.set(ch, []);
const who = row.role === 'assistant' ? '🤖 Zelin' : `👤 ${row.username ?? 'Usuario'}`;
byChannel.get(ch).push(`[${String(row.created_at ?? '').slice(0,19)}] ${who}: ${String(row.content ?? '').slice(0,500)}`);
}
for (const [ch, lines] of byChannel)
archive.append(lines.reverse().join('\n\n'), { name: `02-conversaciones/canal-${ch}.txt` });
archive.append(
[...byChannel.entries()].map(([ch, l]) => `${ch}: ${l.length} mensajes`).join('\n') || '(sin conversaciones)',
{ name: '02-conversaciones/RESUMEN.txt' }
);
} catch (e) { archive.append(`Error: ${e.message}`, { name: '02-conversaciones/error.txt' }); }
// 03 — MENSAJES DEL SERVIDOR
try {
const msgs = await q(`SELECT m.channel_id, u.username, m.content, m.created_at, m.is_edited FROM messages m LEFT JOIN users u ON m.user_id = u.user_id WHERE m.is_deleted = 0 ORDER BY m.created_at DESC LIMIT 500`);
const byChannel = new Map();
for (const r of msgs) {
const ch = r.channel_id ?? 'unknown';
if (!byChannel.has(ch)) byChannel.set(ch, []);
byChannel.get(ch).push(`[${String(r.created_at ?? '').slice(11,19)}] ${r.username ?? '?'}${r.is_edited ? '[editado]' : ''}: ${String(r.content ?? '').slice(0,200)}`);
}
for (const [ch, lines] of byChannel)
archive.append(lines.reverse().join('\n'), { name: `03-mensajes-servidor/canal-${ch}.txt` });
const deleted = await q(`SELECT username, content, channel_name, deleted_at FROM deleted_messages ORDER BY deleted_at DESC LIMIT 50`);
archive.append(
deleted.length ? deleted.map(r => `[${r.deleted_at}] ${r.username} en #${r.channel_name}: ${String(r.content ?? '').slice(0,150)}`).join('\n') : '(ninguno)',
{ name: '03-mensajes-servidor/mensajes-eliminados.txt' }
);
} catch (e) { archive.append(`Error: ${e.message}`, { name: '03-mensajes-servidor/error.txt' }); }
// 04 — DISCORD COMPLETO
try {
const guild = client?.guilds?.cache?.first();
archive.append([
`=== BOT ===`,
`Tag: ${client?.user?.tag ?? '?'} | ID: ${client?.user?.id ?? '?'}`,
`Guilds: ${client?.guilds?.cache?.size ?? 0} | Uptime: ${Math.floor((client?.uptime ?? 0)/60000)}min | Ping: ${client?.ws?.ping ?? '?'}ms`,
``,
`=== GUILD ===`,
`${guild?.name ?? 'N/A'} (ID: ${guild?.id ?? 'N/A'})`,
`Miembros: ${guild?.memberCount ?? '?'} | Canales: ${guild?.channels?.cache?.size ?? '?'} | Roles: ${guild?.roles?.cache?.size ?? '?'}`,
`Boost tier: ${guild?.premiumTier ?? '?'} | Boosts: ${guild?.premiumSubscriptionCount ?? 0}`,
``,
`=== CANALES (${guild?.channels?.cache?.size ?? 0}) ===`,
...(guild?.channels?.cache?.map(ch => ` ${ch.type===2?'🔊':'#'}${ch.name} [${ch.id}]`) ?? []),
``,
`=== ROLES (${guild?.roles?.cache?.size ?? 0}) ===`,
...(guild?.roles?.cache?.map(r => ` @${r.name}: ${r.members.size}m — ${r.id} — ${r.hexColor}`) ?? []),
].join('\n'), { name: '04-discord/estado-completo.txt' });
} catch (e) { archive.append(`Error: ${e.message}`, { name: '04-discord/error.txt' }); }
// 05 — BASE DE DATOS
try {
const s = (await q(`SELECT (SELECT COUNT(*) FROM messages WHERE is_deleted=0) AS total_msgs, (SELECT COUNT(*) FROM messages WHERE created_at>datetime('now','-1 hour') AND is_deleted=0) AS msgs_1h, (SELECT COUNT(*) FROM messages WHERE created_at>datetime('now','-24 hours') AND is_deleted=0) AS msgs_24h, (SELECT COUNT(*) FROM messages WHERE created_at>datetime('now','-7 days') AND is_deleted=0) AS msgs_7d, (SELECT COUNT(DISTINCT user_id) FROM messages WHERE is_deleted=0) AS usuarios_con_msgs, (SELECT COUNT(*) FROM users) AS total_usuarios, (SELECT COUNT(*) FROM conversation_context) AS turns_conv, (SELECT COUNT(*) FROM zelin_memory) AS entradas_mem, (SELECT COUNT(*) FROM deleted_messages) AS msgs_eliminados`))[0] ?? {};
archive.append([
`=== ESTADÍSTICAS DB ===`,
`Mensajes totales: ${s.total_msgs}`,
`Mensajes última hora: ${s.msgs_1h}`,
`Mensajes últimas 24h: ${s.msgs_24h}`,
`Mensajes últimos 7d: ${s.msgs_7d}`,
`Usuarios con mensajes: ${s.usuarios_con_msgs}`,
`Usuarios registrados: ${s.total_usuarios}`,
`Turns de conversación: ${s.turns_conv}`,
`Entradas de memoria: ${s.entradas_mem}`,
`Mensajes eliminados: ${s.msgs_eliminados}`,
].join('\n'), { name: '05-database/stats.txt' });
const topUsers = await q(`SELECT u.username, COUNT(m.id) as msgs, MAX(m.created_at) as ultimo FROM messages m JOIN users u ON m.user_id=u.user_id WHERE m.is_deleted=0 AND m.created_at>datetime('now','-7 days') GROUP BY m.user_id ORDER BY msgs DESC LIMIT 20`);
archive.append(topUsers.map(r => `${String(r.username??'?').padEnd(20)} ${r.msgs} msgs — ${String(r.ultimo??'').slice(0,16)}`).join('\n'), { name: '05-database/top-usuarios-activos.txt' });
const topCh = await q(`SELECT m.channel_id, COUNT(*) as msgs, MAX(m.created_at) as ultimo FROM messages m WHERE m.is_deleted=0 AND m.created_at>datetime('now','-7 days') GROUP BY m.channel_id ORDER BY msgs DESC LIMIT 20`);
archive.append(topCh.map(r => `${String(r.channel_id).padEnd(20)} ${r.msgs} msgs — ${String(r.ultimo??'').slice(0,16)}`).join('\n'), { name: '05-database/canales-activos.txt' });
const allUsers = await q(`SELECT username, nickname, joined_at, left_at, message_count, notes FROM users ORDER BY joined_at DESC LIMIT 200`);
archive.append(allUsers.map(r => `${r.username??'?'} (${r.nickname??'-'}) | entró:${r.joined_at??'?'} | msgs:${r.message_count??0} | notas:${r.notes??'ninguna'}`).join('\n'), { name: '05-database/usuarios-registrados.txt' });
const memByCat = await q(`SELECT category, COUNT(*) as n, MAX(updated_at) as ultimo FROM zelin_memory GROUP BY category ORDER BY n DESC`);
archive.append(memByCat.map(r => `${String(r.category).padEnd(25)} ${r.n} entradas — último: ${String(r.ultimo??'').slice(0,16)}`).join('\n'), { name: '05-database/memoria-por-categoria.txt' });
const allMem = await q(`SELECT key, value, category, updated_at, expires_at FROM zelin_memory WHERE category!='skill' ORDER BY category, updated_at DESC LIMIT 200`);
archive.append(allMem.map(r => `[${r.category}] ${r.key}\n ${String(r.value??'').slice(0,300)}\n actualizado:${r.updated_at} expira:${r.expires_at??'nunca'}`).join('\n---\n'), { name: '05-database/memoria-completa.txt' });
const events = await q(`SELECT type, user_id, target_id, old_val, new_val, created_at FROM server_events ORDER BY created_at DESC LIMIT 100`);
archive.append(events.length ? events.map(r => `[${r.created_at}] ${r.type} user:${r.user_id} target:${r.target_id} ${r.old_val??''}→${r.new_val??''}`).join('\n') : '(sin eventos)', { name: '05-database/eventos-servidor.txt' });
const sums = await q(`SELECT channel_id, period_start, period_end, message_count, summary, top_topics FROM channel_summaries ORDER BY created_at DESC LIMIT 20`);
archive.append(sums.length ? sums.map(r => `Canal:${r.channel_id} ${r.period_start}→${r.period_end} (${r.message_count} msgs)\nTemas:${r.top_topics??'?'}\n${String(r.summary??'').slice(0,400)}`).join('\n═══\n') : '(sin resúmenes)', { name: '05-database/resumenes.txt' });
const skills = await q(`SELECT key, value FROM zelin_memory WHERE category='skill'`);
archive.append(skills.length ? skills.map(r => `KEY: ${r.key}\n${String(r.value??'').slice(0,500)}`).join('\n---\n') : '(sin skills)', { name: '05-database/skills.txt' });
} catch (e) { archive.append(`Error: ${e.message}\n${e.stack??''}`, { name: '05-database/error.txt' }); }
// 06 — ESTADO IA
try {
const { getLocalAIStats, isLocalAIReady, getLocalAIError } = await import('./local-ai.js');
const { getDailyStats, getProviderStatus } = await import('./ai.js');
const { getHealerStats } = await import('./runtime-healer.js');
const aiStats = await getLocalAIStats().catch(() => ({}));
const daily = getDailyStats();
const providers = getProviderStatus();
const healer = getHealerStats();
archive.append([
`=== MODELO LOCAL ===`,
`Listo: ${isLocalAIReady()} | Error: ${getLocalAIError()??'ninguno'}`,
`Modelo: ${aiStats.model??'?'}`,
`Fast: ${aiStats.modelFast??'N/A'} (${aiStats.fastAvailable?'ok':'no disponible'})`,
`Embed: ${aiStats.embedModel??'N/A'} (${aiStats.embedAvailable?'ok':'no disponible'})`,
``,
`=== PROVIDERS (uso diario) ===`,
...Object.entries(daily).map(([n,s]) => ` ${n.padEnd(20)} req:${String(s.requests).padStart(4)}/${s.limit} (${s.pct}) state:${s.state} score:${s.score}`),
``,
`=== CIRCUIT BREAKERS ===`,
...providers.map(p => ` ${p.name.padEnd(20)} estado:${p.state} score:${p.score}`),
``,
`=== RUNTIME HEALER ===`,
`Errores: ${healer.errors_stored} total / ${healer.errors_last_hour} última hora`,
`Healings: ${healer.healing_this_hour}/${healer.healing_cap} | Degradado: ${healer.degraded}`,
`Performance: ${JSON.stringify(healer.performance)}`,
].join('\n'), { name: '06-ia/estado-completo.txt' });
const runtimeErrs = await q(`SELECT key, value, updated_at FROM zelin_memory WHERE category='runtime_errors' ORDER BY updated_at DESC LIMIT 50`);
archive.append(runtimeErrs.length ? runtimeErrs.map(r => `[${r.updated_at}] ${String(r.value??'').slice(0,400)}`).join('\n---\n') : '(sin errores runtime)', { name: '06-ia/errores-runtime.txt' });
const auditLogs = await q(`SELECT key, value, updated_at FROM zelin_memory WHERE category='audit_log' ORDER BY updated_at DESC LIMIT 30`);
archive.append(auditLogs.length ? auditLogs.map(r => `[${r.updated_at}] ${String(r.value??'').slice(0,300)}`).join('\n---\n') : '(sin audit logs)', { name: '06-ia/audit-logs.txt' });
} catch (e) { archive.append(`Error: ${e.message}\n${e.stack??''}`, { name: '06-ia/error.txt' }); }
// 07 — CONFIG
try {
const safe = JSON.parse(JSON.stringify(config ?? {}));
const M = '***REDACTED***';
if (safe.discord?.token) safe.discord.token = M;
if (safe.turso?.token) safe.turso.token = M;
if (safe.sftp?.password) safe.sftp.password = M;
if (safe.ai) for (const k of Object.keys(safe.ai)) {
if (safe.ai[k]?.apiKey) safe.ai[k].apiKey = M;
if (safe.ai[k]?.keys) safe.ai[k].keys = [`${(safe.ai[k].keys??[]).length} keys REDACTED`];
if (safe.ai[k]?.sk) safe.ai[k].sk = M;
if (safe.ai[k]?.apiToken) safe.ai[k].apiToken = M;
}
archive.append(JSON.stringify(safe, null, 2), { name: '07-config/config-sanitizada.json' });
if (existsSync('./package.json'))
archive.append(readFileSync('./package.json', 'utf8'), { name: '07-config/package.json' });
} catch (e) { archive.append(`Error: ${e.message}`, { name: '07-config/error.txt' }); }
// 08 — SISTEMA
const mem = process.memoryUsage();
archive.append([
`=== MEMORIA ===`,
`RSS: ${(mem.rss/1024/1024).toFixed(1)}MB | Heap: ${(mem.heapUsed/1024/1024).toFixed(1)}/${(mem.heapTotal/1024/1024).toFixed(1)}MB | External: ${(mem.external/1024/1024).toFixed(1)}MB`,
``,
`=== PROCESO ===`,
`Uptime: ${Math.floor(process.uptime())}s (${(process.uptime()/3600).toFixed(2)}h)`,
`PID: ${process.pid} | Node: ${process.version} | Platform: ${process.platform}/${process.arch}`,
``,
`Generado: ${now.toISOString()}`,
].join('\n'), { name: '08-sistema/proceso.txt' });
// 09 — PSYCHE
try {
const { getStateSnapshot } = await import('./psyche.js');
archive.append(JSON.stringify(getStateSnapshot(), null, 2), { name: '09-psyche/estado-psicologico.json' });
const psycheData = await q(`SELECT key, value, updated_at FROM zelin_memory WHERE category='psyche' ORDER BY updated_at DESC LIMIT 50`);
archive.append(psycheData.length ? psycheData.map(r => `[${r.updated_at}] ${r.key}: ${String(r.value??'').slice(0,200)}`).join('\n') : '(sin datos psyche)', { name: '09-psyche/afinidades-usuarios.txt' });
} catch (e) { archive.append(`Error: ${e.message}`, { name: '09-psyche/error.txt' }); }
// 10 — LEARNING
try {
const ld = await q(`SELECT key, value, category, updated_at FROM zelin_memory WHERE category IN ('learning','user_profile','knowledge') ORDER BY updated_at DESC LIMIT 100`);
archive.append(ld.length ? ld.map(r => `[${r.category}] ${r.key} — ${r.updated_at}\n ${String(r.value??'').slice(0,300)}`).join('\n---\n') : '(sin datos)', { name: '10-learning/datos.txt' });
} catch (e) { archive.append(`Error: ${e.message}`, { name: '10-learning/error.txt' }); }
// 11 — STANDING PROMPTS
try {
const sp = await q(`SELECT key, value, updated_at FROM zelin_memory WHERE category='standing_prompt' OR key LIKE 'standing%' ORDER BY updated_at DESC`);
archive.append(sp.length ? sp.map(r => `[${r.updated_at}] ${r.key}:\n${String(r.value??'').slice(0,500)}`).join('\n═══\n') : '(sin standing prompts)', { name: '11-standing-prompts/prompts.txt' });
} catch (e) { archive.append(`Error: ${e.message}`, { name: '11-standing-prompts/error.txt' }); }
// README
archive.append([
`DEBUGIFY COMPLETO — Zelin v5.8`,
`Generado: ${now.toISOString()}`,
``,
`01-logs/ → Consola completa, solo errores, últimas 100 líneas`,
`02-conversaciones/ → Todos los chats usuario↔Zelin por canal`,
`03-mensajes-servidor/ → Mensajes del servidor + mensajes eliminados`,
`04-discord/ → Estado completo Discord (guild, canales, roles)`,
`05-database/ → Stats, top usuarios, memoria, eventos, resúmenes, skills`,
`06-ia/ → Modelo local, providers, circuit breakers, errores runtime, audit logs`,
`07-config/ → Config sanitizada + package.json`,
`08-sistema/ → RAM, CPU, uptime, proceso`,
`09-psyche/ → Estado psicológico de Zelin + afinidades con usuarios`,
`10-learning/ → Datos de aprendizaje y perfiles de usuario`,
`11-standing-prompts/ → Standing prompts activos en DB`,
].join('\n'), { name: 'README.txt' });
archive.finalize();
});
}
|