/** * Thin control-plane client. Mirrors the relevant subset of * a2a_pack.cli.api_client.ControlPlaneClient. */ 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; } export class ControlPlaneClient { constructor( private apiUrl: string, private token: string | null, ) { this.apiUrl = apiUrl.replace(/\/+$/, ""); } private headers(): Record { const h: Record = { "content-type": "application/json" }; if (this.token) h["authorization"] = `bearer ${this.token}`; return h; } private async request(method: string, path: string, body?: unknown): Promise { const resp = await fetch(`${this.apiUrl}${path}`, { method, headers: this.headers(), body: body !== undefined ? JSON.stringify(body) : undefined, }); if (!resp.ok) { let detail: string; try { const j: any = await resp.json(); detail = j?.detail ?? JSON.stringify(j); } catch { detail = await resp.text(); } throw new ApiError(resp.status, detail); } if (resp.status === 204) return undefined as T; return (await resp.json()) as T; } login(email: string, password: string) { return this.request<{ access_token: string; user: { id: number; email: string }; }>("POST", "/v1/auth/login", { email, password }); } signup(email: string, password: string) { return this.request<{ access_token: string; user: { id: number; email: string }; }>("POST", "/v1/auth/signup", { email, password }); } me() { return this.request<{ id: number; email: string }>("GET", "/v1/me"); } listAgents() { return this.request("GET", "/v1/agents"); } getAgent(name: string) { return this.request("GET", `/v1/agents/${encodeURIComponent(name)}`); } }