[nexus] feat(19-03): update API client to use agentId instead of agentSkillsDir

- Add AgentSkillEntry type with skillId, source, installedAt fields
- install() now sends agentId in body not agentSkillsDir
- uninstall() new function that sends agentId as query param
- rollback() now sends agentId in body not agentSkillsDir
- skillGroups.assignGroup/removeGroup no longer accept agentSkillsDir param
- listAgentSkills now returns AgentSkillEntry[] not string[]
- Fix AgentDetail.tsx callers and entry rendering for new AgentSkillEntry type
- Fix SkillDetail.tsx callers to use agentId param
This commit is contained in:
Mikkel Georgsen 2026-04-01 11:35:40 +02:00 committed by Nexus Dev
parent db83eb2a00
commit 305ea411da
4 changed files with 36 additions and 31 deletions

View file

@ -1,4 +1,5 @@
import { api, ApiError } from "./client";
import type { AgentSkillEntry } from "./skillRegistry";
export type SkillGroupRow = {
id: string;
@ -65,18 +66,15 @@ export const skillGroupsApi = {
listAgentGroups: (agentId: string) =>
api.get<SkillGroupRow[]>(`/skill-registry/agents/${agentId}/groups`),
assignGroup: (agentId: string, groupId: string, agentSkillsDir: string) =>
assignGroup: (agentId: string, groupId: string) =>
api.post<AssignResult>(`/skill-registry/agents/${agentId}/groups`, {
groupId,
agentSkillsDir,
}),
removeGroup: async (agentId: string, groupId: string, agentSkillsDir: string): Promise<void> => {
removeGroup: async (agentId: string, groupId: string): Promise<void> => {
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);
@ -89,5 +87,5 @@ export const skillGroupsApi = {
},
listAgentSkills: (agentId: string) =>
api.get<string[]>(`/skill-registry/agents/${agentId}/skills`),
api.get<AgentSkillEntry[]>(`/skill-registry/agents/${agentId}/skills`),
};

View file

@ -1,5 +1,11 @@
import { api } from "./client";
export type AgentSkillEntry = {
skillId: string;
source: "managed" | "native";
installedAt: number;
};
export type SkillListItem = {
id: string;
name: string;
@ -50,10 +56,12 @@ export const skillRegistryApi = {
api.get<SkillVersion[]>(`${skillPath(skillId)}/versions`),
fetch: () =>
api.post<{ fetched: number; errors: string[] }>("/skill-registry/fetch", {}),
install: (skillId: string, agentSkillsDir: string) =>
api.post(`${skillPath(skillId)}/install`, { agentSkillsDir }),
rollback: (skillId: string, versionId: string, agentSkillsDir: string) =>
api.post(`${skillPath(skillId)}/rollback`, { versionId, agentSkillsDir }),
install: (skillId: string, agentId: string) =>
api.post(`${skillPath(skillId)}/install`, { agentId }),
uninstall: (skillId: string, agentId: string) =>
api.delete(`${skillPath(skillId)}?agentId=${encodeURIComponent(agentId)}`),
rollback: (skillId: string, versionId: string, agentId: string) =>
api.post(`${skillPath(skillId)}/rollback`, { versionId, agentId }),
remove: (skillId: string) =>
api.delete(skillPath(skillId)),
getRatings: (skillId: string) =>

View file

@ -2440,7 +2440,7 @@ function AgentSkillsTab({
// Group mutations
const assignGroupMut = useMutation({
mutationFn: ({ groupId }: { groupId: string }) =>
skillGroupsApi.assignGroup(agent.id, groupId, ""),
skillGroupsApi.assignGroup(agent.id, groupId),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentGroups(agent.id) });
void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentSkills(agent.id) });
@ -2450,7 +2450,7 @@ function AgentSkillsTab({
const removeGroupMut = useMutation({
mutationFn: ({ groupId }: { groupId: string }) =>
skillGroupsApi.removeGroup(agent.id, groupId, ""),
skillGroupsApi.removeGroup(agent.id, groupId),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentGroups(agent.id) });
void queryClient.invalidateQueries({ queryKey: queryKeys.skillGroups.agentSkills(agent.id) });
@ -2735,9 +2735,9 @@ function AgentSkillsTab({
) : (
<ScrollArea className="max-h-[300px] pt-2">
<ul className="space-y-1">
{(agentEffectiveSkillsQuery.data ?? []).map((skillId) => (
<li key={skillId} className="text-sm text-muted-foreground font-mono">
{skillId}
{(agentEffectiveSkillsQuery.data ?? []).map((entry) => (
<li key={entry.skillId} className="text-sm text-muted-foreground font-mono">
{entry.skillId}
</li>
))}
</ul>

View file

@ -95,7 +95,7 @@ export function SkillDetail() {
const [installDialog, setInstallDialog] = useState<{
skillId: string;
isUpdate: boolean;
agentSkillsDir?: string;
agentId?: string;
} | null>(null);
// Uninstall confirmation dialog state
@ -149,8 +149,8 @@ export function SkillDetail() {
};
const installMutation = useMutation({
mutationFn: ({ agentSkillsDir }: { agentSkillsDir: string }) =>
skillRegistryApi.install(skillId, agentSkillsDir),
mutationFn: ({ agentId }: { agentId: string }) =>
skillRegistryApi.install(skillId, agentId),
onSuccess: () => {
invalidateSkill();
setInstallDialog(null);
@ -162,8 +162,8 @@ export function SkillDetail() {
});
const updateMutation = useMutation({
mutationFn: ({ agentSkillsDir }: { agentSkillsDir: string }) =>
skillRegistryApi.install(skillId, agentSkillsDir),
mutationFn: ({ agentId }: { agentId: string }) =>
skillRegistryApi.install(skillId, agentId),
onSuccess: () => {
invalidateSkill();
setInstallDialog(null);
@ -175,8 +175,8 @@ export function SkillDetail() {
});
const rollbackMutation = useMutation({
mutationFn: ({ versionId, agentSkillsDir }: { versionId: string; agentSkillsDir: string }) =>
skillRegistryApi.rollback(skillId, versionId, agentSkillsDir),
mutationFn: ({ versionId, agentId }: { versionId: string; agentId: string }) =>
skillRegistryApi.rollback(skillId, versionId, agentId),
onSuccess: () => {
invalidateSkill();
pushToast({ title: "Rolled back to previous version", tone: "success" });
@ -596,16 +596,15 @@ export function SkillDetail() {
</DialogHeader>
<div className="py-2">
<p className="text-sm text-muted-foreground">
Agent skills directory is required to install. Enter the path to the agent&apos;s
skills directory.
Enter the agent ID to install this skill.
</p>
<input
type="text"
className="mt-2 w-full rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-ring"
placeholder="/path/to/agent/skills"
value={installDialog?.agentSkillsDir ?? ""}
placeholder="Agent ID"
value={installDialog?.agentId ?? ""}
onChange={(e) =>
setInstallDialog((d) => d ? { ...d, agentSkillsDir: e.target.value } : null)
setInstallDialog((d) => d ? { ...d, agentId: e.target.value } : null)
}
/>
</div>
@ -614,13 +613,13 @@ export function SkillDetail() {
Cancel
</Button>
<Button
disabled={!installDialog?.agentSkillsDir || isMutating}
disabled={!installDialog?.agentId || isMutating}
onClick={() => {
if (!installDialog?.agentSkillsDir) return;
if (!installDialog?.agentId) return;
if (installDialog.isUpdate) {
updateMutation.mutate({ agentSkillsDir: installDialog.agentSkillsDir });
updateMutation.mutate({ agentId: installDialog.agentId });
} else {
installMutation.mutate({ agentSkillsDir: installDialog.agentSkillsDir });
installMutation.mutate({ agentId: installDialog.agentId });
}
}}
>