const BASE = "/api"; async function request(path: string, options?: RequestInit): Promise { const res = await fetch(`${BASE}${path}`, { headers: { "Content-Type": "application/json" }, ...options, }); if (!res.ok) { const body = await res.json().catch(() => ({ error: res.statusText })); throw new Error(body.error || res.statusText); } return res.json(); } export interface FileNode { name: string; path: string; type: "file" | "directory"; children?: FileNode[]; } export const api = { health: () => request<{ status: string; wsClients: number }>("/health"), editor: { tree: (repo: string) => request(`/editor/tree/${repo}`), readFile: (repo: string, filePath: string) => request<{ content: string }>(`/editor/file/${repo}/${filePath}`), writeFile: (repo: string, filePath: string, content: string) => request(`/editor/file/${repo}/${filePath}`, { method: "PUT", body: JSON.stringify({ content }), }), deleteFile: (repo: string, filePath: string) => request(`/editor/file/${repo}/${filePath}`, { method: "DELETE" }), search: (repo: string, query: string, opts?: Record) => request<{ matches: Array<{ file: string; line: number; column: number; text: string; matchLength: number }>; totalMatches: number; filesSearched: number; }>("/editor/search", { method: "POST", body: JSON.stringify({ repo, query, ...opts }) }), gffSchema: (type: string) => request(`/editor/gff-schema/${type}`), }, workspace: { getConfig: () => request>("/workspace/config"), updateConfig: (data: Record) => request("/workspace/config", { method: "PUT", body: JSON.stringify(data) }), init: () => request("/workspace/init", { method: "POST" }), }, build: { compileModule: (target?: string) => request("/build/module/compile", { method: "POST", body: JSON.stringify({ target }) }), packModule: (target?: string) => request("/build/module/pack", { method: "POST", body: JSON.stringify({ target }) }), deploy: () => request("/build/deploy", { method: "POST" }), compileSingle: (filePath: string) => request("/build/compile-single", { method: "POST", body: JSON.stringify({ filePath }) }), buildHaks: () => request("/build/haks", { method: "POST" }), buildNwnx: (target?: string) => request("/build/nwnx", { method: "POST", body: JSON.stringify({ target }) }), }, server: { status: () => request<{ nwserver: string; mariadb: string }>("/server/status"), start: () => request("/server/start", { method: "POST" }), stop: () => request("/server/stop", { method: "POST" }), restart: () => request("/server/restart", { method: "POST" }), generateConfig: () => request("/server/generate-config", { method: "POST" }), seedDb: (cdKey: string, playerName: string) => request("/server/seed-db", { method: "POST", body: JSON.stringify({ cdKey, playerName }), }), sql: (query: string, allowWrite?: boolean) => request<{ columns: string[]; rows: Record[] }>("/server/sql", { method: "POST", body: JSON.stringify({ query, allowWrite }), }), }, toolset: { status: () => request<{ active: boolean; pendingCount: number }>("/toolset/status"), start: () => request("/toolset/start", { method: "POST" }), stop: () => request("/toolset/stop", { method: "POST" }), changes: () => request< Array<{ filename: string; gffType: string; repoPath: string | null; timestamp: number; }> >("/toolset/changes"), getChange: (filename: string) => request<{ filename: string; gffType: string; repoPath: string | null; timestamp: number; jsonContent?: string; }>(`/toolset/changes/${filename}`), apply: (files: string[]) => request("/toolset/apply", { method: "POST", body: JSON.stringify({ files }), }), applyAll: () => request("/toolset/apply-all", { method: "POST" }), discard: (files: string[]) => request("/toolset/discard", { method: "POST", body: JSON.stringify({ files }), }), discardAll: () => request("/toolset/discard-all", { method: "POST" }), }, github: { validatePat: (pat: string) => request<{ login: string }>("/github/validate-pat", { method: "POST", body: JSON.stringify({ pat }), }), fork: (repo: string) => request("/github/fork", { method: "POST", body: JSON.stringify({ repo }) }), forks: () => request>("/github/forks"), createPr: (repo: string, title: string, body: string, headBranch: string) => request<{ number: number; url: string }>("/github/pr", { method: "POST", body: JSON.stringify({ repo, title, body, headBranch }), }), listPrs: (repo: string) => request>>(`/github/prs/${repo}`), }, repos: { list: () => request>>("/repos"), status: (repo: string) => request>(`/repos/${repo}/status`), clone: (repo: string) => request("/repos/clone", { method: "POST", body: JSON.stringify({ repo }) }), pull: (repo: string) => request(`/repos/${repo}/pull`, { method: "POST" }), commit: (repo: string, data: Record) => request(`/repos/${repo}/commit`, { method: "POST", body: JSON.stringify(data) }), push: (repo: string) => request(`/repos/${repo}/push`, { method: "POST" }), diff: (repo: string) => request<{ diff: string }>(`/repos/${repo}/diff`), }, docker: { containers: () => request>>("/docker/containers"), pull: (image: string) => request("/docker/pull", { method: "POST", body: JSON.stringify({ image }) }), start: (name: string) => request(`/docker/containers/${name}/start`, { method: "POST" }), stop: (name: string) => request(`/docker/containers/${name}/stop`, { method: "POST" }), restart: (name: string) => request(`/docker/containers/${name}/restart`, { method: "POST" }), logs: (name: string, tail = 100) => request<{ logs: string }>(`/docker/containers/${name}/logs?tail=${tail}`), }, };