290 lines
8 KiB
TypeScript
290 lines
8 KiB
TypeScript
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<T>(path: string, init?: RequestInit): Promise<T> {
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "application/json",
|
|
...((init?.headers as Record<string, string>) ?? {}),
|
|
};
|
|
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<T>;
|
|
}
|
|
|
|
// Auth
|
|
async login(email: string, name?: string): Promise<LoginResponse> {
|
|
return this.fetch("/auth/login", {
|
|
method: "POST",
|
|
body: JSON.stringify({ email, name }),
|
|
});
|
|
}
|
|
|
|
async getMe(): Promise<User> {
|
|
return this.fetch("/api/me");
|
|
}
|
|
|
|
async updateMe(data: UpdateMeRequest): Promise<User> {
|
|
return this.fetch("/api/me", {
|
|
method: "PATCH",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
// Issues
|
|
async listIssues(params?: ListIssuesParams): Promise<ListIssuesResponse> {
|
|
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<Issue> {
|
|
return this.fetch(`/api/issues/${id}`);
|
|
}
|
|
|
|
async createIssue(data: CreateIssueRequest): Promise<Issue> {
|
|
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<Issue> {
|
|
return this.fetch(`/api/issues/${id}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteIssue(id: string): Promise<void> {
|
|
await this.fetch(`/api/issues/${id}`, { method: "DELETE" });
|
|
}
|
|
|
|
// Comments
|
|
async listComments(issueId: string): Promise<Comment[]> {
|
|
return this.fetch(`/api/issues/${issueId}/comments`);
|
|
}
|
|
|
|
async createComment(issueId: string, content: string, type?: string): Promise<Comment> {
|
|
return this.fetch(`/api/issues/${issueId}/comments`, {
|
|
method: "POST",
|
|
body: JSON.stringify({ content, type: type ?? "comment" }),
|
|
});
|
|
}
|
|
|
|
async updateComment(commentId: string, content: string): Promise<Comment> {
|
|
return this.fetch(`/api/comments/${commentId}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify({ content }),
|
|
});
|
|
}
|
|
|
|
async deleteComment(commentId: string): Promise<void> {
|
|
await this.fetch(`/api/comments/${commentId}`, { method: "DELETE" });
|
|
}
|
|
|
|
// Agents
|
|
async listAgents(params?: { workspace_id?: string }): Promise<Agent[]> {
|
|
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<Agent> {
|
|
return this.fetch(`/api/agents/${id}`);
|
|
}
|
|
|
|
async createAgent(data: CreateAgentRequest): Promise<Agent> {
|
|
return this.fetch("/api/agents", {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateAgent(id: string, data: UpdateAgentRequest): Promise<Agent> {
|
|
return this.fetch(`/api/agents/${id}`, {
|
|
method: "PUT",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteAgent(id: string): Promise<void> {
|
|
await this.fetch(`/api/agents/${id}`, { method: "DELETE" });
|
|
}
|
|
|
|
async listRuntimes(params?: { workspace_id?: string }): Promise<AgentRuntime[]> {
|
|
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<AgentTask[]> {
|
|
return this.fetch(`/api/agents/${agentId}/tasks`);
|
|
}
|
|
|
|
async getDaemonPairingSession(token: string): Promise<DaemonPairingSession> {
|
|
return this.fetch(`/api/daemon/pairing-sessions/${token}`);
|
|
}
|
|
|
|
async approveDaemonPairingSession(
|
|
token: string,
|
|
data: ApproveDaemonPairingSessionRequest,
|
|
): Promise<DaemonPairingSession> {
|
|
return this.fetch(`/api/daemon/pairing-sessions/${token}/approve`, {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
// Inbox
|
|
async listInbox(): Promise<InboxItem[]> {
|
|
return this.fetch("/api/inbox");
|
|
}
|
|
|
|
async markInboxRead(id: string): Promise<void> {
|
|
await this.fetch(`/api/inbox/${id}/read`, { method: "POST" });
|
|
}
|
|
|
|
async archiveInbox(id: string): Promise<void> {
|
|
await this.fetch(`/api/inbox/${id}/archive`, { method: "POST" });
|
|
}
|
|
|
|
// Workspaces
|
|
async listWorkspaces(): Promise<Workspace[]> {
|
|
return this.fetch("/api/workspaces");
|
|
}
|
|
|
|
async getWorkspace(id: string): Promise<Workspace> {
|
|
return this.fetch(`/api/workspaces/${id}`);
|
|
}
|
|
|
|
async createWorkspace(data: { name: string; slug: string; description?: string }): Promise<Workspace> {
|
|
return this.fetch("/api/workspaces", {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateWorkspace(id: string, data: { name?: string; description?: string; settings?: Record<string, unknown> }): Promise<Workspace> {
|
|
return this.fetch(`/api/workspaces/${id}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
// Members
|
|
async listMembers(workspaceId: string): Promise<MemberWithUser[]> {
|
|
return this.fetch(`/api/workspaces/${workspaceId}/members`);
|
|
}
|
|
|
|
async createMember(workspaceId: string, data: CreateMemberRequest): Promise<MemberWithUser> {
|
|
return this.fetch(`/api/workspaces/${workspaceId}/members`, {
|
|
method: "POST",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateMember(workspaceId: string, memberId: string, data: UpdateMemberRequest): Promise<MemberWithUser> {
|
|
return this.fetch(`/api/workspaces/${workspaceId}/members/${memberId}`, {
|
|
method: "PATCH",
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteMember(workspaceId: string, memberId: string): Promise<void> {
|
|
await this.fetch(`/api/workspaces/${workspaceId}/members/${memberId}`, {
|
|
method: "DELETE",
|
|
});
|
|
}
|
|
|
|
async leaveWorkspace(workspaceId: string): Promise<void> {
|
|
await this.fetch(`/api/workspaces/${workspaceId}/leave`, {
|
|
method: "POST",
|
|
});
|
|
}
|
|
|
|
async deleteWorkspace(workspaceId: string): Promise<void> {
|
|
await this.fetch(`/api/workspaces/${workspaceId}`, {
|
|
method: "DELETE",
|
|
});
|
|
}
|
|
}
|