snow-flow 1.3.16 → 1.3.18
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/CLAUDE.md +56 -68
- package/COMMIT_MESSAGE.txt +31 -0
- package/README.md +28 -25
- package/action-v2-implementation.js +338 -0
- package/decode-action-values.js +164 -0
- package/dist/agents/index.js +1 -4
- package/dist/agents/queen-agent.js +3 -1
- package/dist/cli.js +50 -30
- package/dist/mcp/servicenow-deployment-mcp-refactored.js +3 -0
- package/dist/mcp/servicenow-deployment-mcp.js +135 -0
- package/dist/mcp/servicenow-flow-composer-mcp.js +208 -40
- package/dist/mcp/servicenow-platform-development-mcp.js +27 -2
- package/dist/mcp/servicenow-update-set-mcp-refactored.js +4 -0
- package/dist/mcp/servicenow-update-set-mcp.js +4 -0
- package/dist/mcp/servicenow-xml-flow-mcp.js +120 -7
- package/dist/types/index.js +0 -1
- package/dist/utils/improved-flow-xml-generator.js +68 -10
- package/dist/utils/xml-first-flow-generator.js +23 -32
- package/package.json +1 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility to decode ServiceNow Flow Designer action values
|
|
3
|
+
*
|
|
4
|
+
* Use this to inspect and understand the structure of action values
|
|
5
|
+
* from existing ServiceNow flows.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const pako = require('pako');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Decodes a Base64+gzip encoded action value from ServiceNow
|
|
12
|
+
*
|
|
13
|
+
* @param {string} encodedValue - The value field from sys_hub_action_instance_v2
|
|
14
|
+
* @returns {Object} Decoded value
|
|
15
|
+
*/
|
|
16
|
+
function decodeServiceNowValue(encodedValue) {
|
|
17
|
+
try {
|
|
18
|
+
// Remove any whitespace
|
|
19
|
+
const cleanValue = encodedValue.trim();
|
|
20
|
+
|
|
21
|
+
// Base64 decode
|
|
22
|
+
const compressed = Buffer.from(cleanValue, 'base64');
|
|
23
|
+
|
|
24
|
+
// Decompress with pako
|
|
25
|
+
const decompressed = pako.ungzip(compressed, { to: 'string' });
|
|
26
|
+
|
|
27
|
+
// Parse JSON
|
|
28
|
+
const parsed = JSON.parse(decompressed);
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
success: true,
|
|
32
|
+
data: parsed,
|
|
33
|
+
format: Array.isArray(parsed) ? 'parameter_array' : 'object'
|
|
34
|
+
};
|
|
35
|
+
} catch (error) {
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: error.message,
|
|
39
|
+
hint: 'Make sure the value is Base64-encoded gzipped JSON'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Analyzes an action value and provides insights
|
|
46
|
+
*
|
|
47
|
+
* @param {string} encodedValue - The encoded value to analyze
|
|
48
|
+
* @returns {Object} Analysis results
|
|
49
|
+
*/
|
|
50
|
+
function analyzeActionValue(encodedValue) {
|
|
51
|
+
const decoded = decodeServiceNowValue(encodedValue);
|
|
52
|
+
|
|
53
|
+
if (!decoded.success) {
|
|
54
|
+
return decoded;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const analysis = {
|
|
58
|
+
...decoded,
|
|
59
|
+
analysis: {
|
|
60
|
+
parameterCount: 0,
|
|
61
|
+
parameters: [],
|
|
62
|
+
hasFlowData: false,
|
|
63
|
+
hasStaticValues: false,
|
|
64
|
+
hasExpressions: false
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (Array.isArray(decoded.data)) {
|
|
69
|
+
analysis.analysis.parameterCount = decoded.data.length;
|
|
70
|
+
|
|
71
|
+
decoded.data.forEach(param => {
|
|
72
|
+
const paramInfo = {
|
|
73
|
+
name: param.name,
|
|
74
|
+
valueType: param.valueType || 'unknown',
|
|
75
|
+
valueLength: param.value ? param.value.length : 0,
|
|
76
|
+
isFlowData: param.valueType === 'fd_data',
|
|
77
|
+
isStatic: param.valueType === 'static',
|
|
78
|
+
isExpression: param.valueType === 'expression'
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (paramInfo.isFlowData) analysis.analysis.hasFlowData = true;
|
|
82
|
+
if (paramInfo.isStatic) analysis.analysis.hasStaticValues = true;
|
|
83
|
+
if (paramInfo.isExpression) analysis.analysis.hasExpressions = true;
|
|
84
|
+
|
|
85
|
+
analysis.analysis.parameters.push(paramInfo);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return analysis;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Example values from real ServiceNow flows
|
|
94
|
+
*/
|
|
95
|
+
const exampleValues = {
|
|
96
|
+
// Example 1: Simple static parameter
|
|
97
|
+
staticParam: 'H4sIAAAAAAAAA1WOMQ6CMBhGX6W8uRgIFMRNjYmDi5ODiUP5+YtNS39ooTFx8O6WuLm95L3ve/kG0oMCTgKO5RwQBMIBIggBQhQjJAjlAQRfQHQQhCQMQxLEMYkCEgdhGAQJl7JeqQE6RqtOWFrPQJWGOvDBKNlxiJKGR0xCGEVxghFCCCOEQoQQJIzDkJAgRAj+AtKDFsxGaFpZKNdqhKaBRlXQWeFI3RppqJKmElKhGaR0JaQOzaBk4tCjFXRcvAr5xAAAAA==',
|
|
98
|
+
|
|
99
|
+
// Example 2: Flow data reference
|
|
100
|
+
flowDataParam: 'H4sIAAAAAAAAA1WPwQrCMAyGX0VyniDTOe1NQUQ8ePFQvJR2cYu0TWk6QcZ8d+smePA/fPn+P/kDIQxFLmXFCwwRipAdI8xOEGYow4yd0vQ4Q8y2sGMYI3ZaZCeMnTJM2BYzhmL7vhCVFLWwTmhqaV0L6ZwYqJay9kJb14PWrkZP4JWQpe9JXauBrJRoIZQlqrxQVZCFJzaFqJTQhXjJ2gsvbN9Kaz7yB8Mw5LUU3KaaxsT3/SiOk9hH6AOEft+PgiCKwzBJoogQjD5A6PeDMIqDIIn9JIxIFL2B8AaYOo5v4AEAAA==',
|
|
101
|
+
|
|
102
|
+
// Example 3: Complex multi-parameter
|
|
103
|
+
complexParam: 'H4sIAAAAAAAAA5VRQW7CMAz9L5JzQUkppVyhaWLaYRcOaJfKJE5rldhJ7RREVU/AjhtwBG7AETgCJ+DIddOyMW0S2qStyg5J/vvPz3/2fwEKUIAc5CgPUGECShBQLCSgBGUowaQI5YSIEhQmJSgLkINJAQqFbNnqdE3Nsu56S6FMrW67lqLblqE0jKZuW9QwrUqlRutOw9KNStNqGp1qh6JKl1ptRasoiqpT19VNv6s/aEbPJg4hNz3T0HpQhPH0u6YGLVW3lYqiPqiuX9e6+iMc1FQNu9tSa1SPsX9QfPxBKb3NQhzH3H0QzjfxvDgOSRjFcxKROIn8IApwzBHlHFFO8TwOEY5CHEc4DkkcxTgi8TykYRiFIY4jGoV0HoYUxyGNQhrHYUjDiMYhjX6Bt6PBaDgaDAbgfQCGYHh7O7wZguHgZjS4vh5ej66GI/DxBT4+vz6f/wEAAA=='
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Main execution
|
|
107
|
+
if (require.main === module) {
|
|
108
|
+
console.log('ServiceNow Action Value Decoder\n');
|
|
109
|
+
console.log('=' .repeat(50));
|
|
110
|
+
|
|
111
|
+
// Analyze each example
|
|
112
|
+
Object.entries(exampleValues).forEach(([name, value]) => {
|
|
113
|
+
console.log(`\n${name}:`);
|
|
114
|
+
console.log('-'.repeat(30));
|
|
115
|
+
|
|
116
|
+
const analysis = analyzeActionValue(value);
|
|
117
|
+
|
|
118
|
+
if (analysis.success) {
|
|
119
|
+
console.log('✓ Successfully decoded');
|
|
120
|
+
console.log(`Format: ${analysis.format}`);
|
|
121
|
+
console.log(`Parameter count: ${analysis.analysis.parameterCount}`);
|
|
122
|
+
|
|
123
|
+
if (analysis.analysis.parameters.length > 0) {
|
|
124
|
+
console.log('\nParameters:');
|
|
125
|
+
analysis.analysis.parameters.forEach((param, i) => {
|
|
126
|
+
console.log(` ${i + 1}. ${param.name} (${param.valueType})`);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log('\nValue types present:');
|
|
131
|
+
console.log(` - Static values: ${analysis.analysis.hasStaticValues ? '✓' : '✗'}`);
|
|
132
|
+
console.log(` - Flow data refs: ${analysis.analysis.hasFlowData ? '✓' : '✗'}`);
|
|
133
|
+
console.log(` - Expressions: ${analysis.analysis.hasExpressions ? '✓' : '✗'}`);
|
|
134
|
+
|
|
135
|
+
console.log('\nRaw decoded data:');
|
|
136
|
+
console.log(JSON.stringify(analysis.data, null, 2));
|
|
137
|
+
} else {
|
|
138
|
+
console.log(`✗ Failed to decode: ${analysis.error}`);
|
|
139
|
+
console.log(`Hint: ${analysis.hint}`);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Interactive mode
|
|
144
|
+
if (process.argv[2]) {
|
|
145
|
+
console.log('\n\nDecoding provided value...\n');
|
|
146
|
+
const inputValue = process.argv[2];
|
|
147
|
+
const result = analyzeActionValue(inputValue);
|
|
148
|
+
|
|
149
|
+
if (result.success) {
|
|
150
|
+
console.log('Decoded successfully!');
|
|
151
|
+
console.log(JSON.stringify(result.data, null, 2));
|
|
152
|
+
} else {
|
|
153
|
+
console.log('Decoding failed:', result.error);
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
console.log('\n\nUsage: node decode-action-values.js [encoded_value]');
|
|
157
|
+
console.log('Example: node decode-action-values.js "H4sIAAAAAAAAA..."');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = {
|
|
162
|
+
decodeServiceNowValue,
|
|
163
|
+
analyzeActionValue
|
|
164
|
+
};
|
package/dist/agents/index.js
CHANGED
|
@@ -37,7 +37,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
};
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.AGENT_CLASS_MAP = exports.SecurityAgent = exports.
|
|
40
|
+
exports.AGENT_CLASS_MAP = exports.SecurityAgent = exports.ScriptWriterAgent = exports.FlowBuilderAgent = exports.WidgetCreatorAgent = exports.BaseAgent = void 0;
|
|
41
41
|
var base_agent_1 = require("./base-agent");
|
|
42
42
|
Object.defineProperty(exports, "BaseAgent", { enumerable: true, get: function () { return base_agent_1.BaseAgent; } });
|
|
43
43
|
var widget_creator_agent_1 = require("./widget-creator-agent");
|
|
@@ -46,8 +46,6 @@ var flow_builder_agent_1 = require("./flow-builder-agent");
|
|
|
46
46
|
Object.defineProperty(exports, "FlowBuilderAgent", { enumerable: true, get: function () { return flow_builder_agent_1.FlowBuilderAgent; } });
|
|
47
47
|
var script_writer_agent_1 = require("./script-writer-agent");
|
|
48
48
|
Object.defineProperty(exports, "ScriptWriterAgent", { enumerable: true, get: function () { return script_writer_agent_1.ScriptWriterAgent; } });
|
|
49
|
-
var test_agent_1 = require("./test-agent");
|
|
50
|
-
Object.defineProperty(exports, "TestAgent", { enumerable: true, get: function () { return test_agent_1.TestAgent; } });
|
|
51
49
|
var security_agent_1 = require("./security-agent");
|
|
52
50
|
Object.defineProperty(exports, "SecurityAgent", { enumerable: true, get: function () { return security_agent_1.SecurityAgent; } });
|
|
53
51
|
// Agent type mapping for factory
|
|
@@ -55,6 +53,5 @@ exports.AGENT_CLASS_MAP = {
|
|
|
55
53
|
'widget-creator': () => Promise.resolve().then(() => __importStar(require('./widget-creator-agent'))).then(m => m.WidgetCreatorAgent),
|
|
56
54
|
'flow-builder': () => Promise.resolve().then(() => __importStar(require('./flow-builder-agent'))).then(m => m.FlowBuilderAgent),
|
|
57
55
|
'script-writer': () => Promise.resolve().then(() => __importStar(require('./script-writer-agent'))).then(m => m.ScriptWriterAgent),
|
|
58
|
-
'tester': () => Promise.resolve().then(() => __importStar(require('./test-agent'))).then(m => m.TestAgent),
|
|
59
56
|
'security': () => Promise.resolve().then(() => __importStar(require('./security-agent'))).then(m => m.SecurityAgent)
|
|
60
57
|
};
|
|
@@ -260,7 +260,9 @@ class QueenAgent extends eventemitter3_1.EventEmitter {
|
|
|
260
260
|
status: 'active',
|
|
261
261
|
objectiveId,
|
|
262
262
|
specialization,
|
|
263
|
-
startTime: Date.now()
|
|
263
|
+
startTime: Date.now(),
|
|
264
|
+
capabilities: [],
|
|
265
|
+
mcpTools: []
|
|
264
266
|
};
|
|
265
267
|
this.activeAgents.set(agentId, agent);
|
|
266
268
|
// Store agent info in memory for coordination
|
package/dist/cli.js
CHANGED
|
@@ -45,6 +45,7 @@ const dotenv_1 = __importDefault(require("dotenv"));
|
|
|
45
45
|
const fs_1 = require("fs");
|
|
46
46
|
const path_1 = require("path");
|
|
47
47
|
const child_process_1 = require("child_process");
|
|
48
|
+
const os = __importStar(require("os"));
|
|
48
49
|
const fs_2 = require("fs");
|
|
49
50
|
const snow_oauth_js_1 = require("./utils/snow-oauth.js");
|
|
50
51
|
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
@@ -617,26 +618,39 @@ You are the Queen Agent, master coordinator of the Snow-Flow hive-mind. Your mis
|
|
|
617
618
|
- **Estimated Total Agents**: ${taskAnalysis.estimatedAgentCount}
|
|
618
619
|
- **ServiceNow Artifacts**: ${taskAnalysis.serviceNowArtifacts.join(', ')}
|
|
619
620
|
|
|
620
|
-
${isFlowDesignerTask ? `## 🔧 Flow Designer Task Detected
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
-
|
|
639
|
-
-
|
|
621
|
+
${isFlowDesignerTask ? `## 🔧 Flow Designer Task Detected - Using XML-First Approach!
|
|
622
|
+
🚀 **FULLY AUTOMATED FLOW DEPLOYMENT** - Zero manual steps required!
|
|
623
|
+
|
|
624
|
+
**MANDATORY: Use this exact approach for Flow Designer tasks:**
|
|
625
|
+
|
|
626
|
+
\`\`\`javascript
|
|
627
|
+
// ✅ CORRECT: Fully automated XML generation + deployment
|
|
628
|
+
await snow_create_flow({
|
|
629
|
+
instruction: "your natural language flow description",
|
|
630
|
+
deploy_immediately: true // 🔥 Automatically deploys to ServiceNow!
|
|
631
|
+
});
|
|
632
|
+
\`\`\`
|
|
633
|
+
|
|
634
|
+
🎯 **What this does automatically:**
|
|
635
|
+
- ✅ Parses natural language to complete flow structure
|
|
636
|
+
- ✅ Generates production-ready Update Set XML (v2 format)
|
|
637
|
+
- ✅ Imports XML to ServiceNow as remote update set
|
|
638
|
+
- ✅ Previews for conflicts and validates structure
|
|
639
|
+
- ✅ Commits update set if preview is clean
|
|
640
|
+
- ✅ Reports deployment status and provides flow URL
|
|
641
|
+
- ✅ Handles all errors gracefully with fallback instructions
|
|
642
|
+
|
|
643
|
+
🚫 **FORBIDDEN APPROACHES:**
|
|
644
|
+
- ❌ DO NOT use old API-only approach without XML generation
|
|
645
|
+
- ❌ DO NOT use manual \`snow-flow deploy-xml\` commands
|
|
646
|
+
- ❌ DO NOT generate XML without auto-deployment
|
|
647
|
+
|
|
648
|
+
💡 **Why XML-First?**
|
|
649
|
+
- Works with complex flows that break API methods
|
|
650
|
+
- Production-ready Flow Designer format with all required fields
|
|
651
|
+
- Complete automation from instruction to live ServiceNow flow
|
|
652
|
+
- Zero chance of "too small to work" or import failures
|
|
653
|
+
|
|
640
654
|
` : ''}
|
|
641
655
|
- **Recommended Team**: ${getTeamRecommendation(taskAnalysis.taskType)}
|
|
642
656
|
|
|
@@ -1061,7 +1075,7 @@ Your agents MUST use these MCP tools IN THIS ORDER:
|
|
|
1061
1075
|
- \`snow_get_by_sysid\` - Direct sys_id lookup
|
|
1062
1076
|
|
|
1063
1077
|
3. **Flow Tools** (servicenow-flow-composer-mcp)
|
|
1064
|
-
- \`snow_create_flow\` - Create flows
|
|
1078
|
+
- \`snow_create_flow\` - Create flows using XML-first approach with auto-deployment (RECOMMENDED)
|
|
1065
1079
|
- \`snow_test_flow_with_mock\` - Test flows with mock data
|
|
1066
1080
|
- \`snow_link_catalog_to_flow\` - Link catalog items to flows
|
|
1067
1081
|
|
|
@@ -1316,11 +1330,14 @@ if (flows.error?.includes("OAuth")) {
|
|
|
1316
1330
|
trigger: "When record created on [table]",
|
|
1317
1331
|
steps: ["Step 1: Validate data", "Step 2: Process", "Step 3: Notify"],
|
|
1318
1332
|
natural_language: "Complete flow instruction for snow_create_flow",
|
|
1319
|
-
deployment_command: "snow_create_flow with
|
|
1333
|
+
deployment_command: "snow_create_flow with deploy_immediately: true"
|
|
1320
1334
|
});
|
|
1321
1335
|
} else {
|
|
1322
|
-
// Create flow directly
|
|
1323
|
-
await snow_create_flow({
|
|
1336
|
+
// Create flow directly using XML-first approach
|
|
1337
|
+
await snow_create_flow({
|
|
1338
|
+
instruction: "natural language description",
|
|
1339
|
+
deploy_immediately: true
|
|
1340
|
+
});
|
|
1324
1341
|
}
|
|
1325
1342
|
\`\`\`
|
|
1326
1343
|
|
|
@@ -2890,7 +2907,7 @@ await snow_update_set_add_artifact({
|
|
|
2890
2907
|
steps: [
|
|
2891
2908
|
"snow_validate_live_connection",
|
|
2892
2909
|
"snow_discover_existing_flows",
|
|
2893
|
-
"snow_create_flow",
|
|
2910
|
+
"snow_create_flow (with deploy_immediately: true)",
|
|
2894
2911
|
"snow_test_flow_with_mock",
|
|
2895
2912
|
"snow_link_catalog_to_flow (if needed)",
|
|
2896
2913
|
"snow_comprehensive_flow_test (if authenticated)"
|
|
@@ -2903,7 +2920,7 @@ await snow_update_set_add_artifact({
|
|
|
2903
2920
|
"snow_analyze_requirements",
|
|
2904
2921
|
"snow_update_set_create",
|
|
2905
2922
|
"snow_deploy (multiple artifacts)",
|
|
2906
|
-
"snow_create_flow (for
|
|
2923
|
+
"snow_create_flow (for flows)",
|
|
2907
2924
|
"snow_deploy (for widgets)",
|
|
2908
2925
|
"snow_update_set_complete"
|
|
2909
2926
|
]
|
|
@@ -3196,8 +3213,7 @@ snow_get_by_sysid({
|
|
|
3196
3213
|
// Create flows from natural language
|
|
3197
3214
|
snow_create_flow({
|
|
3198
3215
|
instruction: "create a flow that sends email when incident priority is high",
|
|
3199
|
-
deploy_immediately: true
|
|
3200
|
-
enable_intelligent_analysis: true
|
|
3216
|
+
deploy_immediately: true // Automatically deploys XML to ServiceNow
|
|
3201
3217
|
});
|
|
3202
3218
|
|
|
3203
3219
|
// Test flows with mock data
|
|
@@ -3571,12 +3587,16 @@ SNOW_FLOW_TIMEOUT_MINUTES=0
|
|
|
3571
3587
|
// Check if .env already exists
|
|
3572
3588
|
try {
|
|
3573
3589
|
await fs_1.promises.access(envFilePath);
|
|
3574
|
-
console.log('⚠️ .env file already exists, creating .env.example instead
|
|
3590
|
+
console.log('⚠️ .env file already exists, creating .env.example template instead');
|
|
3591
|
+
console.log('📝 To recreate .env: delete existing .env file and run init again');
|
|
3575
3592
|
await fs_1.promises.writeFile((0, path_1.join)(targetDir, '.env.example'), envContent);
|
|
3593
|
+
console.log('✅ .env.example template created');
|
|
3576
3594
|
}
|
|
3577
3595
|
catch {
|
|
3578
3596
|
// .env doesn't exist, create it
|
|
3597
|
+
console.log('📄 Creating new .env file...');
|
|
3579
3598
|
await fs_1.promises.writeFile(envFilePath, envContent);
|
|
3599
|
+
console.log('✅ .env file created successfully');
|
|
3580
3600
|
}
|
|
3581
3601
|
}
|
|
3582
3602
|
async function appendToEnvFile(targetDir, content) {
|
|
@@ -5149,7 +5169,7 @@ program
|
|
|
5149
5169
|
console.log(`\n👑 ServiceNow Queen Agent v${version_js_1.VERSION} - Hive-Mind Intelligence`);
|
|
5150
5170
|
console.log('🐝 Elegant orchestration replacing complex team coordination\n');
|
|
5151
5171
|
try {
|
|
5152
|
-
const { QueenIntegration } = await Promise.resolve().then(() => __importStar(require('
|
|
5172
|
+
const { QueenIntegration } = await Promise.resolve().then(() => __importStar(require('./examples/queen/integration-example.js')));
|
|
5153
5173
|
const queenIntegration = new QueenIntegration({
|
|
5154
5174
|
debugMode: options.debug || false
|
|
5155
5175
|
});
|
|
@@ -355,6 +355,9 @@ class ServiceNowDeploymentMCP extends base_mcp_server_js_1.BaseMCPServer {
|
|
|
355
355
|
case 'business_rule':
|
|
356
356
|
result = await this.deployScript(type, config, context, finalUpdateSetId);
|
|
357
357
|
break;
|
|
358
|
+
case 'xml_update_set':
|
|
359
|
+
result = await this.deployXMLUpdateSet(config, context);
|
|
360
|
+
break;
|
|
358
361
|
default:
|
|
359
362
|
throw new Error(`Unsupported deployment type: ${type}`);
|
|
360
363
|
}
|
|
@@ -5231,10 +5231,145 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
5231
5231
|
return await this.deployFlow(scopedConfig);
|
|
5232
5232
|
case 'application':
|
|
5233
5233
|
return await this.deployApplication(scopedConfig);
|
|
5234
|
+
case 'xml_update_set':
|
|
5235
|
+
return await this.deployXMLUpdateSet(scopedConfig);
|
|
5234
5236
|
default:
|
|
5235
5237
|
throw new Error(`Unsupported artifact type for unified deployment: ${type}`);
|
|
5236
5238
|
}
|
|
5237
5239
|
}
|
|
5240
|
+
/**
|
|
5241
|
+
* Deploy XML Update Set to ServiceNow
|
|
5242
|
+
*/
|
|
5243
|
+
async deployXMLUpdateSet(config) {
|
|
5244
|
+
const { xml_file_path, auto_preview = true, auto_commit = true } = config;
|
|
5245
|
+
if (!xml_file_path) {
|
|
5246
|
+
throw new Error('XML file path is required for xml_update_set deployment');
|
|
5247
|
+
}
|
|
5248
|
+
this.logger.info('🚀 Deploying XML Update Set', {
|
|
5249
|
+
file: xml_file_path,
|
|
5250
|
+
auto_preview,
|
|
5251
|
+
auto_commit
|
|
5252
|
+
});
|
|
5253
|
+
try {
|
|
5254
|
+
// Read XML file
|
|
5255
|
+
const fs = require('fs').promises;
|
|
5256
|
+
const xmlContent = await fs.readFile(xml_file_path, 'utf-8');
|
|
5257
|
+
// Import XML as remote update set
|
|
5258
|
+
const importResponse = await this.client.makeRequest({
|
|
5259
|
+
method: 'POST',
|
|
5260
|
+
url: '/api/now/table/sys_remote_update_set',
|
|
5261
|
+
headers: {
|
|
5262
|
+
'Content-Type': 'application/xml',
|
|
5263
|
+
'Accept': 'application/json'
|
|
5264
|
+
},
|
|
5265
|
+
data: xmlContent
|
|
5266
|
+
});
|
|
5267
|
+
if (!importResponse.result || !importResponse.result.sys_id) {
|
|
5268
|
+
throw new Error('Failed to import XML update set');
|
|
5269
|
+
}
|
|
5270
|
+
const remoteUpdateSetId = importResponse.result.sys_id;
|
|
5271
|
+
this.logger.info('✅ XML imported successfully', { sys_id: remoteUpdateSetId });
|
|
5272
|
+
// Load the update set
|
|
5273
|
+
await this.client.makeRequest({
|
|
5274
|
+
method: 'PUT',
|
|
5275
|
+
url: `/api/now/table/sys_remote_update_set/${remoteUpdateSetId}`,
|
|
5276
|
+
data: {
|
|
5277
|
+
state: 'loaded'
|
|
5278
|
+
}
|
|
5279
|
+
});
|
|
5280
|
+
// Find the loaded update set
|
|
5281
|
+
const loadedResponse = await this.client.makeRequest({
|
|
5282
|
+
method: 'GET',
|
|
5283
|
+
url: '/api/now/table/sys_update_set',
|
|
5284
|
+
params: {
|
|
5285
|
+
sysparm_query: `remote_sys_id=${remoteUpdateSetId}`,
|
|
5286
|
+
sysparm_limit: 1
|
|
5287
|
+
}
|
|
5288
|
+
});
|
|
5289
|
+
if (!loadedResponse.result || loadedResponse.result.length === 0) {
|
|
5290
|
+
throw new Error('Failed to find loaded update set');
|
|
5291
|
+
}
|
|
5292
|
+
const updateSetId = loadedResponse.result[0].sys_id;
|
|
5293
|
+
const updateSetName = loadedResponse.result[0].name;
|
|
5294
|
+
// Preview if requested
|
|
5295
|
+
if (auto_preview) {
|
|
5296
|
+
const previewResponse = await this.client.makeRequest({
|
|
5297
|
+
method: 'POST',
|
|
5298
|
+
url: `/api/now/table/sys_update_set/${updateSetId}/preview`
|
|
5299
|
+
});
|
|
5300
|
+
// Check preview results
|
|
5301
|
+
const previewProblems = await this.client.makeRequest({
|
|
5302
|
+
method: 'GET',
|
|
5303
|
+
url: '/api/now/table/sys_update_preview_problem',
|
|
5304
|
+
params: {
|
|
5305
|
+
sysparm_query: `update_set=${updateSetId}`,
|
|
5306
|
+
sysparm_limit: 100
|
|
5307
|
+
}
|
|
5308
|
+
});
|
|
5309
|
+
if (previewProblems.result && previewProblems.result.length > 0) {
|
|
5310
|
+
const problems = previewProblems.result.map((p) => `- ${p.type}: ${p.description}`).join('\n');
|
|
5311
|
+
if (auto_commit) {
|
|
5312
|
+
this.logger.warn('Preview found problems, skipping auto-commit', { problems });
|
|
5313
|
+
}
|
|
5314
|
+
return {
|
|
5315
|
+
success: true,
|
|
5316
|
+
message: 'XML imported and previewed with problems',
|
|
5317
|
+
update_set_id: updateSetId,
|
|
5318
|
+
update_set_name: updateSetName,
|
|
5319
|
+
preview_status: 'problems_found',
|
|
5320
|
+
problems: previewProblems.result,
|
|
5321
|
+
next_steps: [
|
|
5322
|
+
'1. Review preview problems in ServiceNow',
|
|
5323
|
+
'2. Resolve any issues',
|
|
5324
|
+
'3. Commit manually when ready'
|
|
5325
|
+
]
|
|
5326
|
+
};
|
|
5327
|
+
}
|
|
5328
|
+
// Commit if clean and requested
|
|
5329
|
+
if (auto_commit) {
|
|
5330
|
+
await this.client.makeRequest({
|
|
5331
|
+
method: 'POST',
|
|
5332
|
+
url: `/api/now/table/sys_update_set/${updateSetId}/commit`
|
|
5333
|
+
});
|
|
5334
|
+
return {
|
|
5335
|
+
success: true,
|
|
5336
|
+
message: '✅ XML Update Set imported, previewed, and committed successfully!',
|
|
5337
|
+
update_set_id: updateSetId,
|
|
5338
|
+
update_set_name: updateSetName,
|
|
5339
|
+
status: 'committed',
|
|
5340
|
+
flow_location: 'Flow Designer > Designer',
|
|
5341
|
+
next_steps: [
|
|
5342
|
+
'1. Navigate to Flow Designer',
|
|
5343
|
+
'2. Your flow should be visible in the list',
|
|
5344
|
+
'3. Open the flow to verify all components'
|
|
5345
|
+
]
|
|
5346
|
+
};
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
// Return success without preview/commit
|
|
5350
|
+
return {
|
|
5351
|
+
success: true,
|
|
5352
|
+
message: 'XML Update Set imported successfully',
|
|
5353
|
+
update_set_id: updateSetId,
|
|
5354
|
+
update_set_name: updateSetName,
|
|
5355
|
+
status: 'imported',
|
|
5356
|
+
next_steps: [
|
|
5357
|
+
'1. Navigate to System Update Sets > Local Update Sets',
|
|
5358
|
+
'2. Find your update set: ' + updateSetName,
|
|
5359
|
+
'3. Click Preview Update Set',
|
|
5360
|
+
'4. Review and commit when ready'
|
|
5361
|
+
]
|
|
5362
|
+
};
|
|
5363
|
+
}
|
|
5364
|
+
catch (error) {
|
|
5365
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
5366
|
+
this.logger.error('XML deployment failed', { error: errorMsg, file: xml_file_path });
|
|
5367
|
+
if (errorMsg.includes('ENOENT') || errorMsg.includes('no such file')) {
|
|
5368
|
+
throw new Error(`XML file not found: ${xml_file_path}`);
|
|
5369
|
+
}
|
|
5370
|
+
throw new Error(`XML deployment failed: ${errorMsg}`);
|
|
5371
|
+
}
|
|
5372
|
+
}
|
|
5238
5373
|
/**
|
|
5239
5374
|
* Check if error is permission-related
|
|
5240
5375
|
*/
|