/** * Self-contained AgentHub task board, served at `GET /board`. * * Constraints: * - One static HTML page: inline CSS + JS, no framework and no runtime deps. * - Reads only same-origin endpoints (`/tasks`, `/events` and `/status`). * - Never mutates state. */ export interface BoardColumn { key: string; label: string; } /** Task statuses, in board order. Mirrors `TaskStatus` in core/schema.ts. */ export const BOARD_COLUMNS: BoardColumn[] = [ { key: 'open', label: 'Open' }, { key: 'in_progress', label: 'In Progress' }, { key: 'review', label: 'Review' }, { key: 'done', label: 'Done' }, { key: 'cancelled', label: 'Cancelled' }, ]; const ACTIVE_COLUMNS = BOARD_COLUMNS.filter((c) => ['open', 'in_progress', 'review'].includes(c.key)); function columnSkeleton(): string { return ACTIVE_COLUMNS.map( (c) => `
${c.label} 0
`, ).join('\n'); } function escapeHtml(value: string): string { return value .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } export function renderBoardHtml(projectName = 'AgentHub Project'): string { const initialProjectName = JSON.stringify(projectName).replace(/ AgentHub Board
AgentHub ${projectNameHtml}
connecting
Active tasks
0
Open
0
In progress
0
Review
0
Done total
0
Agents busy
0/0
${columnSkeleton()}
`; }