refactor(api): extract assistant tool admin slices
This commit is contained in:
@@ -0,0 +1,318 @@
|
||||
import type { TRPCContext } from "../../trpc.js";
|
||||
import {
|
||||
CreateClientSchema,
|
||||
CreateOrgUnitSchema,
|
||||
UpdateClientSchema,
|
||||
UpdateOrgUnitSchema,
|
||||
} from "@capakraken/shared";
|
||||
import { z } from "zod";
|
||||
import type { ToolContext, ToolDef, ToolExecutor } from "./shared.js";
|
||||
|
||||
type AssistantToolErrorResult = { error: string };
|
||||
|
||||
type ClientsOrgUnitsDeps = {
|
||||
createClientCaller: (ctx: TRPCContext) => {
|
||||
create: (params: z.input<typeof CreateClientSchema>) => Promise<{
|
||||
id: string;
|
||||
name: string;
|
||||
}>;
|
||||
update: (params: {
|
||||
id: string;
|
||||
data: z.input<typeof UpdateClientSchema>;
|
||||
}) => Promise<{
|
||||
id: string;
|
||||
name: string;
|
||||
}>;
|
||||
getById: (params: { id: string }) => Promise<{ name: string }>;
|
||||
delete: (params: { id: string }) => Promise<unknown>;
|
||||
};
|
||||
createOrgUnitCaller: (ctx: TRPCContext) => {
|
||||
create: (params: z.input<typeof CreateOrgUnitSchema>) => Promise<{
|
||||
id: string;
|
||||
name: string;
|
||||
}>;
|
||||
update: (params: {
|
||||
id: string;
|
||||
data: z.input<typeof UpdateOrgUnitSchema>;
|
||||
}) => Promise<{
|
||||
id: string;
|
||||
name: string;
|
||||
}>;
|
||||
};
|
||||
createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
|
||||
toAssistantClientMutationError: (
|
||||
error: unknown,
|
||||
action?: "create" | "update" | "delete",
|
||||
) => AssistantToolErrorResult | null;
|
||||
toAssistantOrgUnitMutationError: (
|
||||
error: unknown,
|
||||
) => AssistantToolErrorResult | null;
|
||||
};
|
||||
|
||||
export const clientMutationToolDefinitions: ToolDef[] = [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_client",
|
||||
description: "Create a new client. Requires manager or admin role. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", description: "Client name" },
|
||||
code: { type: "string", description: "Client code" },
|
||||
parentId: { type: "string", description: "Optional parent client ID" },
|
||||
sortOrder: { type: "integer", description: "Sort order. Default: 0" },
|
||||
tags: { type: "array", items: { type: "string" }, description: "Optional client tags" },
|
||||
},
|
||||
required: ["name"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_client",
|
||||
description: "Update a client. Requires manager or admin role. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Client ID" },
|
||||
name: { type: "string", description: "New name" },
|
||||
code: { type: "string", description: "New code" },
|
||||
sortOrder: { type: "integer", description: "New sort order" },
|
||||
isActive: { type: "boolean", description: "Set active state" },
|
||||
parentId: { type: "string", description: "Parent client ID; use null to clear" },
|
||||
tags: { type: "array", items: { type: "string" }, description: "Replacement client tags" },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "delete_client",
|
||||
description: "Delete a client. Requires admin role. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Client ID" },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const orgUnitMutationToolDefinitions: ToolDef[] = [
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "create_org_unit",
|
||||
description: "Create a new organizational unit. Admin role required. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", description: "Org unit name" },
|
||||
shortName: { type: "string", description: "Short name/code" },
|
||||
level: { type: "integer", description: "Level (5, 6, or 7)" },
|
||||
parentId: { type: "string", description: "Parent org unit ID (optional)" },
|
||||
sortOrder: { type: "integer", description: "Sort order. Default: 0" },
|
||||
},
|
||||
required: ["name", "level"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "function",
|
||||
function: {
|
||||
name: "update_org_unit",
|
||||
description: "Update an organizational unit. Admin role required. Always confirm first.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
id: { type: "string", description: "Org unit ID" },
|
||||
name: { type: "string", description: "New name" },
|
||||
shortName: { type: "string", description: "New short name" },
|
||||
sortOrder: { type: "integer", description: "New sort order" },
|
||||
isActive: { type: "boolean", description: "Set active state" },
|
||||
parentId: { type: "string", description: "Parent org unit ID; use null to clear" },
|
||||
},
|
||||
required: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function createClientsOrgUnitsExecutors(
|
||||
deps: ClientsOrgUnitsDeps,
|
||||
): Record<string, ToolExecutor> {
|
||||
return {
|
||||
async create_client(params: {
|
||||
name: string;
|
||||
code?: string;
|
||||
parentId?: string;
|
||||
sortOrder?: number;
|
||||
tags?: string[];
|
||||
}, ctx: ToolContext) {
|
||||
const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx));
|
||||
|
||||
let client;
|
||||
try {
|
||||
client = await caller.create(CreateClientSchema.parse(params));
|
||||
} catch (error) {
|
||||
const mapped = deps.toAssistantClientMutationError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
__action: "invalidate" as const,
|
||||
scope: ["client"],
|
||||
success: true,
|
||||
message: `Created client: ${client.name}`,
|
||||
clientId: client.id,
|
||||
client,
|
||||
};
|
||||
},
|
||||
|
||||
async update_client(params: {
|
||||
id: string;
|
||||
name?: string;
|
||||
code?: string | null;
|
||||
sortOrder?: number;
|
||||
isActive?: boolean;
|
||||
parentId?: string | null;
|
||||
tags?: string[];
|
||||
}, ctx: ToolContext) {
|
||||
const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx));
|
||||
const data = UpdateClientSchema.parse({
|
||||
...(params.name !== undefined ? { name: params.name } : {}),
|
||||
...(params.code !== undefined ? { code: params.code } : {}),
|
||||
...(params.sortOrder !== undefined ? { sortOrder: params.sortOrder } : {}),
|
||||
...(params.isActive !== undefined ? { isActive: params.isActive } : {}),
|
||||
...(params.parentId !== undefined ? { parentId: params.parentId } : {}),
|
||||
...(params.tags !== undefined ? { tags: params.tags } : {}),
|
||||
});
|
||||
if (Object.keys(data).length === 0) {
|
||||
return { error: "No fields to update" };
|
||||
}
|
||||
|
||||
let client;
|
||||
try {
|
||||
client = await caller.update({ id: params.id, data });
|
||||
} catch (error) {
|
||||
const mapped = deps.toAssistantClientMutationError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
__action: "invalidate" as const,
|
||||
scope: ["client"],
|
||||
success: true,
|
||||
message: `Updated client: ${client.name}`,
|
||||
clientId: client.id,
|
||||
client,
|
||||
};
|
||||
},
|
||||
|
||||
async delete_client(params: { id: string }, ctx: ToolContext) {
|
||||
const caller = deps.createClientCaller(deps.createScopedCallerContext(ctx));
|
||||
|
||||
let client;
|
||||
try {
|
||||
client = await caller.getById({ id: params.id });
|
||||
await caller.delete({ id: params.id });
|
||||
} catch (error) {
|
||||
const mapped = deps.toAssistantClientMutationError(error, "delete");
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
__action: "invalidate" as const,
|
||||
scope: ["client"],
|
||||
success: true,
|
||||
message: `Deleted client: ${client.name}`,
|
||||
};
|
||||
},
|
||||
|
||||
async create_org_unit(params: {
|
||||
name: string;
|
||||
shortName?: string;
|
||||
level: number;
|
||||
parentId?: string;
|
||||
sortOrder?: number;
|
||||
}, ctx: ToolContext) {
|
||||
const caller = deps.createOrgUnitCaller(deps.createScopedCallerContext(ctx));
|
||||
|
||||
let orgUnit;
|
||||
try {
|
||||
orgUnit = await caller.create(CreateOrgUnitSchema.parse(params));
|
||||
} catch (error) {
|
||||
const mapped = deps.toAssistantOrgUnitMutationError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
__action: "invalidate" as const,
|
||||
scope: ["orgUnit"],
|
||||
success: true,
|
||||
message: `Created org unit: ${orgUnit.name}`,
|
||||
orgUnitId: orgUnit.id,
|
||||
orgUnit,
|
||||
};
|
||||
},
|
||||
|
||||
async update_org_unit(params: {
|
||||
id: string;
|
||||
name?: string;
|
||||
shortName?: string | null;
|
||||
sortOrder?: number;
|
||||
isActive?: boolean;
|
||||
parentId?: string | null;
|
||||
}, ctx: ToolContext) {
|
||||
const caller = deps.createOrgUnitCaller(deps.createScopedCallerContext(ctx));
|
||||
const data = UpdateOrgUnitSchema.parse({
|
||||
...(params.name !== undefined ? { name: params.name } : {}),
|
||||
...(params.shortName !== undefined ? { shortName: params.shortName } : {}),
|
||||
...(params.sortOrder !== undefined ? { sortOrder: params.sortOrder } : {}),
|
||||
...(params.isActive !== undefined ? { isActive: params.isActive } : {}),
|
||||
...(params.parentId !== undefined ? { parentId: params.parentId } : {}),
|
||||
});
|
||||
if (Object.keys(data).length === 0) {
|
||||
return { error: "No fields to update" };
|
||||
}
|
||||
|
||||
let orgUnit;
|
||||
try {
|
||||
orgUnit = await caller.update({ id: params.id, data });
|
||||
} catch (error) {
|
||||
const mapped = deps.toAssistantOrgUnitMutationError(error);
|
||||
if (mapped) {
|
||||
return mapped;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
return {
|
||||
__action: "invalidate" as const,
|
||||
scope: ["orgUnit"],
|
||||
success: true,
|
||||
message: `Updated org unit: ${orgUnit.name}`,
|
||||
orgUnitId: orgUnit.id,
|
||||
orgUnit,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user