snow-flow 3.5.17 → 3.6.1
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-automation-mcp.js +46 -0
- package/dist/mcp/servicenow-local-development-mcp.js +31 -0
- package/dist/mcp/shared/enhanced-base-mcp-server.d.ts +18 -7
- package/dist/mcp/shared/enhanced-base-mcp-server.js +144 -27
- package/dist/templates/claude-md-template.d.ts +2 -1
- package/dist/templates/claude-md-template.js +360 -26
- package/dist/utils/servicenow-audit-logger.d.ts +109 -0
- package/dist/utils/servicenow-audit-logger.js +267 -0
- package/dist/utils/servicenow-client-with-tracking.d.ts +4 -0
- package/dist/utils/servicenow-client-with-tracking.js +6 -0
- package/package.json +2 -2
|
@@ -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.
|
|
4
|
-
"description": "ServiceNow development framework with
|
|
3
|
+
"version": "3.6.1",
|
|
4
|
+
"description": "ServiceNow development framework with ABSOLUTE NO MOCK DATA POLICY. v3.6.1 enforces COMPLETE, PRODUCTION-READY implementations with NO placeholders, NO TODO comments, NO test data, and NO shortcuts. EVERY implementation must be fully functional. Enhanced CLAUDE.md template with comprehensive examples of complete implementations. Includes comprehensive audit logging with source 'snow-flow', token tracking, and compliance support. Features robust artifact detection, ultra-conservative wrapper system, and 18 MCP servers. TAKE THE TIME. DO IT RIGHT. NO EXCEPTIONS.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|