snow-flow 3.5.17 → 3.6.0

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.
@@ -13,6 +13,7 @@ const servicenow_client_js_1 = require("../utils/servicenow-client.js");
13
13
  const mcp_auth_middleware_js_1 = require("../utils/mcp-auth-middleware.js");
14
14
  const mcp_config_manager_js_1 = require("../utils/mcp-config-manager.js");
15
15
  const mcp_logger_js_1 = require("./shared/mcp-logger.js");
16
+ const servicenow_audit_logger_js_1 = require("../utils/servicenow-audit-logger.js");
16
17
  class ServiceNowAutomationMCP {
17
18
  constructor() {
18
19
  this.server = new index_js_1.Server({
@@ -25,6 +26,8 @@ class ServiceNowAutomationMCP {
25
26
  });
26
27
  this.client = new servicenow_client_js_1.ServiceNowClient();
27
28
  this.logger = new mcp_logger_js_1.MCPLogger('ServiceNowAutomationMCP');
29
+ this.auditLogger = (0, servicenow_audit_logger_js_1.getAuditLogger)(this.logger, 'servicenow-automation');
30
+ this.auditLogger.setServiceNowClient(this.client);
28
31
  this.config = mcp_config_manager_js_1.mcpConfig.getConfig();
29
32
  this.setupHandlers();
30
33
  }
@@ -1756,10 +1759,22 @@ ${groupedResults.suites.slice(0, 10).map(suite => `- ${suite.name} ${suite.activ
1756
1759
  * Execute script with output retrieval
1757
1760
  */
1758
1761
  async executeScriptWithOutput(args) {
1762
+ const startTime = Date.now();
1759
1763
  try {
1760
1764
  this.logger.info('Executing script with output retrieval...');
1761
1765
  // Create a unique execution ID
1762
1766
  const executionId = `snow_flow_exec_${Date.now()}_${Math.random().toString(36).substring(7)}`;
1767
+ // Log script execution start
1768
+ await this.getAuditLogger().logOperation('script_execution_start', 'INFO', {
1769
+ message: 'Starting background script execution with output capture',
1770
+ metadata: {
1771
+ execution_id: executionId,
1772
+ script_length: args.script.length,
1773
+ has_es5_validation: true,
1774
+ capture_logs: args.capture_logs,
1775
+ max_wait: args.max_wait || 5000
1776
+ }
1777
+ });
1763
1778
  // Wrap the script to capture output
1764
1779
  const wrappedScript = `
1765
1780
  var snowFlowOutput = [];
@@ -1864,6 +1879,27 @@ ${groupedResults.suites.slice(0, 10).map(suite => `- ${suite.name} ${suite.activ
1864
1879
  this.logger.warn('Could not parse script output:', parseError);
1865
1880
  }
1866
1881
  }
1882
+ const duration = Date.now() - startTime;
1883
+ const success = scriptOutput?.success !== false;
1884
+ const outputLines = scriptOutput?.output?.length || 0;
1885
+ const errorLines = scriptOutput?.errors?.length || 0;
1886
+ // Log script execution completion with audit
1887
+ await this.getAuditLogger().logScriptExecution('background', duration, success, scriptOutput?.errors || [], outputLines);
1888
+ // Enhanced audit log with detailed execution info
1889
+ await this.getAuditLogger().logOperation('script_execution_complete', success ? 'INFO' : 'ERROR', {
1890
+ message: `Background script execution ${success ? 'completed successfully' : 'failed'}`,
1891
+ duration_ms: duration,
1892
+ metadata: {
1893
+ execution_id: executionId,
1894
+ output_lines: outputLines,
1895
+ error_lines: errorLines,
1896
+ script_success: success,
1897
+ execution_method: 'background_with_output',
1898
+ es5_validated: true,
1899
+ captured_output: !!scriptOutput
1900
+ },
1901
+ success
1902
+ });
1867
1903
  return {
1868
1904
  content: [{
1869
1905
  type: 'text',
@@ -1872,6 +1908,10 @@ ${groupedResults.suites.slice(0, 10).map(suite => `- ${suite.name} ${suite.activ
1872
1908
  };
1873
1909
  }
1874
1910
  catch (error) {
1911
+ const duration = Date.now() - startTime;
1912
+ const errorMessage = error instanceof Error ? error.message : String(error);
1913
+ // Log failed script execution
1914
+ await this.getAuditLogger().logScriptExecution('background', duration, false, { message: errorMessage, type: 'exception' });
1875
1915
  this.logger.error('Failed to execute script with output:', error);
1876
1916
  throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to execute script: ${error}`);
1877
1917
  }
@@ -2412,6 +2452,12 @@ ${groupedResults.suites.slice(0, 10).map(suite => `- ${suite.name} ${suite.activ
2412
2452
  throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to trace execution: ${error}`);
2413
2453
  }
2414
2454
  }
2455
+ /**
2456
+ * Get audit logger for logging Snow-Flow activities
2457
+ */
2458
+ getAuditLogger() {
2459
+ return this.auditLogger;
2460
+ }
2415
2461
  async run() {
2416
2462
  const transport = new stdio_js_1.StdioServerTransport();
2417
2463
  await this.server.connect(transport);
@@ -261,6 +261,7 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
261
261
  }
262
262
  async pullArtifact(args) {
263
263
  const { sys_id, table } = args;
264
+ const startTime = Date.now();
264
265
  // Add timeout for pull operations
265
266
  const PULL_TIMEOUT = 15000; // 15 seconds for pull operations
266
267
  try {
@@ -280,6 +281,13 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
280
281
  setTimeout(() => reject(new Error(`Pull operation timed out after ${PULL_TIMEOUT / 1000}s`)), PULL_TIMEOUT);
281
282
  });
282
283
  const artifact = await Promise.race([pullPromise, timeoutPromise]);
284
+ const duration = Date.now() - startTime;
285
+ // Log artifact sync operation
286
+ await this.getAuditLogger().logArtifactSync('pull', artifact.tableName, sys_id, artifact.name, artifact.files.length, duration, true);
287
+ // Special logging for widgets
288
+ if (artifact.tableName === 'sp_widget') {
289
+ await this.getAuditLogger().logWidgetOperation('pull', sys_id, artifact.name, duration, true);
290
+ }
283
291
  return {
284
292
  content: [
285
293
  {
@@ -290,7 +298,10 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
290
298
  };
291
299
  }
292
300
  catch (error) {
301
+ const duration = Date.now() - startTime;
293
302
  const errorMessage = error instanceof Error ? error.message : String(error);
303
+ // Log failed operation
304
+ await this.getAuditLogger().logArtifactSync('pull', table || 'unknown', sys_id, undefined, 0, duration, false);
294
305
  return {
295
306
  content: [
296
307
  {
@@ -303,8 +314,25 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
303
314
  }
304
315
  async pushArtifact(args) {
305
316
  const { sys_id, force = false } = args;
317
+ const startTime = Date.now();
306
318
  try {
307
319
  const success = await this.syncManager.pushArtifact(sys_id);
320
+ const duration = Date.now() - startTime;
321
+ // Get artifact info for logging
322
+ const localArtifacts = this.syncManager.listLocalArtifacts();
323
+ const artifact = localArtifacts.find(a => a.sys_id === sys_id);
324
+ if (success) {
325
+ // Log successful push
326
+ await this.getAuditLogger().logArtifactSync('push', artifact?.tableName || 'unknown', sys_id, artifact?.name, artifact?.files.length, duration, true);
327
+ // Special logging for widgets
328
+ if (artifact?.tableName === 'sp_widget') {
329
+ await this.getAuditLogger().logWidgetOperation('push', sys_id, artifact.name, duration, true);
330
+ }
331
+ }
332
+ else {
333
+ // Log failed push
334
+ await this.getAuditLogger().logArtifactSync('push', artifact?.tableName || 'unknown', sys_id, artifact?.name, artifact?.files.length, duration, false);
335
+ }
308
336
  return {
309
337
  content: [
310
338
  {
@@ -317,7 +345,10 @@ class ServiceNowLocalDevelopmentMCP extends enhanced_base_mcp_server_js_1.Enhanc
317
345
  };
318
346
  }
319
347
  catch (error) {
348
+ const duration = Date.now() - startTime;
320
349
  const errorMessage = error instanceof Error ? error.message : String(error);
350
+ // Log failed operation
351
+ await this.getAuditLogger().logArtifactSync('push', 'unknown', sys_id, undefined, 0, duration, false);
321
352
  return {
322
353
  content: [
323
354
  {
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Enhanced Base MCP Server with Logging and Token Tracking
2
+ * Enhanced Base MCP Server with Logging, Token Tracking, and ServiceNow Audit Logging
3
3
  */
4
4
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
5
  import { ServiceNowClientWithTracking } from '../../utils/servicenow-client-with-tracking.js';
6
6
  import { MCPLogger } from './mcp-logger.js';
7
7
  import { ServiceNowOAuth } from '../../utils/snow-oauth.js';
8
+ import { ServiceNowAuditLogger } from '../../utils/servicenow-audit-logger.js';
8
9
  export interface MCPToolResult {
9
10
  content: Array<{
10
11
  type: string;
@@ -16,13 +17,15 @@ export declare abstract class EnhancedBaseMCPServer {
16
17
  protected server: Server;
17
18
  protected client: ServiceNowClientWithTracking;
18
19
  protected logger: MCPLogger;
20
+ protected auditLogger: ServiceNowAuditLogger;
19
21
  protected oauth: ServiceNowOAuth;
20
22
  protected isAuthenticated: boolean;
23
+ protected serverName: string;
21
24
  constructor(name: string, version?: string);
22
25
  /**
23
- * Execute tool with enhanced tracking
26
+ * Execute tool with enhanced tracking and audit logging
24
27
  */
25
- protected executeTool(toolName: string, handler: () => Promise<MCPToolResult>): Promise<MCPToolResult>;
28
+ protected executeTool(toolName: string, handler: () => Promise<MCPToolResult>, params?: any): Promise<MCPToolResult>;
26
29
  /**
27
30
  * Validate ServiceNow connection with progress
28
31
  */
@@ -35,19 +38,19 @@ export declare abstract class EnhancedBaseMCPServer {
35
38
  */
36
39
  protected createResponse(message: string, data?: any): MCPToolResult;
37
40
  /**
38
- * Query table with progress tracking
41
+ * Query table with progress tracking and audit logging
39
42
  */
40
43
  protected queryTable(table: string, query: string, limit?: number): Promise<any>;
41
44
  /**
42
- * Create record with tracking
45
+ * Create record with tracking and audit logging
43
46
  */
44
47
  protected createRecord(table: string, data: any): Promise<any>;
45
48
  /**
46
- * Update record with tracking
49
+ * Update record with tracking and audit logging
47
50
  */
48
51
  protected updateRecord(table: string, sysId: string, data: any): Promise<any>;
49
52
  /**
50
- * Get record with tracking
53
+ * Get record with tracking and audit logging
51
54
  */
52
55
  protected getRecord(table: string, sysId: string): Promise<any>;
53
56
  /**
@@ -58,5 +61,13 @@ export declare abstract class EnhancedBaseMCPServer {
58
61
  * Get logger for direct use
59
62
  */
60
63
  getLogger(): MCPLogger;
64
+ /**
65
+ * Get audit logger for direct use
66
+ */
67
+ getAuditLogger(): ServiceNowAuditLogger;
68
+ /**
69
+ * Cleanup resources and flush audit logs
70
+ */
71
+ cleanup(): Promise<void>;
61
72
  }
62
73
  //# sourceMappingURL=enhanced-base-mcp-server.d.ts.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Enhanced Base MCP Server with Logging and Token Tracking
3
+ * Enhanced Base MCP Server with Logging, Token Tracking, and ServiceNow Audit Logging
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.EnhancedBaseMCPServer = void 0;
@@ -9,15 +9,21 @@ const servicenow_client_with_tracking_js_1 = require("../../utils/servicenow-cli
9
9
  const mcp_logger_js_1 = require("./mcp-logger.js");
10
10
  const snow_oauth_js_1 = require("../../utils/snow-oauth.js");
11
11
  const mcp_auth_middleware_js_1 = require("../../utils/mcp-auth-middleware.js");
12
+ const servicenow_audit_logger_js_1 = require("../../utils/servicenow-audit-logger.js");
12
13
  class EnhancedBaseMCPServer {
13
14
  constructor(name, version = '1.0.0') {
14
15
  this.isAuthenticated = false;
16
+ this.serverName = name;
15
17
  // Create enhanced logger
16
18
  this.logger = new mcp_logger_js_1.MCPLogger(name);
19
+ // Initialize ServiceNow audit logger
20
+ this.auditLogger = (0, servicenow_audit_logger_js_1.getAuditLogger)(this.logger, name);
17
21
  // Log startup
18
22
  this.logger.info(`🚀 Starting ${name} MCP Server v${version}`);
19
23
  // Create enhanced client with tracking
20
24
  this.client = new servicenow_client_with_tracking_js_1.ServiceNowClientWithTracking(this.logger);
25
+ // Connect audit logger to ServiceNow client
26
+ this.auditLogger.setServiceNowClient(this.client.getBaseClient());
21
27
  // Initialize OAuth
22
28
  this.oauth = new snow_oauth_js_1.ServiceNowOAuth();
23
29
  // Create server with capabilities
@@ -29,13 +35,19 @@ class EnhancedBaseMCPServer {
29
35
  tools: {},
30
36
  },
31
37
  });
38
+ // Log server initialization
39
+ this.auditLogger.logOperation('server_initialization', 'INFO', {
40
+ message: `${name} MCP Server v${version} initialized`,
41
+ metadata: { version, capabilities: ['tools'] }
42
+ });
32
43
  // Report initialization
33
- this.logger.info(`✅ ${name} initialized and ready`);
44
+ this.logger.info(`✅ ${name} initialized and ready with audit logging`);
34
45
  }
35
46
  /**
36
- * Execute tool with enhanced tracking
47
+ * Execute tool with enhanced tracking and audit logging
37
48
  */
38
- async executeTool(toolName, handler) {
49
+ async executeTool(toolName, handler, params) {
50
+ const startTime = Date.now();
39
51
  // Reset tokens at start of each operation to avoid accumulation
40
52
  this.logger.resetTokens();
41
53
  // Start operation tracking
@@ -44,30 +56,57 @@ class EnhancedBaseMCPServer {
44
56
  // Ensure authentication
45
57
  await mcp_auth_middleware_js_1.mcpAuth.ensureAuthenticated();
46
58
  this.isAuthenticated = true;
59
+ // Log authentication success
60
+ await this.auditLogger.logAuthOperation('token_refresh', true);
47
61
  // Execute the tool handler
48
62
  const result = await handler();
63
+ const duration = Date.now() - startTime;
64
+ const tokenUsage = this.logger.getTokenUsage();
65
+ // Log successful tool execution
66
+ await this.auditLogger.logOperation('tool_execution', 'INFO', {
67
+ message: `Successfully executed ${toolName}`,
68
+ duration_ms: duration,
69
+ metadata: {
70
+ tool_name: toolName,
71
+ parameters: params ? JSON.stringify(params) : undefined,
72
+ token_usage: tokenUsage
73
+ },
74
+ success: true
75
+ });
49
76
  // Log completion
50
77
  this.logger.operationComplete(toolName);
51
78
  // Send token usage summary if in Claude
52
79
  if (process.send) {
53
- const usage = this.logger.getTokenUsage();
54
80
  process.send({
55
81
  type: 'token_usage',
56
82
  data: {
57
83
  tool: toolName,
58
- tokens: usage
84
+ tokens: tokenUsage
59
85
  }
60
86
  });
61
87
  }
62
88
  return result;
63
89
  }
64
90
  catch (error) {
91
+ const duration = Date.now() - startTime;
92
+ const errorMessage = error instanceof Error ? error.message : String(error);
93
+ // Log failed tool execution
94
+ await this.auditLogger.logOperation('tool_execution', 'ERROR', {
95
+ message: `Failed to execute ${toolName}: ${errorMessage}`,
96
+ duration_ms: duration,
97
+ metadata: {
98
+ tool_name: toolName,
99
+ parameters: params ? JSON.stringify(params) : undefined,
100
+ error_details: error instanceof Error ? { message: error.message, stack: error.stack } : error
101
+ },
102
+ success: false
103
+ });
65
104
  this.logger.error(`Tool execution failed: ${toolName}`, error);
66
105
  // Return error as tool result
67
106
  return {
68
107
  content: [{
69
108
  type: 'text',
70
- text: `❌ Error executing ${toolName}: ${error instanceof Error ? error.message : String(error)}`
109
+ text: `❌ Error executing ${toolName}: ${errorMessage}`
71
110
  }]
72
111
  };
73
112
  }
@@ -133,47 +172,95 @@ class EnhancedBaseMCPServer {
133
172
  return response;
134
173
  }
135
174
  /**
136
- * Query table with progress tracking
175
+ * Query table with progress tracking and audit logging
137
176
  */
138
177
  async queryTable(table, query, limit = 10) {
178
+ const startTime = Date.now();
139
179
  this.logger.progress(`Querying ${table} table (limit: ${limit})...`);
140
- const result = await this.client.searchRecords(table, query, limit);
141
- const recordCount = result?.data?.result?.length || 0;
142
- this.logger.info(`Query completed: ${recordCount} records found`);
143
- return result;
180
+ try {
181
+ const result = await this.client.searchRecords(table, query, limit);
182
+ const recordCount = result?.data?.result?.length || 0;
183
+ const duration = Date.now() - startTime;
184
+ // Log API call
185
+ await this.auditLogger.logAPICall('searchRecords', table, 'query', recordCount, duration, true);
186
+ this.logger.info(`Query completed: ${recordCount} records found`);
187
+ return result;
188
+ }
189
+ catch (error) {
190
+ const duration = Date.now() - startTime;
191
+ await this.auditLogger.logAPICall('searchRecords', table, 'query', 0, duration, false);
192
+ throw error;
193
+ }
144
194
  }
145
195
  /**
146
- * Create record with tracking
196
+ * Create record with tracking and audit logging
147
197
  */
148
198
  async createRecord(table, data) {
199
+ const startTime = Date.now();
149
200
  this.logger.progress(`Creating ${table} record...`);
150
- const result = await this.client.createRecord(table, data);
151
- if (result?.success) {
152
- this.logger.info(`✅ Created ${table} record: ${result.data?.result?.sys_id}`);
201
+ try {
202
+ const result = await this.client.createRecord(table, data);
203
+ const duration = Date.now() - startTime;
204
+ const success = !!result?.success;
205
+ const sysId = result?.data?.result?.sys_id;
206
+ // Log API call with audit
207
+ await this.auditLogger.logAPICall('createRecord', table, 'create', 1, duration, success);
208
+ if (success) {
209
+ this.logger.info(`✅ Created ${table} record: ${sysId}`);
210
+ }
211
+ return result;
212
+ }
213
+ catch (error) {
214
+ const duration = Date.now() - startTime;
215
+ await this.auditLogger.logAPICall('createRecord', table, 'create', 0, duration, false);
216
+ throw error;
153
217
  }
154
- return result;
155
218
  }
156
219
  /**
157
- * Update record with tracking
220
+ * Update record with tracking and audit logging
158
221
  */
159
222
  async updateRecord(table, sysId, data) {
223
+ const startTime = Date.now();
160
224
  this.logger.progress(`Updating ${table} record ${sysId}...`);
161
- const result = await this.client.updateRecord(table, sysId, data);
162
- if (result?.success) {
163
- this.logger.info(`✅ Updated ${table} record: ${sysId}`);
225
+ try {
226
+ const result = await this.client.updateRecord(table, sysId, data);
227
+ const duration = Date.now() - startTime;
228
+ const success = !!result?.success;
229
+ // Log API call with audit
230
+ await this.auditLogger.logAPICall('updateRecord', table, 'update', 1, duration, success);
231
+ if (success) {
232
+ this.logger.info(`✅ Updated ${table} record: ${sysId}`);
233
+ }
234
+ return result;
235
+ }
236
+ catch (error) {
237
+ const duration = Date.now() - startTime;
238
+ await this.auditLogger.logAPICall('updateRecord', table, 'update', 0, duration, false);
239
+ throw error;
164
240
  }
165
- return result;
166
241
  }
167
242
  /**
168
- * Get record with tracking
243
+ * Get record with tracking and audit logging
169
244
  */
170
245
  async getRecord(table, sysId) {
246
+ const startTime = Date.now();
171
247
  this.logger.progress(`Getting ${table} record ${sysId}...`);
172
- const result = await this.client.getRecord(table, sysId);
173
- if (result?.success) {
174
- this.logger.info(`✅ Retrieved ${table} record: ${sysId}`);
248
+ try {
249
+ const result = await this.client.getRecord(table, sysId);
250
+ const duration = Date.now() - startTime;
251
+ const success = !!result?.success;
252
+ // Log API call with audit
253
+ await this.auditLogger.logAPICall('getRecord', table, 'read', success ? 1 : 0, duration, success);
254
+ if (success) {
255
+ this.logger.info(`✅ Retrieved ${table} record: ${sysId}`);
256
+ }
257
+ return result;
258
+ }
259
+ catch (error) {
260
+ const duration = Date.now() - startTime;
261
+ await this.auditLogger.logAPICall('getRecord', table, 'read', 0, duration, false);
262
+ throw error;
175
263
  }
176
- return result;
177
264
  }
178
265
  /**
179
266
  * Get client for direct use
@@ -187,6 +274,36 @@ class EnhancedBaseMCPServer {
187
274
  getLogger() {
188
275
  return this.logger;
189
276
  }
277
+ /**
278
+ * Get audit logger for direct use
279
+ */
280
+ getAuditLogger() {
281
+ return this.auditLogger;
282
+ }
283
+ /**
284
+ * Cleanup resources and flush audit logs
285
+ */
286
+ async cleanup() {
287
+ this.logger.info(`🧹 Cleaning up ${this.serverName} MCP Server`);
288
+ try {
289
+ // Flush pending audit logs
290
+ await this.auditLogger.flush();
291
+ // Log server shutdown
292
+ await this.auditLogger.logOperation('server_shutdown', 'INFO', {
293
+ message: `${this.serverName} MCP Server shutting down`,
294
+ metadata: {
295
+ uptime_ms: Date.now() - this.logger['startTime'],
296
+ final_token_usage: this.logger.getTokenUsage()
297
+ }
298
+ });
299
+ // Stop progress indicators
300
+ this.logger.stopProgress();
301
+ this.logger.info(`✅ ${this.serverName} cleanup completed`);
302
+ }
303
+ catch (error) {
304
+ this.logger.error('Error during cleanup', error);
305
+ }
306
+ }
190
307
  }
191
308
  exports.EnhancedBaseMCPServer = EnhancedBaseMCPServer;
192
309
  //# sourceMappingURL=enhanced-base-mcp-server.js.map
@@ -0,0 +1,109 @@
1
+ /**
2
+ * ServiceNow Audit Logger - Snow-Flow Activity Tracking
3
+ *
4
+ * Extends the existing MCPLogger to send comprehensive audit logs
5
+ * to ServiceNow for all Snow-Flow activities with token usage tracking.
6
+ *
7
+ * Creates audit trail with source 'snow-flow' for compliance & debugging.
8
+ */
9
+ import { MCPLogger } from '../mcp/shared/mcp-logger.js';
10
+ import { ServiceNowClient } from './servicenow-client.js';
11
+ export interface AuditLogEntry {
12
+ source: 'snow-flow';
13
+ level: 'INFO' | 'WARN' | 'ERROR' | 'DEBUG';
14
+ message: string;
15
+ operation: string;
16
+ table?: string;
17
+ sys_id?: string;
18
+ user_id?: string;
19
+ token_usage?: {
20
+ input: number;
21
+ output: number;
22
+ total: number;
23
+ };
24
+ duration_ms?: number;
25
+ metadata?: any;
26
+ timestamp: string;
27
+ session_id?: string;
28
+ mcp_server: string;
29
+ }
30
+ export declare class ServiceNowAuditLogger {
31
+ private mcpLogger;
32
+ private serviceNowClient?;
33
+ private sessionId;
34
+ private mcpServerName;
35
+ private isEnabled;
36
+ private auditQueue;
37
+ private batchTimer?;
38
+ constructor(mcpLogger: MCPLogger, mcpServerName: string);
39
+ /**
40
+ * Initialize with ServiceNow client for audit log transmission
41
+ */
42
+ setServiceNowClient(client: ServiceNowClient): void;
43
+ /**
44
+ * Generate unique session ID for tracking related operations
45
+ */
46
+ private generateSessionId;
47
+ /**
48
+ * Log Snow-Flow operation with comprehensive audit trail
49
+ */
50
+ logOperation(operation: string, level?: 'INFO' | 'WARN' | 'ERROR' | 'DEBUG', details?: {
51
+ message?: string;
52
+ table?: string;
53
+ sys_id?: string;
54
+ duration_ms?: number;
55
+ metadata?: any;
56
+ success?: boolean;
57
+ }): Promise<void>;
58
+ /**
59
+ * Log API call with token tracking
60
+ */
61
+ logAPICall(apiMethod: string, table: string, operation: string, recordCount?: number, duration_ms?: number, success?: boolean): Promise<void>;
62
+ /**
63
+ * Log widget operations with specific tracking
64
+ */
65
+ logWidgetOperation(operation: 'pull' | 'push' | 'validate' | 'deploy', widgetSysId: string, widgetName?: string, duration_ms?: number, success?: boolean, errorDetails?: any): Promise<void>;
66
+ /**
67
+ * Log artifact sync operations
68
+ */
69
+ logArtifactSync(action: 'pull' | 'push' | 'cleanup', table: string, sys_id: string, artifactName?: string, fileCount?: number, duration_ms?: number, success?: boolean): Promise<void>;
70
+ /**
71
+ * Log script execution with ES5 validation
72
+ */
73
+ logScriptExecution(scriptType: 'background' | 'business_rule' | 'client_script', duration_ms?: number, success?: boolean, errorDetails?: any, outputLines?: number): Promise<void>;
74
+ /**
75
+ * Log authentication and token operations
76
+ */
77
+ logAuthOperation(operation: 'login' | 'token_refresh' | 'scope_elevation', success?: boolean, details?: any): Promise<void>;
78
+ /**
79
+ * Schedule batch sending to ServiceNow to avoid API flooding
80
+ */
81
+ private scheduleBatchSend;
82
+ /**
83
+ * Send audit log batch to ServiceNow
84
+ */
85
+ private sendAuditBatch;
86
+ /**
87
+ * Flush all pending audit logs immediately
88
+ */
89
+ flush(): Promise<void>;
90
+ /**
91
+ * Create audit logger wrapper for existing MCP server
92
+ */
93
+ static wrap(mcpLogger: MCPLogger, mcpServerName: string): ServiceNowAuditLogger;
94
+ /**
95
+ * Get audit statistics
96
+ */
97
+ getAuditStats(): {
98
+ session_id: string;
99
+ pending_logs: number;
100
+ is_enabled: boolean;
101
+ has_servicenow_client: boolean;
102
+ };
103
+ }
104
+ export declare function getAuditLogger(mcpLogger: MCPLogger, mcpServerName: string): ServiceNowAuditLogger;
105
+ /**
106
+ * Initialize all audit loggers with ServiceNow client
107
+ */
108
+ export declare function initializeAuditLogging(serviceNowClient: ServiceNowClient): void;
109
+ //# sourceMappingURL=servicenow-audit-logger.d.ts.map
@@ -0,0 +1,267 @@
1
+ "use strict";
2
+ /**
3
+ * ServiceNow Audit Logger - Snow-Flow Activity Tracking
4
+ *
5
+ * Extends the existing MCPLogger to send comprehensive audit logs
6
+ * to ServiceNow for all Snow-Flow activities with token usage tracking.
7
+ *
8
+ * Creates audit trail with source 'snow-flow' for compliance & debugging.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.ServiceNowAuditLogger = void 0;
12
+ exports.getAuditLogger = getAuditLogger;
13
+ exports.initializeAuditLogging = initializeAuditLogging;
14
+ class ServiceNowAuditLogger {
15
+ constructor(mcpLogger, mcpServerName) {
16
+ this.isEnabled = true;
17
+ this.auditQueue = [];
18
+ this.mcpLogger = mcpLogger;
19
+ this.mcpServerName = mcpServerName;
20
+ this.sessionId = this.generateSessionId();
21
+ // Enable audit logging if not explicitly disabled
22
+ this.isEnabled = process.env.SNOW_FLOW_AUDIT_LOGGING !== 'false';
23
+ if (this.isEnabled) {
24
+ this.mcpLogger.info('🔍 ServiceNow Audit Logger initialized', {
25
+ source: 'snow-flow',
26
+ session_id: this.sessionId,
27
+ mcp_server: this.mcpServerName
28
+ });
29
+ }
30
+ }
31
+ /**
32
+ * Initialize with ServiceNow client for audit log transmission
33
+ */
34
+ setServiceNowClient(client) {
35
+ this.serviceNowClient = client;
36
+ if (this.isEnabled) {
37
+ this.mcpLogger.info('🔗 ServiceNow client connected for audit logging');
38
+ }
39
+ }
40
+ /**
41
+ * Generate unique session ID for tracking related operations
42
+ */
43
+ generateSessionId() {
44
+ return `snow-flow-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
45
+ }
46
+ /**
47
+ * Log Snow-Flow operation with comprehensive audit trail
48
+ */
49
+ async logOperation(operation, level = 'INFO', details = {}) {
50
+ if (!this.isEnabled)
51
+ return;
52
+ const tokenUsage = this.mcpLogger.getTokenUsage();
53
+ const timestamp = new Date().toISOString();
54
+ const auditEntry = {
55
+ source: 'snow-flow',
56
+ level,
57
+ message: details.message || `Snow-Flow ${operation}`,
58
+ operation,
59
+ table: details.table,
60
+ sys_id: details.sys_id,
61
+ token_usage: tokenUsage.total > 0 ? tokenUsage : undefined,
62
+ duration_ms: details.duration_ms,
63
+ metadata: {
64
+ ...details.metadata,
65
+ success: details.success,
66
+ mcp_server: this.mcpServerName,
67
+ session_id: this.sessionId
68
+ },
69
+ timestamp,
70
+ session_id: this.sessionId,
71
+ mcp_server: this.mcpServerName
72
+ };
73
+ // Log to console immediately via MCPLogger
74
+ this.mcpLogger.info(`🔍 [AUDIT] ${operation}`, auditEntry);
75
+ // Queue for ServiceNow transmission
76
+ this.auditQueue.push(auditEntry);
77
+ // Batch send to avoid overwhelming ServiceNow
78
+ this.scheduleBatchSend();
79
+ }
80
+ /**
81
+ * Log API call with token tracking
82
+ */
83
+ async logAPICall(apiMethod, table, operation, recordCount, duration_ms, success = true) {
84
+ await this.logOperation('api_call', success ? 'INFO' : 'ERROR', {
85
+ message: `API ${apiMethod} on ${table} (${recordCount || 0} records)`,
86
+ table,
87
+ duration_ms,
88
+ metadata: {
89
+ api_method: apiMethod,
90
+ record_count: recordCount,
91
+ operation
92
+ },
93
+ success
94
+ });
95
+ }
96
+ /**
97
+ * Log widget operations with specific tracking
98
+ */
99
+ async logWidgetOperation(operation, widgetSysId, widgetName, duration_ms, success = true, errorDetails) {
100
+ await this.logOperation('widget_operation', success ? 'INFO' : 'ERROR', {
101
+ message: `Widget ${operation}: ${widgetName || widgetSysId}`,
102
+ table: 'sp_widget',
103
+ sys_id: widgetSysId,
104
+ duration_ms,
105
+ metadata: {
106
+ widget_name: widgetName,
107
+ operation_type: operation,
108
+ error_details: errorDetails
109
+ },
110
+ success
111
+ });
112
+ }
113
+ /**
114
+ * Log artifact sync operations
115
+ */
116
+ async logArtifactSync(action, table, sys_id, artifactName, fileCount, duration_ms, success = true) {
117
+ await this.logOperation('artifact_sync', success ? 'INFO' : 'ERROR', {
118
+ message: `Artifact ${action}: ${artifactName || sys_id} (${fileCount || 0} files)`,
119
+ table,
120
+ sys_id,
121
+ duration_ms,
122
+ metadata: {
123
+ action,
124
+ artifact_name: artifactName,
125
+ file_count: fileCount
126
+ },
127
+ success
128
+ });
129
+ }
130
+ /**
131
+ * Log script execution with ES5 validation
132
+ */
133
+ async logScriptExecution(scriptType, duration_ms, success = true, errorDetails, outputLines) {
134
+ await this.logOperation('script_execution', success ? 'INFO' : 'ERROR', {
135
+ message: `Script execution: ${scriptType}`,
136
+ duration_ms,
137
+ metadata: {
138
+ script_type: scriptType,
139
+ output_lines: outputLines,
140
+ error_details: errorDetails,
141
+ es5_validated: true // Snow-Flow always uses ES5
142
+ },
143
+ success
144
+ });
145
+ }
146
+ /**
147
+ * Log authentication and token operations
148
+ */
149
+ async logAuthOperation(operation, success = true, details) {
150
+ await this.logOperation('authentication', success ? 'INFO' : 'WARN', {
151
+ message: `Auth ${operation}`,
152
+ metadata: {
153
+ operation,
154
+ ...details
155
+ },
156
+ success
157
+ });
158
+ }
159
+ /**
160
+ * Schedule batch sending to ServiceNow to avoid API flooding
161
+ */
162
+ scheduleBatchSend() {
163
+ if (this.batchTimer) {
164
+ clearTimeout(this.batchTimer);
165
+ }
166
+ // Send batches every 10 seconds or when queue reaches 20 entries
167
+ const shouldSendImmediately = this.auditQueue.length >= 20;
168
+ const delay = shouldSendImmediately ? 0 : 10000;
169
+ this.batchTimer = setTimeout(() => {
170
+ this.sendAuditBatch();
171
+ }, delay);
172
+ }
173
+ /**
174
+ * Send audit log batch to ServiceNow
175
+ */
176
+ async sendAuditBatch() {
177
+ if (this.auditQueue.length === 0 || !this.serviceNowClient) {
178
+ return;
179
+ }
180
+ const batch = [...this.auditQueue];
181
+ this.auditQueue = []; // Clear queue
182
+ try {
183
+ // Send to ServiceNow sys_log table with source 'snow-flow'
184
+ for (const entry of batch) {
185
+ await this.serviceNowClient.createRecord('sys_log', {
186
+ source: 'snow-flow',
187
+ level: entry.level,
188
+ message: entry.message,
189
+ sys_created_on: entry.timestamp,
190
+ // Custom fields for Snow-Flow specific data
191
+ u_operation: entry.operation,
192
+ u_table: entry.table,
193
+ u_sys_id: entry.sys_id,
194
+ u_session_id: entry.session_id,
195
+ u_mcp_server: entry.mcp_server,
196
+ u_token_usage: entry.token_usage ? JSON.stringify(entry.token_usage) : null,
197
+ u_duration_ms: entry.duration_ms,
198
+ u_metadata: entry.metadata ? JSON.stringify(entry.metadata) : null
199
+ });
200
+ }
201
+ this.mcpLogger.info(`📨 Sent ${batch.length} audit log entries to ServiceNow`);
202
+ }
203
+ catch (error) {
204
+ // If ServiceNow logging fails, log locally but don't fail the operation
205
+ this.mcpLogger.warn('Failed to send audit logs to ServiceNow', {
206
+ error: error instanceof Error ? error.message : String(error),
207
+ batch_size: batch.length
208
+ });
209
+ // Re-queue failed entries (with max retry limit)
210
+ const retriedEntries = batch.map(entry => ({
211
+ ...entry,
212
+ metadata: {
213
+ ...entry.metadata,
214
+ retry_count: (entry.metadata?.retry_count || 0) + 1
215
+ }
216
+ })).filter(entry => (entry.metadata?.retry_count || 0) < 3);
217
+ this.auditQueue.unshift(...retriedEntries);
218
+ }
219
+ }
220
+ /**
221
+ * Flush all pending audit logs immediately
222
+ */
223
+ async flush() {
224
+ if (this.batchTimer) {
225
+ clearTimeout(this.batchTimer);
226
+ this.batchTimer = undefined;
227
+ }
228
+ await this.sendAuditBatch();
229
+ }
230
+ /**
231
+ * Create audit logger wrapper for existing MCP server
232
+ */
233
+ static wrap(mcpLogger, mcpServerName) {
234
+ return new ServiceNowAuditLogger(mcpLogger, mcpServerName);
235
+ }
236
+ /**
237
+ * Get audit statistics
238
+ */
239
+ getAuditStats() {
240
+ return {
241
+ session_id: this.sessionId,
242
+ pending_logs: this.auditQueue.length,
243
+ is_enabled: this.isEnabled,
244
+ has_servicenow_client: !!this.serviceNowClient
245
+ };
246
+ }
247
+ }
248
+ exports.ServiceNowAuditLogger = ServiceNowAuditLogger;
249
+ /**
250
+ * Global audit logger factory for consistent usage across MCP servers
251
+ */
252
+ const auditLoggers = new Map();
253
+ function getAuditLogger(mcpLogger, mcpServerName) {
254
+ if (!auditLoggers.has(mcpServerName)) {
255
+ auditLoggers.set(mcpServerName, new ServiceNowAuditLogger(mcpLogger, mcpServerName));
256
+ }
257
+ return auditLoggers.get(mcpServerName);
258
+ }
259
+ /**
260
+ * Initialize all audit loggers with ServiceNow client
261
+ */
262
+ function initializeAuditLogging(serviceNowClient) {
263
+ auditLoggers.forEach(logger => {
264
+ logger.setServiceNowClient(serviceNowClient);
265
+ });
266
+ }
267
+ //# sourceMappingURL=servicenow-audit-logger.js.map
@@ -6,6 +6,10 @@ import { MCPLogger } from '../mcp/shared/mcp-logger.js';
6
6
  export declare class ServiceNowClientWithTracking extends ServiceNowClient {
7
7
  private mcpLogger;
8
8
  constructor(logger?: MCPLogger);
9
+ /**
10
+ * Get the base ServiceNow client for audit logger integration
11
+ */
12
+ getBaseClient(): ServiceNowClient;
9
13
  /**
10
14
  * Override makeRequest to add tracking
11
15
  */
@@ -11,6 +11,12 @@ class ServiceNowClientWithTracking extends servicenow_client_js_1.ServiceNowClie
11
11
  super();
12
12
  this.mcpLogger = logger || new mcp_logger_js_1.MCPLogger('ServiceNow-API');
13
13
  }
14
+ /**
15
+ * Get the base ServiceNow client for audit logger integration
16
+ */
17
+ getBaseClient() {
18
+ return this;
19
+ }
14
20
  /**
15
21
  * Override makeRequest to add tracking
16
22
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "3.5.17",
4
- "description": "ServiceNow development framework with ROBUST artifact detection fix. v3.5.17 FIXES the intermittent 'Could not find artifact' errors in snow_pull_artifact. Enhanced pullArtifactBySysId with detailed error logging, longer timeouts (8s per table), smart table ordering, and comprehensive troubleshooting guidance. ULTRA-CONSERVATIVE wrapper system prevents duplicate HTML comments. Enhanced 'snow-flow init' creates optimized settings.json with 18 MCP servers, extensive development permissions, intelligent hooks, and ServiceNow-specific commands. Features intelligent timeout configuration and comprehensive ServiceNow development workflow.",
3
+ "version": "3.6.0",
4
+ "description": "ServiceNow development framework with COMPREHENSIVE AUDIT LOGGING. v3.6.0 introduces complete audit trail for all Snow-Flow activities in ServiceNow with source 'snow-flow'. Tracks API calls, widget operations, script execution, authentication, and tool usage with token consumption data. Enhanced compliance support for SOX/GDPR/HIPAA with detailed security monitoring. Includes robust artifact detection fix, ultra-conservative wrapper system, and optimized settings.json with 18 MCP servers. Features intelligent timeout configuration and comprehensive ServiceNow development workflow.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {