base-tool.ts•4.69 kB
import { Config } from '../config/types.js';
import { ToolResponse } from './types.js';
import * as path from 'path';
/**
* 工具错误类
*/
export class ToolError extends Error {
constructor(
message: string,
public readonly toolName: string,
public readonly cause?: Error
) {
super(message);
this.name = 'ToolError';
}
}
/**
* 工具基类
* 提供通用的工具功能
*/
export abstract class BaseTool {
private toolName: string;
private disposed: boolean = false;
constructor(
protected readonly config: Config,
name: string
) {
this.toolName = name;
}
/**
* 获取工具名称
*/
getName(): string {
return this.toolName;
}
/**
* 包装工具错误
*/
protected wrapError(message: string, error: unknown): ToolError {
if (error instanceof ToolError) {
return error;
}
if (error instanceof Error) {
return new ToolError(message, this.toolName, error);
}
return new ToolError(`${message}: ${String(error)}`, this.toolName);
}
/**
* 解析为绝对路径
*/
protected resolveAbsolute(inputPath: string): string {
if (!inputPath) {
throw new Error('路径不能为空');
}
// 如果已经是绝对路径,直接规范化
if (path.isAbsolute(inputPath)) {
return path.normalize(inputPath);
}
// 使用当前工作目录解析相对路径
return path.normalize(
path.join(
process.cwd(),
inputPath
)
);
}
/**
* 解析为相对路径
*/
protected resolveRelative(inputPath: string, base?: string): string {
if (!inputPath) {
throw new Error('路径不能为空');
}
const absPath = this.resolveAbsolute(inputPath);
const basePath = base ? this.resolveAbsolute(base) : process.cwd();
return path.relative(basePath, absPath);
}
/**
* 解析工具输入中的路径参数
*/
protected resolvePaths<T extends Record<string, unknown>>(args: T): T {
const result = { ...args } as T;
for (const [key, value] of Object.entries(args)) {
// 处理路径相关的属性
if (typeof value === 'string' && (
key.toLowerCase().includes('path') ||
key.toLowerCase().includes('dir') ||
key.toLowerCase().includes('file')
)) {
try {
(result as Record<string, unknown>)[key] = this.resolveAbsolute(value);
} catch (err) {
const error = err instanceof Error ? err : new Error(String(err));
throw new Error(`解析路径失败 ${key}: ${value} - ${error.message}`);
}
}
// 递归处理嵌套对象
else if (value && typeof value === 'object' && !Array.isArray(value)) {
(result as Record<string, unknown>)[key] = this.resolvePaths(value as Record<string, unknown>);
}
}
return result;
}
/**
* 创建成功响应
*/
protected createSuccessResponse(content: unknown): ToolResponse {
return {
content: [{
type: 'text',
text: typeof content === 'string' ? content : JSON.stringify(content, null, 2)
}]
};
}
/**
* 创建错误响应
*/
protected createErrorResponse(error: unknown): ToolResponse {
const toolError = this.wrapError('工具执行失败', error);
return {
content: [{
type: 'text',
text: `${this.toolName} - 错误: ${toolError.message}${
toolError.cause ? `\n原因: ${toolError.cause.message}` : ''
}`
}],
isError: true
};
}
/**
* 工具执行前的通用检查
*/
protected async preExecuteChecks(): Promise<void> {
if (this.disposed) {
throw new ToolError('工具已被销毁', this.toolName);
}
}
/**
* 工具执行后的通用清理
*/
protected async postExecuteCleanup(): Promise<void> {
// 子类可以覆盖此方法添加额外的清理工作
}
/**
* 通用的错误处理包装器
*/
protected async executeWithErrorHandling<T>(
action: () => Promise<T>
): Promise<ToolResponse> {
try {
await this.preExecuteChecks();
const result = await action();
await this.postExecuteCleanup();
return this.createSuccessResponse(result);
} catch (error) {
// 确保在错误情况下也执行清理
try {
await this.postExecuteCleanup();
} catch (cleanupError) {
console.error(`[${this.toolName}] 清理失败:`, cleanupError);
}
return this.createErrorResponse(error);
}
}
/**
* 销毁工具实例
*/
dispose(): void {
if (this.disposed) {
return;
}
this.disposed = true;
}
}