完善了agent
This commit is contained in:
82
agent/tools/system/envs.js
Normal file
82
agent/tools/system/envs.js
Normal file
@@ -0,0 +1,82 @@
|
||||
// LangChain 环境变量工具
|
||||
|
||||
import { tool } from '@langchain/core/tools';
|
||||
import { z } from 'zod';
|
||||
|
||||
/**
|
||||
* 获取环境变量工具
|
||||
* @param {Object} input - 输入参数
|
||||
* @param {string} [input.name] - 环境变量名,不指定则返回所有
|
||||
* @returns {string} - 返回环境变量值或所有环境变量的JSON字符串
|
||||
*/
|
||||
export const getEnvTool = tool(
|
||||
async (input) => {
|
||||
const { name } = input || {};
|
||||
|
||||
if (name) {
|
||||
const value = process.env[name];
|
||||
if (value === undefined) {
|
||||
return JSON.stringify({ error: `Environment variable '${name}' not found` });
|
||||
}
|
||||
return JSON.stringify({ name, value });
|
||||
}
|
||||
|
||||
// 返回所有环境变量(过滤敏感信息)
|
||||
const env = { ...process.env };
|
||||
const sensitiveKeys = ['API_KEY', 'SECRET', 'PASSWORD', 'TOKEN', 'CREDENTIAL'];
|
||||
|
||||
for (const key of Object.keys(env)) {
|
||||
const upperKey = key.toUpperCase();
|
||||
if (sensitiveKeys.some(s => upperKey.includes(s))) {
|
||||
env[key] = '***REDACTED***';
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify({ env });
|
||||
},
|
||||
{
|
||||
name: 'env_get',
|
||||
description: '获取环境变量的值。如果不指定变量名,则返回所有环境变量。',
|
||||
schema: z.object({
|
||||
name: z.string().optional().describe('环境变量名,不指定则返回所有')
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
/**
|
||||
* 设置环境变量工具
|
||||
* @param {Object} input - 输入参数
|
||||
* @param {string} input.name - 环境变量名
|
||||
* @param {string} input.value - 环境变量值
|
||||
* @returns {string} - 返回设置结果的JSON字符串
|
||||
*/
|
||||
export const setEnvTool = tool(
|
||||
async (input) => {
|
||||
const { name, value } = input || {};
|
||||
|
||||
if (!name) {
|
||||
return JSON.stringify({ error: 'Environment variable name is required' });
|
||||
}
|
||||
|
||||
if (value === undefined) {
|
||||
return JSON.stringify({ error: 'Environment variable value is required' });
|
||||
}
|
||||
|
||||
try {
|
||||
process.env[name] = value;
|
||||
return JSON.stringify({ success: true, name, value });
|
||||
} catch (error) {
|
||||
return JSON.stringify({ error: `Failed to set environment variable: ${error.message}` });
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'env_set',
|
||||
description: '设置环境变量的值。',
|
||||
schema: z.object({
|
||||
name: z.string().describe('环境变量名'),
|
||||
value: z.string().describe('环境变量值')
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
export default getEnvTool;
|
||||
257
agent/tools/system/fs_info.js
Normal file
257
agent/tools/system/fs_info.js
Normal file
@@ -0,0 +1,257 @@
|
||||
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({})
|
||||
}
|
||||
)
|
||||
112
agent/tools/system/hardware_info.js
Normal file
112
agent/tools/system/hardware_info.js
Normal file
@@ -0,0 +1,112 @@
|
||||
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 getHardwareInfoTool = tool(
|
||||
async (input) => {
|
||||
try {
|
||||
const hardwareInfo = {
|
||||
cpu: {
|
||||
cores: os.cpus().length,
|
||||
model: os.cpus()[0].model,
|
||||
speed: os.cpus()[0].speed
|
||||
},
|
||||
memory: {
|
||||
total: `${(os.totalmem() / (1024 ** 3)).toFixed(2)} GB`,
|
||||
free: `${(os.freemem() / (1024 ** 3)).toFixed(2)} GB`
|
||||
},
|
||||
disks: [],
|
||||
gpu: []
|
||||
}
|
||||
|
||||
// 获取硬盘信息(Windows)
|
||||
if (os.platform() === 'win32') {
|
||||
try {
|
||||
const { stdout } = await execPromise('powershell -Command "Get-WmiObject Win32_DiskDrive | Select-Object Model,InterfaceType,Size | ConvertTo-Json"')
|
||||
const diskData = JSON.parse(stdout)
|
||||
const diskArray = Array.isArray(diskData) ? diskData : [diskData]
|
||||
hardwareInfo.disks = diskArray.map(disk => ({
|
||||
model: disk.Model || 'N/A',
|
||||
interfaceType: disk.InterfaceType || '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 info:', error)
|
||||
}
|
||||
|
||||
// 获取显卡信息(Windows)
|
||||
try {
|
||||
const { stdout } = await execPromise('wmic path win32_videocontroller get name')
|
||||
hardwareInfo.gpu = stdout.trim().split('\n').slice(1).filter(line => line.trim())
|
||||
} catch (error) {
|
||||
console.error('Error getting GPU info:', error)
|
||||
}
|
||||
}
|
||||
// 获取硬盘和显卡信息(Linux)
|
||||
else if (os.platform() === 'linux') {
|
||||
try {
|
||||
const { stdout } = await execPromise('lsblk -o NAME,MODEL,SIZE,TYPE | grep disk')
|
||||
const diskLines = stdout.trim().split('\n')
|
||||
hardwareInfo.disks = diskLines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 3) {
|
||||
return {
|
||||
name: parts[0],
|
||||
model: parts.slice(1, -1).join(' '),
|
||||
size: parts[parts.length - 1]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting disk info:', error)
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout } = await execPromise('lspci | grep VGA')
|
||||
hardwareInfo.gpu = stdout.trim().split('\n').map(line => line.trim())
|
||||
} catch (error) {
|
||||
console.error('Error getting GPU info:', error)
|
||||
}
|
||||
}
|
||||
// 获取硬盘和显卡信息(macOS)
|
||||
else if (os.platform() === 'darwin') {
|
||||
try {
|
||||
const { stdout } = await execPromise('diskutil list')
|
||||
hardwareInfo.disks = [{
|
||||
info: stdout.trim()
|
||||
}]
|
||||
} catch (error) {
|
||||
console.error('Error getting disk info:', error)
|
||||
}
|
||||
|
||||
try {
|
||||
const { stdout } = await execPromise('system_profiler SPDisplaysDataType')
|
||||
hardwareInfo.gpu = [{
|
||||
info: stdout.trim()
|
||||
}]
|
||||
} catch (error) {
|
||||
console.error('Error getting GPU info:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(hardwareInfo, null, 2)
|
||||
} catch (error) {
|
||||
console.error('Error getting hardware info:', error)
|
||||
return JSON.stringify({ error: error.message }, null, 2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "get_hardware_info",
|
||||
description: `获取系统硬件配置信息,包括CPU、内存、硬盘、显卡等`,
|
||||
schema: z.object({})
|
||||
}
|
||||
)
|
||||
101
agent/tools/system/installed_software.js
Normal file
101
agent/tools/system/installed_software.js
Normal file
@@ -0,0 +1,101 @@
|
||||
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 getInstalledSoftwareTool = tool(
|
||||
async (input) => {
|
||||
try {
|
||||
const softwareInfo = {
|
||||
platform: os.platform(),
|
||||
software: []
|
||||
}
|
||||
|
||||
// 获取Windows已安装软件
|
||||
if (os.platform() === 'win32') {
|
||||
try {
|
||||
const { stdout } = await execPromise('powershell -Command "Get-WmiObject Win32_Product | Select-Object Name,Version,Vendor,InstallDate | ConvertTo-Json"')
|
||||
const softwareData = JSON.parse(stdout)
|
||||
const softwareArray = Array.isArray(softwareData) ? softwareData : [softwareData]
|
||||
softwareInfo.software = softwareArray.map(soft => ({
|
||||
name: soft.Name || 'N/A',
|
||||
version: soft.Version || 'N/A',
|
||||
vendor: soft.Vendor || 'N/A',
|
||||
installDate: soft.InstallDate || 'N/A'
|
||||
})).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting installed software:', error)
|
||||
}
|
||||
}
|
||||
// 获取Linux已安装软件
|
||||
else if (os.platform() === 'linux') {
|
||||
try {
|
||||
// 尝试使用dpkg(Debian/Ubuntu)
|
||||
const { stdout: dpkgOutput } = await execPromise('dpkg -l 2>/dev/null || echo "N/A"')
|
||||
if (dpkgOutput && dpkgOutput !== 'N/A') {
|
||||
const lines = dpkgOutput.trim().split('\n').slice(5)
|
||||
softwareInfo.software = lines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 3) {
|
||||
return {
|
||||
name: parts[1],
|
||||
version: parts[2],
|
||||
description: parts.slice(3).join(' ')
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} else {
|
||||
// 尝试使用rpm(RHEL/CentOS)
|
||||
const { stdout: rpmOutput } = await execPromise('rpm -qa 2>/dev/null || echo "N/A"')
|
||||
if (rpmOutput && rpmOutput !== 'N/A') {
|
||||
const lines = rpmOutput.trim().split('\n')
|
||||
softwareInfo.software = lines.map(line => {
|
||||
const match = line.match(/(.+)-(.+)-(.+)/)
|
||||
if (match) {
|
||||
return {
|
||||
name: match[1],
|
||||
version: `${match[2]}-${match[3]}`
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting installed software:', error)
|
||||
}
|
||||
}
|
||||
// 获取macOS已安装软件
|
||||
else if (os.platform() === 'darwin') {
|
||||
try {
|
||||
const { stdout } = await execPromise('find /Applications -name "*.app" -type d | sort')
|
||||
const apps = stdout.trim().split('\n')
|
||||
softwareInfo.software = apps.map(app => ({
|
||||
name: app.split('/').pop().replace('.app', ''),
|
||||
path: app
|
||||
})).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting installed software:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(softwareInfo, null, 2)
|
||||
} catch (error) {
|
||||
console.error('Error getting installed software:', error)
|
||||
return JSON.stringify({ error: error.message }, null, 2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "get_installed_software",
|
||||
description: `获取系统已安装的软件信息,包括软件名称、版本、供应商等`,
|
||||
schema: z.object({})
|
||||
}
|
||||
)
|
||||
0
agent/tools/system/linux_cmd.js
Normal file
0
agent/tools/system/linux_cmd.js
Normal file
89
agent/tools/system/os_info.js
Normal file
89
agent/tools/system/os_info.js
Normal file
@@ -0,0 +1,89 @@
|
||||
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 getOsInfoTool = tool(
|
||||
async (input) => {
|
||||
try {
|
||||
const osInfo = {
|
||||
platform: os.platform(),
|
||||
arch: os.arch(),
|
||||
release: os.release(),
|
||||
hostname: os.hostname(),
|
||||
uptime: `${Math.round(os.uptime() / 3600)} hours`,
|
||||
nodeVersion: process.version,
|
||||
homeDir: os.homedir(),
|
||||
tempDir: os.tmpdir(),
|
||||
userInfo: os.userInfo()
|
||||
}
|
||||
|
||||
// 获取Windows详细信息
|
||||
if (os.platform() === 'win32') {
|
||||
try {
|
||||
const { stdout } = await execPromise('powershell -Command "Get-WmiObject Win32_OperatingSystem | Select-Object Caption,Version,BuildNumber,OSArchitecture | ConvertTo-Json"')
|
||||
const winInfo = JSON.parse(stdout)
|
||||
osInfo.windows = {
|
||||
caption: winInfo.Caption || 'N/A',
|
||||
version: winInfo.Version || 'N/A',
|
||||
buildNumber: winInfo.BuildNumber || 'N/A',
|
||||
architecture: winInfo.OSArchitecture || 'N/A'
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting Windows info:', error)
|
||||
}
|
||||
}
|
||||
// 获取Linux详细信息
|
||||
else if (os.platform() === 'linux') {
|
||||
try {
|
||||
const { stdout } = await execPromise('cat /etc/os-release')
|
||||
const lines = stdout.trim().split('\n')
|
||||
const linuxInfo = {}
|
||||
lines.forEach(line => {
|
||||
const match = line.match(/(.+?)=(.+)/)
|
||||
if (match) {
|
||||
linuxInfo[match[1]] = match[2].replace(/"/g, '')
|
||||
}
|
||||
})
|
||||
osInfo.linux = linuxInfo
|
||||
} catch (error) {
|
||||
console.error('Error getting Linux info:', error)
|
||||
}
|
||||
}
|
||||
// 获取macOS详细信息
|
||||
else if (os.platform() === 'darwin') {
|
||||
try {
|
||||
const { stdout } = await execPromise('sw_vers')
|
||||
const lines = stdout.trim().split('\n')
|
||||
const macInfo = {}
|
||||
lines.forEach(line => {
|
||||
const match = line.match(/(.+?):\s+(.+)/)
|
||||
if (match) {
|
||||
macInfo[match[1]] = match[2]
|
||||
}
|
||||
})
|
||||
osInfo.macOS = macInfo
|
||||
} catch (error) {
|
||||
console.error('Error getting macOS info:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(osInfo, null, 2)
|
||||
} catch (error) {
|
||||
console.error('Error getting OS info:', error)
|
||||
return JSON.stringify({ error: error.message }, null, 2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "get_os_info",
|
||||
description: `获取操作系统信息,包括平台、架构、版本、主机名、运行时间等`,
|
||||
schema: z.object({})
|
||||
}
|
||||
)
|
||||
242
agent/tools/system/os_network_info.js
Normal file
242
agent/tools/system/os_network_info.js
Normal file
@@ -0,0 +1,242 @@
|
||||
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 getNetworkInfoTool = tool(
|
||||
async (input) => {
|
||||
try {
|
||||
const networkInfo = {
|
||||
platform: os.platform(),
|
||||
networkInterfaces: [],
|
||||
dnsServers: [],
|
||||
gateway: []
|
||||
}
|
||||
|
||||
// 获取网络接口信息(跨平台)
|
||||
const interfaces = os.networkInterfaces()
|
||||
for (const [name, addresses] of Object.entries(interfaces)) {
|
||||
networkInfo.networkInterfaces.push({
|
||||
name,
|
||||
addresses: addresses.map(addr => ({
|
||||
family: addr.family,
|
||||
address: addr.address,
|
||||
netmask: addr.netmask,
|
||||
mac: addr.mac,
|
||||
internal: addr.internal,
|
||||
cidr: addr.cidr
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
// 获取详细网络信息(Windows)
|
||||
if (os.platform() === 'win32') {
|
||||
try {
|
||||
const { stdout } = await execPromise('ipconfig /all')
|
||||
const lines = stdout.trim().split('\n')
|
||||
let currentAdapter = null
|
||||
|
||||
lines.forEach(line => {
|
||||
const adapterMatch = line.match(/适配器\s+(.+?):/)
|
||||
if (adapterMatch) {
|
||||
if (currentAdapter) {
|
||||
networkInfo.networkInterfaces.push(currentAdapter)
|
||||
}
|
||||
currentAdapter = {
|
||||
name: adapterMatch[1],
|
||||
description: '',
|
||||
macAddress: '',
|
||||
dhcpEnabled: false,
|
||||
ipAddress: [],
|
||||
subnetMask: [],
|
||||
defaultGateway: [],
|
||||
dnsServers: []
|
||||
}
|
||||
} else if (currentAdapter) {
|
||||
const descMatch = line.match(/描述\s+(.+)/)
|
||||
if (descMatch) currentAdapter.description = descMatch[1].trim()
|
||||
|
||||
const macMatch = line.match(/物理地址\s+:\s+(.+)/)
|
||||
if (macMatch) currentAdapter.macAddress = macMatch[1].trim()
|
||||
|
||||
const dhcpMatch = line.match(/DHCP 已启用\s+:\s+(.+)/)
|
||||
if (dhcpMatch) currentAdapter.dhcpEnabled = dhcpMatch[1].trim() === '是'
|
||||
|
||||
const ipMatch = line.match(/IPv4 地址\s+:\s+(.+)/)
|
||||
if (ipMatch) currentAdapter.ipAddress.push(ipMatch[1].trim())
|
||||
|
||||
const subnetMatch = line.match(/子网掩码\s+:\s+(.+)/)
|
||||
if (subnetMatch) currentAdapter.subnetMask.push(subnetMatch[1].trim())
|
||||
|
||||
const gatewayMatch = line.match(/默认网关\s+:\s+(.+)/)
|
||||
if (gatewayMatch && gatewayMatch[1].trim()) currentAdapter.defaultGateway.push(gatewayMatch[1].trim())
|
||||
|
||||
const dnsMatch = line.match(/DNS 服务器\s+:\s+(.+)/)
|
||||
if (dnsMatch && dnsMatch[1].trim()) currentAdapter.dnsServers.push(dnsMatch[1].trim())
|
||||
}
|
||||
})
|
||||
|
||||
if (currentAdapter) {
|
||||
networkInfo.networkInterfaces.push(currentAdapter)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting network config:', error)
|
||||
}
|
||||
|
||||
// 获取DNS服务器(Windows)
|
||||
try {
|
||||
const { stdout } = await execPromise('nslookup localhost 2>&1')
|
||||
const dnsMatch = stdout.match(/Server:\s+(.+)/)
|
||||
if (dnsMatch) {
|
||||
networkInfo.dnsServers.push(dnsMatch[1].trim())
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting DNS servers:', error)
|
||||
}
|
||||
}
|
||||
// 获取详细网络信息(Linux)
|
||||
else if (os.platform() === 'linux') {
|
||||
try {
|
||||
const { stdout } = await execPromise('ip addr show')
|
||||
const lines = stdout.trim().split('\n')
|
||||
let currentInterface = null
|
||||
|
||||
lines.forEach(line => {
|
||||
const interfaceMatch = line.match(/^\d+:\s+(\w+):/)
|
||||
if (interfaceMatch) {
|
||||
if (currentInterface) {
|
||||
networkInfo.networkInterfaces.push(currentInterface)
|
||||
}
|
||||
currentInterface = {
|
||||
name: interfaceMatch[1],
|
||||
state: '',
|
||||
macAddress: '',
|
||||
ipAddress: [],
|
||||
subnetMask: []
|
||||
}
|
||||
} else if (currentInterface) {
|
||||
const stateMatch = line.match(/state\s+(\w+)/)
|
||||
if (stateMatch) currentInterface.state = stateMatch[1]
|
||||
|
||||
const macMatch = line.match(/link\/ether\s+([a-fA-F0-9:]+)/)
|
||||
if (macMatch) currentInterface.macAddress = macMatch[1]
|
||||
|
||||
const ipMatch = line.match(/inet\s+(\d+\.\d+\.\d+\.\d+)\/(\d+)/)
|
||||
if (ipMatch) {
|
||||
currentInterface.ipAddress.push(ipMatch[1])
|
||||
const maskLength = parseInt(ipMatch[2])
|
||||
currentInterface.subnetMask.push(maskLength)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (currentInterface) {
|
||||
networkInfo.networkInterfaces.push(currentInterface)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting network config:', error)
|
||||
}
|
||||
|
||||
// 获取DNS服务器(Linux)
|
||||
try {
|
||||
const { stdout } = await execPromise('cat /etc/resolv.conf')
|
||||
const dnsMatches = stdout.match(/nameserver\s+(.+)/g)
|
||||
if (dnsMatches) {
|
||||
networkInfo.dnsServers = dnsMatches.map(match => match.replace('nameserver', '').trim())
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting DNS servers:', error)
|
||||
}
|
||||
|
||||
// 获取网关(Linux)
|
||||
try {
|
||||
const { stdout } = await execPromise('ip route show default')
|
||||
const gatewayMatch = stdout.match(/default via (\d+\.\d+\.\d+\.\d+)/)
|
||||
if (gatewayMatch) {
|
||||
networkInfo.gateway.push(gatewayMatch[1])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting gateway:', error)
|
||||
}
|
||||
}
|
||||
// 获取详细网络信息(macOS)
|
||||
else if (os.platform() === 'darwin') {
|
||||
try {
|
||||
const { stdout } = await execPromise('ifconfig')
|
||||
const lines = stdout.trim().split('\n')
|
||||
let currentInterface = null
|
||||
|
||||
lines.forEach(line => {
|
||||
const interfaceMatch = line.match(/^(\w+):/)
|
||||
if (interfaceMatch) {
|
||||
if (currentInterface) {
|
||||
networkInfo.networkInterfaces.push(currentInterface)
|
||||
}
|
||||
currentInterface = {
|
||||
name: interfaceMatch[1],
|
||||
status: '',
|
||||
macAddress: '',
|
||||
ipAddress: [],
|
||||
subnetMask: []
|
||||
}
|
||||
} else if (currentInterface) {
|
||||
const statusMatch = line.match(/status:\s+(\w+)/)
|
||||
if (statusMatch) currentInterface.status = statusMatch[1]
|
||||
|
||||
const macMatch = line.match(/ether\s+([a-fA-F0-9:]+)/)
|
||||
if (macMatch) currentInterface.macAddress = macMatch[1]
|
||||
|
||||
const ipMatch = line.match(/inet\s+(\d+\.\d+\.\d+\.\d+)\s+netmask\s+(0x[0-9a-fA-F]+)/)
|
||||
if (ipMatch) {
|
||||
currentInterface.ipAddress.push(ipMatch[1])
|
||||
currentInterface.subnetMask.push(ipMatch[2])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (currentInterface) {
|
||||
networkInfo.networkInterfaces.push(currentInterface)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting network config:', error)
|
||||
}
|
||||
|
||||
// 获取DNS服务器(macOS)
|
||||
try {
|
||||
const { stdout } = await execPromise('scutil --dns | grep nameserver | awk \'{print $3}\'')
|
||||
networkInfo.dnsServers = stdout.trim().split('\n').filter(dns => dns)
|
||||
} catch (error) {
|
||||
console.error('Error getting DNS servers:', error)
|
||||
}
|
||||
|
||||
// 获取网关(macOS)
|
||||
try {
|
||||
const { stdout } = await execPromise('netstat -nr | grep default')
|
||||
const gatewayMatch = stdout.match(/default\s+(\d+\.\d+\.\d+\.\d+)/)
|
||||
if (gatewayMatch) {
|
||||
networkInfo.gateway.push(gatewayMatch[1])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error getting gateway:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(networkInfo, null, 2)
|
||||
} catch (error) {
|
||||
console.error('Error getting network info:', error)
|
||||
return JSON.stringify({ error: error.message }, null, 2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "get_network_info",
|
||||
description: `获取系统网卡和网络信息,包括网卡配置、IP地址、MAC地址、DNS服务器、网关等`,
|
||||
schema: z.object({})
|
||||
}
|
||||
)
|
||||
157
agent/tools/system/os_socket_info.js
Normal file
157
agent/tools/system/os_socket_info.js
Normal file
@@ -0,0 +1,157 @@
|
||||
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)
|
||||
|
||||
/**
|
||||
* 获取系统活动连接和Socket信息工具
|
||||
* @returns {string} - 返回Socket信息的 JSON 字符串,包括活动连接和监听端口等
|
||||
*/
|
||||
export const getSocketInfoTool = tool(
|
||||
async (input) => {
|
||||
try {
|
||||
const socketInfo = {
|
||||
platform: os.platform(),
|
||||
activeConnections: [],
|
||||
listeningPorts: []
|
||||
}
|
||||
|
||||
// 获取活动连接(Windows)
|
||||
if (os.platform() === 'win32') {
|
||||
try {
|
||||
const { stdout } = await execPromise('netstat -ano')
|
||||
const lines = stdout.trim().split('\n').slice(4)
|
||||
socketInfo.activeConnections = lines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 5) {
|
||||
return {
|
||||
protocol: parts[0],
|
||||
localAddress: parts[1],
|
||||
foreignAddress: parts[2],
|
||||
state: parts[3],
|
||||
processId: parts[4]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting active connections:', error)
|
||||
}
|
||||
|
||||
// 获取监听端口(Windows)
|
||||
try {
|
||||
const { stdout } = await execPromise('netstat -an | findstr LISTENING')
|
||||
const lines = stdout.trim().split('\n')
|
||||
socketInfo.listeningPorts = lines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 4) {
|
||||
return {
|
||||
protocol: parts[0],
|
||||
localAddress: parts[1],
|
||||
state: parts[3]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting listening ports:', error)
|
||||
}
|
||||
}
|
||||
// 获取活动连接(Linux)
|
||||
else if (os.platform() === 'linux') {
|
||||
try {
|
||||
const { stdout } = await execPromise('ss -tuln')
|
||||
const lines = stdout.trim().split('\n').slice(1)
|
||||
socketInfo.activeConnections = lines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 5) {
|
||||
return {
|
||||
protocol: parts[0],
|
||||
state: parts[1],
|
||||
localAddress: parts[4],
|
||||
peerAddress: parts[5]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting active connections:', error)
|
||||
}
|
||||
|
||||
// 获取监听端口(Linux)
|
||||
try {
|
||||
const { stdout } = await execPromise('ss -tulpn')
|
||||
const lines = stdout.trim().split('\n').slice(1)
|
||||
socketInfo.listeningPorts = lines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 6) {
|
||||
return {
|
||||
protocol: parts[0],
|
||||
state: parts[1],
|
||||
localAddress: parts[4],
|
||||
processInfo: parts[5]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting listening ports:', error)
|
||||
}
|
||||
}
|
||||
// 获取活动连接(macOS)
|
||||
else if (os.platform() === 'darwin') {
|
||||
try {
|
||||
const { stdout } = await execPromise('netstat -an')
|
||||
const lines = stdout.trim().split('\n').slice(2)
|
||||
socketInfo.activeConnections = lines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 6) {
|
||||
return {
|
||||
protocol: parts[0],
|
||||
localAddress: parts[3],
|
||||
foreignAddress: parts[4],
|
||||
state: parts[5]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting active connections:', error)
|
||||
}
|
||||
|
||||
// 获取监听端口(macOS)
|
||||
try {
|
||||
const { stdout } = await execPromise('netstat -an | grep LISTEN')
|
||||
const lines = stdout.trim().split('\n')
|
||||
socketInfo.listeningPorts = lines.map(line => {
|
||||
const parts = line.trim().split(/\s+/)
|
||||
if (parts.length >= 6) {
|
||||
return {
|
||||
protocol: parts[0],
|
||||
localAddress: parts[3],
|
||||
foreignAddress: parts[4],
|
||||
state: parts[5]
|
||||
}
|
||||
}
|
||||
return null
|
||||
}).filter(item => item)
|
||||
} catch (error) {
|
||||
console.error('Error getting listening ports:', error)
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(socketInfo, null, 2)
|
||||
} catch (error) {
|
||||
console.error('Error getting socket info:', error)
|
||||
return JSON.stringify({ error: error.message }, null, 2)
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "get_socket_info",
|
||||
description: `获取系统活动连接和Socket信息,包括TCP/UDP连接、监听端口、进程ID等`,
|
||||
schema: z.object({})
|
||||
}
|
||||
)
|
||||
17
agent/tools/system/system_time.js
Normal file
17
agent/tools/system/system_time.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import * as z from "zod"
|
||||
import { tool } from "langchain"
|
||||
|
||||
/**
|
||||
* 获取系统时间工具
|
||||
* @returns {string} - 返回当前系统时间
|
||||
*/
|
||||
export const getSystemTimeTool = tool(
|
||||
(input) => {
|
||||
return new Date().toLocaleString()
|
||||
},
|
||||
{
|
||||
name: "get_system_time",
|
||||
description: `获取当前系统时间`,
|
||||
schema: z.object({})
|
||||
}
|
||||
)
|
||||
74
agent/tools/system/win_cmd.js
Normal file
74
agent/tools/system/win_cmd.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import * as z from 'zod';
|
||||
import { tool } from 'langchain';
|
||||
import { spawn } from 'child_process';
|
||||
|
||||
export const winCmdTool = tool(
|
||||
async ({ command, cwd, timeout = 30000 }) => {
|
||||
const workingDir = cwd || process.cwd();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
let killed = false;
|
||||
|
||||
const proc = spawn('cmd.exe', ['/c', command], {
|
||||
cwd: workingDir,
|
||||
shell: false,
|
||||
windowsHide: true
|
||||
});
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
killed = true;
|
||||
proc.kill('SIGKILL');
|
||||
}, timeout);
|
||||
|
||||
proc.stdout.on('data', (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
proc.stderr.on('data', (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
clearTimeout(timer);
|
||||
if (killed) {
|
||||
resolve(JSON.stringify({
|
||||
success: false,
|
||||
error: `Command timed out after ${timeout}ms`,
|
||||
command,
|
||||
cwd: workingDir
|
||||
}));
|
||||
return;
|
||||
}
|
||||
resolve(JSON.stringify({
|
||||
success: code === 0,
|
||||
exitCode: code,
|
||||
stdout: stdout.trim(),
|
||||
stderr: stderr.trim(),
|
||||
command,
|
||||
cwd: workingDir
|
||||
}));
|
||||
});
|
||||
|
||||
proc.on('error', (error) => {
|
||||
clearTimeout(timer);
|
||||
resolve(JSON.stringify({
|
||||
success: false,
|
||||
error: error.message,
|
||||
command,
|
||||
cwd: workingDir
|
||||
}));
|
||||
});
|
||||
});
|
||||
},
|
||||
{
|
||||
name: 'win_cmd',
|
||||
description: '在 Windows 系统上执行 cmd.exe 命令行命令。适用于运行 Windows 批处理命令、文件系统操作命令(如 dir、copy、del、mkdir 等)。不支持 PowerShell 命令。',
|
||||
schema: z.object({
|
||||
command: z.string().describe('要执行的 cmd.exe 命令或命令组合,使用 && 连接多个命令,如 "dir /b" 或 "echo hello && dir"'),
|
||||
cwd: z.string().optional().describe('执行命令的工作目录,默认为当前工作目录'),
|
||||
timeout: z.number().optional().describe('命令执行超时时间(毫秒),默认30000')
|
||||
})
|
||||
}
|
||||
);
|
||||
Reference in New Issue
Block a user