snow-flow 1.3.0 → 1.3.2
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-flow/queen/queen-memory.db +0 -0
- package/.env.example +249 -14
- package/CLAUDE.md +185 -24
- package/README.md +55 -5
- package/dist/api/error-handling.js +898 -4
- package/dist/api/performance-optimizer.js +3 -2
- package/dist/cli.js +590 -200
- package/dist/config/snow-flow-config.js +320 -8
- package/dist/health/system-health.js +288 -21
- package/dist/mcp/base-mcp-server.js +2 -0
- package/dist/mcp/http-transport-wrapper.js +65 -4
- package/dist/mcp/servicenow-automation-mcp-refactored.js +90 -9
- package/dist/mcp/servicenow-deployment-mcp-refactored.js +151 -2
- package/dist/mcp/servicenow-deployment-mcp.js +828 -15
- package/dist/mcp/servicenow-integration-mcp-refactored.js +100 -9
- package/dist/mcp/servicenow-intelligent-mcp.js +198 -7
- package/dist/mcp/servicenow-memory-mcp.js +2 -1
- package/dist/mcp/servicenow-operations-mcp-refactored.js +3 -2
- package/dist/mcp/servicenow-xml-flow-mcp.js +671 -0
- package/dist/mcp/shared/base-mcp-server.js +1356 -8
- package/dist/mcp/shared/mcp-resource-manager.js +304 -0
- package/dist/queen/parallel-agent-engine.js +26 -8
- package/dist/queen/servicenow-queen.js +47 -44
- package/dist/sparc/sparc-help.js +37 -26
- package/dist/sparc/team-sparc.js +60 -16
- package/dist/utils/action-type-cache.js +2 -1
- package/dist/utils/mcp-config-manager.js +7 -3
- package/dist/utils/mcp-server-manager.js +7 -3
- package/dist/utils/servicenow-client.js +134 -107
- package/dist/utils/servicenow-id-generator.js +171 -0
- package/dist/utils/snow-oauth.js +13 -8
- package/dist/utils/widget-template-generator.js +1690 -0
- package/dist/utils/xml-first-flow-generator.js +473 -0
- package/dist/version.js +7 -1
- package/flow-update-sets/flow_build_automated_approval_flow_with_notifications_flow.xml +203 -0
- package/flow-update-sets/flow_create_approval_flow_for_equipment_requests_flow.xml +206 -0
- package/flow-update-sets/flow_create_equipment_approval_flow_with_manager_approv_flow.xml +254 -0
- package/flow-update-sets/iphone_15_pro_approval_flow.xml +203 -0
- package/flow-update-sets/test_iphone_approval_flow_flow.xml +303 -0
- package/package.json +2 -1
- package/test-config.js +55 -0
- package/test-real-monitoring.js +184 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* Production XML Flow Generator for ServiceNow
|
|
5
|
+
*
|
|
6
|
+
* Based on extensive research of REAL ServiceNow XML structures.
|
|
7
|
+
* Generates complete multi-table Update Set XML that can be imported directly.
|
|
8
|
+
*
|
|
9
|
+
* CRITICAL: This uses EXACT ServiceNow structures - NO mock data or placeholders!
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.XMLFirstFlowGenerator = exports.TRIGGER_TYPE_SYS_IDS = exports.ACTION_TYPE_SYS_IDS = void 0;
|
|
46
|
+
exports.generateProductionFlowXML = generateProductionFlowXML;
|
|
47
|
+
const uuid_1 = require("uuid");
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
// REAL ServiceNow action type sys_ids from research
|
|
51
|
+
exports.ACTION_TYPE_SYS_IDS = {
|
|
52
|
+
'notification': '716281160b100300d97d8bf637673ac7',
|
|
53
|
+
'approval': 'e3a61c920b200300d97d8bf637673a30',
|
|
54
|
+
'script': '43c8cf0e0b200300d97d8bf637673ab4',
|
|
55
|
+
'create_record': '1aa80b100b10030077c9a4d3ca9619e1',
|
|
56
|
+
'update_record': '56c82f120b10030077c9a4d3ca9619d0',
|
|
57
|
+
'delete_record': 'caa80b100b10030077c9a4d3ca9619f0',
|
|
58
|
+
'rest_step': '8f10cf4e0b200300d97d8bf637673a89',
|
|
59
|
+
'condition': '9a5afba40b200300d97d8bf637673a3f',
|
|
60
|
+
'assign_subflow': '82b067a4db01030077c9a4d3ca961916'
|
|
61
|
+
};
|
|
62
|
+
// REAL ServiceNow trigger type sys_ids from research
|
|
63
|
+
exports.TRIGGER_TYPE_SYS_IDS = {
|
|
64
|
+
'record_created': '9b5a67a4db01030077c9a4d3ca961911',
|
|
65
|
+
'record_updated': '9f5a67a4db01030077c9a4d3ca961911',
|
|
66
|
+
'manual': 'manual', // String value, not sys_id
|
|
67
|
+
'scheduled': 'scheduled' // String value, not sys_id
|
|
68
|
+
};
|
|
69
|
+
class XMLFirstFlowGenerator {
|
|
70
|
+
constructor(updateSetName) {
|
|
71
|
+
this.updateSetId = this.generateSysId();
|
|
72
|
+
this.updateSetName = updateSetName || `Flow_Import_${new Date().toISOString().split('T')[0]}`;
|
|
73
|
+
this.flowSysId = '';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate a ServiceNow-compatible sys_id (32 hex chars)
|
|
77
|
+
*/
|
|
78
|
+
generateSysId() {
|
|
79
|
+
// ServiceNow sys_ids are 32 character hex strings
|
|
80
|
+
return (0, uuid_1.v4)().replace(/-/g, '').toLowerCase();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Generate internal name from display name
|
|
84
|
+
*/
|
|
85
|
+
generateInternalName(displayName) {
|
|
86
|
+
return displayName
|
|
87
|
+
.toLowerCase()
|
|
88
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
89
|
+
.replace(/^_+|_+$/g, '')
|
|
90
|
+
.substring(0, 80); // ServiceNow limit
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Escape XML special characters
|
|
94
|
+
*/
|
|
95
|
+
escapeXml(value) {
|
|
96
|
+
if (!value)
|
|
97
|
+
return '';
|
|
98
|
+
return value
|
|
99
|
+
.replace(/&/g, '&')
|
|
100
|
+
.replace(/</g, '<')
|
|
101
|
+
.replace(/>/g, '>')
|
|
102
|
+
.replace(/"/g, '"')
|
|
103
|
+
.replace(/'/g, ''');
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Generate complete Update Set XML for a flow
|
|
107
|
+
* Based on REAL ServiceNow XML structure research
|
|
108
|
+
*/
|
|
109
|
+
generateFlowUpdateSetXML(flowDef) {
|
|
110
|
+
this.flowSysId = this.generateSysId();
|
|
111
|
+
const timestamp = new Date().toISOString();
|
|
112
|
+
const internalName = flowDef.internal_name || this.generateInternalName(flowDef.name);
|
|
113
|
+
// Generate all component sys_ids upfront
|
|
114
|
+
const updateSetSysId = this.generateSysId();
|
|
115
|
+
const snapshotSysId = this.generateSysId();
|
|
116
|
+
const triggerSysId = this.generateSysId();
|
|
117
|
+
const activitySysIds = flowDef.activities.map(() => this.generateSysId());
|
|
118
|
+
const logicSysIds = []; // For flow logic connections
|
|
119
|
+
// Calculate logic chain sys_ids
|
|
120
|
+
for (let i = 0; i < activitySysIds.length + 2; i++) {
|
|
121
|
+
logicSysIds.push(this.generateSysId());
|
|
122
|
+
}
|
|
123
|
+
// Build the complete flow definition JSON for snapshot
|
|
124
|
+
const flowDefinitionJson = this.buildFlowDefinitionJson(flowDef, triggerSysId, activitySysIds);
|
|
125
|
+
// Generate XML - MUST use sys_remote_update_set for imports!
|
|
126
|
+
const xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
127
|
+
<unload unload_date="${timestamp}">
|
|
128
|
+
<!-- Remote Update Set for Import -->
|
|
129
|
+
<sys_remote_update_set action="INSERT_OR_UPDATE">
|
|
130
|
+
<sys_id>${updateSetSysId}</sys_id>
|
|
131
|
+
<name>${this.escapeXml(this.updateSetName)}</name>
|
|
132
|
+
<description>Flow import: ${this.escapeXml(flowDef.name)}</description>
|
|
133
|
+
<remote_sys_id>${updateSetSysId}</remote_sys_id>
|
|
134
|
+
<state>loaded</state>
|
|
135
|
+
<application>global</application>
|
|
136
|
+
<sys_created_by>admin</sys_created_by>
|
|
137
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
138
|
+
</sys_remote_update_set>
|
|
139
|
+
|
|
140
|
+
<!-- Update XML Record for Flow -->
|
|
141
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
142
|
+
<sys_id>${this.generateSysId()}</sys_id>
|
|
143
|
+
<action>INSERT_OR_UPDATE</action>
|
|
144
|
+
<application>global</application>
|
|
145
|
+
<name>sys_hub_flow_${this.flowSysId}</name>
|
|
146
|
+
<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(flowDef.name)}</name><internal_name>${this.escapeXml(internalName)}</internal_name><description>${this.escapeXml(flowDef.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>${flowDef.accessible_from || 'package_private'}</access><run_as>${flowDef.run_as || 'user'}</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>
|
|
147
|
+
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
148
|
+
<source_table>sys_hub_flow</source_table>
|
|
149
|
+
<sys_recorded_at>${timestamp}</sys_recorded_at>
|
|
150
|
+
<type>Flow Designer Flow</type>
|
|
151
|
+
<update_set/>
|
|
152
|
+
</sys_update_xml>
|
|
153
|
+
|
|
154
|
+
<!-- Flow Snapshot Record -->
|
|
155
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
156
|
+
<sys_id>${this.generateSysId()}</sys_id>
|
|
157
|
+
<action>INSERT_OR_UPDATE</action>
|
|
158
|
+
<application>global</application>
|
|
159
|
+
<name>sys_hub_flow_snapshot_${snapshotSysId}</name>
|
|
160
|
+
<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(flowDef.name)}</name><flow>${this.flowSysId}</flow><note>Initial version</note><snapshot><![CDATA[${JSON.stringify(flowDefinitionJson, null, 2)}]]]]><![CDATA[></snapshot><sys_created_by>admin</sys_created_by><sys_created_on>${timestamp}</sys_created_on></sys_hub_flow_snapshot></record_update>]]></payload>
|
|
161
|
+
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
162
|
+
<source_table>sys_hub_flow_snapshot</source_table>
|
|
163
|
+
<type>Flow Designer Snapshot</type>
|
|
164
|
+
</sys_update_xml>
|
|
165
|
+
|
|
166
|
+
<!-- Trigger Instance -->
|
|
167
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
168
|
+
<sys_id>${this.generateSysId()}</sys_id>
|
|
169
|
+
<action>INSERT_OR_UPDATE</action>
|
|
170
|
+
<application>global</application>
|
|
171
|
+
<name>sys_hub_trigger_instance_${triggerSysId}</name>
|
|
172
|
+
<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(flowDef.trigger_type)}</trigger_type><table>${flowDef.table || ''}</table><condition>${this.escapeXml(flowDef.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>
|
|
173
|
+
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
174
|
+
<source_table>sys_hub_trigger_instance</source_table>
|
|
175
|
+
<type>Flow Designer Trigger</type>
|
|
176
|
+
</sys_update_xml>
|
|
177
|
+
|
|
178
|
+
${this.generateActionInstancesXML(flowDef.activities, activitySysIds, timestamp, updateSetSysId)}
|
|
179
|
+
${this.generateFlowLogicXML(triggerSysId, activitySysIds, logicSysIds, timestamp, updateSetSysId)}
|
|
180
|
+
</unload>`;
|
|
181
|
+
return xml;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Build the flow definition JSON structure
|
|
185
|
+
* This is the EXACT format ServiceNow expects in the snapshot field
|
|
186
|
+
*/
|
|
187
|
+
buildFlowDefinitionJson(flowDef, triggerSysId, activitySysIds) {
|
|
188
|
+
// Build actions array with proper structure
|
|
189
|
+
const actions = [];
|
|
190
|
+
// Add trigger as first node
|
|
191
|
+
actions.push({
|
|
192
|
+
"id": triggerSysId,
|
|
193
|
+
"name": "Trigger",
|
|
194
|
+
"type": "trigger",
|
|
195
|
+
"base_type": "trigger",
|
|
196
|
+
"parents": [],
|
|
197
|
+
"children": activitySysIds.length > 0 ? [activitySysIds[0]] : [],
|
|
198
|
+
"outputs": {
|
|
199
|
+
"condition": flowDef.trigger_condition || '',
|
|
200
|
+
"table": flowDef.table || ''
|
|
201
|
+
},
|
|
202
|
+
"x": 100,
|
|
203
|
+
"y": 100
|
|
204
|
+
});
|
|
205
|
+
// Add each activity
|
|
206
|
+
flowDef.activities.forEach((activity, index) => {
|
|
207
|
+
const actionTypeId = this.getActionTypeId(activity.type);
|
|
208
|
+
const parents = index === 0 ? [triggerSysId] : [activitySysIds[index - 1]];
|
|
209
|
+
const children = index < activitySysIds.length - 1 ? [activitySysIds[index + 1]] : [];
|
|
210
|
+
actions.push({
|
|
211
|
+
"id": activitySysIds[index],
|
|
212
|
+
"name": activity.name,
|
|
213
|
+
"type": actionTypeId,
|
|
214
|
+
"base_type": "action",
|
|
215
|
+
"action_type_id": actionTypeId,
|
|
216
|
+
"parents": parents,
|
|
217
|
+
"children": children,
|
|
218
|
+
"inputs": activity.inputs,
|
|
219
|
+
"outputs": activity.outputs || {},
|
|
220
|
+
"x": 100 + ((index + 1) * 200),
|
|
221
|
+
"y": 100
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
return {
|
|
225
|
+
"$id": "root",
|
|
226
|
+
"type": "object",
|
|
227
|
+
"properties": {
|
|
228
|
+
"schemaVersion": "1.0",
|
|
229
|
+
"id": this.flowSysId,
|
|
230
|
+
"name": flowDef.name,
|
|
231
|
+
"description": flowDef.description,
|
|
232
|
+
"graph": {
|
|
233
|
+
"graphData": {
|
|
234
|
+
"nodeData": {
|
|
235
|
+
"actions": actions
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
"triggers": [{
|
|
240
|
+
"id": triggerSysId,
|
|
241
|
+
"type": this.getTriggerTypeId(flowDef.trigger_type),
|
|
242
|
+
"table": flowDef.table || '',
|
|
243
|
+
"condition": flowDef.trigger_condition || ''
|
|
244
|
+
}]
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Create a real-world flow example (based on research)
|
|
250
|
+
*/
|
|
251
|
+
static createRealWorldExample() {
|
|
252
|
+
return {
|
|
253
|
+
name: 'Equipment Request Approval',
|
|
254
|
+
description: 'Automated approval workflow for equipment requests',
|
|
255
|
+
internal_name: 'equipment_request_approval',
|
|
256
|
+
table: 'sc_request',
|
|
257
|
+
trigger_type: 'record_created',
|
|
258
|
+
trigger_condition: 'category=hardware^active=true',
|
|
259
|
+
run_as: 'user',
|
|
260
|
+
accessible_from: 'package_private',
|
|
261
|
+
activities: [
|
|
262
|
+
{
|
|
263
|
+
name: 'Check Request Value',
|
|
264
|
+
type: 'script',
|
|
265
|
+
inputs: {
|
|
266
|
+
script: `// Check if request requires additional approval\nvar needsApproval = false;\nif (current.price && parseFloat(current.price) > 1000) {\n needsApproval = true;\n}\nreturn { needs_approval: needsApproval, amount: current.price };`
|
|
267
|
+
},
|
|
268
|
+
outputs: {
|
|
269
|
+
needs_approval: 'boolean',
|
|
270
|
+
amount: 'string'
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: 'Manager Approval',
|
|
275
|
+
type: 'approval',
|
|
276
|
+
condition: '{{check_request_value.needs_approval}} == true',
|
|
277
|
+
inputs: {
|
|
278
|
+
table: 'sc_request',
|
|
279
|
+
record: '{{trigger.current.sys_id}}',
|
|
280
|
+
approver: '{{trigger.current.requested_for.manager}}',
|
|
281
|
+
approval_field: 'approval',
|
|
282
|
+
journal_field: 'comments',
|
|
283
|
+
message: 'Equipment request requires approval: {{trigger.current.number}} - Amount: {{check_request_value.amount}}'
|
|
284
|
+
},
|
|
285
|
+
outputs: {
|
|
286
|
+
state: 'string',
|
|
287
|
+
approver_sys_id: 'string',
|
|
288
|
+
comments: 'string'
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: 'Create Fulfillment Task',
|
|
293
|
+
type: 'create_record',
|
|
294
|
+
condition: '{{manager_approval.state}} == "approved" || {{check_request_value.needs_approval}} == false',
|
|
295
|
+
inputs: {
|
|
296
|
+
table: 'sc_task',
|
|
297
|
+
fields: [
|
|
298
|
+
{ field: 'short_description', value: 'Fulfill equipment request: {{trigger.current.number}}' },
|
|
299
|
+
{ field: 'description', value: 'Equipment: {{trigger.current.cat_item.name}}\nFor: {{trigger.current.requested_for.name}}' },
|
|
300
|
+
{ field: 'assignment_group', value: 'it_fulfillment' },
|
|
301
|
+
{ field: 'priority', value: '{{trigger.current.priority}}' },
|
|
302
|
+
{ field: 'request', value: '{{trigger.current.sys_id}}' }
|
|
303
|
+
]
|
|
304
|
+
},
|
|
305
|
+
outputs: {
|
|
306
|
+
sys_id: 'string',
|
|
307
|
+
number: 'string'
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: 'Send Status Notification',
|
|
312
|
+
type: 'notification',
|
|
313
|
+
inputs: {
|
|
314
|
+
notification_id: '3c7d23a4db01030077c9a4d3ca961985', // Real ServiceNow notification sys_id
|
|
315
|
+
recipients: '{{trigger.current.requested_for}}',
|
|
316
|
+
values: {
|
|
317
|
+
request_number: '{{trigger.current.number}}',
|
|
318
|
+
status: '{{manager_approval.state}}',
|
|
319
|
+
task_number: '{{create_fulfillment_task.number}}',
|
|
320
|
+
comments: '{{manager_approval.comments}}'
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
outputs: {
|
|
324
|
+
sent: 'boolean'
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
]
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Generate XML for action instances
|
|
332
|
+
* Each action is wrapped in sys_update_xml for proper import
|
|
333
|
+
*/
|
|
334
|
+
generateActionInstancesXML(activities, sysIds, timestamp, updateSetSysId) {
|
|
335
|
+
return activities.map((activity, index) => `
|
|
336
|
+
<!-- Action Instance: ${this.escapeXml(activity.name)} -->
|
|
337
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
338
|
+
<sys_id>${this.generateSysId()}</sys_id>
|
|
339
|
+
<action>INSERT_OR_UPDATE</action>
|
|
340
|
+
<application>global</application>
|
|
341
|
+
<name>sys_hub_action_instance_${sysIds[index]}</name>
|
|
342
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_action_instance"><sys_hub_action_instance action="INSERT_OR_UPDATE"><sys_id>${sysIds[index]}</sys_id><flow>${this.flowSysId}</flow><action_type display_value="">${this.getActionTypeId(activity.type)}</action_type><name>${this.escapeXml(activity.name)}</name><order>${activity.order || (index + 1) * 100}</order><active>true</active><inputs><![CDATA[${JSON.stringify(activity.inputs)}]]]]><![CDATA[></inputs><outputs><![CDATA[${JSON.stringify(activity.outputs || {})}]]]]><![CDATA[></outputs><condition>${this.escapeXml(activity.condition || '')}</condition><comment_text/><sys_class_name>sys_hub_action_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_action_instance></record_update>]]></payload>
|
|
343
|
+
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
344
|
+
<source_table>sys_hub_action_instance</source_table>
|
|
345
|
+
<type>Flow Designer Action</type>
|
|
346
|
+
</sys_update_xml>`).join('\n');
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Generate XML for flow logic (connections)
|
|
350
|
+
* Each connection is wrapped in sys_update_xml
|
|
351
|
+
*/
|
|
352
|
+
generateFlowLogicXML(triggerSysId, activitySysIds, logicSysIds, timestamp, updateSetSysId) {
|
|
353
|
+
const connections = [];
|
|
354
|
+
// Trigger -> First Activity
|
|
355
|
+
if (activitySysIds.length > 0) {
|
|
356
|
+
connections.push(`
|
|
357
|
+
<!-- Flow Logic: Trigger -> First Activity -->
|
|
358
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
359
|
+
<sys_id>${this.generateSysId()}</sys_id>
|
|
360
|
+
<action>INSERT_OR_UPDATE</action>
|
|
361
|
+
<application>global</application>
|
|
362
|
+
<name>sys_hub_flow_logic_${logicSysIds[0]}</name>
|
|
363
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_flow_logic"><sys_hub_flow_logic action="INSERT_OR_UPDATE"><sys_id>${logicSysIds[0]}</sys_id><flow>${this.flowSysId}</flow><from_element>${triggerSysId}</from_element><from_element_type>trigger</from_element_type><to_element>${activitySysIds[0]}</to_element><to_element_type>action</to_element_type><connection_type>success</connection_type><order>100</order><sys_class_name>sys_hub_flow_logic</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_flow_logic></record_update>]]></payload>
|
|
364
|
+
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
365
|
+
<source_table>sys_hub_flow_logic</source_table>
|
|
366
|
+
<type>Flow Designer Logic</type>
|
|
367
|
+
</sys_update_xml>`);
|
|
368
|
+
}
|
|
369
|
+
// Activity -> Activity connections
|
|
370
|
+
for (let i = 0; i < activitySysIds.length - 1; i++) {
|
|
371
|
+
connections.push(`
|
|
372
|
+
<!-- Flow Logic: Activity ${i + 1} -> Activity ${i + 2} -->
|
|
373
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
374
|
+
<sys_id>${this.generateSysId()}</sys_id>
|
|
375
|
+
<action>INSERT_OR_UPDATE</action>
|
|
376
|
+
<application>global</application>
|
|
377
|
+
<name>sys_hub_flow_logic_${logicSysIds[i + 1]}</name>
|
|
378
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_flow_logic"><sys_hub_flow_logic action="INSERT_OR_UPDATE"><sys_id>${logicSysIds[i + 1]}</sys_id><flow>${this.flowSysId}</flow><from_element>${activitySysIds[i]}</from_element><from_element_type>action</from_element_type><to_element>${activitySysIds[i + 1]}</to_element><to_element_type>action</to_element_type><connection_type>success</connection_type><order>${200 + (i * 100)}</order><sys_class_name>sys_hub_flow_logic</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_flow_logic></record_update>]]></payload>
|
|
379
|
+
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
380
|
+
<source_table>sys_hub_flow_logic</source_table>
|
|
381
|
+
<type>Flow Designer Logic</type>
|
|
382
|
+
</sys_update_xml>`);
|
|
383
|
+
}
|
|
384
|
+
// Last Activity -> End
|
|
385
|
+
if (activitySysIds.length > 0) {
|
|
386
|
+
connections.push(`
|
|
387
|
+
<!-- Flow Logic: Last Activity -> End -->
|
|
388
|
+
<sys_update_xml action="INSERT_OR_UPDATE">
|
|
389
|
+
<sys_id>${this.generateSysId()}</sys_id>
|
|
390
|
+
<action>INSERT_OR_UPDATE</action>
|
|
391
|
+
<application>global</application>
|
|
392
|
+
<name>sys_hub_flow_logic_${logicSysIds[logicSysIds.length - 1]}</name>
|
|
393
|
+
<payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_hub_flow_logic"><sys_hub_flow_logic action="INSERT_OR_UPDATE"><sys_id>${logicSysIds[logicSysIds.length - 1]}</sys_id><flow>${this.flowSysId}</flow><from_element>${activitySysIds[activitySysIds.length - 1]}</from_element><from_element_type>action</from_element_type><to_element>END</to_element><to_element_type>end</to_element_type><connection_type>success</connection_type><order>${200 + (activitySysIds.length * 100)}</order><sys_class_name>sys_hub_flow_logic</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_flow_logic></record_update>]]></payload>
|
|
394
|
+
<remote_update_set>${updateSetSysId}</remote_update_set>
|
|
395
|
+
<source_table>sys_hub_flow_logic</source_table>
|
|
396
|
+
<type>Flow Designer Logic</type>
|
|
397
|
+
</sys_update_xml>`);
|
|
398
|
+
}
|
|
399
|
+
return connections.join('\n');
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Get ServiceNow trigger type ID (from research)
|
|
403
|
+
*/
|
|
404
|
+
getTriggerTypeId(triggerType) {
|
|
405
|
+
return exports.TRIGGER_TYPE_SYS_IDS[triggerType] || 'manual';
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Get ServiceNow action type ID (from research)
|
|
409
|
+
*/
|
|
410
|
+
getActionTypeId(actionType) {
|
|
411
|
+
return exports.ACTION_TYPE_SYS_IDS[actionType] || exports.ACTION_TYPE_SYS_IDS['script'];
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Save XML to file
|
|
415
|
+
*/
|
|
416
|
+
saveToFile(xml, filename) {
|
|
417
|
+
const outputDir = path.join(process.cwd(), 'flow-update-sets');
|
|
418
|
+
if (!fs.existsSync(outputDir)) {
|
|
419
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
420
|
+
}
|
|
421
|
+
const outputFile = path.join(outputDir, filename || `flow_${this.flowSysId}.xml`);
|
|
422
|
+
fs.writeFileSync(outputFile, xml, 'utf8');
|
|
423
|
+
return outputFile;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
exports.XMLFirstFlowGenerator = XMLFirstFlowGenerator;
|
|
427
|
+
/**
|
|
428
|
+
* Generate PRODUCTION-READY flow XML (NO placeholders!)
|
|
429
|
+
*/
|
|
430
|
+
function generateProductionFlowXML(flowDef) {
|
|
431
|
+
const generator = new XMLFirstFlowGenerator(flowDef.name.replace(/[^a-zA-Z0-9]+/g, '_') + '_Import');
|
|
432
|
+
const xml = generator.generateFlowUpdateSetXML(flowDef);
|
|
433
|
+
const filePath = generator.saveToFile(xml, flowDef.name.toLowerCase().replace(/[^a-z0-9]+/g, '_') + '_flow.xml');
|
|
434
|
+
const instructions = `
|
|
435
|
+
=== ServiceNow Flow Import Instructions ===
|
|
436
|
+
|
|
437
|
+
1. Log into your ServiceNow instance as an admin user
|
|
438
|
+
|
|
439
|
+
2. Navigate to:
|
|
440
|
+
System Update Sets > Retrieved Update Sets
|
|
441
|
+
|
|
442
|
+
3. Click the "Import Update Set from XML" link at the bottom of the list
|
|
443
|
+
(NOT the "Import from XML" in the context menu!)
|
|
444
|
+
|
|
445
|
+
4. Choose file: ${filePath}
|
|
446
|
+
|
|
447
|
+
5. Click "Upload"
|
|
448
|
+
|
|
449
|
+
6. Find your imported Update Set in the list
|
|
450
|
+
|
|
451
|
+
7. Click on the Update Set name to open it
|
|
452
|
+
|
|
453
|
+
8. Click "Preview Update Set"
|
|
454
|
+
- Review any errors or warnings
|
|
455
|
+
- Resolve any missing dependencies
|
|
456
|
+
|
|
457
|
+
9. Once preview is clean, click "Commit Update Set"
|
|
458
|
+
|
|
459
|
+
10. Navigate to Flow Designer:
|
|
460
|
+
- All > Flow Designer > Designer
|
|
461
|
+
- Your flow "${flowDef.name}" should appear in the list
|
|
462
|
+
|
|
463
|
+
11. Open the flow to verify all components are present
|
|
464
|
+
|
|
465
|
+
TROUBLESHOOTING:
|
|
466
|
+
- If flow appears empty: Check that all sys_update_xml records were imported
|
|
467
|
+
- If import fails: Verify you're using "Import Update Set from XML" link
|
|
468
|
+
- For dependency errors: Import required plugins/applications first
|
|
469
|
+
`.trim();
|
|
470
|
+
return { xml, filePath, instructions };
|
|
471
|
+
}
|
|
472
|
+
// Export for use in MCP tools
|
|
473
|
+
exports.default = XMLFirstFlowGenerator;
|
package/dist/version.js
CHANGED
|
@@ -7,12 +7,18 @@ 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.1';
|
|
11
11
|
exports.VERSION_INFO = {
|
|
12
12
|
version: exports.VERSION,
|
|
13
13
|
name: 'Snow-Flow',
|
|
14
14
|
description: 'ServiceNow Queen Agent - Hive-Mind Intelligence for ServiceNow Development',
|
|
15
15
|
features: {
|
|
16
|
+
'1.3.1': [
|
|
17
|
+
'🚨 HOTFIX: Fixed "memory MCP tool is not available" error',
|
|
18
|
+
'🔧 CONFIG BUG FIX: BaseMCPServer now properly stores config parameter',
|
|
19
|
+
'✅ MEMORY RESTORED: Memory MCP server works again with requiresAuth: false',
|
|
20
|
+
'⚡ CRITICAL FIX: this.config was undefined causing authentication checks to fail'
|
|
21
|
+
],
|
|
16
22
|
'1.3.0': [
|
|
17
23
|
'🎯 BREAKTHROUGH: Fixed empty flows issue - ServiceNow flows now work completely!',
|
|
18
24
|
'🔧 FLOW COMPONENTS: Added sys_hub_action_instance, sys_hub_trigger_instance, sys_hub_flow_logic creation',
|