74 lines
1.9 KiB
TypeScript
74 lines
1.9 KiB
TypeScript
/** Thin control-plane client used with Keycloak bearer tokens. */
|
|
export class ApiError extends Error {
|
|
constructor(public status: number, message: string) {
|
|
super(`API ${status}: ${message}`);
|
|
this.name = "ApiError";
|
|
}
|
|
}
|
|
|
|
export interface AgentRow {
|
|
name: string;
|
|
version: string;
|
|
status: string;
|
|
url?: string;
|
|
description?: string;
|
|
card?: Record<string, any>;
|
|
}
|
|
|
|
export class ControlPlaneClient {
|
|
constructor(
|
|
private apiUrl: string,
|
|
private token: string | null,
|
|
) {
|
|
this.apiUrl = apiUrl.replace(/\/+$/, "");
|
|
}
|
|
|
|
private headers(): Record<string, string> {
|
|
const h: Record<string, string> = {
|
|
accept: "application/json",
|
|
"content-type": "application/json",
|
|
};
|
|
if (this.token) h["authorization"] = `Bearer ${this.token}`;
|
|
return h;
|
|
}
|
|
|
|
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
|
const resp = await fetch(`${this.apiUrl}${path}`, {
|
|
method,
|
|
headers: this.headers(),
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
});
|
|
const text = await resp.text();
|
|
if (!resp.ok) {
|
|
throw new ApiError(resp.status, errorDetail(text, resp.statusText));
|
|
}
|
|
if (resp.status === 204) return undefined as T;
|
|
return (text ? JSON.parse(text) : undefined) as T;
|
|
}
|
|
|
|
me() {
|
|
return this.request<{ id: number; email: string }>("GET", "/v1/me");
|
|
}
|
|
|
|
listAgents() {
|
|
return this.request<AgentRow[]>("GET", "/v1/agents");
|
|
}
|
|
|
|
getAgent(name: string) {
|
|
return this.request<AgentRow>("GET", `/v1/agents/${encodeURIComponent(name)}`);
|
|
}
|
|
}
|
|
|
|
function errorDetail(text: string, fallback: string): string {
|
|
if (!text.trim()) return fallback;
|
|
try {
|
|
const parsed = JSON.parse(text);
|
|
const detail = parsed?.detail;
|
|
if (typeof detail === "string") return detail;
|
|
if (detail !== undefined) return JSON.stringify(detail);
|
|
return JSON.stringify(parsed);
|
|
} catch {
|
|
return text;
|
|
}
|
|
}
|