feat(admin): restore org Agent role/skill management and fix 404 after project create

- explorer POST /projects now returns {id,name} matching the SPA contract
  (previously returned ProjectOnboardingResult.projectId, so res.id was
  undefined and the redirect to /projects/undefined 404'd)
- add OrganizationAgentConfiguration.listRoles/listSkills + AgentRoleRow/
  AgentSkillRow exports; upsertRole now returns the full row
- new agentConfigRoutes: GET/PUT /agent-roles, PUT /agent-roles/:id/skills,
  GET /agent-skills, GET /agent-models (env-default picker)
- restore admin-web roles page + RoleCard rewired to ADR-0017/0018 backend
  (label, defaultModel, tools whitelist, skill binding, systemPrompt,
  sortOrder, default toggle); add 角色 nav item + roles icon
- skill installation stays out-of-band (CLI/seed) per spec; the surface only
  lists installed skills and binds them to roles
This commit is contained in:
2026-07-15 18:16:34 +08:00
parent fb66614e38
commit 0726dc13c8
9 changed files with 660 additions and 26 deletions
+55 -4
View File
@@ -226,6 +226,39 @@ export interface CapacityPolicyView {
dimensions: CapacityDimensionRow[];
}
export interface AgentRoleRow {
id: string;
roleId: string;
label: string;
defaultModel: string | null;
systemPrompt: string | null;
tools: readonly string[] | null;
sortOrder: number;
isDefault: boolean;
disabledAt: string | null;
createdAt: string;
updatedAt: string;
skillNames: readonly string[];
}
export interface AgentSkillRow {
id: string;
name: string;
version: string;
description: string | null;
contentDigest: string;
disabledAt: string | null;
createdAt: string;
updatedAt: string;
boundRoleIds: readonly string[];
}
export interface AgentModelRow {
id: string;
label: string;
toolCapable: boolean;
}
// --- API ---
export const api = {
@@ -261,8 +294,7 @@ export const api = {
post(`${orgBase(slug)}/teams/${teamId}/members/${userId}/revoke`),
explorer: (slug: string) => get(`${orgBase(slug)}/explorer`) as Promise<ExplorerData>,
myProjects: (slug: string) =>
get(`${orgBase(slug)}/my-projects`) as Promise<{ projects: ExplorerProject[] }>,
myProjects: (slug: string) => get(`${orgBase(slug)}/my-projects`) as Promise<{ projects: ExplorerProject[] }>,
createFolder: (slug: string, body: { name: string; parentId?: string; sortKey?: string }) =>
post(`${orgBase(slug)}/folders`, body) as Promise<{
id: string;
@@ -338,8 +370,27 @@ export const api = {
disableFeishuApplication: (slug: string) =>
del(`${orgBase(slug)}/feishu-application-connection`) as Promise<FeishuApplicationConnection>,
capacityPolicy: (slug: string) =>
get(`${orgBase(slug)}/capacity-policy`) as Promise<CapacityPolicyView>,
capacityPolicy: (slug: string) => get(`${orgBase(slug)}/capacity-policy`) as Promise<CapacityPolicyView>,
setCapacityPolicy: (slug: string, body: { limits: Partial<Record<CapacityDimension, number | null>> }) =>
put(`${orgBase(slug)}/capacity-policy`, body) as Promise<CapacityPolicyView>,
agentRoles: (slug: string) => get(`${orgBase(slug)}/agent-roles`) as Promise<{ roles: AgentRoleRow[] }>,
upsertAgentRole: (
slug: string,
roleId: string,
body: {
label: string;
defaultModel?: string | null;
systemPrompt?: string | null;
tools?: readonly string[] | null;
sortOrder?: number;
isDefault?: boolean;
},
) => put(`${orgBase(slug)}/agent-roles/${encodeURIComponent(roleId)}`, body) as Promise<AgentRoleRow>,
setAgentRoleSkills: (slug: string, roleId: string, skillNames: readonly string[]) =>
put(`${orgBase(slug)}/agent-roles/${encodeURIComponent(roleId)}/skills`, { skillNames }) as Promise<{
skillNames: string[];
}>,
agentSkills: (slug: string) => get(`${orgBase(slug)}/agent-skills`) as Promise<{ skills: AgentSkillRow[] }>,
agentModels: (slug: string) => get(`${orgBase(slug)}/agent-models`) as Promise<{ models: AgentModelRow[] }>,
};