258 lines
10 KiB
JavaScript
258 lines
10 KiB
JavaScript
import * as z from "zod"
|
||
import { tool } from "langchain"
|
||
import os from 'os'
|
||
import { promisify } from 'util'
|
||
import { exec } from 'child_process'
|
||
|
||
const execPromise = promisify(exec)
|
||
|
||
/**
|
||
* 获取系统文件信息工具
|
||
* @returns {string} - 返回系统文件信息的 JSON 字符串,包括分区信息和各分区剩余空间
|
||
*/
|
||
export const getSystemFileInfoTool = tool(
|
||
async (input) => {
|
||
try {
|
||
const fileSystemInfo = {
|
||
platform: os.platform(),
|
||
partitions: [],
|
||
volumeInfo: [],
|
||
diskHealth: [],
|
||
inodeInfo: []
|
||
}
|
||
|
||
// 获取文件系统信息(Windows)
|
||
if (os.platform() === 'win32') {
|
||
try {
|
||
const { stdout } = await execPromise('powershell -Command "Get-WmiObject Win32_LogicalDisk | Select-Object DeviceID,Description,FileSystem,Size,FreeSpace,VolumeName | ConvertTo-Json"')
|
||
const partitionData = JSON.parse(stdout)
|
||
const partitionsArray = Array.isArray(partitionData) ? partitionData : [partitionData]
|
||
|
||
fileSystemInfo.partitions = partitionsArray.map(disk => {
|
||
const size = disk.Size ? parseInt(disk.Size) : 0
|
||
const freeSpace = disk.FreeSpace ? parseInt(disk.FreeSpace) : 0
|
||
const usedSpace = size - freeSpace
|
||
|
||
return {
|
||
device: disk.DeviceID || 'N/A',
|
||
description: disk.Description || 'N/A',
|
||
filesystem: disk.FileSystem || 'N/A',
|
||
volumeName: disk.VolumeName || 'N/A',
|
||
size: size > 0 ? `${(size / (1024 * 1024 * 1024)).toFixed(2)} GB` : 'N/A',
|
||
freeSpace: freeSpace > 0 ? `${(freeSpace / (1024 * 1024 * 1024)).toFixed(2)} GB` : 'N/A',
|
||
usedSpace: usedSpace > 0 ? `${(usedSpace / (1024 * 1024 * 1024)).toFixed(2)} GB` : 'N/A',
|
||
usePercent: size > 0 ? `${((usedSpace / size) * 100).toFixed(2)}%` : 'N/A'
|
||
}
|
||
}).filter(item => item)
|
||
|
||
// 获取卷详细信息(Windows)
|
||
try {
|
||
const { stdout } = await execPromise('powershell -Command "Get-WmiObject Win32_LogicalDisk | Select-Object VolumeName,VolumeSerialNumber,FileSystem,MaximumComponentLength,DeviceID,DriveType | ConvertTo-Json"')
|
||
const allVolumeData = JSON.parse(stdout)
|
||
const volumeArray = Array.isArray(allVolumeData) ? allVolumeData : [allVolumeData]
|
||
|
||
fileSystemInfo.volumeInfo = volumeArray.map(vol => ({
|
||
device: vol.DeviceID || 'N/A',
|
||
volumeName: vol.VolumeName || 'N/A',
|
||
serialNumber: vol.VolumeSerialNumber || 'N/A',
|
||
fileSystem: vol.FileSystem || 'N/A',
|
||
maxComponentLength: vol.MaximumComponentLength || 'N/A',
|
||
deviceID: vol.DeviceID || 'N/A',
|
||
driveType: vol.DriveType === 2 ? 'Removable' : vol.DriveType === 3 ? 'Local' : vol.DriveType === 4 ? 'Network' : vol.DriveType === 5 ? 'CD-ROM' : 'Unknown'
|
||
})).filter(item => item)
|
||
} catch (error) {
|
||
console.error('Error getting volume info:', error)
|
||
}
|
||
|
||
// 获取磁盘健康状态(Windows SMART)
|
||
try {
|
||
const { stdout } = await execPromise('powershell -Command "Get-WmiObject Win32_DiskDrive | Select-Object Model,Status,SerialNumber,Size,MediaType | ConvertTo-Json"')
|
||
const diskData = JSON.parse(stdout)
|
||
const diskArray = Array.isArray(diskData) ? diskData : [diskData]
|
||
fileSystemInfo.diskHealth = diskArray.map(disk => {
|
||
return {
|
||
model: disk.Model || 'N/A',
|
||
status: disk.Status || 'N/A',
|
||
serialNumber: disk.SerialNumber || 'N/A',
|
||
size: disk.Size ? `${(parseInt(disk.Size) / (1024 * 1024 * 1024)).toFixed(2)} GB` : 'N/A'
|
||
}
|
||
}).filter(item => item)
|
||
} catch (error) {
|
||
console.error('Error getting disk health:', error)
|
||
}
|
||
} catch (error) {
|
||
console.error('Error getting file system info:', error)
|
||
fileSystemInfo.error = 'Error getting file system info'
|
||
}
|
||
}
|
||
// 获取文件系统信息(Linux)
|
||
else if (os.platform() === 'linux') {
|
||
try {
|
||
const { stdout } = await execPromise('df -h')
|
||
fileSystemInfo.partitions = stdout.trim().split('\n').slice(1).map(line => {
|
||
const parts = line.trim().split(/\s+/)
|
||
if (parts.length >= 6) {
|
||
return {
|
||
device: parts[0],
|
||
size: parts[1],
|
||
usedSpace: parts[2],
|
||
freeSpace: parts[3],
|
||
usePercent: parts[4],
|
||
mountedOn: parts[5]
|
||
}
|
||
}
|
||
return null
|
||
}).filter(item => item)
|
||
|
||
// 获取挂载选项和文件系统类型(Linux)
|
||
try {
|
||
const { stdout } = await execPromise('mount | column -t')
|
||
const mountLines = stdout.trim().split('\n')
|
||
const mountInfo = {}
|
||
mountLines.forEach(line => {
|
||
const match = line.match(/^(.+?)\s+on\s+(.+?)\s+type\s+(.+?)\s+\((.*)\)$/)
|
||
if (match) {
|
||
mountInfo[match[2]] = {
|
||
device: match[1],
|
||
filesystem: match[3],
|
||
options: match[4]
|
||
}
|
||
}
|
||
})
|
||
fileSystemInfo.mountInfo = mountInfo
|
||
} catch (error) {
|
||
console.error('Error getting mount info:', error)
|
||
}
|
||
|
||
// 获取inode信息(Linux)
|
||
try {
|
||
const { stdout } = await execPromise('df -i')
|
||
fileSystemInfo.inodeInfo = stdout.trim().split('\n').slice(1).map(line => {
|
||
const parts = line.trim().split(/\s+/)
|
||
if (parts.length >= 6) {
|
||
return {
|
||
device: parts[0],
|
||
totalInodes: parts[1],
|
||
usedInodes: parts[2],
|
||
freeInodes: parts[3],
|
||
usePercent: parts[4],
|
||
mountedOn: parts[5]
|
||
}
|
||
}
|
||
return null
|
||
}).filter(item => item)
|
||
} catch (error) {
|
||
console.error('Error getting inode info:', error)
|
||
}
|
||
|
||
// 获取磁盘健康状态(Linux SMART)
|
||
try {
|
||
const { stdout } = await execPromise('smartctl --health --json /dev/sda 2>/dev/null || echo "{}"')
|
||
if (stdout && stdout.includes('smart_status')) {
|
||
const healthData = JSON.parse(stdout)
|
||
fileSystemInfo.diskHealth.push({
|
||
device: '/dev/sda',
|
||
status: healthData.smart_status?.passed ? 'OK' : 'Failed',
|
||
powerOnHours: healthData.power_on_time?.hours || 'N/A',
|
||
temperature: healthData.temperature?.current || 'N/A'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
console.log('SMART info not available or requires root')
|
||
}
|
||
} catch (error) {
|
||
console.error('Error getting file system info:', error)
|
||
fileSystemInfo.error = 'Error getting file system info'
|
||
}
|
||
}
|
||
// 获取文件系统信息(macOS)
|
||
else if (os.platform() === 'darwin') {
|
||
try {
|
||
const { stdout } = await execPromise('df -h')
|
||
fileSystemInfo.partitions = stdout.trim().split('\n').slice(1).map(line => {
|
||
const parts = line.trim().split(/\s+/)
|
||
if (parts.length >= 6) {
|
||
return {
|
||
device: parts[0],
|
||
size: parts[1],
|
||
usedSpace: parts[2],
|
||
freeSpace: parts[3],
|
||
usePercent: parts[4],
|
||
mountedOn: parts[5]
|
||
}
|
||
}
|
||
return null
|
||
}).filter(item => item)
|
||
|
||
// 获取卷详细信息(macOS)
|
||
try {
|
||
const diskInfo = await execPromise('diskutil info -all')
|
||
const diskLines = diskInfo.stdout.trim().split('\n\n')
|
||
diskLines.forEach(disk => {
|
||
const volMatch = disk.match(/Volume Name:\s+(.+)/)
|
||
const devMatch = disk.match(/Device Node:\s+(.+)/)
|
||
const fsMatch = disk.match(/File System Personality:\s+(.+)/)
|
||
const serialMatch = disk.match(/Disk UUID:\s+(.+)/)
|
||
|
||
if (devMatch) {
|
||
fileSystemInfo.volumeInfo.push({
|
||
device: devMatch[1],
|
||
volumeName: volMatch ? volMatch[1] : 'N/A',
|
||
filesystem: fsMatch ? fsMatch[1] : 'N/A',
|
||
serialNumber: serialMatch ? serialMatch[1] : 'N/A'
|
||
})
|
||
}
|
||
})
|
||
} catch (error) {
|
||
console.error('Error getting volume info:', error)
|
||
}
|
||
|
||
// 获取inode信息(macOS)
|
||
try {
|
||
const { stdout } = await execPromise('df -i')
|
||
fileSystemInfo.inodeInfo = stdout.trim().split('\n').slice(1).map(line => {
|
||
const parts = line.trim().split(/\s+/)
|
||
if (parts.length >= 6) {
|
||
return {
|
||
device: parts[0],
|
||
totalInodes: parts[1],
|
||
usedInodes: parts[2],
|
||
freeInodes: parts[3],
|
||
usePercent: parts[4],
|
||
mountedOn: parts[5]
|
||
}
|
||
}
|
||
return null
|
||
}).filter(item => item)
|
||
} catch (error) {
|
||
console.error('Error getting inode info:', error)
|
||
}
|
||
|
||
// 获取磁盘健康状态(macOS)
|
||
try {
|
||
const { stdout } = await execPromise('diskutil smartStatus -all 2>/dev/null || echo "N/A"')
|
||
if (stdout && stdout !== 'N/A') {
|
||
fileSystemInfo.diskHealth = stdout.trim()
|
||
}
|
||
} catch (error) {
|
||
console.log('SMART status not available')
|
||
}
|
||
} catch (error) {
|
||
console.error('Error getting file system info:', error)
|
||
fileSystemInfo.error = 'Error getting file system info'
|
||
}
|
||
}
|
||
|
||
return JSON.stringify(fileSystemInfo, null, 2)
|
||
} catch (error) {
|
||
console.error('Error getting file system info:', error)
|
||
return JSON.stringify({ error: error.message }, null, 2)
|
||
}
|
||
},
|
||
{
|
||
name: "get_system_file_info",
|
||
description: `获取系统硬盘分区和文件系统信息,包括分区信息、卷标、序列号、磁盘健康状态、inode信息等`,
|
||
schema: z.object({})
|
||
}
|
||
)
|