@lzm04521/ssh-mcp-server 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +15 -0
- package/README.md +157 -0
- package/admin-web/dist/assets/index-DXbr5DVR.js +494 -0
- package/admin-web/dist/index.html +6 -0
- package/admin-web/dist/logo.png +0 -0
- package/build/cli/command-line-parser.js +715 -0
- package/build/cli/command-line-parser.js.map +1 -0
- package/build/cli/run-mode.js +44 -0
- package/build/cli/run-mode.js.map +1 -0
- package/build/cli/stdio-proxy.js +304 -0
- package/build/cli/stdio-proxy.js.map +1 -0
- package/build/config/index.js +5 -0
- package/build/config/index.js.map +1 -0
- package/build/config/server.js +16 -0
- package/build/config/server.js.map +1 -0
- package/build/core/mcp-http-server.js +14 -0
- package/build/core/mcp-http-server.js.map +1 -0
- package/build/core/mcp-server.js +95 -0
- package/build/core/mcp-server.js.map +1 -0
- package/build/index.js +98 -0
- package/build/index.js.map +1 -0
- package/build/models/admin-types.js +135 -0
- package/build/models/admin-types.js.map +1 -0
- package/build/models/types.js +2 -0
- package/build/models/types.js.map +1 -0
- package/build/server/index.js +128 -0
- package/build/server/index.js.map +1 -0
- package/build/server/routes/admin.js +929 -0
- package/build/server/routes/admin.js.map +1 -0
- package/build/server/routes/audit.js +9 -0
- package/build/server/routes/audit.js.map +1 -0
- package/build/server/routes/backups.js +11 -0
- package/build/server/routes/backups.js.map +1 -0
- package/build/server/routes/mcp.js +52 -0
- package/build/server/routes/mcp.js.map +1 -0
- package/build/server/routes/settings.js +49 -0
- package/build/server/routes/settings.js.map +1 -0
- package/build/server/routes/system.js +147 -0
- package/build/server/routes/system.js.map +1 -0
- package/build/services/audit-store.js +123 -0
- package/build/services/audit-store.js.map +1 -0
- package/build/services/autostart-service.js +55 -0
- package/build/services/autostart-service.js.map +1 -0
- package/build/services/backup-scheduler.js +90 -0
- package/build/services/backup-scheduler.js.map +1 -0
- package/build/services/backup-service.js +113 -0
- package/build/services/backup-service.js.map +1 -0
- package/build/services/config-store.js +163 -0
- package/build/services/config-store.js.map +1 -0
- package/build/services/defaults.js +35 -0
- package/build/services/defaults.js.map +1 -0
- package/build/services/restart-helper.js +43 -0
- package/build/services/restart-helper.js.map +1 -0
- package/build/services/ssh-connection-manager.js +1775 -0
- package/build/services/ssh-connection-manager.js.map +1 -0
- package/build/services/update-service.js +93 -0
- package/build/services/update-service.js.map +1 -0
- package/build/tools/download.js +41 -0
- package/build/tools/download.js.map +1 -0
- package/build/tools/execute-command.js +53 -0
- package/build/tools/execute-command.js.map +1 -0
- package/build/tools/index.js +17 -0
- package/build/tools/index.js.map +1 -0
- package/build/tools/list-directory.js +57 -0
- package/build/tools/list-directory.js.map +1 -0
- package/build/tools/list-servers.js +51 -0
- package/build/tools/list-servers.js.map +1 -0
- package/build/tools/upload.js +41 -0
- package/build/tools/upload.js.map +1 -0
- package/build/utils/logger.js +28 -0
- package/build/utils/logger.js.map +1 -0
- package/build/utils/ssh-config-parser.js +195 -0
- package/build/utils/ssh-config-parser.js.map +1 -0
- package/build/utils/status-collector.js +226 -0
- package/build/utils/status-collector.js.map +1 -0
- package/build/utils/tool-error.js +20 -0
- package/build/utils/tool-error.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
/**
|
|
5
|
+
* 查找 SSH 配置文件中指定主机别名的配置
|
|
6
|
+
* @param hostAlias 主机别名
|
|
7
|
+
* @param configFilePath 配置文件路径,默认为 ~/.ssh/config
|
|
8
|
+
* @returns 解析后的配置项,未找到返回 null
|
|
9
|
+
*/
|
|
10
|
+
export function lookupSshConfig(hostAlias, configFilePath) {
|
|
11
|
+
const configPath = configFilePath || path.join(os.homedir(), '.ssh', 'config');
|
|
12
|
+
// 默认路径不存在时静默返回 null
|
|
13
|
+
if (!configFilePath && !fs.existsSync(configPath)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
// 显式指定路径不存在时抛错
|
|
17
|
+
if (configFilePath && !fs.existsSync(configPath)) {
|
|
18
|
+
throw new Error(`SSH config file not found: ${configPath}`);
|
|
19
|
+
}
|
|
20
|
+
const blocks = parseConfigFile(configPath, new Set());
|
|
21
|
+
return matchHost(hostAlias, blocks);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 解析 SSH 配置文件
|
|
25
|
+
*/
|
|
26
|
+
function parseConfigFile(filePath, visited) {
|
|
27
|
+
// 防止循环引用
|
|
28
|
+
const realPath = fs.realpathSync(filePath);
|
|
29
|
+
if (visited.has(realPath)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
visited.add(realPath);
|
|
33
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
34
|
+
const lines = content.split('\n');
|
|
35
|
+
const blocks = [];
|
|
36
|
+
let currentBlock = null;
|
|
37
|
+
for (let line of lines) {
|
|
38
|
+
// 移除注释和前后空白
|
|
39
|
+
const commentIndex = line.indexOf('#');
|
|
40
|
+
if (commentIndex !== -1) {
|
|
41
|
+
line = line.substring(0, commentIndex);
|
|
42
|
+
}
|
|
43
|
+
line = line.trim();
|
|
44
|
+
if (!line)
|
|
45
|
+
continue;
|
|
46
|
+
// 解析 Include 指令
|
|
47
|
+
if (line.toLowerCase().startsWith('include ')) {
|
|
48
|
+
if (currentBlock) {
|
|
49
|
+
blocks.push(currentBlock);
|
|
50
|
+
currentBlock = null;
|
|
51
|
+
}
|
|
52
|
+
const pattern = line.substring(8).trim();
|
|
53
|
+
const includePaths = expandIncludePath(pattern, path.dirname(filePath));
|
|
54
|
+
for (const includePath of includePaths) {
|
|
55
|
+
if (fs.existsSync(includePath)) {
|
|
56
|
+
blocks.push(...parseConfigFile(includePath, visited));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
// 解析 Host 行
|
|
62
|
+
if (line.toLowerCase().startsWith('host ')) {
|
|
63
|
+
if (currentBlock) {
|
|
64
|
+
blocks.push(currentBlock);
|
|
65
|
+
}
|
|
66
|
+
const hostPatterns = line.substring(5).trim().split(/\s+/);
|
|
67
|
+
currentBlock = {
|
|
68
|
+
patterns: hostPatterns,
|
|
69
|
+
config: new Map()
|
|
70
|
+
};
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// 解析配置项
|
|
74
|
+
if (!currentBlock) {
|
|
75
|
+
currentBlock = {
|
|
76
|
+
patterns: ['*'],
|
|
77
|
+
config: new Map()
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const spaceIndex = line.search(/\s/);
|
|
81
|
+
if (spaceIndex !== -1) {
|
|
82
|
+
const key = line.substring(0, spaceIndex).toLowerCase();
|
|
83
|
+
const value = line.substring(spaceIndex + 1).trim();
|
|
84
|
+
// 只保存第一次出现的值(SSH first-match-wins)
|
|
85
|
+
if (!currentBlock.config.has(key)) {
|
|
86
|
+
currentBlock.config.set(key, value);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (currentBlock) {
|
|
91
|
+
blocks.push(currentBlock);
|
|
92
|
+
}
|
|
93
|
+
return blocks;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 展开 Include 路径(支持 ~ 和通配符)
|
|
97
|
+
*/
|
|
98
|
+
function expandIncludePath(pattern, baseDir) {
|
|
99
|
+
// 展开 ~
|
|
100
|
+
if (pattern.startsWith('~/')) {
|
|
101
|
+
pattern = path.join(os.homedir(), pattern.substring(2));
|
|
102
|
+
}
|
|
103
|
+
else if (pattern.startsWith('~')) {
|
|
104
|
+
// ~user 形式不支持,直接返回空
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
else if (!path.isAbsolute(pattern)) {
|
|
108
|
+
// 相对路径相对于配置文件所在目录
|
|
109
|
+
pattern = path.join(baseDir, pattern);
|
|
110
|
+
}
|
|
111
|
+
// 使用 glob 展开通配符
|
|
112
|
+
try {
|
|
113
|
+
// Node.js 22+ 支持 fs.globSync
|
|
114
|
+
if (typeof fs.globSync === 'function') {
|
|
115
|
+
return fs.globSync(pattern);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
// glob 失败时静默跳过
|
|
120
|
+
}
|
|
121
|
+
// 降级:无通配符时直接返回
|
|
122
|
+
if (!pattern.includes('*') && !pattern.includes('?')) {
|
|
123
|
+
return [pattern];
|
|
124
|
+
}
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* 匹配主机别名
|
|
129
|
+
*/
|
|
130
|
+
function matchHost(hostAlias, blocks) {
|
|
131
|
+
const result = {};
|
|
132
|
+
for (const block of blocks) {
|
|
133
|
+
const matched = hostBlockMatches(hostAlias, block.patterns);
|
|
134
|
+
if (!matched)
|
|
135
|
+
continue;
|
|
136
|
+
// first-match-wins:只取第一个匹配到的值
|
|
137
|
+
if (!result.hostName && block.config.has('hostname')) {
|
|
138
|
+
result.hostName = block.config.get('hostname');
|
|
139
|
+
}
|
|
140
|
+
if (!result.user && block.config.has('user')) {
|
|
141
|
+
result.user = block.config.get('user');
|
|
142
|
+
}
|
|
143
|
+
if (!result.port && block.config.has('port')) {
|
|
144
|
+
const portStr = block.config.get('port');
|
|
145
|
+
const portNum = parseInt(portStr, 10);
|
|
146
|
+
if (!isNaN(portNum)) {
|
|
147
|
+
result.port = portNum;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (!result.identityFile && block.config.has('identityfile')) {
|
|
151
|
+
result.identityFile = expandTilde(block.config.get('identityfile'));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return Object.keys(result).length > 0 ? result : null;
|
|
155
|
+
}
|
|
156
|
+
function hostBlockMatches(hostAlias, patterns) {
|
|
157
|
+
let positiveMatch = false;
|
|
158
|
+
for (const pattern of patterns) {
|
|
159
|
+
const isNegated = pattern.startsWith('!');
|
|
160
|
+
const patternBody = isNegated ? pattern.slice(1) : pattern;
|
|
161
|
+
if (!patternBody) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (hostPatternMatches(hostAlias, patternBody)) {
|
|
165
|
+
if (isNegated) {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
positiveMatch = true;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return positiveMatch;
|
|
172
|
+
}
|
|
173
|
+
function hostPatternMatches(hostAlias, pattern) {
|
|
174
|
+
if (pattern === '*') {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
const regexSource = pattern
|
|
178
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
179
|
+
.replace(/\*/g, '.*')
|
|
180
|
+
.replace(/\?/g, '.');
|
|
181
|
+
return new RegExp(`^${regexSource}$`).test(hostAlias);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* 展开路径中的 ~
|
|
185
|
+
*/
|
|
186
|
+
function expandTilde(filePath) {
|
|
187
|
+
if (filePath.startsWith('~/')) {
|
|
188
|
+
return path.join(os.homedir(), filePath.substring(2));
|
|
189
|
+
}
|
|
190
|
+
if (filePath === '~') {
|
|
191
|
+
return os.homedir();
|
|
192
|
+
}
|
|
193
|
+
return filePath;
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=ssh-config-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-config-parser.js","sourceRoot":"","sources":["../../src/utils/ssh-config-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { Logger } from "./logger.js";
|
|
2
|
+
/**
|
|
3
|
+
* Join the probes into a single remote command, each result introduced by a
|
|
4
|
+
* marker line.
|
|
5
|
+
*
|
|
6
|
+
* Running them separately costs one SSH channel per probe — open, pty request,
|
|
7
|
+
* exec and close, several round trips each — plus a remote shell per probe. In
|
|
8
|
+
* shell transport it is worse still: the per-connection queue serialises them,
|
|
9
|
+
* so the first command the user issues after connecting waits behind all of
|
|
10
|
+
* them.
|
|
11
|
+
*
|
|
12
|
+
* Every probe is wrapped so that a missing tool or a non-zero exit cannot
|
|
13
|
+
* abort the rest, and the whole script ends successfully.
|
|
14
|
+
*/
|
|
15
|
+
function buildStatusScript(commands, marker) {
|
|
16
|
+
const probes = Object.entries(commands).map(([field, command]) => `printf '\\n${marker}${field}\\n'; { ${command}; } 2>/dev/null`);
|
|
17
|
+
return `${probes.join("; ")}; true`;
|
|
18
|
+
}
|
|
19
|
+
function parseStatusScriptOutput(output, marker) {
|
|
20
|
+
const values = new Map();
|
|
21
|
+
const normalized = output.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
22
|
+
// Splitting on a capturing group yields [before, field, value, field, ...].
|
|
23
|
+
const segments = normalized.split(new RegExp(`\\n?${marker}(\\w+)\\n`));
|
|
24
|
+
for (let index = 1; index < segments.length; index += 2) {
|
|
25
|
+
values.set(segments[index], (segments[index + 1] ?? "").trim());
|
|
26
|
+
}
|
|
27
|
+
return values;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Collect system status information from remote server
|
|
31
|
+
*/
|
|
32
|
+
export async function collectSystemStatus(runCommand, connectionName, isCommandAllowed = () => true) {
|
|
33
|
+
const status = {
|
|
34
|
+
reachable: true,
|
|
35
|
+
lastUpdated: new Date().toISOString(),
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
// Collected in a single remote command; see buildStatusScript.
|
|
39
|
+
const commands = {
|
|
40
|
+
hostname: "hostname",
|
|
41
|
+
ipAddresses: "ip -o addr show | awk '{print $4}' | grep -v '^127\\.' | cut -d'/' -f1",
|
|
42
|
+
osName: "uname -s",
|
|
43
|
+
osVersion: "cat /etc/os-release 2>/dev/null | grep '^PRETTY_NAME=' | cut -d'=' -f2 | tr -d '\"' || uname -o",
|
|
44
|
+
kernelVersion: "uname -r",
|
|
45
|
+
uptime: "uptime -p 2>/dev/null || uptime | awk -F'up ' '{print $2}' | awk -F',' '{print $1}'",
|
|
46
|
+
diskSpace: "df -h / | tail -1 | awk '{print \"free:\" $4 \" total:\" $2}'",
|
|
47
|
+
memory: "free -h | grep '^Mem:' | awk '{print \"free:\" $7 \" total:\" $2}'",
|
|
48
|
+
cpuName: "sh -c '(lscpu 2>/dev/null | grep \"^Model name:\" | cut -d\":\" -f2 | xargs || cat /proc/cpuinfo 2>/dev/null | grep \"model name\" | head -1 | cut -d\":\" -f2 | xargs || echo \"$(nproc 2>/dev/null || echo '\''?'\'')-core $(uname -m 2>/dev/null || echo '\''unknown'\'') processor\") || true'",
|
|
49
|
+
cpuUsage: "top -bn1 | grep 'Cpu(s)' | sed 's/.*, *\\([0-9.]*\\)%* id.*/\\1/' | awk '{print 100 - $1}'",
|
|
50
|
+
gpus: "sh -c '(nvidia-smi --query-gpu=name,utilization.gpu --format=csv,noheader,nounits 2>/dev/null | while IFS=\",\" read -r name usage; do echo \"NVIDIA|${name}|${usage}\"; done || lspci | grep -iE \"vga|3d|display\" | while read -r line; do gpu_name=$(echo \"$line\" | cut -d\":\" -f3 | xargs); echo \"OTHER|${gpu_name}|\"; done) || true'",
|
|
51
|
+
gpuPaths: "ls -1 /dev/dri/card* 2>/dev/null | sort -V || echo ''",
|
|
52
|
+
drives: "df -h | awk 'NR>1 && $1 !~ /^(tmpfs|devtmpfs|overlay|shfs|rootfs)$/ && $6 !~ /^(\\/dev|\\/run|\\/sys|\\/proc|\\/boot|\\/usr|\\/lib)$/ && $6 != \"\" {print $1\"|\"$2\"|\"$3\"|\"$4\"|\"$5\"|\"$6}'",
|
|
53
|
+
// Old gpuPaths: "sh -c '(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null | head -1 || rocm-smi --showuse 2>/dev/null | grep -i \"GPU use\" | head -1 | awk \"{print \\$NF}\" | tr -d \"%\" || radeontop -l 1 -d - 2>/dev/null | tail -1 | sed -n \"s/.*gpu \\([0-9.]*\\)%.*/\\1/p\" || intel_gpu_top -l 1 -o - 2>/dev/null | tail -1 | awk \"{print \\$NF}\" | tr -d \"%\" || echo \"N/A\") || echo \"N/A\"'",
|
|
54
|
+
processes: "ps aux | wc -l",
|
|
55
|
+
threads: "ps -eLf | wc -l",
|
|
56
|
+
servicesRunning: "systemctl list-units --type=service --state=running 2>/dev/null | wc -l || service --status-all 2>/dev/null | grep running | wc -l || echo '0'",
|
|
57
|
+
servicesInstalled: "systemctl list-unit-files --type=service 2>/dev/null | wc -l || ls /etc/init.d/ 2>/dev/null | wc -l || echo '0'",
|
|
58
|
+
};
|
|
59
|
+
// Validate each probe before batching. Validating only the combined script
|
|
60
|
+
// would allow one whitelist match to authorize every command in it.
|
|
61
|
+
const allowedCommands = Object.fromEntries(Object.entries(commands).filter(([, command]) => isCommandAllowed(command, connectionName)));
|
|
62
|
+
// Execute the allowed probes and collect results.
|
|
63
|
+
const marker = `__MCP_FIELD_${Math.random().toString(16).slice(2, 10)}_`;
|
|
64
|
+
let values = new Map();
|
|
65
|
+
try {
|
|
66
|
+
if (Object.keys(allowedCommands).length === 0) {
|
|
67
|
+
return status;
|
|
68
|
+
}
|
|
69
|
+
values = parseStatusScriptOutput(await runCommand(buildStatusScript(allowedCommands, marker), connectionName), marker);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// A rejected command (an unreachable host, a command whitelist that does
|
|
73
|
+
// not admit the probe) leaves every field unset, the same as when the
|
|
74
|
+
// probes ran separately and each failed on its own.
|
|
75
|
+
}
|
|
76
|
+
// Parse results
|
|
77
|
+
const readField = (field) => values.get(field) ?? "";
|
|
78
|
+
const hostnameValue = readField("hostname");
|
|
79
|
+
const ipAddressesValue = readField("ipAddresses");
|
|
80
|
+
const osNameValue = readField("osName");
|
|
81
|
+
const osVersionValue = readField("osVersion");
|
|
82
|
+
const kernelVersionValue = readField("kernelVersion");
|
|
83
|
+
const uptimeValue = readField("uptime");
|
|
84
|
+
const diskSpaceValue = readField("diskSpace");
|
|
85
|
+
const memoryValue = readField("memory");
|
|
86
|
+
const cpuNameValue = readField("cpuName");
|
|
87
|
+
const cpuUsageValue = readField("cpuUsage");
|
|
88
|
+
const gpusValue = readField("gpus");
|
|
89
|
+
const gpuPathsValue = readField("gpuPaths");
|
|
90
|
+
const drivesValue = readField("drives");
|
|
91
|
+
const processesValue = readField("processes");
|
|
92
|
+
const threadsValue = readField("threads");
|
|
93
|
+
const servicesRunningValue = readField("servicesRunning");
|
|
94
|
+
const servicesInstalledValue = readField("servicesInstalled");
|
|
95
|
+
if (hostnameValue) {
|
|
96
|
+
status.hostname = hostnameValue;
|
|
97
|
+
}
|
|
98
|
+
if (ipAddressesValue) {
|
|
99
|
+
status.ipAddresses = ipAddressesValue
|
|
100
|
+
.split("\n")
|
|
101
|
+
.filter((ip) => ip.trim() && !ip.includes("127.0.0.1"));
|
|
102
|
+
}
|
|
103
|
+
if (osNameValue) {
|
|
104
|
+
status.osName = osNameValue;
|
|
105
|
+
}
|
|
106
|
+
if (osVersionValue) {
|
|
107
|
+
status.osVersion = osVersionValue;
|
|
108
|
+
}
|
|
109
|
+
if (kernelVersionValue) {
|
|
110
|
+
status.kernelVersion = kernelVersionValue;
|
|
111
|
+
}
|
|
112
|
+
if (uptimeValue) {
|
|
113
|
+
status.uptime = uptimeValue;
|
|
114
|
+
}
|
|
115
|
+
if (diskSpaceValue) {
|
|
116
|
+
const diskMatch = diskSpaceValue.match(/free:(\S+)\s+total:(\S+)/);
|
|
117
|
+
if (diskMatch) {
|
|
118
|
+
status.diskSpace = {
|
|
119
|
+
free: diskMatch[1],
|
|
120
|
+
total: diskMatch[2],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (memoryValue) {
|
|
125
|
+
const memMatch = memoryValue.match(/free:(\S+)\s+total:(\S+)/);
|
|
126
|
+
if (memMatch) {
|
|
127
|
+
status.memory = {
|
|
128
|
+
free: memMatch[1],
|
|
129
|
+
total: memMatch[2],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Handle CPU name
|
|
134
|
+
if (cpuNameValue && cpuNameValue.trim()) {
|
|
135
|
+
status.cpu = {
|
|
136
|
+
name: cpuNameValue.trim(),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (status.cpu && cpuUsageValue && cpuUsageValue !== "N/A") {
|
|
140
|
+
status.cpu.usage = `${parseFloat(cpuUsageValue).toFixed(1)}%`;
|
|
141
|
+
}
|
|
142
|
+
// Handle GPUs
|
|
143
|
+
if (gpusValue && gpusValue.trim()) {
|
|
144
|
+
const gpuPaths = [];
|
|
145
|
+
if (gpuPathsValue) {
|
|
146
|
+
gpuPaths.push(...gpuPathsValue.split("\n").filter((p) => p.trim()));
|
|
147
|
+
}
|
|
148
|
+
const gpuLines = gpusValue.split("\n").filter((line) => line.trim());
|
|
149
|
+
const gpus = [];
|
|
150
|
+
gpuLines.forEach((line, index) => {
|
|
151
|
+
const parts = line.split("|");
|
|
152
|
+
if (parts.length >= 2) {
|
|
153
|
+
const name = parts[1].trim();
|
|
154
|
+
const usage = parts[2]?.trim();
|
|
155
|
+
if (name && name !== "N/A") {
|
|
156
|
+
const gpu = {
|
|
157
|
+
name: name,
|
|
158
|
+
};
|
|
159
|
+
if (usage && usage.trim() !== "" && usage.trim() !== "N/A" && !isNaN(parseFloat(usage.trim()))) {
|
|
160
|
+
gpu.usage = `${parseFloat(usage.trim()).toFixed(1)}%`;
|
|
161
|
+
}
|
|
162
|
+
// Assign path if available
|
|
163
|
+
if (gpuPaths[index]) {
|
|
164
|
+
gpu.path = gpuPaths[index];
|
|
165
|
+
}
|
|
166
|
+
gpus.push(gpu);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
if (gpus.length > 0) {
|
|
171
|
+
status.gpus = gpus;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Handle drives
|
|
175
|
+
if (drivesValue && drivesValue.trim()) {
|
|
176
|
+
const driveLines = drivesValue.split("\n").filter((line) => line.trim());
|
|
177
|
+
const drives = [];
|
|
178
|
+
driveLines.forEach((line) => {
|
|
179
|
+
const parts = line.split("|");
|
|
180
|
+
if (parts.length >= 6) {
|
|
181
|
+
const device = parts[0].trim();
|
|
182
|
+
const total = parts[1].trim();
|
|
183
|
+
const used = parts[2].trim();
|
|
184
|
+
const free = parts[3].trim();
|
|
185
|
+
const usagePercent = parts[4].trim();
|
|
186
|
+
const mountPoint = parts[5].trim();
|
|
187
|
+
if (device && mountPoint) {
|
|
188
|
+
drives.push({
|
|
189
|
+
device,
|
|
190
|
+
mountPoint,
|
|
191
|
+
total,
|
|
192
|
+
used,
|
|
193
|
+
free,
|
|
194
|
+
usagePercent,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
if (drives.length > 0) {
|
|
200
|
+
status.drives = drives;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (processesValue || threadsValue) {
|
|
204
|
+
const processCount = parseInt(processesValue || "0", 10) - 1; // Subtract header line
|
|
205
|
+
const threadCount = parseInt(threadsValue || "0", 10) - 1; // Subtract header line
|
|
206
|
+
status.processes = {
|
|
207
|
+
running: Math.max(0, processCount),
|
|
208
|
+
threads: Math.max(0, threadCount),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (servicesRunningValue || servicesInstalledValue) {
|
|
212
|
+
const runningCount = parseInt(servicesRunningValue || "0", 10) - 1; // Subtract header line
|
|
213
|
+
const installedCount = parseInt(servicesInstalledValue || "0", 10) - 1; // Subtract header line
|
|
214
|
+
status.services = {
|
|
215
|
+
running: Math.max(0, runningCount),
|
|
216
|
+
installed: Math.max(0, installedCount),
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
Logger.log(`Failed to collect system status for [${connectionName}]: ${error.message}`, "error");
|
|
222
|
+
status.reachable = false;
|
|
223
|
+
}
|
|
224
|
+
return status;
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=status-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-collector.js","sourceRoot":"","sources":["../../src/utils/status-collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAYrC;;;;;;;;;;;;GAYG;AACH,SAAS,iBAAiB,CACxB,QAAgC,EAChC,MAAc;IAEd,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,CACzC,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,EAAE,CACnB,cAAc,MAAM,GAAG,KAAK,WAAW,OAAO,iBAAiB,CAClE,CAAC;IACF,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;AACtC,CAAC;AAED,SAAS,uBAAuB,CAC9B,MAAc,EACd,MAAc;IAEd,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACtE,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,MAAM,WAAW,CAAC,CAAC,CAAC;IAExE,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,UAA+B,EAC/B,cAAsB,EACtB,mBAA4C,GAAG,EAAE,CAAC,IAAI;IAEtD,MAAM,MAAM,GAAiB;QAC3B,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACtC,CAAC;IAEF,IAAI,CAAC;QACH,+DAA+D;QAC/D,MAAM,QAAQ,GAAG;YACf,QAAQ,EAAE,UAAU;YACpB,WAAW,EAAE,wEAAwE;YACrF,MAAM,EAAE,UAAU;YAClB,SAAS,EAAE,iGAAiG;YAC5G,aAAa,EAAE,UAAU;YACzB,MAAM,EAAE,qFAAqF;YAC7F,SAAS,EAAE,+DAA+D;YAC1E,MAAM,EAAE,oEAAoE;YAC5E,OAAO,EAAE,oSAAoS;YAC7S,QAAQ,EAAE,4FAA4F;YACtG,IAAI,EAAE,iVAAiV;YACvV,QAAQ,EAAE,uDAAuD;YACjE,MAAM,EAAE,oMAAoM;YAC5M,gbAAgb;YAChb,SAAS,EAAE,gBAAgB;YAC3B,OAAO,EAAE,iBAAiB;YAC1B,eAAe,EAAE,gJAAgJ;YACjK,iBAAiB,EAAE,iHAAiH;SACrI,CAAC;QAEF,2EAA2E;QAC3E,oEAAoE;QACpE,MAAM,eAAe,GAAG,MAAM,CAAC,WAAW,CACxC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,EAAE,CAC9C,gBAAgB,CAAC,OAAO,EAAE,cAAc,CAAC,CAC1C,CACF,CAAC;QAEF,kDAAkD;QAClD,MAAM,MAAM,GAAG,eAAe,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC;QACzE,IAAI,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACvC,IAAI,CAAC;YACH,IAAI,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9C,OAAO,MAAM,CAAC;YAChB,CAAC;YACD,MAAM,GAAG,uBAAuB,CAC9B,MAAM,UAAU,CACd,iBAAiB,CAAC,eAAe,EAAE,MAAM,CAAC,EAC1C,cAAc,CACf,EACD,MAAM,CACP,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,sEAAsE;YACtE,oDAAoD;QACtD,CAAC;QAED,gBAAgB;QAChB,MAAM,SAAS,GAAG,CAAC,KAA4B,EAAU,EAAE,CACzD,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAE1B,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,gBAAgB,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,cAAc,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,kBAAkB,GAAG,SAAS,CAAC,eAAe,CAAC,CAAC;QACtD,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,cAAc,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;QACpC,MAAM,aAAa,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,cAAc,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC;QAC9C,MAAM,YAAY,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,oBAAoB,GAAG,SAAS,CAAC,iBAAiB,CAAC,CAAC;QAC1D,MAAM,sBAAsB,GAAG,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAE9D,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,CAAC,QAAQ,GAAG,aAAa,CAAC;QAClC,CAAC;QAED,IAAI,gBAAgB,EAAE,CAAC;YACrB,MAAM,CAAC,WAAW,GAAG,gBAAgB;iBAClC,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC;QAC9B,CAAC;QAED,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,CAAC,SAAS,GAAG,cAAc,CAAC;QACpC,CAAC;QAED,IAAI,kBAAkB,EAAE,CAAC;YACvB,MAAM,CAAC,aAAa,GAAG,kBAAkB,CAAC;QAC5C,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC;QAC9B,CAAC;QAED,IAAI,cAAc,EAAE,CAAC;YACnB,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YACnE,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,CAAC,SAAS,GAAG;oBACjB,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;oBAClB,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;iBACpB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;YAC/D,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CAAC,MAAM,GAAG;oBACd,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;oBACjB,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;iBACnB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,kBAAkB;QAClB,IAAI,YAAY,IAAI,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC;YACxC,MAAM,CAAC,GAAG,GAAG;gBACX,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE;aAC1B,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,CAAC,GAAG,IAAI,aAAa,IAAI,aAAa,KAAK,KAAK,EAAE,CAAC;YAC3D,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;QAChE,CAAC;QAED,cAAc;QACd,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAa,EAAE,CAAC;YAC9B,IAAI,aAAa,EAAE,CAAC;gBAClB,QAAQ,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YACtE,CAAC;YAED,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACrE,MAAM,IAAI,GAA2D,EAAE,CAAC;YAExE,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;gBAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC9B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;oBACtB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;oBAE/B,IAAI,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;wBAC3B,MAAM,GAAG,GAAoD;4BAC3D,IAAI,EAAE,IAAI;yBACX,CAAC;wBAEF,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;4BAC/F,GAAG,CAAC,KAAK,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;wBACxD,CAAC;wBAED,2BAA2B;wBAC3B,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;4BACpB,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;wBAC7B,CAAC;wBAED,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;oBACjB,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;YACrB,CAAC;QACH,CAAC;QAED,gBAAgB;QAChB,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;YACtC,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACzE,MAAM,MAAM,GAQP,EAAE,CAAC;YAER,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;gBAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC9B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;oBACtB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC/B,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC7B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAC7B,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBACrC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;oBAEnC,IAAI,MAAM,IAAI,UAAU,EAAE,CAAC;wBACzB,MAAM,CAAC,IAAI,CAAC;4BACV,MAAM;4BACN,UAAU;4BACV,KAAK;4BACL,IAAI;4BACJ,IAAI;4BACJ,YAAY;yBACb,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;YACzB,CAAC;QACH,CAAC;QAED,IAAI,cAAc,IAAI,YAAY,EAAE,CAAC;YACnC,MAAM,YAAY,GAAG,QAAQ,CAAC,cAAc,IAAI,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,uBAAuB;YACrF,MAAM,WAAW,GAAG,QAAQ,CAAC,YAAY,IAAI,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,uBAAuB;YAClF,MAAM,CAAC,SAAS,GAAG;gBACjB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC;gBAClC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC;aAClC,CAAC;QACJ,CAAC;QAED,IAAI,oBAAoB,IAAI,sBAAsB,EAAE,CAAC;YACnD,MAAM,YAAY,GAAG,QAAQ,CAAC,oBAAoB,IAAI,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,uBAAuB;YAC3F,MAAM,cAAc,GAAG,QAAQ,CAAC,sBAAsB,IAAI,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,uBAAuB;YAC/F,MAAM,CAAC,QAAQ,GAAG;gBAChB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC;gBAClC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,CAAC;aACvC,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,GAAG,CACR,wCAAwC,cAAc,MAAO,KAAe,CAAC,OAAO,EAAE,EACtF,OAAO,CACR,CAAC;QACF,MAAM,CAAC,SAAS,GAAG,KAAK,CAAC;IAC3B,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export class ToolError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
retriable;
|
|
4
|
+
constructor(code, message, retriable = false) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.retriable = retriable;
|
|
8
|
+
this.name = "ToolError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function toToolError(error, fallbackCode) {
|
|
12
|
+
if (error instanceof ToolError) {
|
|
13
|
+
return error;
|
|
14
|
+
}
|
|
15
|
+
if (error instanceof Error) {
|
|
16
|
+
return new ToolError(fallbackCode, error.message, false);
|
|
17
|
+
}
|
|
18
|
+
return new ToolError(fallbackCode, String(error), false);
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=tool-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-error.js","sourceRoot":"","sources":["../../src/utils/tool-error.ts"],"names":[],"mappings":"AAqBA,MAAM,OAAO,SAAU,SAAQ,KAAK;IAEhB;IAEA;IAHlB,YACkB,IAAmB,EACnC,OAAe,EACC,YAAqB,KAAK;QAE1C,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAe;QAEnB,cAAS,GAAT,SAAS,CAAiB;QAG1C,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,UAAU,WAAW,CACzB,KAAc,EACd,YAA2B;IAE3B,IAAI,KAAK,YAAY,SAAS,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,IAAI,SAAS,CAAC,YAAY,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,IAAI,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;AAC3D,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lzm04521/ssh-mcp-server",
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "SSH-based MCP Server (基于 SSH 的 MCP 服务器)",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18.0.0"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/lzm04521/ssh-mcp-server.git"
|
|
13
|
+
},
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/lzm04521/ssh-mcp-server/issues"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/lzm04521/ssh-mcp-server#readme",
|
|
18
|
+
"bin": {
|
|
19
|
+
"ssh-mcp-server": "build/index.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"test": "node scripts/run-tests.js",
|
|
23
|
+
"test:watch": "node --test --watch test/**/*.test.js",
|
|
24
|
+
"build": "node scripts/build.js",
|
|
25
|
+
"build:watch": "tsc --watch",
|
|
26
|
+
"build:admin": "npm --prefix admin-web run build",
|
|
27
|
+
"dev:watch": "concurrently --shell bash \"npm:build:watch\" \"npm:dev:frontend\"",
|
|
28
|
+
"dev:backend": "npm run build:watch",
|
|
29
|
+
"dev:frontend": "npm --prefix admin-web run dev",
|
|
30
|
+
"dev:admin": "node build/index.js --admin --admin-port 61823",
|
|
31
|
+
"dev:admin:watch": "concurrently --shell bash \"npm:build:watch\" \"npx nodemon --watch build --delay 500 build/index.js --admin --admin-port 61823\"",
|
|
32
|
+
"prepublishOnly": "npm run build",
|
|
33
|
+
"prepack": "node scripts/prepack.js",
|
|
34
|
+
"version:bump": "node scripts/bump-version.mjs"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"ssh",
|
|
38
|
+
"mcp",
|
|
39
|
+
"server",
|
|
40
|
+
"cli"
|
|
41
|
+
],
|
|
42
|
+
"author": "lzm04521",
|
|
43
|
+
"license": "ISC",
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@fastify/static": "^10.1.3",
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.27.0",
|
|
47
|
+
"better-sqlite3": "^13.0.3",
|
|
48
|
+
"chokidar": "^5.0.0",
|
|
49
|
+
"fastify": "^5.12.1",
|
|
50
|
+
"socks": "^2.8.7",
|
|
51
|
+
"ssh2": "^1.17.0",
|
|
52
|
+
"zod": "^4.3.6"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^22.13.10",
|
|
56
|
+
"@types/ssh2": "^1.15.5",
|
|
57
|
+
"c8": "^12.0.0",
|
|
58
|
+
"concurrently": "^10.0.5",
|
|
59
|
+
"cross-env": "^10.1.0",
|
|
60
|
+
"nodemon": "^3.1.14",
|
|
61
|
+
"typescript": "^5.8.2"
|
|
62
|
+
},
|
|
63
|
+
"files": [
|
|
64
|
+
"build/**/*",
|
|
65
|
+
"admin-web/dist/**/*",
|
|
66
|
+
"README.md",
|
|
67
|
+
"LICENSE"
|
|
68
|
+
]
|
|
69
|
+
}
|