snow-flow 1.3.0 → 1.3.2

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.
Files changed (42) hide show
  1. package/.claude-flow/queen/queen-memory.db +0 -0
  2. package/.env.example +249 -14
  3. package/CLAUDE.md +185 -24
  4. package/README.md +55 -5
  5. package/dist/api/error-handling.js +898 -4
  6. package/dist/api/performance-optimizer.js +3 -2
  7. package/dist/cli.js +590 -200
  8. package/dist/config/snow-flow-config.js +320 -8
  9. package/dist/health/system-health.js +288 -21
  10. package/dist/mcp/base-mcp-server.js +2 -0
  11. package/dist/mcp/http-transport-wrapper.js +65 -4
  12. package/dist/mcp/servicenow-automation-mcp-refactored.js +90 -9
  13. package/dist/mcp/servicenow-deployment-mcp-refactored.js +151 -2
  14. package/dist/mcp/servicenow-deployment-mcp.js +828 -15
  15. package/dist/mcp/servicenow-integration-mcp-refactored.js +100 -9
  16. package/dist/mcp/servicenow-intelligent-mcp.js +198 -7
  17. package/dist/mcp/servicenow-memory-mcp.js +2 -1
  18. package/dist/mcp/servicenow-operations-mcp-refactored.js +3 -2
  19. package/dist/mcp/servicenow-xml-flow-mcp.js +671 -0
  20. package/dist/mcp/shared/base-mcp-server.js +1356 -8
  21. package/dist/mcp/shared/mcp-resource-manager.js +304 -0
  22. package/dist/queen/parallel-agent-engine.js +26 -8
  23. package/dist/queen/servicenow-queen.js +47 -44
  24. package/dist/sparc/sparc-help.js +37 -26
  25. package/dist/sparc/team-sparc.js +60 -16
  26. package/dist/utils/action-type-cache.js +2 -1
  27. package/dist/utils/mcp-config-manager.js +7 -3
  28. package/dist/utils/mcp-server-manager.js +7 -3
  29. package/dist/utils/servicenow-client.js +134 -107
  30. package/dist/utils/servicenow-id-generator.js +171 -0
  31. package/dist/utils/snow-oauth.js +13 -8
  32. package/dist/utils/widget-template-generator.js +1690 -0
  33. package/dist/utils/xml-first-flow-generator.js +473 -0
  34. package/dist/version.js +7 -1
  35. package/flow-update-sets/flow_build_automated_approval_flow_with_notifications_flow.xml +203 -0
  36. package/flow-update-sets/flow_create_approval_flow_for_equipment_requests_flow.xml +206 -0
  37. package/flow-update-sets/flow_create_equipment_approval_flow_with_manager_approv_flow.xml +254 -0
  38. package/flow-update-sets/iphone_15_pro_approval_flow.xml +203 -0
  39. package/flow-update-sets/test_iphone_approval_flow_flow.xml +303 -0
  40. package/package.json +2 -1
  41. package/test-config.js +55 -0
  42. package/test-real-monitoring.js +184 -0
@@ -3,6 +3,39 @@
3
3
  * Snow-Flow System Health Monitoring
4
4
  * Comprehensive health checks for all components
5
5
  */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
6
39
  var __importDefault = (this && this.__importDefault) || function (mod) {
7
40
  return (mod && mod.__esModule) ? mod : { "default": mod };
8
41
  };
@@ -12,12 +45,14 @@ const events_1 = require("events");
12
45
  const servicenow_client_1 = require("../utils/servicenow-client");
13
46
  const logger_1 = require("../utils/logger");
14
47
  const os_1 = __importDefault(require("os"));
48
+ const child_process_1 = require("child_process");
15
49
  class SystemHealth extends events_1.EventEmitter {
16
50
  constructor(options) {
17
51
  super();
18
52
  this.checkHistory = [];
19
53
  this.totalChecks = 0;
20
54
  this.failedChecks = 0;
55
+ this.lastCPUInfo = null;
21
56
  this.memory = options.memory;
22
57
  this.mcpManager = options.mcpManager;
23
58
  this.config = options.config;
@@ -32,7 +67,8 @@ class SystemHealth extends events_1.EventEmitter {
32
67
  // Initialize ServiceNow client for health checks
33
68
  try {
34
69
  this.serviceNowClient = new servicenow_client_1.ServiceNowClient();
35
- await this.serviceNowClient.initialize();
70
+ // Note: ServiceNowClient may not have an initialize method
71
+ // await this.serviceNowClient.initialize();
36
72
  }
37
73
  catch (error) {
38
74
  this.logger.warn('ServiceNow client initialization failed:', error);
@@ -72,6 +108,10 @@ class SystemHealth extends events_1.EventEmitter {
72
108
  this.checkInterval = undefined;
73
109
  this.logger.info('Health monitoring stopped');
74
110
  }
111
+ if (this.cpuCheckInterval) {
112
+ clearInterval(this.cpuCheckInterval);
113
+ this.cpuCheckInterval = undefined;
114
+ }
75
115
  }
76
116
  /**
77
117
  * Perform comprehensive health check
@@ -214,10 +254,10 @@ class SystemHealth extends events_1.EventEmitter {
214
254
  async checkMCP() {
215
255
  const startTime = Date.now();
216
256
  try {
217
- const serverStatuses = await this.mcpManager.getServerStatuses();
218
- const totalServers = Object.keys(serverStatuses).length;
219
- const healthyServers = Object.values(serverStatuses).filter(s => s.status === 'running').length;
220
- const unhealthyServers = Object.values(serverStatuses).filter(s => s.status === 'error').length;
257
+ const serverStatuses = this.mcpManager.getServerStatus();
258
+ const totalServers = serverStatuses.length;
259
+ const healthyServers = serverStatuses.filter(s => s.status === 'running').length;
260
+ const unhealthyServers = serverStatuses.filter(s => s.status === 'error').length;
221
261
  const responseTime = Date.now() - startTime;
222
262
  let status = 'healthy';
223
263
  let message = `All ${totalServers} MCP servers running`;
@@ -286,8 +326,7 @@ class SystemHealth extends events_1.EventEmitter {
286
326
  status,
287
327
  message,
288
328
  details: {
289
- instance: testResult.instance,
290
- version: testResult.version,
329
+ connected: true,
291
330
  responseTime
292
331
  },
293
332
  timestamp: new Date(),
@@ -355,6 +394,7 @@ class SystemHealth extends events_1.EventEmitter {
355
394
  const startTime = Date.now();
356
395
  try {
357
396
  const resources = await this.getSystemResources();
397
+ const enhancedMetrics = await this.getEnhancedSystemMetrics();
358
398
  const responseTime = Date.now() - startTime;
359
399
  let status = 'healthy';
360
400
  let message = 'System resources within normal limits';
@@ -375,6 +415,21 @@ class SystemHealth extends events_1.EventEmitter {
375
415
  status = 'unhealthy';
376
416
  issues.push(`Critical disk usage: ${resources.diskUsage.toFixed(1)}%`);
377
417
  }
418
+ else if (resources.diskUsage > 80) {
419
+ status = status === 'healthy' ? 'degraded' : status;
420
+ issues.push(`High disk usage: ${resources.diskUsage.toFixed(1)}%`);
421
+ }
422
+ // Check process memory
423
+ const heapUsagePercent = (enhancedMetrics.process.memory.heapUsed / enhancedMetrics.process.memory.heapTotal) * 100;
424
+ if (heapUsagePercent > 90) {
425
+ status = status === 'degraded' ? 'unhealthy' : 'degraded';
426
+ issues.push(`High heap usage: ${heapUsagePercent.toFixed(1)}%`);
427
+ }
428
+ // Check network connectivity
429
+ if (!enhancedMetrics.network.connected) {
430
+ status = 'degraded';
431
+ issues.push('Network connectivity issues detected');
432
+ }
378
433
  if (issues.length > 0) {
379
434
  message = issues.join(', ');
380
435
  }
@@ -382,7 +437,10 @@ class SystemHealth extends events_1.EventEmitter {
382
437
  component: 'system',
383
438
  status,
384
439
  message,
385
- details: resources,
440
+ details: {
441
+ ...resources,
442
+ enhanced: enhancedMetrics
443
+ },
386
444
  timestamp: new Date(),
387
445
  responseTime
388
446
  };
@@ -465,19 +523,10 @@ class SystemHealth extends events_1.EventEmitter {
465
523
  const totalMemory = os_1.default.totalmem();
466
524
  const freeMemory = os_1.default.freemem();
467
525
  const usedMemory = totalMemory - freeMemory;
468
- // Calculate CPU usage
469
- let totalIdle = 0;
470
- let totalTick = 0;
471
- cpus.forEach(cpu => {
472
- for (const type in cpu.times) {
473
- totalTick += cpu.times[type];
474
- }
475
- totalIdle += cpu.times.idle;
476
- });
477
- const cpuUsage = 100 - ~~(100 * totalIdle / totalTick);
478
- // Get disk usage (simplified - you may want to use a library like 'diskusage')
479
- const diskUsage = 50; // Placeholder - implement actual disk usage check
480
- const diskTotal = 100; // Placeholder
526
+ // Calculate CPU usage with averaging
527
+ const cpuUsage = await this.calculateCPUUsage();
528
+ // Get real disk usage
529
+ const { diskUsage, diskTotal } = await this.getDiskUsage();
481
530
  return {
482
531
  cpuUsage,
483
532
  memoryUsage: usedMemory / 1024 / 1024, // MB
@@ -575,5 +624,223 @@ class SystemHealth extends events_1.EventEmitter {
575
624
  return 0;
576
625
  return responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length;
577
626
  }
627
+ /**
628
+ * Calculate real CPU usage with averaging
629
+ */
630
+ async calculateCPUUsage() {
631
+ const cpus = os_1.default.cpus();
632
+ const currentCPUInfo = cpus.map(cpu => {
633
+ const total = Object.values(cpu.times).reduce((acc, time) => acc + time, 0);
634
+ return {
635
+ idle: cpu.times.idle,
636
+ total: total
637
+ };
638
+ });
639
+ if (!this.lastCPUInfo) {
640
+ this.lastCPUInfo = currentCPUInfo;
641
+ // Wait a bit and calculate again for initial reading
642
+ await new Promise(resolve => setTimeout(resolve, 100));
643
+ return this.calculateCPUUsage();
644
+ }
645
+ let totalIdleDiff = 0;
646
+ let totalDiff = 0;
647
+ for (let i = 0; i < cpus.length; i++) {
648
+ const lastCPU = this.lastCPUInfo[i];
649
+ const currentCPU = currentCPUInfo[i];
650
+ const idleDiff = currentCPU.idle - lastCPU.idle;
651
+ const totalCPUDiff = currentCPU.total - lastCPU.total;
652
+ totalIdleDiff += idleDiff;
653
+ totalDiff += totalCPUDiff;
654
+ }
655
+ this.lastCPUInfo = currentCPUInfo;
656
+ if (totalDiff === 0)
657
+ return 0;
658
+ const idlePercentage = (totalIdleDiff / totalDiff) * 100;
659
+ const usagePercentage = 100 - idlePercentage;
660
+ return Math.max(0, Math.min(100, usagePercentage));
661
+ }
662
+ /**
663
+ * Get real disk usage statistics
664
+ */
665
+ async getDiskUsage() {
666
+ try {
667
+ // Platform-specific disk usage calculation
668
+ if (process.platform === 'darwin' || process.platform === 'linux') {
669
+ return this.getUnixDiskUsage();
670
+ }
671
+ else if (process.platform === 'win32') {
672
+ return this.getWindowsDiskUsage();
673
+ }
674
+ else {
675
+ // Fallback for unsupported platforms
676
+ return { diskUsage: 0, diskTotal: 0 };
677
+ }
678
+ }
679
+ catch (error) {
680
+ this.logger.warn('Failed to get disk usage:', error);
681
+ return { diskUsage: 0, diskTotal: 0 };
682
+ }
683
+ }
684
+ /**
685
+ * Get disk usage for Unix-based systems (macOS, Linux)
686
+ */
687
+ getUnixDiskUsage() {
688
+ try {
689
+ // Get current working directory's mount point
690
+ const cwd = process.cwd();
691
+ // Get disk usage for the filesystem containing the current directory
692
+ const output = (0, child_process_1.execSync)(`df -k "${cwd}"`, { encoding: 'utf8' });
693
+ const lines = output.trim().split('\n');
694
+ if (lines.length < 2) {
695
+ throw new Error('Unexpected df output format');
696
+ }
697
+ // Handle cases where the output might span multiple lines
698
+ let dataLine = lines[1];
699
+ if (lines.length > 2 && !lines[1].includes('/')) {
700
+ // Some systems split long filesystem names across lines
701
+ dataLine = lines[1] + ' ' + lines[2];
702
+ }
703
+ // Parse the data line
704
+ const parts = dataLine.split(/\s+/);
705
+ // Find the numeric values (skip filesystem name)
706
+ let totalBlocks = 0;
707
+ let usedBlocks = 0;
708
+ let foundNumbers = false;
709
+ for (let i = 0; i < parts.length; i++) {
710
+ const num = parseInt(parts[i], 10);
711
+ if (!isNaN(num) && !foundNumbers) {
712
+ totalBlocks = num;
713
+ if (i + 1 < parts.length) {
714
+ usedBlocks = parseInt(parts[i + 1], 10);
715
+ foundNumbers = true;
716
+ break;
717
+ }
718
+ }
719
+ }
720
+ if (!foundNumbers || isNaN(totalBlocks) || isNaN(usedBlocks)) {
721
+ throw new Error('Failed to parse disk usage numbers');
722
+ }
723
+ const diskTotal = totalBlocks / 1024 / 1024; // Convert to GB
724
+ const diskUsed = usedBlocks / 1024 / 1024; // Convert to GB
725
+ const diskUsage = (diskUsed / diskTotal) * 100;
726
+ return {
727
+ diskUsage: Math.round(diskUsage * 10) / 10, // Round to 1 decimal
728
+ diskTotal: Math.round(diskTotal * 10) / 10
729
+ };
730
+ }
731
+ catch (error) {
732
+ this.logger.error('Unix disk usage check failed:', error);
733
+ throw error;
734
+ }
735
+ }
736
+ /**
737
+ * Get disk usage for Windows systems
738
+ */
739
+ getWindowsDiskUsage() {
740
+ try {
741
+ // Get disk usage for C: drive using WMIC
742
+ const output = (0, child_process_1.execSync)('wmic logicaldisk where caption="C:" get size,freespace /value', { encoding: 'utf8' });
743
+ const sizeMatch = output.match(/Size=(\d+)/);
744
+ const freeMatch = output.match(/FreeSpace=(\d+)/);
745
+ if (!sizeMatch || !freeMatch) {
746
+ throw new Error('Failed to parse Windows disk usage');
747
+ }
748
+ const totalBytes = parseInt(sizeMatch[1], 10);
749
+ const freeBytes = parseInt(freeMatch[1], 10);
750
+ const usedBytes = totalBytes - freeBytes;
751
+ const diskTotal = totalBytes / 1024 / 1024 / 1024; // Convert to GB
752
+ const diskUsed = usedBytes / 1024 / 1024 / 1024; // Convert to GB
753
+ const diskUsage = (diskUsed / diskTotal) * 100;
754
+ return {
755
+ diskUsage: Math.round(diskUsage * 10) / 10,
756
+ diskTotal: Math.round(diskTotal * 10) / 10
757
+ };
758
+ }
759
+ catch (error) {
760
+ this.logger.error('Windows disk usage check failed:', error);
761
+ throw error;
762
+ }
763
+ }
764
+ /**
765
+ * Get process-specific memory usage
766
+ */
767
+ getProcessMemoryUsage() {
768
+ const usage = process.memoryUsage();
769
+ return {
770
+ heapUsed: usage.heapUsed / 1024 / 1024, // MB
771
+ heapTotal: usage.heapTotal / 1024 / 1024, // MB
772
+ rss: usage.rss / 1024 / 1024, // MB
773
+ external: usage.external / 1024 / 1024, // MB
774
+ arrayBuffers: usage.arrayBuffers / 1024 / 1024 // MB
775
+ };
776
+ }
777
+ /**
778
+ * Check network connectivity
779
+ */
780
+ async checkNetworkConnectivity() {
781
+ try {
782
+ // Simple DNS lookup to check network connectivity
783
+ const dns = await Promise.resolve().then(() => __importStar(require('dns'))).then(m => m.promises);
784
+ await dns.lookup('google.com');
785
+ return true;
786
+ }
787
+ catch (error) {
788
+ return false;
789
+ }
790
+ }
791
+ /**
792
+ * Get additional SQLite database statistics
793
+ */
794
+ async getExtendedDatabaseStats() {
795
+ try {
796
+ // Use the existing memory system stats
797
+ const dbStats = await this.memory.getDatabaseStats();
798
+ // Get additional table information if possible
799
+ try {
800
+ const tableInfo = await this.memory.query(`
801
+ SELECT
802
+ COUNT(DISTINCT name) as table_count,
803
+ SUM(CASE WHEN type = 'index' THEN 1 ELSE 0 END) as index_count
804
+ FROM sqlite_master
805
+ WHERE type IN ('table', 'index')
806
+ `);
807
+ return {
808
+ ...dbStats,
809
+ tables: tableInfo[0]?.table_count || 0,
810
+ indexes: tableInfo[0]?.index_count || 0
811
+ };
812
+ }
813
+ catch (error) {
814
+ // If query fails, just return basic stats
815
+ return dbStats;
816
+ }
817
+ }
818
+ catch (error) {
819
+ this.logger.warn('Failed to get extended database stats:', error);
820
+ return null;
821
+ }
822
+ }
823
+ /**
824
+ * Enhanced system check with more detailed metrics
825
+ */
826
+ async getEnhancedSystemMetrics() {
827
+ const processMemory = this.getProcessMemoryUsage();
828
+ const networkConnected = await this.checkNetworkConnectivity();
829
+ const dbStats = await this.getExtendedDatabaseStats();
830
+ return {
831
+ process: {
832
+ memory: processMemory,
833
+ uptime: process.uptime(),
834
+ pid: process.pid,
835
+ version: process.version,
836
+ platform: process.platform,
837
+ arch: process.arch
838
+ },
839
+ network: {
840
+ connected: networkConnected
841
+ },
842
+ database: dbStats
843
+ };
844
+ }
578
845
  }
579
846
  exports.SystemHealth = SystemHealth;
@@ -30,6 +30,8 @@ class BaseMCPServer {
30
30
  * Tool handlers map
31
31
  */
32
32
  this.toolHandlers = new Map();
33
+ // Store the config
34
+ this.config = config;
33
35
  // Initialize server with config
34
36
  this.server = new index_js_1.Server({
35
37
  name: config.name,
@@ -11,6 +11,7 @@ exports.HttpTransportWrapper = void 0;
11
11
  const express_1 = __importDefault(require("express"));
12
12
  const logger_js_1 = require("../utils/logger.js");
13
13
  const service_discovery_client_js_1 = require("./service-discovery-client.js");
14
+ const mcp_resource_manager_js_1 = require("./shared/mcp-resource-manager.js");
14
15
  class HttpTransportWrapper {
15
16
  constructor(mcpServer, config) {
16
17
  this.isReady = false;
@@ -19,6 +20,7 @@ class HttpTransportWrapper {
19
20
  this.logger = new logger_js_1.Logger(`HttpTransport:${config.name}`);
20
21
  this.app = (0, express_1.default)();
21
22
  this.serviceDiscovery = new service_discovery_client_js_1.ServiceDiscoveryClient();
23
+ this.resourceManager = new mcp_resource_manager_js_1.MCPResourceManager(config.name);
22
24
  this.setupMiddleware();
23
25
  this.setupRoutes();
24
26
  }
@@ -117,6 +119,7 @@ class HttpTransportWrapper {
117
119
  });
118
120
  // Service info endpoint
119
121
  this.app.get('/info', (req, res) => {
122
+ const resourceStats = this.resourceManager.getResourceStats();
120
123
  res.json({
121
124
  name: this.config.name,
122
125
  version: this.config.version,
@@ -130,9 +133,41 @@ class HttpTransportWrapper {
130
133
  '/mcp/list-resources',
131
134
  '/mcp/read-resource'
132
135
  ]
136
+ },
137
+ resources: {
138
+ total: resourceStats.total,
139
+ cached: resourceStats.cached,
140
+ categories: resourceStats.categories
133
141
  }
134
142
  });
135
143
  });
144
+ // Resource management endpoint
145
+ this.app.get('/resources', async (req, res) => {
146
+ try {
147
+ const resources = await this.resourceManager.listResources();
148
+ const stats = this.resourceManager.getResourceStats();
149
+ res.json({
150
+ resources,
151
+ stats
152
+ });
153
+ }
154
+ catch (error) {
155
+ this.handleError(res, error);
156
+ }
157
+ });
158
+ // Resource content endpoint
159
+ this.app.get('/resources/*', async (req, res) => {
160
+ try {
161
+ const resourcePath = req.params[0];
162
+ const uri = `servicenow://${resourcePath}`;
163
+ const resource = await this.resourceManager.readResource(uri);
164
+ res.set('Content-Type', resource.mimeType);
165
+ res.send(resource.text);
166
+ }
167
+ catch (error) {
168
+ this.handleError(res, error);
169
+ }
170
+ });
136
171
  // 404 handler
137
172
  this.app.use('*', (req, res) => {
138
173
  res.status(404).json({
@@ -184,12 +219,38 @@ class HttpTransportWrapper {
184
219
  result = await this.handleToolCall(toolRequest);
185
220
  break;
186
221
  case 'listResources':
187
- // Handle list resources - return empty list if not supported
188
- result = { resources: [] };
222
+ // Handle list resources - return available resources
223
+ try {
224
+ const resources = await this.resourceManager.listResources();
225
+ result = { resources };
226
+ this.logger.debug(`Listed ${resources.length} resources`);
227
+ }
228
+ catch (error) {
229
+ this.logger.error('Failed to list resources:', error);
230
+ result = { resources: [] };
231
+ }
189
232
  break;
190
233
  case 'readResource':
191
- // Handle read resource - throw error if not supported
192
- throw new Error('Resource reading not implemented for this server');
234
+ // Handle read resource - read specific resource by URI
235
+ if (!params.uri) {
236
+ throw new Error('Resource URI is required for readResource');
237
+ }
238
+ try {
239
+ const resource = await this.resourceManager.readResource(params.uri);
240
+ result = {
241
+ contents: [{
242
+ uri: resource.uri,
243
+ mimeType: resource.mimeType,
244
+ text: resource.text
245
+ }]
246
+ };
247
+ this.logger.debug(`Read resource: ${params.uri}`);
248
+ }
249
+ catch (error) {
250
+ this.logger.error(`Failed to read resource ${params.uri}:`, error);
251
+ throw new Error(`Failed to read resource: ${error instanceof Error ? error.message : String(error)}`);
252
+ }
253
+ break;
193
254
  default:
194
255
  throw new Error(`Unsupported MCP method: ${method}`);
195
256
  }
@@ -497,15 +497,96 @@ class ServiceNowAutomationMCP extends base_mcp_server_js_1.BaseMCPServer {
497
497
  throw new Error(`Scheduled job not found: ${args.jobName}`);
498
498
  }
499
499
  const job = jobResponse.data.result[0];
500
- // In a real implementation, this would trigger the job execution
501
- // For now, return a simulated test result
502
- const testResult = {
503
- job_name: job.name,
504
- job_id: job.sys_id,
505
- test_status: 'success',
506
- execution_time: Math.floor(Math.random() * 5000) + 1000,
507
- message: 'Job executed successfully in test mode'
508
- };
500
+ // Execute the scheduled job and monitor its execution
501
+ const executionStartTime = Date.now();
502
+ let testResult;
503
+ try {
504
+ // Trigger the scheduled job execution
505
+ this.logger.info('Triggering scheduled job execution', {
506
+ jobName: job.name,
507
+ jobId: job.sys_id
508
+ });
509
+ // Method 1: Try to execute via sys_trigger endpoint
510
+ const triggerResponse = await this.client.post('/api/now/table/sys_trigger', {
511
+ script: `gs.eventQueue('sysauto.script.execute', null, '${job.sys_id}', '');`,
512
+ name: `Test execution of ${job.name}`,
513
+ next_action: new Date(Date.now() + 5000).toISOString() // Execute in 5 seconds
514
+ });
515
+ if (triggerResponse.result) {
516
+ this.logger.info('Job execution trigger created', {
517
+ triggerId: triggerResponse.result.sys_id
518
+ });
519
+ // Wait a brief moment for execution to begin
520
+ await new Promise(resolve => setTimeout(resolve, 2000));
521
+ // Check for recent execution history
522
+ const executionHistoryResponse = await this.client.get(`/api/now/table/sys_execution_tracker?sysparm_query=source=${job.sys_id}^ORDERBYDESCsys_created_on&sysparm_limit=1`);
523
+ const realExecutionTime = Date.now() - executionStartTime;
524
+ if (executionHistoryResponse.result && executionHistoryResponse.result.length > 0) {
525
+ const execution = executionHistoryResponse.result[0];
526
+ testResult = {
527
+ job_name: job.name,
528
+ job_id: job.sys_id,
529
+ test_status: 'executed',
530
+ execution_time: realExecutionTime,
531
+ trigger_id: triggerResponse.result.sys_id,
532
+ execution_tracker_id: execution.sys_id,
533
+ execution_state: execution.state,
534
+ execution_progress: execution.completion_percentage || 0,
535
+ message: `Job execution initiated successfully. Trigger created: ${triggerResponse.result.sys_id}`,
536
+ details: {
537
+ job_active: job.active,
538
+ job_script: job.script ? 'Present' : 'Missing',
539
+ job_condition: job.condition_script || 'None',
540
+ execution_history_found: true
541
+ }
542
+ };
543
+ }
544
+ else {
545
+ testResult = {
546
+ job_name: job.name,
547
+ job_id: job.sys_id,
548
+ test_status: 'triggered',
549
+ execution_time: realExecutionTime,
550
+ trigger_id: triggerResponse.result.sys_id,
551
+ message: `Job execution trigger created successfully. Check sys_execution_tracker for progress.`,
552
+ details: {
553
+ job_active: job.active,
554
+ job_script: job.script ? 'Present' : 'Missing',
555
+ job_condition: job.condition_script || 'None',
556
+ execution_history_found: false
557
+ }
558
+ };
559
+ }
560
+ }
561
+ else {
562
+ throw new Error('Failed to create execution trigger');
563
+ }
564
+ }
565
+ catch (executionError) {
566
+ // If direct execution fails, perform validation instead
567
+ this.logger.warn('Job execution failed, performing validation instead', {
568
+ jobName: job.name,
569
+ error: executionError instanceof Error ? executionError.message : String(executionError)
570
+ });
571
+ const realExecutionTime = Date.now() - executionStartTime;
572
+ testResult = {
573
+ job_name: job.name,
574
+ job_id: job.sys_id,
575
+ test_status: 'validated',
576
+ execution_time: realExecutionTime,
577
+ message: `Job validation completed. Execution failed: ${executionError instanceof Error ? executionError.message : String(executionError)}`,
578
+ details: {
579
+ job_active: job.active,
580
+ job_script: job.script ? 'Present' : 'Missing',
581
+ job_condition: job.condition_script || 'None',
582
+ job_run_as: job.run_as || 'system',
583
+ job_schedule: job.run_period || 'Not scheduled',
584
+ validation_performed: true,
585
+ execution_attempted: true,
586
+ execution_error: executionError instanceof Error ? executionError.message : String(executionError)
587
+ }
588
+ };
589
+ }
509
590
  return {
510
591
  success: true,
511
592
  result: testResult,