Week simulation engine: 5 business days, 4-8 contracts per day, 3 rotating staffers with handoffs between days. Runs hybrid search per contract via the gateway. 28 contracts, 108/108 filled (100%), 5 emergencies, 4 handoffs, 3.2s total. Dashboard at :3700/ — dark theme, shows: - Contract cards sorted by priority with match status - Day navigation across the work week - Week summary stats (fill rate, emergencies, handoffs) - Live alerts (erratic/silent workers) - Playbook entries - Real-time service health + VRAM Self-orientation (/context) + verification (/verify) endpoints so any agent can understand the system and fact-check claims without human intermediary. Accessible on LAN at http://192.168.1.177:3700 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
202 lines
6.2 KiB
TypeScript
202 lines
6.2 KiB
TypeScript
const GW = "http://localhost:3700";
|
|
let simData: any = null;
|
|
let currentDay = 0;
|
|
|
|
async function api(path: string, body?: any) {
|
|
const opts: RequestInit = body
|
|
? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }
|
|
: {};
|
|
const r = await fetch(GW + path, opts);
|
|
return r.json();
|
|
}
|
|
|
|
function addLog(msg: string, cls = "") {
|
|
const el = document.getElementById("log")!;
|
|
const div = document.createElement("div");
|
|
const t = new Date().toLocaleTimeString();
|
|
div.textContent = `${t} ${msg}`;
|
|
if (cls) div.className = cls;
|
|
el.prepend(div);
|
|
while (el.children.length > 50) el.lastChild?.remove();
|
|
}
|
|
|
|
function setText(id: string, text: string) {
|
|
const el = document.getElementById(id);
|
|
if (el) el.textContent = text;
|
|
}
|
|
|
|
function renderContracts(day: any) {
|
|
const el = document.getElementById("contracts")!;
|
|
el.replaceChildren();
|
|
if (!day?.contracts?.length) {
|
|
el.textContent = "No contracts for this day";
|
|
return;
|
|
}
|
|
for (const c of day.contracts) {
|
|
const div = document.createElement("div");
|
|
div.className = `contract ${c.filled >= c.headcount ? "filled" : c.priority === "urgent" ? "urgent" : c.priority === "high" ? "high" : ""}`;
|
|
|
|
const icon = c.priority === "urgent" ? "!!" : c.priority === "high" ? "!" : "";
|
|
const title = document.createElement("div");
|
|
title.className = "title";
|
|
title.textContent = `${icon} ${c.id} - ${c.client}`;
|
|
div.appendChild(title);
|
|
|
|
const meta = document.createElement("div");
|
|
meta.className = "meta";
|
|
meta.textContent = `${c.role} x${c.headcount} | ${c.city || c.state} | ${c.filled}/${c.headcount} filled | ${c.priority}`;
|
|
div.appendChild(meta);
|
|
|
|
if (c.matches?.length) {
|
|
const workers = document.createElement("div");
|
|
workers.className = "workers";
|
|
workers.textContent = c.matches.map((m: any) => `${m.name || m.doc_id} (${m.score?.toFixed(2) || "?"})`).join(", ");
|
|
div.appendChild(workers);
|
|
}
|
|
|
|
if (c.notes) {
|
|
const notes = document.createElement("div");
|
|
notes.className = "meta";
|
|
notes.textContent = c.notes;
|
|
div.appendChild(notes);
|
|
}
|
|
|
|
el.appendChild(div);
|
|
}
|
|
}
|
|
|
|
function renderDayNav() {
|
|
const nav = document.getElementById("day-nav")!;
|
|
nav.replaceChildren();
|
|
if (!simData?.days) return;
|
|
simData.days.forEach((d: any, i: number) => {
|
|
const btn = document.createElement("button");
|
|
btn.className = `day-btn ${i === currentDay ? "active" : ""}`;
|
|
btn.textContent = d.label;
|
|
btn.onclick = () => { currentDay = i; renderDayNav(); renderContracts(simData.days[i]); };
|
|
nav.appendChild(btn);
|
|
});
|
|
}
|
|
|
|
function renderWeekStats() {
|
|
const el = document.getElementById("week-stats")!;
|
|
el.replaceChildren();
|
|
if (!simData?.summary) return;
|
|
const s = simData.summary;
|
|
const rows = [
|
|
["Total contracts", s.total_contracts],
|
|
["Total positions", s.total_needed],
|
|
["Filled", s.total_filled],
|
|
["Fill rate", `${s.fill_pct}%`],
|
|
["Emergencies", s.emergencies],
|
|
["Handoffs", s.handoffs],
|
|
["Playbooks", s.playbook_entries],
|
|
];
|
|
for (const [label, val] of rows) {
|
|
const row = document.createElement("div");
|
|
row.className = "stat-row";
|
|
const nameSpan = document.createElement("span");
|
|
nameSpan.textContent = String(label);
|
|
const valSpan = document.createElement("span");
|
|
valSpan.className = "val";
|
|
valSpan.textContent = String(val);
|
|
row.appendChild(nameSpan);
|
|
row.appendChild(valSpan);
|
|
el.appendChild(row);
|
|
}
|
|
}
|
|
|
|
async function loadAlerts() {
|
|
const el = document.getElementById("alerts")!;
|
|
el.replaceChildren();
|
|
try {
|
|
const r = await api("/sql", { sql: "SELECT archetype, COUNT(*) cnt FROM ethereal_workers WHERE archetype IN ('erratic','silent') GROUP BY archetype" });
|
|
for (const row of r.rows || []) {
|
|
const div = document.createElement("div");
|
|
div.className = "alert warn";
|
|
div.textContent = `${row.archetype}: ${row.cnt} workers flagged`;
|
|
el.appendChild(div);
|
|
}
|
|
const info = document.createElement("div");
|
|
info.className = "alert info";
|
|
info.textContent = "System: all services running";
|
|
el.appendChild(info);
|
|
} catch {
|
|
const div = document.createElement("div");
|
|
div.className = "alert warn";
|
|
div.textContent = "Could not load alerts";
|
|
el.appendChild(div);
|
|
}
|
|
}
|
|
|
|
async function loadPlaybooks() {
|
|
const el = document.getElementById("playbooks")!;
|
|
el.replaceChildren();
|
|
try {
|
|
const r = await api("/playbooks", {});
|
|
const pbs = r.playbooks || [];
|
|
if (pbs.length === 0) {
|
|
el.textContent = "No playbooks yet - run the simulation";
|
|
return;
|
|
}
|
|
for (const p of pbs.slice(0, 8)) {
|
|
const div = document.createElement("div");
|
|
div.className = "playbook";
|
|
const op = document.createElement("span");
|
|
op.className = "op";
|
|
op.textContent = (p.operation || "").slice(0, 50);
|
|
div.appendChild(op);
|
|
div.appendChild(document.createElement("br"));
|
|
div.appendChild(document.createTextNode((p.result || "").slice(0, 80)));
|
|
el.appendChild(div);
|
|
}
|
|
} catch {
|
|
el.textContent = "Could not load playbooks";
|
|
}
|
|
}
|
|
|
|
async function checkServices() {
|
|
try {
|
|
await fetch(GW + "/health");
|
|
document.getElementById("svc-gw")!.className = "dot green";
|
|
} catch {
|
|
document.getElementById("svc-gw")!.className = "dot red";
|
|
}
|
|
try {
|
|
const r = await api("/vram");
|
|
setText("vram-display", `VRAM: ${r.gpu?.used_mib || "?"}/${r.gpu?.total_mib || "?"} MiB`);
|
|
} catch {}
|
|
}
|
|
|
|
// Week simulation runner
|
|
(window as any).runWeek = async function () {
|
|
addLog("Starting week simulation...");
|
|
try {
|
|
const r = await fetch(GW + "/simulation/run", { method: "POST" });
|
|
simData = await r.json();
|
|
if (simData.error) {
|
|
addLog(`Error: ${simData.error}`, "fail");
|
|
return;
|
|
}
|
|
renderDayNav();
|
|
renderContracts(simData.days[0]);
|
|
renderWeekStats();
|
|
addLog(`Week complete: ${simData.summary?.total_filled}/${simData.summary?.total_needed} filled`, "ok");
|
|
} catch (e) {
|
|
addLog(`Simulation failed: ${e}`, "fail");
|
|
}
|
|
};
|
|
|
|
(window as any).refresh = async function () {
|
|
addLog("Refreshing...");
|
|
await checkServices();
|
|
await loadAlerts();
|
|
await loadPlaybooks();
|
|
};
|
|
|
|
// Initial load
|
|
checkServices();
|
|
loadAlerts();
|
|
loadPlaybooks();
|
|
setInterval(checkServices, 30000);
|