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.
 
 
 
 

127 lines
3.9 KiB

import { z } from "zod";
import { createHash } from "node:crypto";
import type { JSONSchema7 } from "json-schema";
import type { ToolExecutor, ToolContext, ToolResult } from "../../registry";
import type { HashToolConfig } from "./config";
export const hashInputSchema = z.object({
operation: z
.enum(["hash", "compare"])
.describe("操作类型: hash=计算哈希, compare=比较哈希值"),
text: z
.string()
.min(1)
.max(100000)
.describe("要计算哈希的文本内容"),
algorithm: z
.enum(["md5", "sha1", "sha256", "sha512"])
.optional()
.describe("哈希算法,不传则使用配置默认值"),
expectedHash: z
.string()
.optional()
.describe("compare 操作时,要对比的期望哈希值"),
});
type HashInput = z.infer<typeof hashInputSchema>;
function computeHash(text: string, algorithm: string, uppercase: boolean): string {
const hash = createHash(algorithm).update(text, "utf8").digest("hex");
return uppercase ? hash.toUpperCase() : hash;
}
export const hashExecutor: ToolExecutor<HashToolConfig> = {
buildInputSchema(_config: HashToolConfig): JSONSchema7 {
return {
type: "object",
properties: {
operation: {
type: "string",
enum: ["hash", "compare"],
description: "操作类型: hash=计算哈希, compare=比较哈希值",
},
text: { type: "string", description: "要计算哈希的文本内容" },
algorithm: {
type: "string",
enum: ["md5", "sha1", "sha256", "sha512"],
description: "哈希算法",
},
expectedHash: { type: "string", description: "compare 操作的期望哈希值" },
},
required: ["operation", "text"],
};
},
buildDescription(config: HashToolConfig): string {
return `哈希计算工具。支持 md5、sha1、sha256、sha512 算法。可计算文本哈希值或对比哈希是否匹配。默认算法: ${config.defaultAlgorithm},输出${config.uppercase ? "大写" : "小写"}。`;
},
async execute(
input: unknown,
config: HashToolConfig,
_ctx: ToolContext,
): Promise<ToolResult> {
const start = Date.now();
const parsed = hashInputSchema.safeParse(input);
if (!parsed.success) {
return {
success: false,
data: null,
error: `输入参数校验失败: ${parsed.error.message}`,
metadata: { durationMs: Date.now() - start },
};
}
const inp = parsed.data as HashInput;
const algorithm = inp.algorithm ?? config.defaultAlgorithm;
try {
switch (inp.operation) {
case "hash": {
const hashValue = computeHash(inp.text, algorithm, config.uppercase);
return {
success: true,
data: {
algorithm,
inputLength: inp.text.length,
hash: hashValue,
},
metadata: { durationMs: Date.now() - start },
};
}
case "compare": {
if (!inp.expectedHash) {
throw new Error("compare 操作需要 expectedHash 参数");
}
const actualHash = computeHash(inp.text, algorithm, config.uppercase);
const expected = config.uppercase
? inp.expectedHash.toUpperCase()
: inp.expectedHash.toLowerCase();
const matched = actualHash === expected;
return {
success: true,
data: {
algorithm,
matched,
actualHash,
expectedHash: expected,
},
metadata: { durationMs: Date.now() - start },
};
}
default:
throw new Error(`未知操作: ${inp.operation}`);
}
} catch (e) {
return {
success: false,
data: null,
error: e instanceof Error ? e.message : String(e),
metadata: { durationMs: Date.now() - start },
};
}
},
};