import { cookies } from "next/headers"; export const SESSION_COOKIE_NAME = "popiart_session"; type ApiEnvelope = { ok: boolean; data?: T; error?: { code?: string; message?: string; details?: unknown; }; }; export type PopiartUser = { id: string; email: string; name: string; scopes?: string[]; }; export type LoginResponse = { key: string; token: string; user: PopiartUser; }; export type AuthSessionView = { user: PopiartUser; session_key?: string; upstream_key_masked?: string; }; export type Skill = { id: string; name: string; description: string; tags: string[]; version: string; model_type: string; route_key?: string; estimated_duration_s: number; }; export type SkillListResponse = { items: Skill[]; total: number; limit: number; offset: number; }; export type BudgetSummary = { period: { start: string; end: string; }; used: { tokens: number; cost_usd: number; }; limit: { monthly_tokens: number; monthly_cost_usd: number; }; remaining: { tokens: number; cost_usd: number; }; }; export type BudgetUsageRow = { dimension: string; tokens_used: number; cost_usd: number; job_count: number; }; export type BudgetUsage = { rows: BudgetUsageRow[]; total: { tokens_used: number; cost_usd: number; job_count: number; }; }; export class PopiartApiError extends Error { status: number; code?: string; details?: unknown; constructor(status: number, message: string, code?: string, details?: unknown) { super(message); this.name = "PopiartApiError"; this.status = status; this.code = code; this.details = details; } } function stripTrailingSlash(value: string) { return value.replace(/\/+$/, ""); } export function getPopiartEndpoint() { const configured = process.env.POPIART_ENDPOINT ?? process.env.POPIART_SERVER_URL ?? "http://127.0.0.1:8080/v1"; return stripTrailingSlash(configured); } export function buildPopiartUrl(pathname: string) { const path = pathname.startsWith("/") ? pathname : `/${pathname}`; return `${getPopiartEndpoint()}${path}`; } async function readEnvelope(response: Response) { try { return (await response.json()) as ApiEnvelope; } catch { return null; } } export async function popiartFetchEnvelope(pathname: string, init: RequestInit = {}) { const response = await fetch(buildPopiartUrl(pathname), { ...init, cache: "no-store", }); const payload = await readEnvelope(response); return { response, payload }; } export async function popiartRequest( pathname: string, options: { method?: string; body?: BodyInit | object; headers?: HeadersInit; token?: string; } = {}, ) { const headers = new Headers(options.headers); let body: BodyInit | undefined; if (options.token) { headers.set("Authorization", `Bearer ${options.token}`); } if (options.body instanceof FormData || typeof options.body === "string") { body = options.body; } else if (options.body !== undefined) { headers.set("Content-Type", "application/json"); body = JSON.stringify(options.body); } const { response, payload } = await popiartFetchEnvelope(pathname, { method: options.method ?? (body ? "POST" : "GET"), headers, body, }); if (!response.ok || !payload?.ok) { throw new PopiartApiError( response.status, payload?.error?.message ?? `Request failed with status ${response.status}`, payload?.error?.code, payload?.error?.details, ); } return payload.data as T; } export async function getSessionToken() { const cookieStore = await cookies(); return cookieStore.get(SESSION_COOKIE_NAME)?.value ?? null; } export async function getViewerSession() { const token = await getSessionToken(); if (!token) { return null; } try { return await popiartRequest("/auth/me", { token }); } catch (error) { if (error instanceof PopiartApiError && error.status === 401) { return null; } throw error; } } export async function getSkillsCatalog(search?: string) { const token = await getSessionToken(); if (!token) { return null; } const query = new URLSearchParams(); query.set("limit", "100"); if (search) { query.set("search", search); } return popiartRequest(`/skills?${query.toString()}`, { token }); } export async function getBudgetSummary() { const token = await getSessionToken(); if (!token) { return null; } return popiartRequest("/budget", { token }); } export async function getBudgetUsage() { const token = await getSessionToken(); if (!token) { return null; } return popiartRequest("/budget/usage", { token }); }