import * as fs from 'fs/promises'; import * as path from 'path'; import type { ToolResult } from '../../types/index.js'; import type { ToolWithMetadata } from '../types.js'; import { loadDescription } from '../load_description.js'; import { getPermissionManager } from '../../permission/index.js'; function formatSize(bytes: number): string { const units = ['B', 'KB', 'MB', 'GB', 'TB']; let unitIndex = 0; let size = bytes; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(2)} ${units[unitIndex]}`; } function formatPermissions(mode: number): string { const types: Record = { 0o140000: 'socket', 0o120000: 'symbolic link', 0o100000: 'regular file', 0o060000: 'block device', 0o040000: 'directory', 0o020000: 'character device', 0o010000: 'FIFO', }; const fileType = Object.entries(types).find(([mask]) => (mode & 0o170000) === Number(mask)); const perms = [ (mode & 0o400) ? 'r' : '-', (mode & 0o200) ? 'w' : '-', (mode & 0o100) ? 'x' : '-', (mode & 0o040) ? 'r' : '-', (mode & 0o020) ? 'w' : '-', (mode & 0o010) ? 'x' : '-', (mode & 0o004) ? 'r' : '-', (mode & 0o002) ? 'w' : '-', (mode & 0o001) ? 'x' : '-', ].join(''); return `${fileType?.[1] || 'unknown'} (${perms})`; } export const getFileInfoTool: ToolWithMetadata = { name: 'get_file_info', description: loadDescription('get_file_info'), metadata: { name: 'get_file_info', category: 'filesystem', description: '获取文件元信息', keywords: ['file', 'info', 'stat', 'size', 'permission', 'metadata', '文件', '信息', '大小', '权限', '属性'], deferLoading: true, }, parameters: { path: { type: 'string', description: '文件或目录的路径', required: true, }, }, execute: async (params: Record): Promise => { const filePath = params.path as string; const cwd = process.cwd(); const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(cwd, filePath); // 权限检查 const permissionManager = getPermissionManager(); const permResult = await permissionManager.checkFilePermission({ operation: 'info', path: absolutePath, workdir: cwd, }); if (!permResult.allowed) { if (permResult.needsConfirmation) { return { success: false, output: '', error: `需要用户确认: 获取文件信息 ${absolutePath}\n原因: ${permResult.reason || '需要权限确认'}`, }; } return { success: false, output: '', error: `权限被拒绝: ${permResult.reason || '不允许获取此文件信息'}`, }; } try { const stats = await fs.stat(absolutePath); const info = [ `路径: ${absolutePath}`, `类型: ${stats.isDirectory() ? '目录' : stats.isFile() ? '文件' : stats.isSymbolicLink() ? '符号链接' : '其他'}`, `大小: ${formatSize(stats.size)}`, `权限: ${formatPermissions(stats.mode)}`, `创建时间: ${stats.birthtime.toLocaleString()}`, `修改时间: ${stats.mtime.toLocaleString()}`, `访问时间: ${stats.atime.toLocaleString()}`, `inode: ${stats.ino}`, `硬链接数: ${stats.nlink}`, ]; // 如果是符号链接,显示目标 if (stats.isSymbolicLink()) { try { const target = await fs.readlink(absolutePath); info.push(`链接目标: ${target}`); } catch { // 忽略 } } // 如果是目录,统计子项数量 if (stats.isDirectory()) { try { const entries = await fs.readdir(absolutePath); info.push(`子项数量: ${entries.length}`); } catch { // 忽略 } } return { success: true, output: info.join('\n'), }; } catch (error) { return { success: false, output: '', error: error instanceof Error ? error.message : String(error), }; } }, };