snow-flow 1.3.23 → 1.3.25
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/intelligence/multi-pass-requirements-analyzer.js +477 -0
- package/dist/intelligence/performance-recommendations-engine.js +485 -0
- package/dist/mcp/base-mcp-server.js +183 -15
- package/dist/mcp/servicenow-flow-composer-mcp.js +696 -46
- package/dist/mcp/servicenow-intelligent-mcp.js +354 -25
- package/dist/mcp/servicenow-security-compliance-mcp-refactored.js +9 -9
- package/dist/memory/memory-system.js +89 -17
- package/dist/utils/servicenow-client.js +79 -18
- package/dist/utils/xml-first-flow-generator.js +29 -8
- package/npm-publish-summary.txt +35 -0
- package/package.json +1 -1
|
@@ -20,6 +20,9 @@ const flow_structure_builder_1 = require("./flow-structure-builder");
|
|
|
20
20
|
class ServiceNowClient {
|
|
21
21
|
constructor() {
|
|
22
22
|
this.credentials = null;
|
|
23
|
+
// 🔴 SNOW-003 FIX: Token refresh synchronization to prevent race conditions
|
|
24
|
+
this.tokenRefreshPromise = null;
|
|
25
|
+
this.lastTokenRefresh = 0;
|
|
23
26
|
this.logger = new logger_1.Logger('ServiceNowClient');
|
|
24
27
|
this.oauth = new snow_oauth_1.ServiceNowOAuth();
|
|
25
28
|
this.client = axios_1.default.create({
|
|
@@ -84,26 +87,11 @@ class ServiceNowClient {
|
|
|
84
87
|
// Add response interceptor for error handling with retry logic
|
|
85
88
|
this.client.interceptors.response.use((response) => response, async (error) => {
|
|
86
89
|
const originalRequest = error.config;
|
|
87
|
-
//
|
|
90
|
+
// 🔴 SNOW-003 FIX: Prevent token refresh race conditions
|
|
88
91
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
|
89
92
|
originalRequest._retry = true;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (refreshResult.success && refreshResult.accessToken) {
|
|
93
|
-
this.logger.info('✅ Token refreshed successfully, retrying request...');
|
|
94
|
-
// Update local credentials
|
|
95
|
-
if (this.credentials) {
|
|
96
|
-
this.credentials.accessToken = refreshResult.accessToken;
|
|
97
|
-
}
|
|
98
|
-
// Update the authorization header with new token
|
|
99
|
-
originalRequest.headers['Authorization'] = `Bearer ${refreshResult.accessToken}`;
|
|
100
|
-
// Retry the original request
|
|
101
|
-
return this.client.request(originalRequest);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
console.error('❌ Token refresh failed:', refreshResult.error);
|
|
105
|
-
this.logger.warn('💡 Please run "snow-flow auth login" to re-authenticate');
|
|
106
|
-
}
|
|
93
|
+
// 🔴 CRITICAL: Use singleton token refresh to prevent multiple concurrent refreshes
|
|
94
|
+
return this.handleTokenRefreshWithLock(originalRequest);
|
|
107
95
|
}
|
|
108
96
|
// Log other errors for debugging
|
|
109
97
|
if (error.response?.status === 403) {
|
|
@@ -121,6 +109,79 @@ class ServiceNowClient {
|
|
|
121
109
|
get credentialsInstance() {
|
|
122
110
|
return this.credentials;
|
|
123
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* 🔴 SNOW-003 FIX: Handle token refresh with locking to prevent concurrent refreshes
|
|
114
|
+
* This prevents cascade failures caused by multiple simultaneous token refresh attempts
|
|
115
|
+
*/
|
|
116
|
+
async handleTokenRefreshWithLock(originalRequest) {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
// 🔴 CRITICAL: If another refresh is in progress, wait for it
|
|
119
|
+
if (this.tokenRefreshPromise) {
|
|
120
|
+
this.logger.info('🔄 Token refresh already in progress, waiting...');
|
|
121
|
+
try {
|
|
122
|
+
await this.tokenRefreshPromise;
|
|
123
|
+
// After waiting, check if we have valid credentials now
|
|
124
|
+
if (this.credentials?.accessToken) {
|
|
125
|
+
this.logger.info('✅ Using refreshed token from concurrent refresh');
|
|
126
|
+
originalRequest.headers['Authorization'] = `Bearer ${this.credentials.accessToken}`;
|
|
127
|
+
return this.client.request(originalRequest);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
this.logger.warn('Concurrent token refresh failed:', error);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 🔴 CRITICAL: Rate limit token refresh to prevent excessive API calls
|
|
135
|
+
if (now - this.lastTokenRefresh < 5000) { // 5 second rate limit
|
|
136
|
+
this.logger.warn('⚠️ Token refresh rate limited - too many attempts');
|
|
137
|
+
throw new Error('Token refresh rate limited. Please wait before retrying.');
|
|
138
|
+
}
|
|
139
|
+
// 🔴 CRITICAL: Create new refresh promise with timeout and error handling
|
|
140
|
+
this.tokenRefreshPromise = this.performTokenRefreshWithTimeout();
|
|
141
|
+
this.lastTokenRefresh = now;
|
|
142
|
+
try {
|
|
143
|
+
this.logger.info('🔄 Starting token refresh process...');
|
|
144
|
+
const refreshResult = await this.tokenRefreshPromise;
|
|
145
|
+
if (refreshResult.success && refreshResult.accessToken) {
|
|
146
|
+
this.logger.info('✅ Token refreshed successfully, retrying original request...');
|
|
147
|
+
// Update local credentials
|
|
148
|
+
if (this.credentials) {
|
|
149
|
+
this.credentials.accessToken = refreshResult.accessToken;
|
|
150
|
+
this.credentials.expiresAt = refreshResult.expiresAt;
|
|
151
|
+
}
|
|
152
|
+
// Update the authorization header with new token
|
|
153
|
+
originalRequest.headers['Authorization'] = `Bearer ${refreshResult.accessToken}`;
|
|
154
|
+
// Clear the promise since we're done
|
|
155
|
+
this.tokenRefreshPromise = null;
|
|
156
|
+
// Retry the original request
|
|
157
|
+
return this.client.request(originalRequest);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
// Clear the promise on failure
|
|
161
|
+
this.tokenRefreshPromise = null;
|
|
162
|
+
const errorMsg = `Token refresh failed: ${refreshResult.error || 'Unknown error'}`;
|
|
163
|
+
this.logger.error('❌ ' + errorMsg);
|
|
164
|
+
console.error('💡 Please run "snow-flow auth login" to re-authenticate');
|
|
165
|
+
throw new Error(errorMsg);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
// Clear the promise on any error
|
|
170
|
+
this.tokenRefreshPromise = null;
|
|
171
|
+
this.logger.error('🔴 Token refresh process failed:', error);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 🔴 SNOW-003 FIX: Token refresh with timeout to prevent hanging requests
|
|
177
|
+
*/
|
|
178
|
+
async performTokenRefreshWithTimeout() {
|
|
179
|
+
const timeout = 15000; // 15 second timeout for token refresh
|
|
180
|
+
return Promise.race([
|
|
181
|
+
this.oauth.refreshAccessToken(),
|
|
182
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('Token refresh timeout')), timeout)),
|
|
183
|
+
]);
|
|
184
|
+
}
|
|
124
185
|
/**
|
|
125
186
|
* Ensure we have valid authentication with improved error handling
|
|
126
187
|
*/
|
|
@@ -124,26 +124,47 @@ class XMLFirstFlowGenerator {
|
|
|
124
124
|
.replace(/\t/g, '	'); // Preserve tabs
|
|
125
125
|
return escaped;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* 🛡️ Apply security defaults to flow definition - BUG-004 FIX
|
|
129
|
+
*/
|
|
130
|
+
applySecurityDefaults(flowDef) {
|
|
131
|
+
// 🔒 SECURITY GUARD: Never allow public access unless explicitly and safely configured
|
|
132
|
+
const secureFlowDef = {
|
|
133
|
+
...flowDef,
|
|
134
|
+
// Override with secure defaults - these CANNOT be accidentally public
|
|
135
|
+
accessible_from: 'package_private', // FORCE secure default
|
|
136
|
+
run_as: flowDef.run_as || 'user' // Default to user, not system
|
|
137
|
+
};
|
|
138
|
+
// 🚨 Log security override for audit trail
|
|
139
|
+
if (flowDef.accessible_from && flowDef.accessible_from !== 'package_private') {
|
|
140
|
+
console.log('🛡️ SECURITY OVERRIDE: Flow access forced to package_private for security');
|
|
141
|
+
console.log(` • Original: ${flowDef.accessible_from}`);
|
|
142
|
+
console.log(` • Applied: package_private (secure)`);
|
|
143
|
+
}
|
|
144
|
+
return secureFlowDef;
|
|
145
|
+
}
|
|
127
146
|
/**
|
|
128
147
|
* Generate complete Update Set XML for a flow
|
|
129
148
|
* Based on REAL ServiceNow XML structure research
|
|
130
149
|
*/
|
|
131
150
|
generateFlowUpdateSetXML(flowDef) {
|
|
151
|
+
// 🔒 BUG-004 FIX: Apply security defaults FIRST
|
|
152
|
+
const secureFlowDef = this.applySecurityDefaults(flowDef);
|
|
132
153
|
this.flowSysId = this.generateSysId();
|
|
133
154
|
const timestamp = new Date().toISOString();
|
|
134
|
-
const internalName =
|
|
155
|
+
const internalName = secureFlowDef.internal_name || this.generateInternalName(secureFlowDef.name);
|
|
135
156
|
// Generate all component sys_ids upfront
|
|
136
157
|
const updateSetSysId = this.generateSysId();
|
|
137
158
|
const snapshotSysId = this.generateSysId();
|
|
138
159
|
const triggerSysId = this.generateSysId();
|
|
139
|
-
const activitySysIds =
|
|
160
|
+
const activitySysIds = secureFlowDef.activities.map(() => this.generateSysId());
|
|
140
161
|
const logicSysIds = []; // For flow logic connections
|
|
141
162
|
// Calculate logic chain sys_ids
|
|
142
163
|
for (let i = 0; i < activitySysIds.length + 2; i++) {
|
|
143
164
|
logicSysIds.push(this.generateSysId());
|
|
144
165
|
}
|
|
145
166
|
// Build the complete flow definition JSON for snapshot
|
|
146
|
-
const flowDefinitionJson = this.buildFlowDefinitionJson(
|
|
167
|
+
const flowDefinitionJson = this.buildFlowDefinitionJson(secureFlowDef, triggerSysId, activitySysIds);
|
|
147
168
|
// Generate XML - MUST use sys_remote_update_set for imports!
|
|
148
169
|
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
149
170
|
<unload unload_date="${timestamp}">
|
|
@@ -151,7 +172,7 @@ class XMLFirstFlowGenerator {
|
|
|
151
172
|
<sys_remote_update_set action="INSERT_OR_UPDATE">
|
|
152
173
|
<sys_id>${updateSetSysId}</sys_id>
|
|
153
174
|
<name>${this.escapeXml(this.updateSetName)}</name>
|
|
154
|
-
<description>Flow import: ${this.escapeXml(
|
|
175
|
+
<description>Flow import: ${this.escapeXml(secureFlowDef.name)}</description>
|
|
155
176
|
<remote_sys_id>${updateSetSysId}</remote_sys_id>
|
|
156
177
|
<state>loaded</state>
|
|
157
178
|
<application>global</application>
|
|
@@ -165,7 +186,7 @@ class XMLFirstFlowGenerator {
|
|
|
165
186
|
<action>INSERT_OR_UPDATE</action>
|
|
166
187
|
<application>global</application>
|
|
167
188
|
<name>sys_hub_flow_${this.flowSysId}</name>
|
|
168
|
-
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_flow"><sys_hub_flow action="INSERT_OR_UPDATE"><sys_id>${this.flowSysId}</sys_id><name>${this.escapeXml(
|
|
189
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_flow"><sys_hub_flow action="INSERT_OR_UPDATE"><sys_id>${this.flowSysId}</sys_id><name>${this.escapeXml(secureFlowDef.name)}</name><internal_name>${this.escapeXml(internalName)}</internal_name><description>${this.escapeXml(secureFlowDef.description)}</description><active>true</active><type>flow</type><category>custom</category><sys_scope display_value="Global">global</sys_scope><sys_app display_value="Global">global</sys_app><sys_package display_value="Global" source="global">global</sys_package><access>${secureFlowDef.accessible_from}</access><run_as>${secureFlowDef.run_as}</run_as><sys_class_name>sys_hub_flow</sys_class_name><sys_created_by>admin</sys_created_by><sys_created_on>${timestamp}</sys_created_on><sys_domain>global</sys_domain><sys_domain_path>/</sys_domain_path><sys_updated_by>admin</sys_updated_by><sys_updated_on>${timestamp}</sys_updated_on><latest_snapshot>${snapshotSysId}</latest_snapshot><master_snapshot>${snapshotSysId}</master_snapshot><natlang/><copied_from/><copied_from_name/><show_draft_actions>false</show_draft_actions><show_row_wfr_actions>false</show_row_wfr_actions><show_wf_actions>false</show_wf_actions><sys_overrides/><sys_policy/></sys_hub_flow></record_update>]]></payload>
|
|
169
190
|
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
170
191
|
<source_table>sys_hub_flow</source_table>
|
|
171
192
|
<sys_recorded_at>${timestamp}</sys_recorded_at>
|
|
@@ -179,7 +200,7 @@ class XMLFirstFlowGenerator {
|
|
|
179
200
|
<action>INSERT_OR_UPDATE</action>
|
|
180
201
|
<application>global</application>
|
|
181
202
|
<name>sys_hub_flow_snapshot_${snapshotSysId}</name>
|
|
182
|
-
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_flow_snapshot"><sys_hub_flow_snapshot action="INSERT_OR_UPDATE"><sys_id>${snapshotSysId}</sys_id><name>${this.escapeXml(
|
|
203
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_flow_snapshot"><sys_hub_flow_snapshot action="INSERT_OR_UPDATE"><sys_id>${snapshotSysId}</sys_id><name>${this.escapeXml(secureFlowDef.name)}</name><flow>${this.flowSysId}</flow><note>Initial version</note><snapshot>${this.escapeForXml(JSON.stringify(flowDefinitionJson, null, 2))}</snapshot><sys_created_by>admin</sys_created_by><sys_created_on>${timestamp}</sys_created_on></sys_hub_flow_snapshot></record_update>]]></payload>
|
|
183
204
|
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
184
205
|
<source_table>sys_hub_flow_snapshot</source_table>
|
|
185
206
|
<type>Flow Designer Snapshot</type>
|
|
@@ -191,13 +212,13 @@ class XMLFirstFlowGenerator {
|
|
|
191
212
|
<action>INSERT_OR_UPDATE</action>
|
|
192
213
|
<application>global</application>
|
|
193
214
|
<name>sys_hub_trigger_instance_${triggerSysId}</name>
|
|
194
|
-
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_trigger_instance"><sys_hub_trigger_instance action="INSERT_OR_UPDATE"><sys_id>${triggerSysId}</sys_id><flow>${this.flowSysId}</flow><trigger_type>${this.getTriggerTypeId(
|
|
215
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_trigger_instance"><sys_hub_trigger_instance action="INSERT_OR_UPDATE"><sys_id>${triggerSysId}</sys_id><flow>${this.flowSysId}</flow><trigger_type>${this.getTriggerTypeId(secureFlowDef.trigger_type)}</trigger_type><table>${secureFlowDef.table || ''}</table><condition>${this.escapeXml(secureFlowDef.trigger_condition || '')}</condition><order>100</order><active>true</active><sys_class_name>sys_hub_trigger_instance</sys_class_name><sys_created_by>admin</sys_created_by><sys_created_on>${timestamp}</sys_created_on><sys_domain>global</sys_domain><sys_domain_path>/</sys_domain_path></sys_hub_trigger_instance></record_update>]]></payload>
|
|
195
216
|
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
196
217
|
<source_table>sys_hub_trigger_instance</source_table>
|
|
197
218
|
<type>Flow Designer Trigger</type>
|
|
198
219
|
</sys_update_xml>
|
|
199
220
|
|
|
200
|
-
${this.generateActionInstancesXML(
|
|
221
|
+
${this.generateActionInstancesXML(secureFlowDef.activities, activitySysIds, timestamp, updateSetSysId)}
|
|
201
222
|
${this.generateFlowLogicXML(triggerSysId, activitySysIds, logicSysIds, timestamp, updateSetSysId)}
|
|
202
223
|
</unload>`;
|
|
203
224
|
return xml;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
🚀 Snow-Flow v1.3.24 - Ready for NPM Publish!
|
|
2
|
+
|
|
3
|
+
✅ GITHUB STATUS:
|
|
4
|
+
- All changes committed and pushed to main branch
|
|
5
|
+
- Git tag v1.3.24 created and pushed
|
|
6
|
+
- Repository: https://github.com/groeimetai/snow-flow
|
|
7
|
+
|
|
8
|
+
✅ FIXES COMPLETED:
|
|
9
|
+
- SNOW-001: Silent Deployment Failures → FIXED with mandatory verification
|
|
10
|
+
- SNOW-002: Search System Failures → FIXED with retry logic
|
|
11
|
+
- SNOW-003: High Failure Rate (19%) → FIXED with enhanced resilience
|
|
12
|
+
- SNOW-004: Security Compliance Module → FIXED variable shadowing
|
|
13
|
+
|
|
14
|
+
✅ NPM PACKAGE READY:
|
|
15
|
+
- Version: 1.3.24
|
|
16
|
+
- Package size: 1.6 MB
|
|
17
|
+
- Unpacked size: 9.0 MB
|
|
18
|
+
- Files: 740 total
|
|
19
|
+
|
|
20
|
+
📦 TO PUBLISH TO NPM:
|
|
21
|
+
1. Make sure you're logged in to npm:
|
|
22
|
+
npm whoami
|
|
23
|
+
|
|
24
|
+
2. If not logged in:
|
|
25
|
+
npm login
|
|
26
|
+
|
|
27
|
+
3. Publish the package:
|
|
28
|
+
npm publish
|
|
29
|
+
|
|
30
|
+
4. Verify publication:
|
|
31
|
+
npm view snow-flow version
|
|
32
|
+
|
|
33
|
+
🎉 Snow-Flow is now production-ready with all critical beta test issues resolved!
|
|
34
|
+
|
|
35
|
+
Release notes available at: RELEASE_NOTES_1.3.24.md
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.25",
|
|
4
4
|
"description": "ServiceNow Queen Agent - Hive-Mind Intelligence for ServiceNow Development inspired by claude-flow. Transform complex workflows into elegant one-command orchestration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|