const fs = require("fs"); const path = require("path"); const { resolveData } = require("./paths"); const LOG_FILE = "assistant-panel.log"; class AssistantPanelDiagnostics { constructor(templatePath) { this.templatePath = path.resolve(templatePath); this.state = { panel_endpoint_status: null, panel_html_length: 0, panel_html_error: null, panel_template_path: this.templatePath, missing_locals: [], mount_error: null, updated_at: null }; } templateCheck(requiredLocals = [], locals = {}) { const missing = requiredLocals.filter((key) => locals[key] === undefined || locals[key] === null); let error = null; try { fs.accessSync(this.templatePath, fs.constants.R_OK); } catch { error = "Assistant panel template is missing or unreadable."; } this.update({ missing_locals: missing, panel_html_error: error }); return { valid: !error && missing.length === 0, missing, error }; } rendered(html) { const content = String(html || ""); const marker = 'data-assistant-panel-id="lumi_ai"'; const error = !content.trim() ? "Assistant panel rendered empty HTML." : !content.includes(marker) ? `Assistant panel HTML is missing ${marker}.` : null; this.update({ panel_endpoint_status: error ? 500 : 200, panel_html_length: content.length, panel_html_error: error }); return { valid: !error, error, length: content.length }; } renderFailed(error) { this.update({ panel_endpoint_status: 500, panel_html_length: 0, panel_html_error: error?.message || String(error) }, "render_failure"); } frontend(report = {}) { this.update({ panel_endpoint_status: numberOrNull(report.panel_endpoint_status) ?? this.state.panel_endpoint_status, panel_html_length: numberOrNull(report.panel_html_length) ?? this.state.panel_html_length, panel_html_error: report.panel_html_error || this.state.panel_html_error, mount_error: report.mount_error || null }, report.mount_error ? "mount_failure" : null); } snapshot() { return { ...this.state }; } update(values, event = null) { this.state = { ...this.state, ...values, updated_at: new Date().toISOString() }; if (event || values.panel_html_error || values.mount_error) { this.log(event || "diagnostic", values); } } log(event, details) { const target = resolveData("logs", LOG_FILE); const line = JSON.stringify({ at: new Date().toISOString(), event, ...details }); fs.appendFileSync(target, `${line}\n`, "utf8"); } } function numberOrNull(value) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; } module.exports = { AssistantPanelDiagnostics, LOG_FILE };