319 lines
9.7 KiB
TypeScript
319 lines
9.7 KiB
TypeScript
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,
|
|
};
|
|
},
|
|
};
|
|
}
|