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
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 },
|
|
};
|
|
}
|
|
},
|
|
};
|
|
|