You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

191 lines
5.8 KiB

import { z } from "zod";
import { randomBytes, randomUUID } from "node:crypto";
import type { JSONSchema7 } from "json-schema";
import type { ToolExecutor, ToolContext, ToolResult } from "../../registry";
import type { UuidToolConfig } from "./config";
export const uuidInputSchema = z.object({
operation: z
.enum(["uuid", "random-string", "random-int"])
.describe("操作类型: uuid=生成UUID, random-string=随机字符串, random-int=随机整数"),
version: z
.enum(["v4", "v7"])
.optional()
.describe("UUID 版本 (uuid 操作使用)"),
length: z
.number()
.int()
.positive()
.max(256)
.optional()
.describe("随机字符串长度 (random-string 操作使用)"),
encoding: z
.enum(["hex", "base64", "base64url", "alphanumeric"])
.optional()
.describe("随机字符串编码方式 (random-string 操作使用)"),
min: z
.number()
.int()
.optional()
.describe("最小值 (random-int 操作使用)"),
max: z
.number()
.int()
.optional()
.describe("最大值 (random-int 操作使用)"),
count: z
.number()
.int()
.positive()
.max(100)
.optional()
.describe("生成数量,默认 1"),
});
type UuidInput = z.infer<typeof uuidInputSchema>;
const ALPHANUMERIC_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
function generateUuidV7(): string {
const timestamp = Date.now();
const timestampHex = timestamp.toString(16).padStart(12, "0");
const random = randomBytes(10);
random[0] = (random[0]! & 0x0f) | 0x70;
random[2] = (random[2]! & 0x3f) | 0x80;
const randomHex = random.toString("hex");
return `${timestampHex.slice(0, 8)}-${timestampHex.slice(8, 12)}-${randomHex.slice(0, 4)}-${randomHex.slice(4, 8)}-${randomHex.slice(8, 20)}`;
}
function generateRandomString(length: number, encoding: string): string {
switch (encoding) {
case "hex":
return randomBytes(Math.ceil(length / 2)).toString("hex").slice(0, length);
case "base64":
return randomBytes(Math.ceil(length * 0.75)).toString("base64").slice(0, length);
case "base64url":
return randomBytes(Math.ceil(length * 0.75))
.toString("base64url")
.slice(0, length);
case "alphanumeric": {
const bytes = randomBytes(length);
let result = "";
for (let i = 0; i < length; i++) {
result += ALPHANUMERIC_CHARS[bytes[i]! % ALPHANUMERIC_CHARS.length];
}
return result;
}
default:
throw new Error(`未知编码: ${encoding}`);
}
}
function generateRandomInt(min: number, max: number): number {
if (min >= max) throw new Error("min 必须小于 max");
const range = max - min;
const bytes = randomBytes(8);
const randomValue = bytes.readBigUInt64BE();
return min + Number(randomValue % BigInt(range));
}
export const uuidExecutor: ToolExecutor<UuidToolConfig> = {
buildInputSchema(_config: UuidToolConfig): JSONSchema7 {
return {
type: "object",
properties: {
operation: {
type: "string",
enum: ["uuid", "random-string", "random-int"],
description: "操作类型",
},
version: { type: "string", enum: ["v4", "v7"], description: "UUID 版本" },
length: { type: "number", description: "随机字符串长度" },
encoding: {
type: "string",
enum: ["hex", "base64", "base64url", "alphanumeric"],
description: "编码方式",
},
min: { type: "number", description: "最小值" },
max: { type: "number", description: "最大值" },
count: { type: "number", description: "生成数量" },
},
required: ["operation"],
};
},
buildDescription(config: UuidToolConfig): string {
return `生成随机标识符。支持 UUID v4/v7、随机字符串(hex/base64/base64url/alphanumeric)、随机整数。默认 UUID 版本: ${config.defaultVersion},默认字符串长度: ${config.defaultLength}`;
},
async execute(
input: unknown,
config: UuidToolConfig,
_ctx: ToolContext,
): Promise<ToolResult> {
const start = Date.now();
const parsed = uuidInputSchema.safeParse(input);
if (!parsed.success) {
return {
success: false,
data: null,
error: `输入参数校验失败: ${parsed.error.message}`,
metadata: { durationMs: Date.now() - start },
};
}
const inp = parsed.data as UuidInput;
const count = inp.count ?? 1;
try {
const results: unknown[] = [];
for (let i = 0; i < count; i++) {
switch (inp.operation) {
case "uuid": {
const version = inp.version ?? config.defaultVersion;
if (version === "v4") {
results.push(randomUUID());
} else {
results.push(generateUuidV7());
}
break;
}
case "random-string": {
const length = inp.length ?? config.defaultLength;
const encoding = inp.encoding ?? "hex";
results.push(generateRandomString(length, encoding));
break;
}
case "random-int": {
const min = inp.min ?? 0;
const max = inp.max ?? 100;
results.push(generateRandomInt(min, max));
break;
}
default:
throw new Error(`未知操作: ${inp.operation}`);
}
}
return {
success: true,
data: {
operation: inp.operation,
count,
results: count === 1 ? results[0] : results,
},
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 },
};
}
},
};