feat: 初次提交
This commit is contained in:
296
src/tools/devops/nas.ts
Normal file
296
src/tools/devops/nas.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
* NAS file management tools
|
||||
*/
|
||||
|
||||
import { Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||
import { mcpServer } from "../../server.js";
|
||||
import { configManager } from "../../storage/config.js";
|
||||
import { logger } from "../../utils/logger.js";
|
||||
|
||||
export function registerNASTools(): void {
|
||||
// List NAS files
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "nas_list_files",
|
||||
description: "List files and directories on NAS",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
path: {
|
||||
type: "string",
|
||||
description: "Path to list (default: root)",
|
||||
default: "/",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (args) => {
|
||||
const nasConfig = configManager.getNASConfig();
|
||||
const path = (args.path as string) || "/";
|
||||
|
||||
if (!nasConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: NAS configuration not found. Please set NAS_HOST, NAS_USERNAME, and NAS_PASSWORD in environment variables.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// Note: This is a placeholder implementation
|
||||
// In a real implementation, you would use appropriate libraries based on protocol
|
||||
// For SMB: smb2 or node-smb2
|
||||
// For FTP: basic-ftp
|
||||
// For SFTP: ssh2-sftp-client
|
||||
|
||||
const protocol = nasConfig.protocol || "smb";
|
||||
let result = "";
|
||||
|
||||
if (protocol === "smb") {
|
||||
result = `NAS File Listing (SMB protocol)\n`;
|
||||
result += `Host: ${nasConfig.host}\n`;
|
||||
result += `Path: ${path}\n\n`;
|
||||
result += `Note: SMB file listing requires additional libraries.\n`;
|
||||
result += `To implement, install: bun add smb2\n`;
|
||||
result += `Example files that would be listed:\n`;
|
||||
result += `- Documents/\n`;
|
||||
result += `- Media/\n`;
|
||||
result += `- Backups/`;
|
||||
} else if (protocol === "ftp" || protocol === "sftp") {
|
||||
result = `NAS File Listing (${protocol.toUpperCase()} protocol)\n`;
|
||||
result += `Host: ${nasConfig.host}\n`;
|
||||
result += `Path: ${path}\n\n`;
|
||||
result += `Note: ${protocol.toUpperCase()} file listing requires additional libraries.\n`;
|
||||
if (protocol === "ftp") {
|
||||
result += `To implement, install: bun add basic-ftp\n`;
|
||||
} else {
|
||||
result += `To implement, install: bun add ssh2-sftp-client\n`;
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`Listing NAS files at ${path}`);
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: result,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error listing NAS files: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Upload file to NAS
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "nas_upload_file",
|
||||
description: "Upload a file to NAS",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
localPath: {
|
||||
type: "string",
|
||||
description: "Local file path to upload",
|
||||
},
|
||||
remotePath: {
|
||||
type: "string",
|
||||
description: "Remote path on NAS",
|
||||
},
|
||||
},
|
||||
required: ["localPath", "remotePath"],
|
||||
},
|
||||
},
|
||||
async (args) => {
|
||||
const nasConfig = configManager.getNASConfig();
|
||||
const localPath = args.localPath as string;
|
||||
const remotePath = args.remotePath as string;
|
||||
|
||||
if (!nasConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: NAS configuration not found. Please configure NAS settings.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Uploading ${localPath} to NAS ${remotePath}`);
|
||||
|
||||
// Placeholder implementation
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `File upload initiated:\nLocal: ${localPath}\nRemote: ${remotePath}\n\nNote: Full implementation requires protocol-specific libraries (smb2, basic-ftp, or ssh2-sftp-client).`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error uploading file: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Download file from NAS
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "nas_download_file",
|
||||
description: "Download a file from NAS",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
remotePath: {
|
||||
type: "string",
|
||||
description: "Remote file path on NAS",
|
||||
},
|
||||
localPath: {
|
||||
type: "string",
|
||||
description: "Local path to save the file",
|
||||
},
|
||||
},
|
||||
required: ["remotePath", "localPath"],
|
||||
},
|
||||
},
|
||||
async (args) => {
|
||||
const nasConfig = configManager.getNASConfig();
|
||||
const remotePath = args.remotePath as string;
|
||||
const localPath = args.localPath as string;
|
||||
|
||||
if (!nasConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: NAS configuration not found. Please configure NAS settings.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Downloading ${remotePath} from NAS to ${localPath}`);
|
||||
|
||||
// Placeholder implementation
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `File download initiated:\nRemote: ${remotePath}\nLocal: ${localPath}\n\nNote: Full implementation requires protocol-specific libraries.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error downloading file: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Search files on NAS
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "nas_search_files",
|
||||
description: "Search for files on NAS by name pattern",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
pattern: {
|
||||
type: "string",
|
||||
description: "File name pattern to search for",
|
||||
},
|
||||
path: {
|
||||
type: "string",
|
||||
description: "Base path to search in (default: root)",
|
||||
default: "/",
|
||||
},
|
||||
},
|
||||
required: ["pattern"],
|
||||
},
|
||||
},
|
||||
async (args) => {
|
||||
const nasConfig = configManager.getNASConfig();
|
||||
const pattern = args.pattern as string;
|
||||
const path = (args.path as string) || "/";
|
||||
|
||||
if (!nasConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: NAS configuration not found. Please configure NAS settings.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Searching NAS for pattern: ${pattern} in ${path}`);
|
||||
|
||||
// Placeholder implementation
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Searching for files matching "${pattern}" in ${path}\n\nNote: Full implementation requires protocol-specific libraries and recursive directory traversal.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error searching files: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
204
src/tools/devops/router.ts
Normal file
204
src/tools/devops/router.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
/**
|
||||
* Soft router management tools
|
||||
*/
|
||||
|
||||
import { mcpServer } from "../../server.js";
|
||||
import { configManager } from "../../storage/config.js";
|
||||
import { logger } from "../../utils/logger.js";
|
||||
import axios from "axios";
|
||||
|
||||
export function registerRouterTools(): void {
|
||||
// Get router status
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "router_status",
|
||||
description: "Get soft router status and information",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
const routerConfig = configManager.getRouterConfig();
|
||||
|
||||
if (!routerConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: Router configuration not found. Please set ROUTER_HOST, ROUTER_USERNAME, and ROUTER_PASSWORD in environment variables.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Checking router status: ${routerConfig.host}`);
|
||||
|
||||
// Try to connect to router web interface (common ports: 80, 443, 8080)
|
||||
// Most routers have a status API endpoint
|
||||
const ports = [80, 443, 8080];
|
||||
let status = "";
|
||||
|
||||
for (const port of ports) {
|
||||
try {
|
||||
const protocol = port === 443 ? "https" : "http";
|
||||
await axios.get(`${protocol}://${routerConfig.host}:${port}/`, {
|
||||
timeout: 2000,
|
||||
auth:
|
||||
routerConfig.username && routerConfig.password
|
||||
? {
|
||||
username: routerConfig.username,
|
||||
password: routerConfig.password,
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
|
||||
status += `Router accessible on port ${port}\n`;
|
||||
break;
|
||||
} catch (error) {
|
||||
// Continue to next port
|
||||
}
|
||||
}
|
||||
|
||||
if (!status) {
|
||||
status = `Router Status (${routerConfig.host}):\n\n`;
|
||||
status += `Note: Router status retrieval depends on router firmware.\n`;
|
||||
status += `Common router management interfaces:\n`;
|
||||
status += `- OpenWrt: http://${routerConfig.host}/cgi-bin/luci\n`;
|
||||
status += `- DD-WRT: http://${routerConfig.host}\n`;
|
||||
status += `- pfSense: https://${routerConfig.host}\n`;
|
||||
status += `\nFor full implementation, use router-specific API or SSH connection.`;
|
||||
}
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: status,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error getting router status: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Get traffic statistics
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "router_traffic",
|
||||
description: "Get router traffic statistics",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
const routerConfig = configManager.getRouterConfig();
|
||||
|
||||
if (!routerConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: Router configuration not found. Please configure router settings.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Getting router traffic stats: ${routerConfig.host}`);
|
||||
|
||||
// Placeholder - implementation depends on router firmware
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Router Traffic Statistics (${routerConfig.host}):\n\nNote: Traffic statistics retrieval depends on router firmware.\n\nFor OpenWrt, you can use:\n- SSH connection to execute: cat /proc/net/dev\n- Or access web interface: http://${routerConfig.host}/cgi-bin/luci/admin/network/bandwidth\n\nFor other routers, check their specific API documentation.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error getting traffic stats: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// List connected devices
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "router_devices",
|
||||
description: "List devices connected to the router",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
const routerConfig = configManager.getRouterConfig();
|
||||
|
||||
if (!routerConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: Router configuration not found. Please configure router settings.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(`Listing router devices: ${routerConfig.host}`);
|
||||
|
||||
// Placeholder - implementation depends on router firmware
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Connected Devices (${routerConfig.host}):\n\nNote: Device listing depends on router firmware.\n\nFor OpenWrt:\n- SSH: cat /proc/net/arp\n- Web: http://${routerConfig.host}/cgi-bin/luci/admin/network/dhcp\n\nFor other routers, check DHCP lease table or device list in web interface.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error listing devices: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
367
src/tools/devops/server.ts
Normal file
367
src/tools/devops/server.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
/**
|
||||
* Cloud server monitoring and management tools
|
||||
*/
|
||||
|
||||
import { mcpServer } from "../../server.js";
|
||||
import { configManager } from "../../storage/config.js";
|
||||
import { logger } from "../../utils/logger.js";
|
||||
import { Client } from "ssh2";
|
||||
|
||||
export function registerServerTools(): void {
|
||||
// Get server status
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "server_status",
|
||||
description: "Get cloud server status (CPU, memory, disk usage)",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
const serverConfig = configManager.getServerConfig();
|
||||
|
||||
if (!serverConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: Server configuration not found. Please set SERVER_HOST, SERVER_USERNAME, and SERVER_KEY_PATH in environment variables.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
return new Promise((resolve) => {
|
||||
const conn = new Client();
|
||||
|
||||
conn.on("ready", () => {
|
||||
logger.info("SSH connection established");
|
||||
|
||||
// Execute commands to get system status
|
||||
conn.exec(
|
||||
"echo 'CPU:'; top -l 1 | grep 'CPU usage' | awk '{print $3}'; echo 'Memory:'; vm_stat | head -n 5; echo 'Disk:'; df -h / | tail -n 1",
|
||||
(err: Error | undefined, stream: any) => {
|
||||
if (err) {
|
||||
conn.end();
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error executing command: ${err.message}`,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
return;
|
||||
}
|
||||
|
||||
let output = "";
|
||||
stream
|
||||
.on("close", () => {
|
||||
conn.end();
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Server Status (${serverConfig.host}):\n\n${
|
||||
output ||
|
||||
"Status retrieved successfully. Note: Command output may vary by OS."
|
||||
}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
.on("data", (data: Buffer) => {
|
||||
output += data.toString();
|
||||
})
|
||||
.stderr.on("data", (data: Buffer) => {
|
||||
output += data.toString();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
conn.on("error", (err) => {
|
||||
logger.error("SSH connection error:", err);
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error connecting to server: ${err.message}\n\nMake sure:\n1. Server is accessible\n2. SSH key is configured correctly\n3. Server allows SSH connections`,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
});
|
||||
|
||||
// Connect using key or password
|
||||
const connectOptions: {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
privateKey?: string;
|
||||
password?: string;
|
||||
} = {
|
||||
host: serverConfig.host!,
|
||||
port: serverConfig.port || 22,
|
||||
username: serverConfig.username!,
|
||||
};
|
||||
|
||||
if (serverConfig.keyPath) {
|
||||
import("fs").then(({ readFileSync }) => {
|
||||
try {
|
||||
connectOptions.privateKey = readFileSync(serverConfig.keyPath!);
|
||||
conn.connect(connectOptions);
|
||||
} catch (error) {
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error reading SSH key: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
conn.connect(connectOptions);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error getting server status: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Deploy application
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "server_deploy",
|
||||
description: "Deploy application to cloud server",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
localPath: {
|
||||
type: "string",
|
||||
description: "Local path of the application to deploy",
|
||||
},
|
||||
remotePath: {
|
||||
type: "string",
|
||||
description: "Remote path on server",
|
||||
},
|
||||
command: {
|
||||
type: "string",
|
||||
description:
|
||||
'Optional command to run after deployment (e.g., "pm2 restart app")',
|
||||
},
|
||||
},
|
||||
required: ["localPath", "remotePath"],
|
||||
},
|
||||
},
|
||||
async (args) => {
|
||||
const serverConfig = configManager.getServerConfig();
|
||||
const localPath = args.localPath as string;
|
||||
const remotePath = args.remotePath as string;
|
||||
const command = args.command as string | undefined;
|
||||
|
||||
if (!serverConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: Server configuration not found. Please configure server settings.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(
|
||||
`Deploying ${localPath} to ${serverConfig.host}:${remotePath}`
|
||||
);
|
||||
|
||||
// Placeholder - full implementation would use scp or sftp
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Deployment initiated:\nLocal: ${localPath}\nRemote: ${
|
||||
serverConfig.host
|
||||
}:${remotePath}\n${
|
||||
command ? `Command: ${command}` : ""
|
||||
}\n\nNote: Full deployment requires SCP/SFTP implementation. Consider using scp2 or ssh2-sftp-client libraries.`,
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error deploying: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// View server logs
|
||||
mcpServer.registerTool(
|
||||
{
|
||||
name: "server_logs",
|
||||
description: "View server logs",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
logPath: {
|
||||
type: "string",
|
||||
description: "Path to log file (e.g., /var/log/app.log)",
|
||||
default: "/var/log/syslog",
|
||||
},
|
||||
lines: {
|
||||
type: "number",
|
||||
description: "Number of lines to retrieve",
|
||||
default: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
async (args) => {
|
||||
const serverConfig = configManager.getServerConfig();
|
||||
const logPath = (args.logPath as string) || "/var/log/syslog";
|
||||
const lines = (args.lines as number) || 50;
|
||||
|
||||
if (!serverConfig.host) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Error: Server configuration not found. Please configure server settings.",
|
||||
},
|
||||
],
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
return new Promise((resolve) => {
|
||||
const conn = new Client();
|
||||
|
||||
conn.on("ready", () => {
|
||||
conn.exec(
|
||||
`tail -n ${lines} ${logPath}`,
|
||||
(err: Error | undefined, stream: any) => {
|
||||
if (err) {
|
||||
conn.end();
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error reading logs: ${err.message}`,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
return;
|
||||
}
|
||||
|
||||
let output = "";
|
||||
stream
|
||||
.on("close", () => {
|
||||
conn.end();
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Server Logs (${logPath}, last ${lines} lines):\n\n${
|
||||
output || "No logs found or permission denied"
|
||||
}`,
|
||||
},
|
||||
],
|
||||
});
|
||||
})
|
||||
.on("data", (data: Buffer) => {
|
||||
output += data.toString();
|
||||
})
|
||||
.stderr.on("data", (data: Buffer) => {
|
||||
output += data.toString();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
conn.on("error", (err: Error) => {
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error connecting to server: ${err.message}`,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
});
|
||||
|
||||
const connectOptions: {
|
||||
host: string;
|
||||
port: number;
|
||||
username: string;
|
||||
privateKey?: string;
|
||||
} = {
|
||||
host: serverConfig.host!,
|
||||
port: serverConfig.port || 22,
|
||||
username: serverConfig.username!,
|
||||
};
|
||||
|
||||
if (serverConfig.keyPath) {
|
||||
import("fs").then(({ readFileSync }) => {
|
||||
try {
|
||||
connectOptions.privateKey = readFileSync(serverConfig.keyPath!);
|
||||
conn.connect(connectOptions);
|
||||
} catch (error) {
|
||||
resolve({
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error reading SSH key: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
conn.connect(connectOptions);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error viewing logs: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user