Add three new pages completing the control-room web UI: - Agent activity: polls /agent-activity every 5s, filterable by type/agent - Knowledge search: FTS over /knowledge/search with snippet + entity links - Audit trail: browseable audit log with actor/action/entity filters Enhanced live events page with correlation-id clustering (Groups toggle). Added fetchAgentActivity/searchKnowledge/fetchAudit to the API client. 11 nav items now cover all planned control-room views.
58 lines
1.3 KiB
TypeScript
58 lines
1.3 KiB
TypeScript
import { writable } from 'svelte/store'
|
|
|
|
export interface OikosEvent {
|
|
id: number
|
|
ts: string
|
|
type: string
|
|
entity_id?: string | null
|
|
severity: 'info' | 'warning' | 'critical'
|
|
source: string
|
|
data?: unknown
|
|
correlation_id?: string | null
|
|
}
|
|
|
|
const MAX_BUFFERED = 200
|
|
|
|
export const liveEvents = writable<OikosEvent[]>([])
|
|
export const connectionState = writable<'connecting' | 'open' | 'closed'>('connecting')
|
|
|
|
let source: EventSource | null = null
|
|
let subscriberCount = 0
|
|
|
|
function connect() {
|
|
if (source) return
|
|
connectionState.set('connecting')
|
|
// The browser's EventSource sends Last-Event-ID automatically on reconnect.
|
|
source = new EventSource('/api/v1/events/stream')
|
|
|
|
source.onopen = () => connectionState.set('open')
|
|
|
|
source.onmessage = (ev) => {
|
|
try {
|
|
const parsed: OikosEvent = JSON.parse(ev.data)
|
|
liveEvents.update((events) => [parsed, ...events].slice(0, MAX_BUFFERED))
|
|
} catch {
|
|
// skip malformed
|
|
}
|
|
}
|
|
|
|
source.onerror = () => {
|
|
connectionState.set('closed')
|
|
}
|
|
}
|
|
|
|
function disconnect() {
|
|
source?.close()
|
|
source = null
|
|
}
|
|
|
|
// Reference-counted: the stream stays open as long as at least one page subscribes.
|
|
export function subscribeEvents(): () => void {
|
|
subscriberCount++
|
|
if (subscriberCount === 1) connect()
|
|
return () => {
|
|
subscriberCount--
|
|
if (subscriberCount === 0) disconnect()
|
|
}
|
|
}
|