Files
arcadia-admin/app/lib/arcadia/health.ts
jules ab116f8465 refactor: rename @crema/arcadia-client → @crema/arcadia-core-client
Disambiguates the Phoenix/auth client lib from lib-arcadia-agents-client.
Dir lib-arcadia-client → lib-arcadia-core-client; alias updated in
tsconfig paths, vite config, app.css @source, imports, CI and docs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 13:31:56 +10:00

95 lines
2.7 KiB
TypeScript

// Arcadia health probes.
//
// Backed by /api/v1/health* (public — no auth). Each subsystem is probed
// independently; the overall endpoint aggregates and returns 503 if any
// subsystem is not "ok". See arcadia-app commit f427892.
import type { ArcadiaClient } from "@crema/arcadia-core-client"
export type HealthSubsystem = "api" | "db" | "workers" | "storage"
export type HealthStatus = "ok" | "degraded" | "error" | "unconfigured"
export interface SubsystemHealth {
status: HealthStatus
/** Optional human-readable detail. */
message?: string
/** Free-form metrics — shape is subsystem-specific. */
details?: Record<string, unknown>
}
export interface OverallHealth {
status: HealthStatus
checked_at: string
subsystems: Record<HealthSubsystem, SubsystemHealth>
}
export interface DetailedHealth extends OverallHealth {
/** BEAM info — present on /health/detailed only. */
system?: {
otp_release?: string
elixir_version?: string
process_count?: number
memory_total_bytes?: number
[k: string]: unknown
}
}
export interface HostStats {
cpu: {
util_pct: number | null
per_cpu_pct: number[]
load_avg_1: number | null
load_avg_5: number | null
load_avg_15: number | null
schedulers_online: number
num_cpus: number | null
}
memory: {
total_bytes: number | null
free_bytes: number | null
available_bytes: number | null
buffered_bytes: number | null
cached_bytes: number | null
swap_total_bytes: number | null
swap_free_bytes: number | null
}
disks: Array<{ mount: string; total_kb: number; used_pct: number }>
checked_at: string
}
const BASE = "/api/v1/health"
export async function getHealth(arcadia: ArcadiaClient): Promise<OverallHealth> {
const res = await arcadia.GET<{ data: OverallHealth } | OverallHealth>(BASE)
return unwrap(res)
}
export async function getServiceHealth(
arcadia: ArcadiaClient,
service: HealthSubsystem,
): Promise<SubsystemHealth> {
const res = await arcadia.GET<{ data: SubsystemHealth } | SubsystemHealth>(
`${BASE}/${service}`,
)
return unwrap(res)
}
export async function getHealthDetailed(arcadia: ArcadiaClient): Promise<DetailedHealth> {
const res = await arcadia.GET<{ data: DetailedHealth } | DetailedHealth>(`${BASE}/detailed`)
return unwrap(res)
}
export async function getHostStats(arcadia: ArcadiaClient): Promise<HostStats> {
const res = await arcadia.GET<{ data: HostStats } | HostStats>(`${BASE}/host`)
return unwrap(res)
}
export const SUBSYSTEMS: HealthSubsystem[] = ["api", "db", "workers", "storage"]
function unwrap<T>(res: { data: T } | T): T {
return res && typeof res === "object" && "data" in (res as object)
? (res as { data: T }).data
: (res as T)
}