Merge branch 'worktree-agent-a6820222665a87090'

# Conflicts:
#	webapp/src/lib/api.ts
This commit is contained in:
Hugh Kaznowski
2026-09-05 13:23:08 +01:00
6 changed files with 772 additions and 26 deletions
+83
View File
@@ -75,6 +75,29 @@ export class ApiError extends Error {
}
}
export type ColorScheme = 'light' | 'dark' | 'system'
export interface Settings {
color_scheme: ColorScheme
active_profile: string | null
}
export type CssEntryKind = 'imported' | 'linked'
export interface CssEntry {
id: string
kind: CssEntryKind
filename: string | null
path_or_url: string | null
}
export interface ProfileSummary {
name: string
param_count: number
metric_count: number
dashboard_count: number
}
async function getJson<T>(path: string): Promise<T> {
const res = await fetch(path)
if (!res.ok) {
@@ -118,6 +141,26 @@ async function postJson<T>(path: string, body: unknown): Promise<T> {
return res.json() as Promise<T>
}
async function sendJson<T>(path: string, method: string, body?: unknown): Promise<T> {
const res = await fetch(path, {
method,
headers: body !== undefined ? { 'Content-Type': 'application/json' } : undefined,
body: body !== undefined ? JSON.stringify(body) : undefined,
})
if (!res.ok) {
let message = `${path} failed: ${res.status}`
try {
const data = await res.json()
if (data?.error) message = data.error
} catch {
// response body wasn't JSON (or was empty) — fall back to the generic message
}
throw new Error(message)
}
if (res.status === 204) return undefined as T
return res.json() as Promise<T>
}
export function fetchLogs(params: {
sinceSequence?: number
startMs?: number
@@ -182,3 +225,43 @@ export function fetchMetricBucketValues(
if (params.limit !== undefined) search.set('limit', String(params.limit))
return getJson(`/api/metrics/${encodeURIComponent(name)}?${search.toString()}`)
}
export function fetchSettings(): Promise<Settings> {
return getJson('/api/settings')
}
export function updateSettings(update: { colorScheme?: ColorScheme }): Promise<Settings> {
return sendJson('/api/settings', 'PATCH', { color_scheme: update.colorScheme })
}
export function fetchCssEntries(): Promise<CssEntry[]> {
return getJson('/api/settings/css')
}
export function importCss(filename: string, content: string): Promise<CssEntry> {
return sendJson('/api/settings/css', 'POST', { filename, content })
}
export function deleteCssEntry(id: string): Promise<void> {
return sendJson(`/api/settings/css/${encodeURIComponent(id)}`, 'DELETE')
}
export function cssContentUrl(id: string): string {
return `/api/settings/css/${encodeURIComponent(id)}/content`
}
export function fetchProfiles(): Promise<ProfileSummary[]> {
return getJson('/api/settings/profiles')
}
export function createProfile(name: string): Promise<ProfileSummary> {
return sendJson('/api/settings/profiles', 'POST', { name })
}
export function deleteProfile(name: string): Promise<void> {
return sendJson(`/api/settings/profiles/${encodeURIComponent(name)}`, 'DELETE')
}
export function activateProfile(name: string): Promise<Settings> {
return sendJson(`/api/settings/profiles/${encodeURIComponent(name)}/activate`, 'POST')
}