refactor(api): extract assistant self-service slice
This commit is contained in:
@@ -31,12 +31,13 @@
|
|||||||
- the adjacent chargeability/computation read helpers now live in their own domain module, keeping the advanced financial transparency read models out of the monolithic router without changing the assistant contract
|
- the adjacent chargeability/computation read helpers now live in their own domain module, keeping the advanced financial transparency read models out of the monolithic router without changing the assistant contract
|
||||||
- the neighboring country and metro-city admin mutations now live in their own domain module, keeping more settings-side CRUD wiring out of the monolithic router without changing the assistant contract
|
- the neighboring country and metro-city admin mutations now live in their own domain module, keeping more settings-side CRUD wiring out of the monolithic router without changing the assistant contract
|
||||||
- the adjacent management-level, utilization, calculation-rule, effort-rule, and experience-multiplier read helpers now live in their own domain module, further shrinking the monolithic assistant router without changing the assistant contract
|
- the adjacent management-level, utilization, calculation-rule, effort-rule, and experience-multiplier read helpers now live in their own domain module, further shrinking the monolithic assistant router without changing the assistant contract
|
||||||
|
- the authenticated user self-service assistant helpers now live in their own domain module, covering assignable users, dashboard preferences, favorites, column preferences, and MFA self-service without changing the assistant contract
|
||||||
|
|
||||||
## Next Up
|
## Next Up
|
||||||
|
|
||||||
Pin the next structural cleanup on the API side:
|
Pin the next structural cleanup on the API side:
|
||||||
continue splitting `packages/api/src/router/assistant-tools.ts` into domain-oriented tool modules without changing the public tool contract.
|
continue splitting `packages/api/src/router/assistant-tools.ts` into domain-oriented tool modules without changing the public tool contract.
|
||||||
The next clean slice should stay adjacent to the extracted domains and target one cohesive block such as the authenticated user/self-service assistant tools, or the remaining estimate and project admin helper clusters that are still embedded in the monolithic router.
|
The next clean slice should stay adjacent to the extracted domains and target one cohesive block such as the remaining admin-user helper cluster around `list_users` and user mutations, or the embedded estimate and project admin helper clusters that are still in the monolithic router.
|
||||||
|
|
||||||
## Remaining Major Themes
|
## Remaining Major Themes
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,10 @@ import {
|
|||||||
countryMetroAdminToolDefinitions,
|
countryMetroAdminToolDefinitions,
|
||||||
createCountryMetroAdminExecutors,
|
createCountryMetroAdminExecutors,
|
||||||
} from "./assistant-tools/country-metro-admin.js";
|
} from "./assistant-tools/country-metro-admin.js";
|
||||||
|
import {
|
||||||
|
createUserSelfServiceExecutors,
|
||||||
|
userSelfServiceToolDefinitions,
|
||||||
|
} from "./assistant-tools/user-self-service.js";
|
||||||
import type { ToolContext, ToolDef, ToolExecutor } from "./assistant-tools/shared.js";
|
import type { ToolContext, ToolDef, ToolExecutor } from "./assistant-tools/shared.js";
|
||||||
import { getCommentToolEntityDescription, getCommentToolScopeSentence } from "../lib/comment-entity-registry.js";
|
import { getCommentToolEntityDescription, getCommentToolScopeSentence } from "../lib/comment-entity-registry.js";
|
||||||
|
|
||||||
@@ -2794,152 +2798,7 @@ export const TOOL_DEFINITIONS: ToolDef[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
...userSelfServiceToolDefinitions,
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "list_assignable_users",
|
|
||||||
description: "List lightweight users available for assignment workflows. Manager or admin role required.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "get_current_user",
|
|
||||||
description: "Get the authenticated user's own profile, role, and permission overrides.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "get_dashboard_layout",
|
|
||||||
description: "Get the authenticated user's saved dashboard widget layout and last update timestamp.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "save_dashboard_layout",
|
|
||||||
description: "Save the authenticated user's dashboard layout. Always confirm first.",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
layout: {
|
|
||||||
type: "array",
|
|
||||||
description: "Dashboard layout items as stored by the user router.",
|
|
||||||
items: { type: "object" },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["layout"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "get_favorite_project_ids",
|
|
||||||
description: "Get the authenticated user's favorite project IDs.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "toggle_favorite_project",
|
|
||||||
description: "Add or remove a project from the authenticated user's favorites. Always confirm first.",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
projectId: { type: "string", description: "Project ID." },
|
|
||||||
},
|
|
||||||
required: ["projectId"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "get_column_preferences",
|
|
||||||
description: "Get the authenticated user's saved table column preferences for all supported views.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "set_column_preferences",
|
|
||||||
description: "Update the authenticated user's table column preferences for one view. Always confirm first.",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
view: {
|
|
||||||
type: "string",
|
|
||||||
enum: ["resources", "projects", "allocations", "vacations", "roles", "users", "blueprints"],
|
|
||||||
description: "View key to update.",
|
|
||||||
},
|
|
||||||
visible: {
|
|
||||||
type: "array",
|
|
||||||
items: { type: "string" },
|
|
||||||
description: "Visible column IDs.",
|
|
||||||
},
|
|
||||||
sort: {
|
|
||||||
type: ["object", "null"],
|
|
||||||
properties: {
|
|
||||||
field: { type: "string" },
|
|
||||||
dir: { type: "string", enum: ["asc", "desc"] },
|
|
||||||
},
|
|
||||||
description: "Sort state. Use null to clear it.",
|
|
||||||
},
|
|
||||||
rowOrder: {
|
|
||||||
type: ["array", "null"],
|
|
||||||
items: { type: "string" },
|
|
||||||
description: "Optional row order. Use null to clear it.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
required: ["view"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "generate_totp_secret",
|
|
||||||
description: "Generate a new MFA TOTP secret and provisioning URI for the authenticated user. Always confirm first. The secret is sensitive.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "verify_and_enable_totp",
|
|
||||||
description: "Verify a 6-digit MFA TOTP token and enable MFA for the authenticated user. Always confirm first.",
|
|
||||||
parameters: {
|
|
||||||
type: "object",
|
|
||||||
properties: {
|
|
||||||
token: { type: "string", description: "6-digit TOTP token." },
|
|
||||||
},
|
|
||||||
required: ["token"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "get_mfa_status",
|
|
||||||
description: "Get the authenticated user's MFA status.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "function",
|
|
||||||
function: {
|
|
||||||
name: "get_active_user_count",
|
|
||||||
description: "Get the number of users active in the last five minutes. Admin role required.",
|
|
||||||
parameters: { type: "object", properties: {} },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: "function",
|
type: "function",
|
||||||
function: {
|
function: {
|
||||||
@@ -5482,120 +5341,11 @@ const executors = {
|
|||||||
const users = await caller.list();
|
const users = await caller.list();
|
||||||
return users.slice(0, Math.min(params.limit ?? 50, 100));
|
return users.slice(0, Math.min(params.limit ?? 50, 100));
|
||||||
},
|
},
|
||||||
|
...createUserSelfServiceExecutors({
|
||||||
async list_assignable_users(_params: Record<string, never>, ctx: ToolContext) {
|
createUserCaller,
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
createScopedCallerContext,
|
||||||
return caller.listAssignable();
|
toAssistantTotpEnableError,
|
||||||
},
|
}),
|
||||||
|
|
||||||
async get_current_user(_params: Record<string, never>, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
return caller.me();
|
|
||||||
},
|
|
||||||
|
|
||||||
async get_dashboard_layout(_params: Record<string, never>, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
return caller.getDashboardLayout();
|
|
||||||
},
|
|
||||||
|
|
||||||
async save_dashboard_layout(params: { layout: unknown[] }, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
const result = await caller.saveDashboardLayout({ layout: params.layout });
|
|
||||||
return {
|
|
||||||
__action: "invalidate",
|
|
||||||
scope: ["dashboard"],
|
|
||||||
success: true,
|
|
||||||
...result,
|
|
||||||
message: "Saved dashboard layout.",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
async get_favorite_project_ids(_params: Record<string, never>, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
return caller.getFavoriteProjectIds();
|
|
||||||
},
|
|
||||||
|
|
||||||
async toggle_favorite_project(params: { projectId: string }, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
const result = await caller.toggleFavoriteProject({ projectId: params.projectId });
|
|
||||||
return {
|
|
||||||
__action: "invalidate",
|
|
||||||
scope: ["project"],
|
|
||||||
success: true,
|
|
||||||
...result,
|
|
||||||
message: result.added ? "Added project to favorites." : "Removed project from favorites.",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
async get_column_preferences(_params: Record<string, never>, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
return caller.getColumnPreferences();
|
|
||||||
},
|
|
||||||
|
|
||||||
async set_column_preferences(params: {
|
|
||||||
view: "resources" | "projects" | "allocations" | "vacations" | "roles" | "users" | "blueprints";
|
|
||||||
visible?: string[];
|
|
||||||
sort?: { field: string; dir: "asc" | "desc" } | null;
|
|
||||||
rowOrder?: string[] | null;
|
|
||||||
}, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
const result = await caller.setColumnPreferences({
|
|
||||||
view: params.view,
|
|
||||||
...(params.visible !== undefined ? { visible: params.visible } : {}),
|
|
||||||
...(params.sort !== undefined ? { sort: params.sort } : {}),
|
|
||||||
...(params.rowOrder !== undefined ? { rowOrder: params.rowOrder } : {}),
|
|
||||||
});
|
|
||||||
return {
|
|
||||||
__action: "invalidate",
|
|
||||||
scope: ["user"],
|
|
||||||
success: true,
|
|
||||||
...result,
|
|
||||||
message: `Updated column preferences for ${params.view}.`,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
async generate_totp_secret(_params: Record<string, never>, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
const result = await caller.generateTotpSecret();
|
|
||||||
return {
|
|
||||||
__action: "invalidate",
|
|
||||||
scope: ["user"],
|
|
||||||
success: true,
|
|
||||||
...result,
|
|
||||||
message: "Generated a new MFA TOTP secret.",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
async verify_and_enable_totp(params: { token: string }, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
let result;
|
|
||||||
try {
|
|
||||||
result = await caller.verifyAndEnableTotp({ token: params.token });
|
|
||||||
} catch (error) {
|
|
||||||
const mapped = toAssistantTotpEnableError(error);
|
|
||||||
if (mapped) {
|
|
||||||
return mapped;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
__action: "invalidate",
|
|
||||||
scope: ["user"],
|
|
||||||
success: true,
|
|
||||||
...result,
|
|
||||||
message: "Enabled MFA TOTP.",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
async get_mfa_status(_params: Record<string, never>, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
return caller.getMfaStatus();
|
|
||||||
},
|
|
||||||
|
|
||||||
async get_active_user_count(_params: Record<string, never>, ctx: ToolContext) {
|
|
||||||
const caller = createUserCaller(createScopedCallerContext(ctx));
|
|
||||||
return caller.activeCount();
|
|
||||||
},
|
|
||||||
|
|
||||||
async create_user(params: {
|
async create_user(params: {
|
||||||
email: string;
|
email: string;
|
||||||
|
|||||||
@@ -0,0 +1,310 @@
|
|||||||
|
import type { TRPCContext } from "../../trpc.js";
|
||||||
|
import type { ToolContext, ToolDef, ToolExecutor } from "./shared.js";
|
||||||
|
|
||||||
|
type AssistantToolErrorResult = { error: string };
|
||||||
|
|
||||||
|
type ColumnPreferenceView =
|
||||||
|
| "resources"
|
||||||
|
| "projects"
|
||||||
|
| "allocations"
|
||||||
|
| "vacations"
|
||||||
|
| "roles"
|
||||||
|
| "users"
|
||||||
|
| "blueprints";
|
||||||
|
|
||||||
|
type UserSelfServiceDeps = {
|
||||||
|
createUserCaller: (ctx: TRPCContext) => {
|
||||||
|
listAssignable: () => Promise<unknown>;
|
||||||
|
me: () => Promise<unknown>;
|
||||||
|
getDashboardLayout: () => Promise<unknown>;
|
||||||
|
saveDashboardLayout: (params: { layout: unknown[] }) => Promise<Record<string, unknown>>;
|
||||||
|
getFavoriteProjectIds: () => Promise<unknown>;
|
||||||
|
toggleFavoriteProject: (
|
||||||
|
params: { projectId: string },
|
||||||
|
) => Promise<Record<string, unknown> & { added: boolean }>;
|
||||||
|
getColumnPreferences: () => Promise<unknown>;
|
||||||
|
setColumnPreferences: (params: {
|
||||||
|
view: ColumnPreferenceView;
|
||||||
|
visible?: string[];
|
||||||
|
sort?: { field: string; dir: "asc" | "desc" } | null;
|
||||||
|
rowOrder?: string[] | null;
|
||||||
|
}) => Promise<Record<string, unknown>>;
|
||||||
|
generateTotpSecret: () => Promise<Record<string, unknown>>;
|
||||||
|
verifyAndEnableTotp: (params: { token: string }) => Promise<Record<string, unknown>>;
|
||||||
|
getMfaStatus: () => Promise<unknown>;
|
||||||
|
activeCount: () => Promise<unknown>;
|
||||||
|
};
|
||||||
|
createScopedCallerContext: (ctx: ToolContext) => TRPCContext;
|
||||||
|
toAssistantTotpEnableError: (
|
||||||
|
error: unknown,
|
||||||
|
) => AssistantToolErrorResult | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const userSelfServiceToolDefinitions: ToolDef[] = [
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "list_assignable_users",
|
||||||
|
description: "List lightweight users available for assignment workflows. Manager or admin role required.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "get_current_user",
|
||||||
|
description: "Get the authenticated user's own profile, role, and permission overrides.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "get_dashboard_layout",
|
||||||
|
description: "Get the authenticated user's saved dashboard widget layout and last update timestamp.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "save_dashboard_layout",
|
||||||
|
description: "Save the authenticated user's dashboard layout. Always confirm first.",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
layout: {
|
||||||
|
type: "array",
|
||||||
|
description: "Dashboard layout items as stored by the user router.",
|
||||||
|
items: { type: "object" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["layout"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "get_favorite_project_ids",
|
||||||
|
description: "Get the authenticated user's favorite project IDs.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "toggle_favorite_project",
|
||||||
|
description: "Add or remove a project from the authenticated user's favorites. Always confirm first.",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
projectId: { type: "string", description: "Project ID." },
|
||||||
|
},
|
||||||
|
required: ["projectId"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "get_column_preferences",
|
||||||
|
description: "Get the authenticated user's saved table column preferences for all supported views.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "set_column_preferences",
|
||||||
|
description: "Update the authenticated user's table column preferences for one view. Always confirm first.",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
view: {
|
||||||
|
type: "string",
|
||||||
|
enum: ["resources", "projects", "allocations", "vacations", "roles", "users", "blueprints"],
|
||||||
|
description: "View key to update.",
|
||||||
|
},
|
||||||
|
visible: {
|
||||||
|
type: "array",
|
||||||
|
items: { type: "string" },
|
||||||
|
description: "Visible column IDs.",
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
type: ["object", "null"],
|
||||||
|
properties: {
|
||||||
|
field: { type: "string" },
|
||||||
|
dir: { type: "string", enum: ["asc", "desc"] },
|
||||||
|
},
|
||||||
|
description: "Sort state. Use null to clear it.",
|
||||||
|
},
|
||||||
|
rowOrder: {
|
||||||
|
type: ["array", "null"],
|
||||||
|
items: { type: "string" },
|
||||||
|
description: "Optional row order. Use null to clear it.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["view"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "generate_totp_secret",
|
||||||
|
description: "Generate a new MFA TOTP secret and provisioning URI for the authenticated user. Always confirm first. The secret is sensitive.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "verify_and_enable_totp",
|
||||||
|
description: "Verify a 6-digit MFA TOTP token and enable MFA for the authenticated user. Always confirm first.",
|
||||||
|
parameters: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
token: { type: "string", description: "6-digit TOTP token." },
|
||||||
|
},
|
||||||
|
required: ["token"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "get_mfa_status",
|
||||||
|
description: "Get the authenticated user's MFA status.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "function",
|
||||||
|
function: {
|
||||||
|
name: "get_active_user_count",
|
||||||
|
description: "Get the number of users active in the last five minutes. Admin role required.",
|
||||||
|
parameters: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function createUserSelfServiceExecutors(
|
||||||
|
deps: UserSelfServiceDeps,
|
||||||
|
): Record<string, ToolExecutor> {
|
||||||
|
return {
|
||||||
|
async list_assignable_users(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
return caller.listAssignable();
|
||||||
|
},
|
||||||
|
|
||||||
|
async get_current_user(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
return caller.me();
|
||||||
|
},
|
||||||
|
|
||||||
|
async get_dashboard_layout(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
return caller.getDashboardLayout();
|
||||||
|
},
|
||||||
|
|
||||||
|
async save_dashboard_layout(params: { layout: unknown[] }, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
const result = await caller.saveDashboardLayout({ layout: params.layout });
|
||||||
|
return {
|
||||||
|
__action: "invalidate" as const,
|
||||||
|
scope: ["dashboard"],
|
||||||
|
success: true,
|
||||||
|
...result,
|
||||||
|
message: "Saved dashboard layout.",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async get_favorite_project_ids(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
return caller.getFavoriteProjectIds();
|
||||||
|
},
|
||||||
|
|
||||||
|
async toggle_favorite_project(params: { projectId: string }, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
const result = await caller.toggleFavoriteProject({ projectId: params.projectId });
|
||||||
|
return {
|
||||||
|
__action: "invalidate" as const,
|
||||||
|
scope: ["project"],
|
||||||
|
success: true,
|
||||||
|
...result,
|
||||||
|
message: result.added ? "Added project to favorites." : "Removed project from favorites.",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async get_column_preferences(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
return caller.getColumnPreferences();
|
||||||
|
},
|
||||||
|
|
||||||
|
async set_column_preferences(params: {
|
||||||
|
view: ColumnPreferenceView;
|
||||||
|
visible?: string[];
|
||||||
|
sort?: { field: string; dir: "asc" | "desc" } | null;
|
||||||
|
rowOrder?: string[] | null;
|
||||||
|
}, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
const result = await caller.setColumnPreferences({
|
||||||
|
view: params.view,
|
||||||
|
...(params.visible !== undefined ? { visible: params.visible } : {}),
|
||||||
|
...(params.sort !== undefined ? { sort: params.sort } : {}),
|
||||||
|
...(params.rowOrder !== undefined ? { rowOrder: params.rowOrder } : {}),
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
__action: "invalidate" as const,
|
||||||
|
scope: ["user"],
|
||||||
|
success: true,
|
||||||
|
...result,
|
||||||
|
message: `Updated column preferences for ${params.view}.`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async generate_totp_secret(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
const result = await caller.generateTotpSecret();
|
||||||
|
return {
|
||||||
|
__action: "invalidate" as const,
|
||||||
|
scope: ["user"],
|
||||||
|
success: true,
|
||||||
|
...result,
|
||||||
|
message: "Generated a new MFA TOTP secret.",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async verify_and_enable_totp(params: { token: string }, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await caller.verifyAndEnableTotp({ token: params.token });
|
||||||
|
} catch (error) {
|
||||||
|
const mapped = deps.toAssistantTotpEnableError(error);
|
||||||
|
if (mapped) {
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
__action: "invalidate" as const,
|
||||||
|
scope: ["user"],
|
||||||
|
success: true,
|
||||||
|
...result,
|
||||||
|
message: "Enabled MFA TOTP.",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
async get_mfa_status(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
return caller.getMfaStatus();
|
||||||
|
},
|
||||||
|
|
||||||
|
async get_active_user_count(_params: Record<string, never>, ctx: ToolContext) {
|
||||||
|
const caller = deps.createUserCaller(deps.createScopedCallerContext(ctx));
|
||||||
|
return caller.activeCount();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user