diff --git a/ui/src/api/skillGroups.ts b/ui/src/api/skillGroups.ts new file mode 100644 index 00000000..712e9eef --- /dev/null +++ b/ui/src/api/skillGroups.ts @@ -0,0 +1,93 @@ +import { api, ApiError } from "./client"; + +export type SkillGroupRow = { + id: string; + name: string; + description: string | null; + isBuiltin: number; + createdAt: number; + updatedAt: number; +}; + +export type GroupMemberRow = { + groupId: string; + skillId: string; + addedAt: number; +}; + +export type AssignResult = { + installed: string[]; + skipped: string[]; + pendingPlugin: string[]; +}; + +export type GroupExport = { + version: "1"; + group: { + id: string; + name: string; + description: string | null; + members: string[]; + parents: string[]; + }; +}; + +export const skillGroupsApi = { + listGroups: () => api.get("/skill-registry/groups"), + + getGroup: (groupId: string) => + api.get(`/skill-registry/groups/${groupId}`), + + createGroup: (input: { name: string; description?: string }) => + api.post("/skill-registry/groups", input), + + updateGroup: (groupId: string, patch: { name?: string; description?: string }) => + api.patch(`/skill-registry/groups/${groupId}`, patch), + + deleteGroup: (groupId: string) => + api.delete(`/skill-registry/groups/${groupId}`), + + listMembers: (groupId: string) => + api.get(`/skill-registry/groups/${groupId}/members`), + + addMember: (groupId: string, skillId: string) => + api.post<{ ok: boolean }>(`/skill-registry/groups/${groupId}/members`, { skillId }), + + removeMember: (groupId: string, skillId: string) => + api.delete(`/skill-registry/groups/${groupId}/members/${skillId}`), + + exportGroup: (groupId: string) => + api.get(`/skill-registry/groups/${groupId}/export`), + + importGroup: (data: GroupExport) => + api.post("/skill-registry/groups/import", data), + + listAgentGroups: (agentId: string) => + api.get(`/skill-registry/agents/${agentId}/groups`), + + assignGroup: (agentId: string, groupId: string, agentSkillsDir: string) => + api.post(`/skill-registry/agents/${agentId}/groups`, { + groupId, + agentSkillsDir, + }), + + removeGroup: async (agentId: string, groupId: string, agentSkillsDir: string): Promise => { + const res = await fetch(`/api/skill-registry/agents/${agentId}/groups/${groupId}`, { + method: "DELETE", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ agentSkillsDir }), + }); + if (!res.ok && res.status !== 204) { + const errorBody = await res.json().catch(() => null); + throw new ApiError( + (errorBody as { error?: string } | null)?.error ?? `Request failed: ${res.status}`, + res.status, + errorBody, + ); + } + }, + + listAgentSkills: (agentId: string) => + api.get(`/skill-registry/agents/${agentId}/skills`), +}; diff --git a/ui/src/lib/queryKeys.ts b/ui/src/lib/queryKeys.ts index 24d5fe18..df94182e 100644 --- a/ui/src/lib/queryKeys.ts +++ b/ui/src/lib/queryKeys.ts @@ -139,6 +139,14 @@ export const queryKeys = { detail: (skillId: string) => ["skill-registry", "skills", skillId] as const, versions: (skillId: string) => ["skill-registry", "skills", skillId, "versions"] as const, }, + skillGroups: { + list: ["skill-groups"] as const, + detail: (groupId: string) => ["skill-groups", groupId] as const, + members: (groupId: string) => ["skill-groups", groupId, "members"] as const, + agentGroups: (agentId: string) => ["skill-groups", "agent", agentId] as const, + agentSkills: (agentId: string) => ["skill-groups", "agent", agentId, "skills"] as const, + agentEffective: (agentId: string) => ["skill-groups", "agent", agentId, "effective"] as const, + }, plugins: { all: ["plugins"] as const, examples: ["plugins", "examples"] as const,