- build-standalone-fixed.cjs: removed renderHeatmap() replacement block - The replacement used string concatenation with '\'' which broke single quotes in generated HTML, causing SyntaxError: unexpected token - Original renderHeatmap() in index.html uses template literals (`...`) which are safe and already contain showCellDetail onclick handler - Rebuilt index.standalone.html from fixed source - Zero console errors, zero JS syntax errors verified on port 3003
152 lines
6.3 KiB
JavaScript
152 lines
6.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Build unified dashboard data by calling export script:
|
|
* 1. parse files → export to JSON
|
|
* 2. embed in HTML
|
|
*
|
|
* Run: node agent-evolution/scripts/build-standalone-fixed.cjs
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const HTML_FILE = path.join(__dirname, '../index.html');
|
|
const OUTPUT_FILE = path.join(__dirname, '../index.standalone.html');
|
|
|
|
try {
|
|
// Step 1: Export data to JSON
|
|
console.log('Exporting data to JSON...');
|
|
const jsonData = require('./export-data-direct.cjs');
|
|
|
|
// ---------- Read HTML ----------
|
|
let html = fs.readFileSync(HTML_FILE, 'utf-8');
|
|
|
|
// ---------- Remove old hardcoded constants ----------
|
|
// Remove INLINE_RECOMMENDATIONS (lines ~1004-1016)
|
|
const inlineRecPattern = /const INLINE_RECOMMENDATIONS = \[[\s\S]*?\];/;
|
|
html = html.replace(inlineRecPattern, 'const INLINE_RECOMMENDATIONS = []; // REMOVED — data now comes from agentData, not hardcoded');
|
|
|
|
// Remove MODEL_BENCHMARKS line ~1021 (will be embedded in JSON)
|
|
const bmPattern = /const MODEL_BENCHMARKS = \{[\s\S]*?\n\};/;
|
|
html = html.replace(bmPattern, '/* MODEL_BENCHMARKS removed — data now in EMBEDDED_DATA.model_benchmarks */');
|
|
|
|
// ---------- Replace EMBEDDED_DATA section ----------
|
|
const startMarker = '// Default embedded data (minimal - updated by sync script)';
|
|
const endMarker = '};';
|
|
|
|
const startIdx = html.indexOf(startMarker);
|
|
if (startIdx === -1) throw new Error('Start marker not found');
|
|
|
|
// Find the start of the EMBEDDED_DATA object
|
|
const dataStartIdx = html.indexOf('const EMBEDDED_DATA = {', startIdx);
|
|
if (dataStartIdx === -1) throw new Error('EMBEDDED_DATA start not found');
|
|
|
|
// Find the end of the EMBEDDED_DATA object (the closing brace followed by semicolon)
|
|
const dataEndIdx = html.indexOf(endMarker, dataStartIdx) + endMarker.length;
|
|
if (dataEndIdx === -1) throw new Error('EMBEDDED_DATA end not found');
|
|
|
|
// Create properly formatted JSON without HTML escaping
|
|
const jsonStr = JSON.stringify(jsonData, null, 2);
|
|
|
|
// Ensure HTML characters are not escaped in string literals
|
|
// This is a workaround for JSON.stringify escaping < and > in some environments
|
|
const safeJsonStr = jsonStr
|
|
.replace(/\\u003c/g, '<')
|
|
.replace(/\\u003e/g, '>');
|
|
|
|
const embeddedData = `// Unified data from REAL sources (${new Date().toISOString()})
|
|
// Sources: .kilo/agents/*.md + kilo-meta.json + model-benchmarks-verified.json
|
|
const EMBEDDED_DATA = ${safeJsonStr};`;
|
|
|
|
html = html.substring(0, dataStartIdx) + embeddedData + html.substring(dataEndIdx);
|
|
|
|
// ---------- Replace init function ----------
|
|
const initStartPattern = /\/\/ Initialize\s*\n\s*async function init\(\)\s*\{/;
|
|
const initStart = html.match(initStartPattern);
|
|
if (initStart) {
|
|
let brace = 0, inFn = false, endIdx = initStart.index;
|
|
for (let i = initStart.index; i < html.length; i++) {
|
|
if (html[i] === '{') { brace++; inFn = true; }
|
|
else if (html[i] === '}') { brace--; if (inFn && brace === 0) { endIdx = i + 1; break; } }
|
|
}
|
|
|
|
const newInit = `// Initialize
|
|
async function init() {
|
|
agentData = EMBEDDED_DATA;
|
|
try {
|
|
document.getElementById('lastSync').textContent = formatDate(agentData.lastUpdated);
|
|
document.getElementById('agentCount').textContent = agentData.evolution_metrics.total_agents + ' agents';
|
|
document.getElementById('historyCount').textContent = agentData.evolution_metrics.agents_with_history + ' with history';
|
|
|
|
if (agentData.evolution_metrics.total_agents === 0) {
|
|
document.getElementById('lastSync').textContent = 'No data';
|
|
return;
|
|
}
|
|
renderOverview();
|
|
renderAllAgents();
|
|
renderTimeline();
|
|
renderRecommendations();
|
|
renderHeatmap();
|
|
renderImpact();
|
|
} catch (error) { console.error('Render error:', error); }
|
|
}`;
|
|
html = html.substring(0, initStart.index) + newInit + html.substring(endIdx);
|
|
}
|
|
|
|
// ---------- Heatmap: original renderHeatmap() in index.html is correct ----------
|
|
|
|
// ---------- Replace renderRecommendations function ----------
|
|
const recStartPattern = /function renderRecommendations\(\)\s*\{/;
|
|
const recStart = html.match(recStartPattern);
|
|
if (recStart) {
|
|
let brace = 0, inFn = false, endIdx = recStart.index;
|
|
for (let i = recStart.index; i < html.length; i++) {
|
|
if (html[i] === '{') { brace++; inFn = true; }
|
|
else if (html[i] === '}') { brace--; if (inFn && brace === 0) { endIdx = i + 1; break; } }
|
|
}
|
|
|
|
const newRec = `// Render Recommendations (only use agentData.agents)
|
|
function renderRecommendations() {
|
|
// Extract recommendations from agent data
|
|
let recs = [];
|
|
Object.entries(agentData.agents).forEach(([name, agent]) => {
|
|
if (agent.current.recommendations && agent.current.recommendations.length > 0) {
|
|
agent.current.recommendations.forEach(rec => {
|
|
recs.push({
|
|
agent: name,
|
|
current_model: agent.current.model,
|
|
recommended_model: rec.target,
|
|
impact: rec.priority || 'medium',
|
|
score_before: rec.score_before || 0,
|
|
score_after: rec.score_after || 0,
|
|
score_delta: rec.score_delta || 0,
|
|
rationale: rec.reason || ''
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
if (recs.length === 0) {
|
|
document.getElementById('allRecommendations').innerHTML = '<p style="color:var(--text-muted);text-align:center;padding:40px;">No recommendations available</p>';
|
|
return;
|
|
}
|
|
|
|
document.getElementById('allRecommendations').innerHTML = recs.map((r, idx) => renderRecCard(r, idx)).join('');
|
|
}`;
|
|
|
|
html = html.substring(0, recStart.index) + newRec + html.substring(endIdx);
|
|
}
|
|
|
|
// ---------- Write ----------
|
|
fs.writeFileSync(OUTPUT_FILE, html);
|
|
fs.writeFileSync(path.join(__dirname, '../data/index.html'), html);
|
|
|
|
console.log('\nBuilt standalone dashboard');
|
|
console.log(' Output:', OUTPUT_FILE);
|
|
console.log(' Size:', (fs.statSync(OUTPUT_FILE).size / 1024).toFixed(1), 'KB');
|
|
|
|
} catch (error) {
|
|
console.error('Error:', error.message);
|
|
console.error(error.stack);
|
|
process.exit(1);
|
|
} |