import type { Issue, CreateIssueRequest, UpdateIssueRequest, ListIssuesResponse, UpdateMeRequest, CreateMemberRequest, UpdateMemberRequest, ListIssuesParams, Agent, CreateAgentRequest, UpdateAgentRequest, AgentTask, AgentRuntime, DaemonPairingSession, ApproveDaemonPairingSessionRequest, InboxItem, Comment, Workspace, MemberWithUser, User, } from "@multica/types"; export interface LoginResponse { token: string; user: User; } export class ApiClient { private baseUrl: string; private token: string | null = null; private workspaceId: string | null = null; constructor(baseUrl: string) { this.baseUrl = baseUrl; } setToken(token: string | null) { this.token = token; } setWorkspaceId(id: string | null) { this.workspaceId = id; } private async fetch(path: string, init?: RequestInit): Promise { const headers: Record = { "Content-Type": "application/json", ...((init?.headers as Record) ?? {}), }; if (this.token) { headers["Authorization"] = `Bearer ${this.token}`; } if (this.workspaceId) { headers["X-Workspace-ID"] = this.workspaceId; } const res = await fetch(`${this.baseUrl}${path}`, { ...init, headers, }); if (!res.ok) { let message = `API error: ${res.status} ${res.statusText}`; try { const data = await res.json() as { error?: string }; if (typeof data.error === "string" && data.error) { message = data.error; } } catch { // Ignore non-JSON error bodies. } throw new Error(message); } // Handle 204 No Content if (res.status === 204) { return undefined as T; } return res.json() as Promise; } // Auth async login(email: string, name?: string): Promise { return this.fetch("/auth/login", { method: "POST", body: JSON.stringify({ email, name }), }); } async getMe(): Promise { return this.fetch("/api/me"); } async updateMe(data: UpdateMeRequest): Promise { return this.fetch("/api/me", { method: "PATCH", body: JSON.stringify(data), }); } // Issues async listIssues(params?: ListIssuesParams): Promise { const search = new URLSearchParams(); if (params?.limit) search.set("limit", String(params.limit)); if (params?.offset) search.set("offset", String(params.offset)); const wsId = params?.workspace_id ?? this.workspaceId; if (wsId) search.set("workspace_id", wsId); if (params?.status) search.set("status", params.status); if (params?.priority) search.set("priority", params.priority); if (params?.assignee_id) search.set("assignee_id", params.assignee_id); return this.fetch(`/api/issues?${search}`); } async getIssue(id: string): Promise { return this.fetch(`/api/issues/${id}`); } async createIssue(data: CreateIssueRequest): Promise { const search = new URLSearchParams(); if (this.workspaceId) search.set("workspace_id", this.workspaceId); return this.fetch(`/api/issues?${search}`, { method: "POST", body: JSON.stringify(data), }); } async updateIssue(id: string, data: UpdateIssueRequest): Promise { return this.fetch(`/api/issues/${id}`, { method: "PUT", body: JSON.stringify(data), }); } async deleteIssue(id: string): Promise { await this.fetch(`/api/issues/${id}`, { method: "DELETE" }); } // Comments async listComments(issueId: string): Promise { return this.fetch(`/api/issues/${issueId}/comments`); } async createComment(issueId: string, content: string, type?: string): Promise { return this.fetch(`/api/issues/${issueId}/comments`, { method: "POST", body: JSON.stringify({ content, type: type ?? "comment" }), }); } async updateComment(commentId: string, content: string): Promise { return this.fetch(`/api/comments/${commentId}`, { method: "PUT", body: JSON.stringify({ content }), }); } async deleteComment(commentId: string): Promise { await this.fetch(`/api/comments/${commentId}`, { method: "DELETE" }); } // Agents async listAgents(params?: { workspace_id?: string }): Promise { const search = new URLSearchParams(); const wsId = params?.workspace_id ?? this.workspaceId; if (wsId) search.set("workspace_id", wsId); return this.fetch(`/api/agents?${search}`); } async getAgent(id: string): Promise { return this.fetch(`/api/agents/${id}`); } async createAgent(data: CreateAgentRequest): Promise { return this.fetch("/api/agents", { method: "POST", body: JSON.stringify(data), }); } async updateAgent(id: string, data: UpdateAgentRequest): Promise { return this.fetch(`/api/agents/${id}`, { method: "PUT", body: JSON.stringify(data), }); } async deleteAgent(id: string): Promise { await this.fetch(`/api/agents/${id}`, { method: "DELETE" }); } async listRuntimes(params?: { workspace_id?: string }): Promise { const search = new URLSearchParams(); const wsId = params?.workspace_id ?? this.workspaceId; if (wsId) search.set("workspace_id", wsId); return this.fetch(`/api/runtimes?${search}`); } async listAgentTasks(agentId: string): Promise { return this.fetch(`/api/agents/${agentId}/tasks`); } async getDaemonPairingSession(token: string): Promise { return this.fetch(`/api/daemon/pairing-sessions/${token}`); } async approveDaemonPairingSession( token: string, data: ApproveDaemonPairingSessionRequest, ): Promise { return this.fetch(`/api/daemon/pairing-sessions/${token}/approve`, { method: "POST", body: JSON.stringify(data), }); } // Inbox async listInbox(): Promise { return this.fetch("/api/inbox"); } async markInboxRead(id: string): Promise { await this.fetch(`/api/inbox/${id}/read`, { method: "POST" }); } async archiveInbox(id: string): Promise { await this.fetch(`/api/inbox/${id}/archive`, { method: "POST" }); } // Workspaces async listWorkspaces(): Promise { return this.fetch("/api/workspaces"); } async getWorkspace(id: string): Promise { return this.fetch(`/api/workspaces/${id}`); } async createWorkspace(data: { name: string; slug: string; description?: string }): Promise { return this.fetch("/api/workspaces", { method: "POST", body: JSON.stringify(data), }); } async updateWorkspace(id: string, data: { name?: string; description?: string; settings?: Record }): Promise { return this.fetch(`/api/workspaces/${id}`, { method: "PATCH", body: JSON.stringify(data), }); } // Members async listMembers(workspaceId: string): Promise { return this.fetch(`/api/workspaces/${workspaceId}/members`); } async createMember(workspaceId: string, data: CreateMemberRequest): Promise { return this.fetch(`/api/workspaces/${workspaceId}/members`, { method: "POST", body: JSON.stringify(data), }); } async updateMember(workspaceId: string, memberId: string, data: UpdateMemberRequest): Promise { return this.fetch(`/api/workspaces/${workspaceId}/members/${memberId}`, { method: "PATCH", body: JSON.stringify(data), }); } async deleteMember(workspaceId: string, memberId: string): Promise { await this.fetch(`/api/workspaces/${workspaceId}/members/${memberId}`, { method: "DELETE", }); } async leaveWorkspace(workspaceId: string): Promise { await this.fetch(`/api/workspaces/${workspaceId}/leave`, { method: "POST", }); } async deleteWorkspace(workspaceId: string): Promise { await this.fetch(`/api/workspaces/${workspaceId}`, { method: "DELETE", }); } }