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.
- package/dist/mcp/servicenow-flow-composer-mcp.js +239 -35
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
354
|
-
await this.
|
|
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:
|
|
362
|
+
method: deployResult.strategy,
|
|
358
363
|
xml_file: xmlResult.filePath,
|
|
359
|
-
message:
|
|
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.
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
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
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
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
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
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
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
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.
|
|
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.
|
|
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",
|