snow-flow 1.3.22 → 1.3.24

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.
@@ -331,6 +331,7 @@ class ServiceNowFlowComposerMCP {
331
331
  console.log('🧠 Generated flow definition:', JSON.stringify(flowDefinition, null, 2));
332
332
  // 🧠 STEP 5: Deploy using XML-first approach for maximum reliability
333
333
  let deploymentResult = null;
334
+ let xmlResult = null; // 🔴 FIX: Declare outside try block to avoid scope issues
334
335
  if (args.deploy_immediately !== false) {
335
336
  console.log('🚀 DEPLOYING flow using XML-first approach...');
336
337
  try {
@@ -348,21 +349,30 @@ class ServiceNowFlowComposerMCP {
348
349
  accessible_from: 'package_private'
349
350
  };
350
351
  // Generate production-ready XML
351
- const xmlResult = generateProductionFlowXML(xmlFlowDef);
352
+ xmlResult = generateProductionFlowXML(xmlFlowDef);
352
353
  console.log('✅ XML generated:', xmlResult.filePath);
353
- // Auto-deploy with fallback strategies
354
+ // 🔴 CRITICAL FIX: Auto-deploy with INTEGRATED verification
355
+ // SNOW-001: Verification is now MANDATORY within each deployment strategy
354
356
  const deployResult = await this.deployWithFallback(xmlResult.filePath, flowDefinition);
355
- // Verify deployment was successful
356
- const flowExists = await this.verifyFlowInServiceNow(parsedIntent.flowName);
357
- if (!flowExists) {
358
- throw new Error('Flow deployment claimed success but flow not found in ServiceNow');
359
- }
357
+ // 🔴 FIXED: No duplicate verification needed - it's integrated into deployment strategies
358
+ // deployResult.verification contains the comprehensive verification results
359
+ // Success with integrated verification
360
360
  deploymentResult = {
361
361
  success: true,
362
362
  method: deployResult.strategy,
363
363
  xml_file: xmlResult.filePath,
364
364
  message: `✅ Flow deployed via ${deployResult.strategy} and verified in ServiceNow!`,
365
- flow_sys_id: flowExists.sys_id
365
+ flow_sys_id: deployResult.verification.sys_id,
366
+ flow_url: deployResult.verification.url,
367
+ verification_score: deployResult.verification.completeness_score,
368
+ verification_details: {
369
+ has_flow: true,
370
+ has_snapshot: deployResult.verification.has_snapshot,
371
+ has_trigger: deployResult.verification.has_trigger,
372
+ attempts_needed: deployResult.verification.verification_attempt,
373
+ deployment_verified: deployResult.deployment_verified
374
+ },
375
+ snow_001_fix: 'Deployment includes mandatory verification - no false positives possible'
366
376
  };
367
377
  }
368
378
  catch (xmlError) {
@@ -382,7 +392,7 @@ class ServiceNowFlowComposerMCP {
382
392
  deploymentResult = {
383
393
  success: false,
384
394
  error: errorDetails,
385
- xml_generated: true,
395
+ xml_generated: xmlResult !== null, // 🔴 FIX: Check if XML was actually generated
386
396
  xml_path: xmlResult?.filePath,
387
397
  deployment_failed: true,
388
398
  manual_steps: this.generateManualImportGuide(xmlResult?.filePath || ''),
@@ -1987,31 +1997,137 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
1987
1997
  return mapping[activityType.toLowerCase()] || 'script';
1988
1998
  }
1989
1999
  /**
1990
- * Verify flow exists in ServiceNow after deployment
2000
+ * Comprehensive flow verification with retry logic
1991
2001
  */
1992
2002
  async verifyFlowInServiceNow(flowName) {
1993
- try {
1994
- const ServiceNowClient = (await Promise.resolve().then(() => __importStar(require('../utils/servicenow-client.js')))).ServiceNowClient;
1995
- const client = new ServiceNowClient();
1996
- // Check if flow exists in sys_hub_flow
1997
- const flowCheck = await client.makeRequest({
1998
- method: 'GET',
1999
- url: '/api/now/table/sys_hub_flow',
2000
- params: {
2001
- sysparm_query: `name=${flowName}`,
2002
- sysparm_limit: 1
2003
+ const maxRetries = 5;
2004
+ const baseDelay = 2000; // Start with 2 seconds
2005
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
2006
+ try {
2007
+ // Progressive delay - ServiceNow needs time to process
2008
+ if (attempt > 1) {
2009
+ const delay = baseDelay * attempt; // 2s, 4s, 6s, 8s, 10s
2010
+ this.logger.info(`Waiting ${delay}ms before verification attempt ${attempt}/${maxRetries}`);
2011
+ await this.sleep(delay);
2012
+ }
2013
+ const ServiceNowClient = (await Promise.resolve().then(() => __importStar(require('../utils/servicenow-client.js')))).ServiceNowClient;
2014
+ const client = new ServiceNowClient();
2015
+ // Multi-table verification for comprehensive check
2016
+ const verificationPromises = [
2017
+ // Check main flow table
2018
+ client.makeRequest({
2019
+ method: 'GET',
2020
+ url: '/api/now/table/sys_hub_flow',
2021
+ params: {
2022
+ sysparm_query: `name=${flowName}^ORsys_name=${flowName}`,
2023
+ sysparm_fields: 'sys_id,name,sys_name,active,table,description',
2024
+ sysparm_limit: 5
2025
+ }
2026
+ }),
2027
+ // Check flow snapshots
2028
+ client.makeRequest({
2029
+ method: 'GET',
2030
+ url: '/api/now/table/sys_hub_flow_snapshot',
2031
+ params: {
2032
+ sysparm_query: `flow.name=${flowName}^ORflow.sys_name=${flowName}`,
2033
+ sysparm_fields: 'sys_id,flow,name,active',
2034
+ sysparm_limit: 5
2035
+ }
2036
+ }),
2037
+ // Check trigger instances
2038
+ client.makeRequest({
2039
+ method: 'GET',
2040
+ url: '/api/now/table/sys_hub_trigger_instance',
2041
+ params: {
2042
+ sysparm_query: `flow.name=${flowName}^ORflow.sys_name=${flowName}`,
2043
+ sysparm_fields: 'sys_id,flow,trigger_type',
2044
+ sysparm_limit: 5
2045
+ }
2046
+ })
2047
+ ];
2048
+ const [flowCheck, snapshotCheck, triggerCheck] = await Promise.allSettled(verificationPromises);
2049
+ // Analyze results
2050
+ const flowExists = flowCheck.status === 'fulfilled' &&
2051
+ flowCheck.value.result &&
2052
+ flowCheck.value.result.length > 0;
2053
+ const snapshotExists = snapshotCheck.status === 'fulfilled' &&
2054
+ snapshotCheck.value.result &&
2055
+ snapshotCheck.value.result.length > 0;
2056
+ const triggerExists = triggerCheck.status === 'fulfilled' &&
2057
+ triggerCheck.value.result &&
2058
+ triggerCheck.value.result.length > 0;
2059
+ // Comprehensive verification result
2060
+ if (flowExists) {
2061
+ const flowData = flowCheck.value.result[0];
2062
+ return {
2063
+ verified: true,
2064
+ sys_id: flowData.sys_id,
2065
+ name: flowData.name || flowData.sys_name,
2066
+ active: flowData.active,
2067
+ table: flowData.table,
2068
+ has_snapshot: snapshotExists,
2069
+ has_trigger: triggerExists,
2070
+ verification_attempt: attempt,
2071
+ verification_method: 'multi_table_comprehensive',
2072
+ completeness_score: (flowExists ? 1 : 0) + (snapshotExists ? 1 : 0) + (triggerExists ? 1 : 0),
2073
+ url: `https://${await this.getInstanceUrl()}/flow-designer/flow/${flowData.sys_id}`
2074
+ };
2075
+ }
2076
+ // Log partial results for debugging
2077
+ this.logger.warn(`Verification attempt ${attempt}: Flow=${flowExists}, Snapshot=${snapshotExists}, Trigger=${triggerExists}`);
2078
+ // If this is the last attempt, return detailed failure info
2079
+ if (attempt === maxRetries) {
2080
+ return {
2081
+ verified: false,
2082
+ reason: 'Flow not found after comprehensive verification',
2083
+ attempts: maxRetries,
2084
+ partial_results: {
2085
+ flow_found: flowExists,
2086
+ snapshot_found: snapshotExists,
2087
+ trigger_found: triggerExists
2088
+ },
2089
+ search_query: `name=${flowName}`,
2090
+ recommendation: 'Check ServiceNow Flow Designer manually or verify deployment was successful'
2091
+ };
2092
+ }
2093
+ }
2094
+ catch (error) {
2095
+ this.logger.warn(`Verification attempt ${attempt} failed:`, error);
2096
+ // If this is the last attempt, include error details
2097
+ if (attempt === maxRetries) {
2098
+ return {
2099
+ verified: false,
2100
+ reason: 'Verification failed due to error',
2101
+ error: error instanceof Error ? error.message : String(error),
2102
+ attempts: maxRetries
2103
+ };
2003
2104
  }
2004
- });
2005
- if (flowCheck.result && flowCheck.result.length > 0) {
2006
- return flowCheck.result[0];
2007
2105
  }
2008
- return null;
2009
2106
  }
2010
- catch (error) {
2011
- this.logger.error('Failed to verify flow:', error);
2012
- return null;
2107
+ return {
2108
+ verified: false,
2109
+ reason: 'Max verification attempts exceeded',
2110
+ attempts: maxRetries
2111
+ };
2112
+ }
2113
+ /**
2114
+ * Helper to get instance URL for flow links
2115
+ */
2116
+ async getInstanceUrl() {
2117
+ try {
2118
+ const credentials = await this.oauth.loadCredentials();
2119
+ return credentials?.instance || 'your-instance.service-now.com';
2120
+ }
2121
+ catch {
2122
+ return 'your-instance.service-now.com';
2013
2123
  }
2014
2124
  }
2125
+ /**
2126
+ * Sleep utility
2127
+ */
2128
+ sleep(ms) {
2129
+ return new Promise(resolve => setTimeout(resolve, ms));
2130
+ }
2015
2131
  /**
2016
2132
  * Generate manual import guide for failed deployments
2017
2133
  */
@@ -2039,17 +2155,19 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
2039
2155
  - Check "My Flows" for your new flow`;
2040
2156
  }
2041
2157
  /**
2042
- * Deploy with fallback strategies
2158
+ * Deploy with fallback strategies + MANDATORY VERIFICATION
2159
+ * 🔴 CRITICAL FIX: SNOW-001 Silent Deployment Failures
2160
+ * Each strategy now MUST verify that the flow actually exists before claiming success
2043
2161
  */
2044
2162
  async deployWithFallback(xmlFilePath, flowDefinition) {
2045
2163
  const strategies = [
2046
2164
  {
2047
2165
  name: 'XML Remote Update Set',
2048
- fn: async () => await this.deployXMLToServiceNow(xmlFilePath)
2166
+ fn: async () => await this.deployXMLToServiceNowWithVerification(xmlFilePath, flowDefinition.name)
2049
2167
  },
2050
2168
  {
2051
2169
  name: 'Direct Table API',
2052
- fn: async () => await this.deployViaTableAPI(flowDefinition)
2170
+ fn: async () => await this.deployViaTableAPIWithVerification(flowDefinition)
2053
2171
  }
2054
2172
  ];
2055
2173
  let lastError;
@@ -2057,7 +2175,17 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
2057
2175
  try {
2058
2176
  this.logger.info(`Trying deployment strategy: ${strategy.name}`);
2059
2177
  const result = await strategy.fn();
2060
- return { success: true, strategy: strategy.name, result };
2178
+ // 🔴 CRITICAL: Strategy can only return if it includes verification proof
2179
+ if (!result.verification || !result.verification.verified) {
2180
+ throw new Error(`${strategy.name} completed but verification failed: ${result.verification?.reason || 'Unknown verification failure'}`);
2181
+ }
2182
+ return {
2183
+ success: true,
2184
+ strategy: strategy.name,
2185
+ result,
2186
+ verification: result.verification,
2187
+ deployment_verified: true
2188
+ };
2061
2189
  }
2062
2190
  catch (error) {
2063
2191
  this.logger.warn(`Strategy ${strategy.name} failed:`, error);
@@ -2067,7 +2195,65 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
2067
2195
  throw lastError || new Error('All deployment strategies failed');
2068
2196
  }
2069
2197
  /**
2070
- * Deploy via direct table API
2198
+ * 🔴 CRITICAL FIX: Deploy XML with MANDATORY verification
2199
+ * SNOW-001: Prevents false positive where XML import succeeds but flow doesn't exist
2200
+ */
2201
+ async deployXMLToServiceNowWithVerification(xmlFilePath, flowName) {
2202
+ this.logger.info(`🔴 CRITICAL FIX: XML deployment with mandatory verification for: ${flowName}`);
2203
+ // Step 1: Deploy the XML (existing logic)
2204
+ await this.deployXMLToServiceNow(xmlFilePath);
2205
+ // Step 2: MANDATORY verification - wait for ServiceNow to process
2206
+ this.logger.info('🔍 Starting mandatory post-deployment verification...');
2207
+ const verification = await this.verifyFlowInServiceNow(flowName);
2208
+ if (!verification.verified) {
2209
+ // 🔴 CRITICAL: XML deployment succeeded but flow doesn't exist
2210
+ const errorMsg = `🔴 CRITICAL: XML deployment appeared to succeed but flow verification failed: ${verification.reason}`;
2211
+ this.logger.error('SNOW-001 detected: Silent deployment failure', {
2212
+ xmlFilePath,
2213
+ flowName,
2214
+ verificationResult: verification,
2215
+ issue: 'XML import/commit succeeded but no flow created'
2216
+ });
2217
+ throw new Error(errorMsg);
2218
+ }
2219
+ this.logger.info(`✅ XML deployment verified successfully: ${flowName} found with sys_id ${verification.sys_id}`);
2220
+ return {
2221
+ deployment_method: 'XML Remote Update Set',
2222
+ xml_file: xmlFilePath,
2223
+ verification: verification,
2224
+ success_message: `XML deployment completed and verified: Flow ${flowName} is live in ServiceNow`
2225
+ };
2226
+ }
2227
+ /**
2228
+ * 🔴 CRITICAL FIX: Deploy via Table API with MANDATORY verification
2229
+ * SNOW-001: Prevents false positive where API call succeeds but flow doesn't exist
2230
+ */
2231
+ async deployViaTableAPIWithVerification(flowDefinition) {
2232
+ this.logger.info(`🔴 CRITICAL FIX: Table API deployment with mandatory verification for: ${flowDefinition.name}`);
2233
+ // Step 1: Deploy via Table API (existing logic)
2234
+ await this.deployViaTableAPI(flowDefinition);
2235
+ // Step 2: MANDATORY verification
2236
+ this.logger.info('🔍 Starting mandatory post-deployment verification...');
2237
+ const verification = await this.verifyFlowInServiceNow(flowDefinition.name);
2238
+ if (!verification.verified) {
2239
+ // 🔴 CRITICAL: Table API deployment succeeded but flow doesn't exist
2240
+ const errorMsg = `🔴 CRITICAL: Table API deployment appeared to succeed but flow verification failed: ${verification.reason}`;
2241
+ this.logger.error('SNOW-001 detected: Silent deployment failure', {
2242
+ flowName: flowDefinition.name,
2243
+ verificationResult: verification,
2244
+ issue: 'Table API call succeeded but no flow created'
2245
+ });
2246
+ throw new Error(errorMsg);
2247
+ }
2248
+ this.logger.info(`✅ Table API deployment verified successfully: ${flowDefinition.name} found with sys_id ${verification.sys_id}`);
2249
+ return {
2250
+ deployment_method: 'Direct Table API',
2251
+ verification: verification,
2252
+ success_message: `Table API deployment completed and verified: Flow ${flowDefinition.name} is live in ServiceNow`
2253
+ };
2254
+ }
2255
+ /**
2256
+ * Deploy via direct table API (LEGACY METHOD - used by new verified method)
2071
2257
  */
2072
2258
  async deployViaTableAPI(flowDefinition) {
2073
2259
  const ServiceNowClient = (await Promise.resolve().then(() => __importStar(require('../utils/servicenow-client.js')))).ServiceNowClient;