snow-flow 1.3.20 → 1.3.22

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.
@@ -350,20 +350,42 @@ class ServiceNowFlowComposerMCP {
350
350
  // Generate production-ready XML
351
351
  const xmlResult = generateProductionFlowXML(xmlFlowDef);
352
352
  console.log('✅ XML generated:', xmlResult.filePath);
353
- // Auto-deploy XML to ServiceNow
354
- await this.deployXMLToServiceNow(xmlResult.filePath);
353
+ // Auto-deploy with fallback strategies
354
+ 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
+ }
355
360
  deploymentResult = {
356
361
  success: true,
357
- method: 'xml_first',
362
+ method: deployResult.strategy,
358
363
  xml_file: xmlResult.filePath,
359
- message: '✅ Flow deployed using XML-first approach!'
364
+ message: `✅ Flow deployed via ${deployResult.strategy} and verified in ServiceNow!`,
365
+ flow_sys_id: flowExists.sys_id
360
366
  };
361
367
  }
362
368
  catch (xmlError) {
363
- console.warn('⚠️ XML deployment failed, providing manual instructions:', xmlError);
369
+ console.error(' XML deployment failed:', xmlError);
370
+ // Extract detailed error information
371
+ let errorDetails = 'Unknown error';
372
+ if (xmlError instanceof Error) {
373
+ errorDetails = xmlError.message;
374
+ // Check for specific error types
375
+ if (xmlError.message.includes('400')) {
376
+ errorDetails = 'ServiceNow rejected the request. Check permissions and Update Set.';
377
+ }
378
+ else if (xmlError.message.includes('401') || xmlError.message.includes('403')) {
379
+ errorDetails = 'Authentication failed. Run: snow-flow auth login';
380
+ }
381
+ }
364
382
  deploymentResult = {
365
383
  success: false,
366
- error: xmlError instanceof Error ? xmlError.message : String(xmlError),
384
+ error: errorDetails,
385
+ xml_generated: true,
386
+ xml_path: xmlResult?.filePath,
387
+ deployment_failed: true,
388
+ manual_steps: this.generateManualImportGuide(xmlResult?.filePath || ''),
367
389
  fallback_instructions: 'Use snow-flow deploy-xml command for manual deployment'
368
390
  };
369
391
  }
@@ -374,9 +396,22 @@ class ServiceNowFlowComposerMCP {
374
396
  content: [
375
397
  {
376
398
  type: 'text',
377
- text: `🎯 FLOW CREATED WITH XML-FIRST APPROACH!
399
+ text: deploymentResult?.success ?
400
+ `✅ FLOW SUCCESSFULLY CREATED AND DEPLOYED!
401
+
402
+ 🚀 **VERIFIED DEPLOYMENT** - Flow is now live in ServiceNow!` :
403
+ deploymentResult?.deployment_failed ?
404
+ `⚠️ FLOW XML GENERATED BUT DEPLOYMENT FAILED
405
+
406
+ ❌ **Deployment Error**: ${deploymentResult.error}
378
407
 
379
- ${args.deploy_immediately !== false ? `🚀 **FULLY AUTOMATED DEPLOYMENT** - XML generated & deployed to ServiceNow!` : `📋 **PLANNING MODE** - Flow structure generated`}
408
+ 📁 **XML File**: ${deploymentResult.xml_path}
409
+
410
+ 📋 **Manual Import Steps**:
411
+ ${deploymentResult.manual_steps || '1. Navigate to System Update Sets > Retrieved Update Sets\n2. Import Update Set from XML\n3. Preview and Commit'}` :
412
+ `🎯 FLOW CREATED WITH XML-FIRST APPROACH!
413
+
414
+ ${args.deploy_immediately !== false ? `🚀 **DEPLOYMENT STATUS** - Processing...` : `📋 **PLANNING MODE** - Flow structure generated`}
380
415
 
381
416
  🧠 **Intelligent Analysis:**
382
417
  - **Flow Name**: ${parsedIntent.flowName}
@@ -394,11 +429,11 @@ ${args.deploy_immediately !== false ? `🚀 **FULLY AUTOMATED DEPLOYMENT** - XML
394
429
 
395
430
  🚀 **XML-First Deployment:**
396
431
  ${deploymentResult ? (deploymentResult.success ?
397
- `✅ Successfully deployed using XML-first approach!
432
+ `✅ Successfully deployed using XML-first approach!
398
433
  - **Method**: Production-ready Update Set XML
399
434
  - **XML File**: ${deploymentResult.xml_file}
400
435
  - **Status**: Imported → Previewed → Committed ✅` :
401
- `❌ Auto-deployment failed: ${deploymentResult.error}
436
+ `❌ Auto-deployment failed: ${deploymentResult.error}
402
437
  - **Fallback**: ${deploymentResult.fallback_instructions}`) : '⏳ Ready for deployment'}
403
438
 
404
439
  🔗 **ServiceNow Access:**
@@ -1951,6 +1986,109 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
1951
1986
  };
1952
1987
  return mapping[activityType.toLowerCase()] || 'script';
1953
1988
  }
1989
+ /**
1990
+ * Verify flow exists in ServiceNow after deployment
1991
+ */
1992
+ 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
+ }
2004
+ });
2005
+ if (flowCheck.result && flowCheck.result.length > 0) {
2006
+ return flowCheck.result[0];
2007
+ }
2008
+ return null;
2009
+ }
2010
+ catch (error) {
2011
+ this.logger.error('Failed to verify flow:', error);
2012
+ return null;
2013
+ }
2014
+ }
2015
+ /**
2016
+ * Generate manual import guide for failed deployments
2017
+ */
2018
+ generateManualImportGuide(xmlFilePath) {
2019
+ return `
2020
+ 1. **Navigate to ServiceNow**:
2021
+ - System Update Sets > Retrieved Update Sets
2022
+
2023
+ 2. **Import XML**:
2024
+ - Click "Import Update Set from XML"
2025
+ - Select file: ${xmlFilePath}
2026
+ - Click "Upload"
2027
+
2028
+ 3. **Preview**:
2029
+ - Find your imported update set
2030
+ - Click "Preview Update Set"
2031
+ - Review any conflicts or issues
2032
+
2033
+ 4. **Commit**:
2034
+ - If preview is clean, click "Commit Update Set"
2035
+ - Your flow will be available in Flow Designer
2036
+
2037
+ 5. **Verify**:
2038
+ - Navigate to Flow Designer
2039
+ - Check "My Flows" for your new flow`;
2040
+ }
2041
+ /**
2042
+ * Deploy with fallback strategies
2043
+ */
2044
+ async deployWithFallback(xmlFilePath, flowDefinition) {
2045
+ const strategies = [
2046
+ {
2047
+ name: 'XML Remote Update Set',
2048
+ fn: async () => await this.deployXMLToServiceNow(xmlFilePath)
2049
+ },
2050
+ {
2051
+ name: 'Direct Table API',
2052
+ fn: async () => await this.deployViaTableAPI(flowDefinition)
2053
+ }
2054
+ ];
2055
+ let lastError;
2056
+ for (const strategy of strategies) {
2057
+ try {
2058
+ this.logger.info(`Trying deployment strategy: ${strategy.name}`);
2059
+ const result = await strategy.fn();
2060
+ return { success: true, strategy: strategy.name, result };
2061
+ }
2062
+ catch (error) {
2063
+ this.logger.warn(`Strategy ${strategy.name} failed:`, error);
2064
+ lastError = error;
2065
+ }
2066
+ }
2067
+ throw lastError || new Error('All deployment strategies failed');
2068
+ }
2069
+ /**
2070
+ * Deploy via direct table API
2071
+ */
2072
+ async deployViaTableAPI(flowDefinition) {
2073
+ const ServiceNowClient = (await Promise.resolve().then(() => __importStar(require('../utils/servicenow-client.js')))).ServiceNowClient;
2074
+ const client = new ServiceNowClient();
2075
+ // Try to create flow directly in sys_hub_flow
2076
+ const flowResponse = await client.makeRequest({
2077
+ method: 'POST',
2078
+ url: '/api/now/table/sys_hub_flow',
2079
+ data: {
2080
+ name: flowDefinition.name,
2081
+ description: flowDefinition.description,
2082
+ active: true,
2083
+ // Additional flow properties
2084
+ table: flowDefinition.table
2085
+ }
2086
+ });
2087
+ if (!flowResponse.result || !flowResponse.result.sys_id) {
2088
+ throw new Error('Failed to create flow via Table API');
2089
+ }
2090
+ this.logger.info(`Flow created via Table API: ${flowResponse.result.sys_id}`);
2091
+ }
1954
2092
  /**
1955
2093
  * Deploy XML file to ServiceNow automatically
1956
2094
  */
@@ -1963,32 +2101,86 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
1963
2101
  // Initialize ServiceNow client
1964
2102
  const ServiceNowClient = (await Promise.resolve().then(() => __importStar(require('../utils/servicenow-client.js')))).ServiceNowClient;
1965
2103
  const client = new ServiceNowClient();
2104
+ // Check for active Update Set first
2105
+ let currentUpdateSet;
2106
+ try {
2107
+ const updateSetResponse = await client.makeRequest({
2108
+ method: 'GET',
2109
+ url: '/api/now/table/sys_update_set',
2110
+ params: {
2111
+ sysparm_query: 'state=in progress^nameNOT LIKEDefault',
2112
+ sysparm_limit: 1
2113
+ }
2114
+ });
2115
+ if (!updateSetResponse.result || updateSetResponse.result.length === 0) {
2116
+ this.logger.warn('No active Update Set found. Flow will be imported but not tracked properly.');
2117
+ // Continue anyway - let ServiceNow handle it
2118
+ }
2119
+ else {
2120
+ currentUpdateSet = updateSetResponse.result[0];
2121
+ this.logger.info(`Active Update Set found: ${currentUpdateSet.name}`);
2122
+ }
2123
+ }
2124
+ catch (error) {
2125
+ this.logger.warn('Could not check for active Update Set:', error);
2126
+ // Continue anyway
2127
+ }
1966
2128
  // Read the XML file
1967
2129
  const fs = require('fs').promises;
1968
2130
  const xmlContent = await fs.readFile(xmlFilePath, 'utf-8');
1969
2131
  // Import XML as remote update set
1970
- const importResponse = await client.makeRequest({
1971
- method: 'POST',
1972
- url: '/api/now/table/sys_remote_update_set',
1973
- headers: {
1974
- 'Content-Type': 'application/xml',
1975
- 'Accept': 'application/json'
1976
- },
1977
- data: xmlContent
1978
- });
2132
+ let importResponse;
2133
+ try {
2134
+ importResponse = await client.makeRequest({
2135
+ method: 'POST',
2136
+ url: '/api/now/table/sys_remote_update_set',
2137
+ headers: {
2138
+ 'Content-Type': 'application/xml',
2139
+ 'Accept': 'application/json'
2140
+ },
2141
+ data: xmlContent
2142
+ });
2143
+ }
2144
+ catch (error) {
2145
+ // Enhanced error handling with detailed messages
2146
+ if (error.response?.status === 400) {
2147
+ const errorDetail = error.response.data?.error?.detail ||
2148
+ error.response.data?.error?.message ||
2149
+ 'Unknown error';
2150
+ this.logger.error('ServiceNow 400 Error:', {
2151
+ status: error.response.status,
2152
+ statusText: error.response.statusText,
2153
+ data: error.response.data,
2154
+ headers: error.response.headers
2155
+ });
2156
+ throw new Error(`ServiceNow rejected the XML: ${errorDetail}. Check if you have an active Update Set.`);
2157
+ }
2158
+ else if (error.response?.status === 401 || error.response?.status === 403) {
2159
+ throw new Error('Authentication failed. Your session may have expired. Run: snow-flow auth login');
2160
+ }
2161
+ else {
2162
+ throw new Error(`Failed to import XML: ${error.message}`);
2163
+ }
2164
+ }
1979
2165
  if (!importResponse.result || !importResponse.result.sys_id) {
1980
2166
  throw new Error('Failed to import XML update set');
1981
2167
  }
1982
2168
  const remoteUpdateSetId = importResponse.result.sys_id;
1983
2169
  this.logger.info(`✅ XML imported successfully (sys_id: ${remoteUpdateSetId})`);
1984
2170
  // Load the update set
1985
- await client.makeRequest({
1986
- method: 'PUT',
1987
- url: `/api/now/table/sys_remote_update_set/${remoteUpdateSetId}`,
1988
- data: {
1989
- state: 'loaded'
1990
- }
1991
- });
2171
+ try {
2172
+ await client.makeRequest({
2173
+ method: 'PUT',
2174
+ url: `/api/now/table/sys_remote_update_set/${remoteUpdateSetId}`,
2175
+ data: {
2176
+ state: 'loaded'
2177
+ }
2178
+ });
2179
+ }
2180
+ catch (error) {
2181
+ this.logger.error('Failed to load update set:', error);
2182
+ throw new Error(`Failed to load update set: ${error.message}. You may need to load it manually.`);
2183
+ }
1992
2184
  // Find the loaded update set
1993
2185
  const loadedResponse = await client.makeRequest({
1994
2186
  method: 'GET',
@@ -2004,10 +2196,16 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
2004
2196
  const updateSetId = loadedResponse.result[0].sys_id;
2005
2197
  const updateSetName = loadedResponse.result[0].name;
2006
2198
  // Preview the update set
2007
- await client.makeRequest({
2008
- method: 'POST',
2009
- url: `/api/now/table/sys_update_set/${updateSetId}/preview`
2010
- });
2199
+ try {
2200
+ await client.makeRequest({
2201
+ method: 'POST',
2202
+ url: `/api/now/table/sys_update_set/${updateSetId}/preview`
2203
+ });
2204
+ }
2205
+ catch (error) {
2206
+ this.logger.error('Failed to preview update set:', error);
2207
+ // Continue anyway, as preview might fail but commit could still work
2208
+ }
2011
2209
  // Check for preview problems
2012
2210
  const previewProblems = await client.makeRequest({
2013
2211
  method: 'GET',
@@ -2022,11 +2220,17 @@ ${categoryFilteredResults.length === 0 ? `🔍 **No templates found matching you
2022
2220
  throw new Error(`Preview found problems:\n${problemsList}\n\nPlease review and resolve in ServiceNow UI`);
2023
2221
  }
2024
2222
  // Commit the update set
2025
- await client.makeRequest({
2026
- method: 'POST',
2027
- url: `/api/now/table/sys_update_set/${updateSetId}/commit`
2028
- });
2029
- this.logger.info(`✅ Update set committed successfully: ${updateSetName}`);
2223
+ try {
2224
+ await client.makeRequest({
2225
+ method: 'POST',
2226
+ url: `/api/now/table/sys_update_set/${updateSetId}/commit`
2227
+ });
2228
+ this.logger.info(`✅ Update set committed successfully: ${updateSetName}`);
2229
+ }
2230
+ catch (error) {
2231
+ this.logger.error('Failed to commit update set:', error);
2232
+ throw new Error(`Failed to commit update set. Manual intervention required in ServiceNow UI.`);
2233
+ }
2030
2234
  }
2031
2235
  }
2032
2236
  // Start the server
package/dist/version.js CHANGED
@@ -7,7 +7,7 @@ exports.VERSION_INFO = exports.VERSION = void 0;
7
7
  exports.getVersionString = getVersionString;
8
8
  exports.getLatestFeatures = getLatestFeatures;
9
9
  exports.isLatestVersion = isLatestVersion;
10
- exports.VERSION = '1.3.16';
10
+ exports.VERSION = '1.3.22';
11
11
  exports.VERSION_INFO = {
12
12
  version: exports.VERSION,
13
13
  name: 'Snow-Flow',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "1.3.20",
3
+ "version": "1.3.22",
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",