Files
atc-agents/ui/src/lib/api.ts
T

173 lines
5.4 KiB
TypeScript
Raw Normal View History

import type {
PresentationData,
Agent,
Approval,
CdcChange,
CdcStats,
DataflowGraph,
FeedEntry,
GpuStatus,
Movement,
PiiDataset,
StatusData,
TerminalLine,
WorkloadData,
} from '../types'
async function fetchJson<T>(url: string, timeoutMs = 10000): Promise<T | null> {
const ctrl = new AbortController()
const timer = setTimeout(() => ctrl.abort(), timeoutMs)
try {
const r = await fetch(url, { signal: ctrl.signal })
if (!r.ok) return null
return (await r.json()) as T
} catch {
return null
} finally {
clearTimeout(timer)
}
}
export async function fetchAgents() {
const j = await fetchJson<{ agents?: Agent[] }>('/api/agents', 8000)
return j?.agents || []
}
export async function fetchStatus() {
return (await fetchJson<StatusData>('/api/status', 8000)) as StatusData
}
export async function fetchFeed() {
const j = await fetchJson<{ entries?: FeedEntry[] }>('/api/feed', 8000)
return j?.entries || []
}
export async function fetchApprovals() {
const j = await fetchJson<{ approvals?: Approval[] }>('/api/approvals', 8000)
return j?.approvals || []
}
export async function fetchApprovalHistory(status: string = 'pending') {
const j = await fetchJson<{
approvals?: Approval[]
stats?: { pending: number; approved: number; denied: number; total: number }
}>(`/api/approvals?status=${encodeURIComponent(status)}&limit=200`, 8000)
return { approvals: j?.approvals || [], stats: j?.stats }
}
export async function fetchGpu(): Promise<GpuStatus | null> {
return fetchJson<GpuStatus>('/api/gpu', 8000)
}
export async function fetchTerminals(): Promise<Record<string, TerminalLine[]>> {
const j = await fetchJson<{ terminals?: Record<string, TerminalLine[]> }>('/api/terminals', 8000)
return j?.terminals || {}
}
export async function fetchWorkload(): Promise<WorkloadData | null> {
return fetchJson<WorkloadData>('/api/workload?fast=true', 25000)
}
export async function fetchNodeDetail(nodeId: string) {
const j = await fetchJson<Record<string, unknown>>(`/api/nodes/${nodeId}`, 15000)
return j || { error: 'timeout' }
}
export function probeNode(nodeId: string) {
return fetch(`/api/nodes/${nodeId}/probe`, { method: 'POST' })
}
export function askNode(nodeId: string, message: string) {
return fetch(`/api/nodes/${nodeId}/ask`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
})
}
export function sendPrompt(message: string, agentId?: string) {
return fetch('/api/prompt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message, agent_id: agentId || undefined }),
})
}
export async function fetchPresentation(): Promise<PresentationData | null> {
return fetchJson<PresentationData>('/api/presentation', 60000)
}
export async function fetchChanges(opts: { source?: string; op?: string; limit?: number } = {}) {
const p = new URLSearchParams()
if (opts.source) p.set('source', opts.source)
if (opts.op) p.set('op', opts.op)
p.set('limit', String(opts.limit ?? 150))
const j = await fetchJson<{ changes?: CdcChange[]; connected?: boolean; consumed?: number }>(
`/api/changes?${p.toString()}`, 8000,
)
return { changes: j?.changes || [], connected: !!j?.connected, consumed: j?.consumed || 0 }
}
export async function fetchChangeStats(minutes = 15): Promise<CdcStats | null> {
return fetchJson<CdcStats>(`/api/changes/stats?minutes=${minutes}`, 8000)
}
export async function fetchAgentOpsStatus() {
return fetchJson<Record<string, unknown>>('/api/agent-ops/status', 8000)
}
export function toggleAgentOps(enabled?: boolean) {
return fetch('/api/agent-ops/toggle', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(enabled === undefined ? {} : { enabled }),
})
}
export function runAgentOpOnce(source?: string, op?: string) {
return fetch('/api/agent-ops/run-once', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ source, op }),
})
}
export async function fetchDataflow(refresh = false): Promise<DataflowGraph | null> {
return fetchJson<DataflowGraph>(`/api/dataflow${refresh ? '?refresh=true' : ''}`, 25000)
}
export function runDataflowMovement(movementId: string, conf?: Record<string, unknown>) {
return fetch(`/api/dataflow/${movementId}/run`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(conf ? { conf } : {}),
})
}
export async function fetchMovements(): Promise<Movement[]> {
const j = await fetchJson<{ movements?: Movement[] }>('/api/movements', 8000)
return j?.movements || []
}
export async function fetchPii(refresh = false): Promise<{ datasets: PiiDataset[]; summary: Record<string, number> } | null> {
return fetchJson<{ datasets: PiiDataset[]; summary: Record<string, number> }>(
`/api/pii${refresh ? '?refresh=true' : ''}`, 15000,
)
}
export function toggleEtlAgent(enabled?: boolean) {
return fetch('/api/agent-ops/etl/toggle', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(enabled === undefined ? {} : { enabled }),
})
}
export async function decideApproval(id: string, approved: boolean, decidedBy: string, note: string) {
return fetch(`/api/approvals/${id}/decide`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ approved, decided_by: decidedBy, note }),
})
}