snow-flow 2.9.4 → 2.9.6
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/dist/mcp/servicenow-deployment-mcp.js +132 -55
- package/dist/utils/deployment-auth-fix.d.ts +43 -0
- package/dist/utils/deployment-auth-fix.js +272 -0
- package/dist/utils/mcp-process-manager-safe.d.ts +70 -0
- package/dist/utils/mcp-process-manager-safe.js +319 -0
- package/dist/utils/mcp-process-manager.backup.d.ts +58 -0
- package/dist/utils/mcp-process-manager.backup.js +220 -0
- package/dist/utils/mcp-process-manager.d.ts +22 -10
- package/dist/utils/mcp-process-manager.js +194 -95
- package/package.json +3 -1
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* MCP Process Manager
|
|
4
|
-
*
|
|
3
|
+
* MCP Process Manager - SAFE VERSION
|
|
4
|
+
* Emergency fix for memory crash issues
|
|
5
|
+
* Implements graceful shutdown and memory-safe cleanup
|
|
5
6
|
*/
|
|
6
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.MCPProcessManager = void 0;
|
|
8
|
+
exports.mcpProcessManager = exports.MCPProcessManager = void 0;
|
|
8
9
|
const child_process_1 = require("child_process");
|
|
9
10
|
const logger_js_1 = require("./logger.js");
|
|
11
|
+
const util_1 = require("util");
|
|
12
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
10
13
|
const logger = new logger_js_1.Logger('MCPProcessManager');
|
|
11
14
|
class MCPProcessManager {
|
|
12
15
|
constructor() {
|
|
13
|
-
|
|
14
|
-
this.
|
|
15
|
-
this.
|
|
16
|
-
//
|
|
17
|
-
this.
|
|
16
|
+
// INCREASED LIMITS TO PREVENT AGGRESSIVE CLEANUP
|
|
17
|
+
this.MAX_MCP_SERVERS = parseInt(process.env.SNOW_MAX_MCP_SERVERS || '30'); // Increased from 10
|
|
18
|
+
this.MAX_MEMORY_MB = parseInt(process.env.SNOW_MCP_MEMORY_LIMIT || '3000'); // Increased from 1500
|
|
19
|
+
// DISABLED AUTOMATIC CLEANUP BY DEFAULT
|
|
20
|
+
this.CLEANUP_ENABLED = process.env.SNOW_MCP_CLEANUP_ENABLED === 'true'; // Off by default
|
|
21
|
+
this.CLEANUP_INTERVAL = parseInt(process.env.SNOW_MCP_CLEANUP_INTERVAL || '300000'); // 5 minutes instead of 1
|
|
22
|
+
this.isCleaningUp = false;
|
|
23
|
+
// Only start cleanup if explicitly enabled
|
|
24
|
+
if (this.CLEANUP_ENABLED) {
|
|
25
|
+
logger.warn('⚠️ MCP cleanup is ENABLED - monitor for memory issues');
|
|
26
|
+
this.startPeriodicCleanup();
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
logger.info('✅ MCP cleanup is DISABLED for stability');
|
|
30
|
+
}
|
|
18
31
|
}
|
|
19
32
|
static getInstance() {
|
|
20
33
|
if (!MCPProcessManager.instance) {
|
|
@@ -27,24 +40,32 @@ class MCPProcessManager {
|
|
|
27
40
|
*/
|
|
28
41
|
canSpawnServer() {
|
|
29
42
|
const status = this.getSystemStatus();
|
|
43
|
+
// More lenient limits
|
|
30
44
|
if (status.processCount >= this.MAX_MCP_SERVERS) {
|
|
31
|
-
logger.warn(
|
|
32
|
-
|
|
45
|
+
logger.warn(`⚠️ At server limit (${status.processCount}/${this.MAX_MCP_SERVERS}) - consider manual cleanup`);
|
|
46
|
+
// Still allow spawning unless critically high
|
|
47
|
+
if (status.processCount >= this.MAX_MCP_SERVERS * 1.5) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
33
50
|
}
|
|
34
51
|
if (status.memoryUsageMB > this.MAX_MEMORY_MB) {
|
|
35
|
-
logger.warn(
|
|
36
|
-
|
|
52
|
+
logger.warn(`⚠️ High memory usage (${status.memoryUsageMB}MB > ${this.MAX_MEMORY_MB}MB)`);
|
|
53
|
+
// Still allow spawning unless critically high
|
|
54
|
+
if (status.memoryUsageMB > this.MAX_MEMORY_MB * 1.5) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
37
57
|
}
|
|
38
58
|
return true;
|
|
39
59
|
}
|
|
40
60
|
/**
|
|
41
|
-
* Get current MCP system status
|
|
61
|
+
* Get current MCP system status - SAFER VERSION
|
|
42
62
|
*/
|
|
43
63
|
getSystemStatus() {
|
|
44
64
|
try {
|
|
45
|
-
//
|
|
46
|
-
const psOutput = (0, child_process_1.execSync)('ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep', {
|
|
47
|
-
encoding: 'utf8'
|
|
65
|
+
// More careful process detection
|
|
66
|
+
const psOutput = (0, child_process_1.execSync)('ps aux | grep -E "mcp|servicenow.*mcp" | grep -v grep || true', {
|
|
67
|
+
encoding: 'utf8',
|
|
68
|
+
maxBuffer: 1024 * 1024 // 1MB buffer limit
|
|
48
69
|
}).trim();
|
|
49
70
|
if (!psOutput) {
|
|
50
71
|
return {
|
|
@@ -53,17 +74,24 @@ class MCPProcessManager {
|
|
|
53
74
|
processes: []
|
|
54
75
|
};
|
|
55
76
|
}
|
|
56
|
-
const lines = psOutput.split('\n');
|
|
77
|
+
const lines = psOutput.split('\n').slice(0, 100); // Limit to 100 processes
|
|
57
78
|
const processes = [];
|
|
58
79
|
let totalMemory = 0;
|
|
59
80
|
for (const line of lines) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
81
|
+
try {
|
|
82
|
+
const parts = line.split(/\s+/);
|
|
83
|
+
if (parts.length > 10) {
|
|
84
|
+
const pid = parseInt(parts[1]);
|
|
85
|
+
const memory = Math.round(parseInt(parts[5]) / 1024); // Convert KB to MB
|
|
86
|
+
const name = parts.slice(10).join(' ').substring(0, 100); // Limit name length
|
|
87
|
+
if (!isNaN(pid) && !isNaN(memory)) {
|
|
88
|
+
processes.push({ pid, memory, name });
|
|
89
|
+
totalMemory += memory;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
// Skip malformed lines
|
|
67
95
|
}
|
|
68
96
|
}
|
|
69
97
|
return {
|
|
@@ -73,7 +101,7 @@ class MCPProcessManager {
|
|
|
73
101
|
};
|
|
74
102
|
}
|
|
75
103
|
catch (error) {
|
|
76
|
-
|
|
104
|
+
logger.error('Failed to get system status:', error);
|
|
77
105
|
return {
|
|
78
106
|
processCount: 0,
|
|
79
107
|
memoryUsageMB: 0,
|
|
@@ -82,100 +110,149 @@ class MCPProcessManager {
|
|
|
82
110
|
}
|
|
83
111
|
}
|
|
84
112
|
/**
|
|
85
|
-
*
|
|
113
|
+
* Gracefully shutdown a process with timeout
|
|
86
114
|
*/
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
115
|
+
async gracefulKill(pid, name) {
|
|
116
|
+
try {
|
|
117
|
+
// First try SIGTERM for graceful shutdown
|
|
118
|
+
process.kill(pid, 'SIGTERM');
|
|
119
|
+
// Wait up to 5 seconds for graceful shutdown
|
|
120
|
+
let waited = 0;
|
|
121
|
+
while (waited < 5000) {
|
|
122
|
+
try {
|
|
123
|
+
process.kill(pid, 0); // Check if still alive
|
|
124
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
125
|
+
waited += 500;
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Process terminated
|
|
129
|
+
logger.info(`✅ Gracefully stopped ${name} (PID: ${pid})`);
|
|
130
|
+
return true;
|
|
98
131
|
}
|
|
99
|
-
serverGroups.get(serverType).push({
|
|
100
|
-
pid: proc.pid,
|
|
101
|
-
memory: proc.memory
|
|
102
|
-
});
|
|
103
132
|
}
|
|
133
|
+
// Force kill if still alive
|
|
134
|
+
process.kill(pid, 'SIGKILL');
|
|
135
|
+
logger.warn(`⚠️ Force killed ${name} (PID: ${pid})`);
|
|
136
|
+
return true;
|
|
104
137
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// Sort by PID (older PIDs first) and keep the first one
|
|
110
|
-
procs.sort((a, b) => a.pid - b.pid);
|
|
111
|
-
for (let i = 1; i < procs.length; i++) {
|
|
112
|
-
try {
|
|
113
|
-
process.kill(procs[i].pid, 'SIGTERM');
|
|
114
|
-
logger.info(`Killed duplicate ${serverType}-mcp (PID: ${procs[i].pid})`);
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
// Process might already be dead
|
|
118
|
-
}
|
|
119
|
-
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
if (error.code === 'ESRCH') {
|
|
140
|
+
// Process already dead
|
|
141
|
+
return true;
|
|
120
142
|
}
|
|
143
|
+
logger.error(`Failed to kill ${name} (PID: ${pid}):`, error);
|
|
144
|
+
return false;
|
|
121
145
|
}
|
|
122
146
|
}
|
|
123
147
|
/**
|
|
124
|
-
* Kill
|
|
148
|
+
* Kill duplicate MCP servers - SAFER VERSION
|
|
125
149
|
*/
|
|
126
|
-
|
|
150
|
+
async killDuplicates() {
|
|
151
|
+
if (this.isCleaningUp) {
|
|
152
|
+
logger.warn('Cleanup already in progress, skipping...');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.isCleaningUp = true;
|
|
127
156
|
try {
|
|
128
|
-
|
|
129
|
-
|
|
157
|
+
const status = this.getSystemStatus();
|
|
158
|
+
// Group processes by server type
|
|
159
|
+
const serverGroups = new Map();
|
|
160
|
+
for (const proc of status.processes) {
|
|
161
|
+
const match = proc.name.match(/servicenow-([^-]+)-mcp\.js/);
|
|
162
|
+
if (match) {
|
|
163
|
+
const serverType = match[1];
|
|
164
|
+
if (!serverGroups.has(serverType)) {
|
|
165
|
+
serverGroups.set(serverType, []);
|
|
166
|
+
}
|
|
167
|
+
serverGroups.get(serverType).push({
|
|
168
|
+
pid: proc.pid,
|
|
169
|
+
memory: proc.memory
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Kill duplicates gracefully
|
|
174
|
+
for (const [serverType, procs] of serverGroups) {
|
|
175
|
+
if (procs.length > 2) { // Only clean if more than 2 duplicates
|
|
176
|
+
logger.info(`Found ${procs.length} instances of ${serverType}-mcp`);
|
|
177
|
+
// Sort by memory usage (kill highest consumers first)
|
|
178
|
+
procs.sort((a, b) => b.memory - a.memory);
|
|
179
|
+
// Keep 2 instances, kill the rest
|
|
180
|
+
for (let i = 2; i < procs.length; i++) {
|
|
181
|
+
await this.gracefulKill(procs[i].pid, `${serverType}-mcp`);
|
|
182
|
+
// Wait between kills to avoid memory spike
|
|
183
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
130
187
|
}
|
|
131
|
-
|
|
132
|
-
|
|
188
|
+
finally {
|
|
189
|
+
this.isCleaningUp = false;
|
|
133
190
|
}
|
|
134
191
|
}
|
|
135
192
|
/**
|
|
136
|
-
*
|
|
193
|
+
* Emergency cleanup - only for critical situations
|
|
137
194
|
*/
|
|
138
|
-
|
|
195
|
+
async emergencyCleanup() {
|
|
196
|
+
logger.warn('🚨 EMERGENCY CLEANUP INITIATED');
|
|
139
197
|
const status = this.getSystemStatus();
|
|
140
|
-
if (status.
|
|
141
|
-
logger.
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
if (status.memoryUsageMB > this.MAX_MEMORY_MB) {
|
|
145
|
-
logger.warn(`🧹 Memory usage too high (${status.memoryUsageMB}MB), killing oldest processes...`);
|
|
146
|
-
// Sort by memory usage and kill the highest consumers
|
|
198
|
+
if (status.memoryUsageMB > this.MAX_MEMORY_MB * 2) {
|
|
199
|
+
logger.error(`🔴 CRITICAL: Memory usage ${status.memoryUsageMB}MB - killing highest consumers`);
|
|
200
|
+
// Sort by memory usage
|
|
147
201
|
const sorted = status.processes.sort((a, b) => b.memory - a.memory);
|
|
148
|
-
|
|
149
|
-
for (
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
catch (error) {
|
|
159
|
-
// Process might already be dead
|
|
160
|
-
}
|
|
202
|
+
// Kill top 3 memory consumers
|
|
203
|
+
for (let i = 0; i < Math.min(3, sorted.length); i++) {
|
|
204
|
+
await this.gracefulKill(sorted[i].pid, sorted[i].name);
|
|
205
|
+
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s between kills
|
|
206
|
+
}
|
|
207
|
+
// Force garbage collection if available
|
|
208
|
+
if (global.gc) {
|
|
209
|
+
global.gc();
|
|
210
|
+
logger.info('Forced garbage collection');
|
|
161
211
|
}
|
|
162
212
|
}
|
|
163
213
|
}
|
|
164
214
|
/**
|
|
165
|
-
*
|
|
215
|
+
* Safe cleanup - only when absolutely necessary
|
|
216
|
+
*/
|
|
217
|
+
async cleanup() {
|
|
218
|
+
if (!this.CLEANUP_ENABLED) {
|
|
219
|
+
logger.info('Cleanup disabled for stability');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (this.isCleaningUp) {
|
|
223
|
+
logger.warn('Cleanup already in progress');
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const status = this.getSystemStatus();
|
|
227
|
+
// Only cleanup if REALLY necessary
|
|
228
|
+
if (status.processCount > this.MAX_MCP_SERVERS * 1.5) {
|
|
229
|
+
logger.warn(`🧹 Too many MCP servers (${status.processCount}), cleaning duplicates...`);
|
|
230
|
+
await this.killDuplicates();
|
|
231
|
+
}
|
|
232
|
+
if (status.memoryUsageMB > this.MAX_MEMORY_MB * 1.5) {
|
|
233
|
+
await this.emergencyCleanup();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Start periodic cleanup - MUCH SAFER
|
|
166
238
|
*/
|
|
167
239
|
startPeriodicCleanup() {
|
|
168
|
-
// Clear existing timer
|
|
169
240
|
if (this.cleanupTimer) {
|
|
170
241
|
clearInterval(this.cleanupTimer);
|
|
171
242
|
}
|
|
172
|
-
//
|
|
173
|
-
this.cleanupTimer = setInterval(() => {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
243
|
+
// Only run cleanup when critically necessary
|
|
244
|
+
this.cleanupTimer = setInterval(async () => {
|
|
245
|
+
try {
|
|
246
|
+
const status = this.getSystemStatus();
|
|
247
|
+
// Only cleanup if CRITICALLY high
|
|
248
|
+
if (status.processCount > this.MAX_MCP_SERVERS * 2 ||
|
|
249
|
+
status.memoryUsageMB > this.MAX_MEMORY_MB * 2) {
|
|
250
|
+
logger.warn('🔄 Critical resource usage detected, running cleanup...');
|
|
251
|
+
await this.cleanup();
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
logger.error('Cleanup failed:', error);
|
|
179
256
|
}
|
|
180
257
|
}, this.CLEANUP_INTERVAL);
|
|
181
258
|
// Don't block process exit
|
|
@@ -188,6 +265,25 @@ class MCPProcessManager {
|
|
|
188
265
|
if (this.cleanupTimer) {
|
|
189
266
|
clearInterval(this.cleanupTimer);
|
|
190
267
|
this.cleanupTimer = undefined;
|
|
268
|
+
logger.info('✅ Periodic cleanup stopped');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Kill all MCP servers - USE WITH CAUTION
|
|
273
|
+
*/
|
|
274
|
+
async killAll() {
|
|
275
|
+
logger.warn('🔴 KILLING ALL MCP PROCESSES');
|
|
276
|
+
try {
|
|
277
|
+
// First try graceful shutdown
|
|
278
|
+
await execAsync('pkill -TERM -f mcp');
|
|
279
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
280
|
+
// Then force kill any remaining
|
|
281
|
+
await execAsync('pkill -KILL -f mcp');
|
|
282
|
+
logger.info('✅ All MCP processes terminated');
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
// pkill returns non-zero if no processes found
|
|
286
|
+
logger.info('No MCP processes to kill');
|
|
191
287
|
}
|
|
192
288
|
}
|
|
193
289
|
/**
|
|
@@ -198,6 +294,7 @@ class MCPProcessManager {
|
|
|
198
294
|
return `MCP Resources:
|
|
199
295
|
Processes: ${status.processCount}/${this.MAX_MCP_SERVERS} (${Math.round(status.processCount / this.MAX_MCP_SERVERS * 100)}%)
|
|
200
296
|
Memory: ${status.memoryUsageMB}MB/${this.MAX_MEMORY_MB}MB (${Math.round(status.memoryUsageMB / this.MAX_MEMORY_MB * 100)}%)
|
|
297
|
+
Cleanup: ${this.CLEANUP_ENABLED ? 'ENABLED' : 'DISABLED'}
|
|
201
298
|
Status: ${this.getHealthStatus()}`;
|
|
202
299
|
}
|
|
203
300
|
/**
|
|
@@ -207,14 +304,16 @@ class MCPProcessManager {
|
|
|
207
304
|
const status = this.getSystemStatus();
|
|
208
305
|
const processPercent = status.processCount / this.MAX_MCP_SERVERS;
|
|
209
306
|
const memoryPercent = status.memoryUsageMB / this.MAX_MEMORY_MB;
|
|
210
|
-
if (processPercent >
|
|
307
|
+
if (processPercent > 1.5 || memoryPercent > 1.5) {
|
|
211
308
|
return 'critical';
|
|
212
309
|
}
|
|
213
|
-
if (processPercent > 0
|
|
310
|
+
if (processPercent > 1.0 || memoryPercent > 1.0) {
|
|
214
311
|
return 'warning';
|
|
215
312
|
}
|
|
216
313
|
return 'healthy';
|
|
217
314
|
}
|
|
218
315
|
}
|
|
219
316
|
exports.MCPProcessManager = MCPProcessManager;
|
|
317
|
+
// Export singleton instance
|
|
318
|
+
exports.mcpProcessManager = MCPProcessManager.getInstance();
|
|
220
319
|
//# sourceMappingURL=mcp-process-manager.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.6",
|
|
4
4
|
"description": "Snow-Flow: ServiceNow Advanced Intelligence Platform - 100+ real MCP tools with AI-powered swarm orchestration and neural networks. Dynamic task categorization using AI. Machine learning for incident classification, change risk prediction, and anomaly detection. Zero Mock Data, 100% Real API Integration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
"reset-mcp": "node scripts/reset-mcp-servers.js",
|
|
23
23
|
"reset-mcp:restart": "node scripts/reset-mcp-servers.js --restart",
|
|
24
24
|
"cleanup-mcp": "node scripts/cleanup-mcp-servers.js",
|
|
25
|
+
"mcp:safe-cleanup": "node scripts/safe-mcp-cleanup.js",
|
|
26
|
+
"mcp:emergency-stop": "pkill -f mcp || true",
|
|
25
27
|
"mcp:clean": "node scripts/cleanup-mcp-servers.js && npm run build",
|
|
26
28
|
"mcp:start": "node scripts/start-mcp-proper.js",
|
|
27
29
|
"mcp:start-proper": "node scripts/start-mcp-proper.js",
|