5 changed files with 154 additions and 3 deletions
Binary file not shown.
@ -0,0 +1,27 @@ |
|||||
|
import { z } from "zod"; |
||||
|
|
||||
|
export const userInfoConfigSchema = z.object({ |
||||
|
includeEmail: z.boolean().default(true), |
||||
|
includeAvatar: z.boolean().default(true), |
||||
|
includeRole: z.boolean().default(true), |
||||
|
includeStatus: z.boolean().default(false), |
||||
|
includeCreatedAt: z.boolean().default(false), |
||||
|
}); |
||||
|
|
||||
|
export type UserInfoToolConfig = z.infer<typeof userInfoConfigSchema>; |
||||
|
|
||||
|
export const DEFAULT_USER_INFO_CONFIG: UserInfoToolConfig = { |
||||
|
includeEmail: true, |
||||
|
includeAvatar: true, |
||||
|
includeRole: true, |
||||
|
includeStatus: false, |
||||
|
includeCreatedAt: false, |
||||
|
}; |
||||
|
|
||||
|
export function parseUserInfoConfig(raw: unknown): UserInfoToolConfig { |
||||
|
const parsed = userInfoConfigSchema.safeParse(raw); |
||||
|
if (!parsed.success) { |
||||
|
throw new Error(`Invalid user-info config: ${parsed.error.message}`); |
||||
|
} |
||||
|
return parsed.data; |
||||
|
} |
||||
@ -0,0 +1,114 @@ |
|||||
|
import { z } from "zod"; |
||||
|
import type { JSONSchema7 } from "json-schema"; |
||||
|
import { dbGlobal } from "drizzle-pkg/lib/db"; |
||||
|
import { users } from "drizzle-pkg/lib/schema/auth"; |
||||
|
import { eq } from "drizzle-orm"; |
||||
|
import type { ToolExecutor, ToolContext, ToolResult } from "../../registry"; |
||||
|
import type { UserInfoToolConfig } from "./config"; |
||||
|
|
||||
|
export const userInfoInputSchema = z.object({}).describe("无需参数,自动获取当前登录用户信息"); |
||||
|
|
||||
|
type UserInfoInput = z.infer<typeof userInfoInputSchema>; |
||||
|
|
||||
|
export const userInfoExecutor: ToolExecutor<UserInfoToolConfig> = { |
||||
|
buildInputSchema(_config: UserInfoToolConfig): JSONSchema7 { |
||||
|
return { |
||||
|
type: "object", |
||||
|
properties: {}, |
||||
|
description: "无需参数,自动根据当前会话获取用户信息", |
||||
|
}; |
||||
|
}, |
||||
|
|
||||
|
buildDescription(config: UserInfoToolConfig): string { |
||||
|
const fields: string[] = []; |
||||
|
if (config.includeEmail) fields.push("邮箱"); |
||||
|
if (config.includeAvatar) fields.push("头像"); |
||||
|
if (config.includeRole) fields.push("角色"); |
||||
|
if (config.includeStatus) fields.push("状态"); |
||||
|
if (config.includeCreatedAt) fields.push("注册时间"); |
||||
|
const fieldDesc = fields.length > 0 ? fields.join("、") : "基本信息"; |
||||
|
return `获取当前登录用户信息(含${fieldDesc})。无需传入参数,自动从会话上下文中识别当前用户。`; |
||||
|
}, |
||||
|
|
||||
|
async execute( |
||||
|
input: unknown, |
||||
|
config: UserInfoToolConfig, |
||||
|
ctx: ToolContext, |
||||
|
): Promise<ToolResult> { |
||||
|
const start = Date.now(); |
||||
|
|
||||
|
const parsed = userInfoInputSchema.safeParse(input); |
||||
|
if (!parsed.success) { |
||||
|
return { |
||||
|
success: false, |
||||
|
data: null, |
||||
|
error: `输入参数校验失败: ${parsed.error.message}`, |
||||
|
metadata: { durationMs: Date.now() - start }, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
if (ctx.userId === null || ctx.userId === undefined) { |
||||
|
return { |
||||
|
success: false, |
||||
|
data: null, |
||||
|
error: "无法识别当前用户:会话上下文中缺少 userId。该工具需要在登录会话中调用。", |
||||
|
metadata: { durationMs: Date.now() - start }, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
const rows = await dbGlobal |
||||
|
.select({ |
||||
|
id: users.id, |
||||
|
username: users.username, |
||||
|
email: users.email, |
||||
|
nickname: users.nickname, |
||||
|
avatar: users.avatar, |
||||
|
role: users.role, |
||||
|
status: users.status, |
||||
|
createdAt: users.createdAt, |
||||
|
}) |
||||
|
.from(users) |
||||
|
.where(eq(users.id, ctx.userId)) |
||||
|
.limit(1); |
||||
|
|
||||
|
const user = rows[0]; |
||||
|
if (!user) { |
||||
|
return { |
||||
|
success: false, |
||||
|
data: null, |
||||
|
error: `未找到用户 (id=${ctx.userId})`, |
||||
|
metadata: { durationMs: Date.now() - start }, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
const data: Record<string, unknown> = { |
||||
|
id: user.id, |
||||
|
username: user.username, |
||||
|
nickname: user.nickname ?? null, |
||||
|
}; |
||||
|
if (config.includeEmail) data.email = user.email ?? null; |
||||
|
if (config.includeAvatar) data.avatar = user.avatar ?? null; |
||||
|
if (config.includeRole) data.role = user.role; |
||||
|
if (config.includeStatus) data.status = user.status; |
||||
|
if (config.includeCreatedAt) { |
||||
|
data.createdAt = user.createdAt |
||||
|
? new Date(user.createdAt).toISOString() |
||||
|
: null; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
success: true, |
||||
|
data, |
||||
|
metadata: { durationMs: Date.now() - start }, |
||||
|
}; |
||||
|
} catch (e) { |
||||
|
return { |
||||
|
success: false, |
||||
|
data: null, |
||||
|
error: e instanceof Error ? e.message : String(e), |
||||
|
metadata: { durationMs: Date.now() - start }, |
||||
|
}; |
||||
|
} |
||||
|
}, |
||||
|
}; |
||||
Loading…
Reference in new issue