Files
api_health/agent/tools/system/fs_info.js
2026-05-30 16:41:26 +08:00

258 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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({})
}
)