Eras Expert domain context: - Full Heizkostenabrechnung business model (Kunde>Objekte>Nutzeinheiten>Geraete) - Known PK/FK mappings: kunden.Kundennummer, objekte.KundenID, etc. - Correct JOIN example in SCHEMA prompt - PA knows domain hierarchy for better job formulation Iterative plan-execute in ExpertNode: - DESCRIBE queries execute first, results injected into re-plan - Re-plan uses actual column names from DESCRIBE - Eliminates "Unknown column" errors on first query Frontend: - Node inspector: per-node cards with model, tokens, progress, last event - Graph switcher buttons in top bar - Clear button in top bar - Nodes panel 300px wide - WS reconnect on 1006 (deploy) without showing login - Model info emitted on context HUD events Domain context test: 21/21 (hierarchy, JOINs, FK, PA job quality) Default graph: v4-eras Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
91 lines
2.8 KiB
JavaScript
91 lines
2.8 KiB
JavaScript
/** Main entry point — wires all modules together. */
|
|
|
|
import { initAuth, authToken, startLogin } from './auth.js';
|
|
import { initTrace, addTrace, clearTrace } from './trace.js';
|
|
import { initChat, clearChat } from './chat.js';
|
|
import { clearDashboard } from './dashboard.js';
|
|
import { clearNodes } from './awareness.js';
|
|
import { initGraph } from './graph.js';
|
|
import { connect } from './ws.js';
|
|
|
|
// Init on load
|
|
window.addEventListener('load', async () => {
|
|
initTrace();
|
|
initChat();
|
|
await initGraph();
|
|
await initAuth(() => {
|
|
connect();
|
|
loadGraphSwitcher();
|
|
});
|
|
});
|
|
|
|
// Clear session
|
|
window.clearSession = async () => {
|
|
try {
|
|
const headers = { 'Content-Type': 'application/json' };
|
|
if (authToken) headers['Authorization'] = 'Bearer ' + authToken;
|
|
await fetch('/api/clear', { method: 'POST', headers });
|
|
clearChat();
|
|
clearTrace();
|
|
clearDashboard();
|
|
clearNodes();
|
|
addTrace('runtime', 'cleared', 'session reset');
|
|
} catch (e) {
|
|
addTrace('runtime', 'error', 'clear failed: ' + e);
|
|
}
|
|
};
|
|
|
|
// Graph switcher — loads available graphs and shows buttons in top bar
|
|
async function loadGraphSwitcher() {
|
|
const container = document.getElementById('graph-switcher');
|
|
if (!container) { console.error('[main] no #graph-switcher'); return; }
|
|
try {
|
|
const headers = {};
|
|
if (authToken) headers['Authorization'] = 'Bearer ' + authToken;
|
|
const r = await fetch('/api/graph/list', { headers });
|
|
if (!r.ok) { console.error('[main] graph/list failed:', r.status); return; }
|
|
const data = await r.json();
|
|
const graphs = data.graphs || data || [];
|
|
console.log('[main] graphs:', graphs.length);
|
|
|
|
// Get current active graph
|
|
let activeGraph = '';
|
|
try {
|
|
const ar = await fetch('/api/graph/active', { headers });
|
|
if (ar.ok) {
|
|
const ag = await ar.json();
|
|
activeGraph = ag.name || '';
|
|
}
|
|
} catch (e) {}
|
|
|
|
container.innerHTML = graphs.map(g => {
|
|
const active = g.name === activeGraph;
|
|
return `<button class="btn-graph${active ? ' active' : ''}" onclick="switchGraph('${g.name}')" title="${g.description}">${g.name}</button>`;
|
|
}).join('');
|
|
} catch (e) {}
|
|
}
|
|
|
|
window.switchGraph = async (name) => {
|
|
try {
|
|
const headers = { 'Content-Type': 'application/json' };
|
|
if (authToken) headers['Authorization'] = 'Bearer ' + authToken;
|
|
await fetch('/api/graph/switch', {
|
|
method: 'POST', headers,
|
|
body: JSON.stringify({ name }),
|
|
});
|
|
addTrace('runtime', 'graph_switch', name);
|
|
clearChat();
|
|
clearTrace();
|
|
clearDashboard();
|
|
clearNodes();
|
|
addTrace('runtime', 'switched', `graph: ${name}`);
|
|
await initGraph();
|
|
loadGraphSwitcher();
|
|
} catch (e) {
|
|
addTrace('runtime', 'error', 'switch failed: ' + e);
|
|
}
|
|
};
|
|
|
|
// Login
|
|
window.startLogin = startLogin;
|