snow-flow-test 10.0.1-test.110 → 10.0.1-test.112

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "10.0.1-test.110",
3
+ "version": "10.0.1-test.112",
4
4
  "name": "snow-flow-test",
5
5
  "description": "Snow-Flow Test - ServiceNow Multi-Agent Development Framework",
6
6
  "license": "Elastic-2.0",
@@ -45,17 +45,90 @@ var _bootstrapPromise: Promise<{ namespace: string; apiSysId: string }> | null =
45
45
  * This runs server-side on ServiceNow and triggers all Business Rules,
46
46
  * unlike direct Table API inserts which skip sys_hub_flow_version creation.
47
47
  */
48
+ /**
49
+ * Discover script — GET /discover endpoint.
50
+ * Probes which sn_fd APIs, methods and fields are available on this instance.
51
+ */
52
+ var FLOW_FACTORY_DISCOVER_SCRIPT = [
53
+ '(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {',
54
+ ' var r = {',
55
+ ' build_tag: gs.getProperty("glide.buildtag") || "unknown",',
56
+ ' build_name: gs.getProperty("glide.buildname") || "unknown",',
57
+ ' apis: {}, methods: {}, fields: {}',
58
+ ' };',
59
+ ' try { r.apis.sn_fd = typeof sn_fd !== "undefined" ? "available" : "unavailable"; } catch(e) { r.apis.sn_fd = "unavailable"; }',
60
+ ' var apiNames = ["FlowDesigner","FlowAPI","FlowPublisher","FlowCompiler"];',
61
+ ' for (var i = 0; i < apiNames.length; i++) {',
62
+ ' try {',
63
+ ' if (typeof sn_fd !== "undefined") { r.apis[apiNames[i]] = typeof sn_fd[apiNames[i]]; }',
64
+ ' else { r.apis[apiNames[i]] = "no_sn_fd"; }',
65
+ ' } catch(e) { r.apis[apiNames[i]] = "error:" + e; }',
66
+ ' }',
67
+ ' var globalNames = ["GlideFlowDesigner","FlowDesignerInternalAPI","FlowDesignerAPI"];',
68
+ ' for (var g = 0; g < globalNames.length; g++) {',
69
+ ' try {',
70
+ ' var gv = this[globalNames[g]];',
71
+ ' r.apis[globalNames[g]] = gv ? typeof gv : "undefined";',
72
+ ' } catch(e) { r.apis[globalNames[g]] = "error:" + e; }',
73
+ ' }',
74
+ ' try {',
75
+ ' if (typeof sn_fd !== "undefined" && sn_fd.FlowDesigner) {',
76
+ ' var fd = sn_fd.FlowDesigner;',
77
+ ' var mns = ["createFlow","publishFlow","activateFlow","compileFlow","createDraftFlow","getFlow"];',
78
+ ' for (var m = 0; m < mns.length; m++) { r.methods[mns[m]] = typeof fd[mns[m]]; }',
79
+ ' }',
80
+ ' } catch(e) { r.methods._error = e + ""; }',
81
+ ' try {',
82
+ ' var gr = new GlideRecord("sys_hub_flow_version");',
83
+ ' var fns = ["compiled_definition","compile_state","is_current","published_flow","flow_definition"];',
84
+ ' for (var f = 0; f < fns.length; f++) { r.fields[fns[f]] = gr.isValidField(fns[f]); }',
85
+ ' } catch(e) { r.fields._error = e + ""; }',
86
+ ' try {',
87
+ ' var br = new GlideRecord("sys_script");',
88
+ ' br.addQuery("collection","sys_hub_flow"); br.addQuery("active",true); br.query();',
89
+ ' r.flow_br_count = br.getRowCount();',
90
+ ' var brv = new GlideRecord("sys_script");',
91
+ ' brv.addQuery("collection","sys_hub_flow_version"); brv.addQuery("active",true); brv.query();',
92
+ ' r.version_br_count = brv.getRowCount();',
93
+ ' } catch(e) { r.br_error = e + ""; }',
94
+ ' response.setStatus(200);',
95
+ ' response.setBody(r);',
96
+ '})(request, response);'
97
+ ].join('\n');
98
+
99
+ /**
100
+ * Create script — POST /create endpoint.
101
+ * Three-tier approach:
102
+ * Tier 1: sn_fd.FlowDesigner API (handles everything internally)
103
+ * Tier 2: GlideRecord INSERT as draft → UPDATE to published (triggers compilation BRs)
104
+ * Tier 3: Raw GlideRecord INSERT (last resort, may not register with engine)
105
+ */
48
106
  var FLOW_FACTORY_SCRIPT = [
49
107
  '(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {',
50
- ' var body;',
51
- ' try { body = JSON.parse(request.body.dataString); }',
52
- ' catch(e) { body = request.body.data; }',
108
+ // ── Body parsing (3-method cascade) ──
109
+ ' var body = null;',
110
+ ' var parseLog = [];',
111
+ ' try {',
112
+ ' var ds = request.body.dataString;',
113
+ ' if (ds) { body = JSON.parse(ds + ""); parseLog.push("dataString:ok"); }',
114
+ ' } catch(e1) { parseLog.push("dataString:" + e1); }',
115
+ ' if (!body) {',
116
+ ' try {',
117
+ ' var d = request.body.data;',
118
+ ' if (d && typeof d === "object") { body = d; parseLog.push("data:ok"); }',
119
+ ' else if (d) { body = JSON.parse(d + ""); parseLog.push("data-parse:ok"); }',
120
+ ' } catch(e2) { parseLog.push("data:" + e2); }',
121
+ ' }',
122
+ ' if (!body) {',
123
+ ' try { body = JSON.parse(request.body + ""); parseLog.push("body-direct:ok"); }',
124
+ ' catch(e3) { parseLog.push("body-direct:" + e3); }',
125
+ ' }',
53
126
  ' if (!body || typeof body !== "object") {',
54
127
  ' response.setStatus(400);',
55
- ' response.setBody({ success: false, error: "Invalid request body: " + typeof body });',
128
+ ' response.setBody({ success: false, error: "No parseable body", parseLog: parseLog, bodyType: typeof request.body });',
56
129
  ' return;',
57
130
  ' }',
58
- ' var result = { success: false, steps: {} };',
131
+ ' var result = { success: false, steps: {}, tier_used: null, parseLog: parseLog };',
59
132
  '',
60
133
  ' try {',
61
134
  ' var flowName = body.name || "Unnamed Flow";',
@@ -70,170 +143,201 @@ var FLOW_FACTORY_SCRIPT = [
70
143
  ' var activities = body.activities || [];',
71
144
  ' var inputs = body.inputs || [];',
72
145
  ' var outputs = body.outputs || [];',
146
+ ' var flowDef = body.flow_definition;',
147
+ ' var flowDefStr = flowDef ? (typeof flowDef === "string" ? flowDef : JSON.stringify(flowDef)) : "";',
148
+ ' var intName = flowName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");',
73
149
  '',
74
- ' // Step 1: Create sys_hub_flow via GlideRecord (triggers all BRs)',
75
- ' var flow = new GlideRecord("sys_hub_flow");',
76
- ' flow.initialize();',
77
- ' flow.setValue("name", flowName);',
78
- ' flow.setValue("description", flowDesc);',
79
- ' flow.setValue("internal_name", flowName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, ""));',
80
- ' flow.setValue("category", flowCategory);',
81
- ' flow.setValue("run_as", runAs);',
82
- ' flow.setValue("active", shouldActivate);',
83
- ' flow.setValue("status", shouldActivate ? "published" : "draft");',
84
- ' flow.setValue("validated", true);',
85
- ' flow.setValue("type", isSubflow ? "subflow" : "flow");',
86
- '',
87
- ' if (body.flow_definition) {',
88
- ' flow.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
89
- ' flow.setValue("latest_snapshot", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
90
- ' }',
91
- '',
92
- ' var flowSysId = flow.insert();',
93
- ' if (!flowSysId) {',
94
- ' result.error = "Failed to insert sys_hub_flow record";',
95
- ' response.setStatus(500);',
96
- ' response.setBody(result);',
97
- ' return;',
98
- ' }',
99
- ' result.steps.flow = { success: true, sys_id: flowSysId + "" };',
150
+ ' var flowSysId = null;',
151
+ ' var verSysId = null;',
100
152
  '',
101
- ' // Step 2: Create sys_hub_flow_version (this is what Table API misses!)',
153
+ // ── TIER 1: sn_fd.FlowDesigner API ──
102
154
  ' try {',
103
- ' var ver = new GlideRecord("sys_hub_flow_version");',
104
- ' ver.initialize();',
105
- ' ver.setValue("flow", flowSysId);',
106
- ' ver.setValue("name", "1.0");',
107
- ' ver.setValue("version", "1.0");',
108
- ' ver.setValue("state", shouldActivate ? "published" : "draft");',
109
- ' ver.setValue("active", true);',
110
- ' ver.setValue("compile_state", "compiled");',
111
- ' ver.setValue("is_current", true);',
112
- ' if (shouldActivate) ver.setValue("published_flow", flowSysId);',
113
- ' if (body.flow_definition) {',
114
- ' ver.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
115
- ' }',
116
- ' var verSysId = ver.insert();',
117
- ' if (verSysId) {',
118
- ' result.steps.version = { success: true, sys_id: verSysId + "" };',
119
- ' // Update flow to point to latest version',
120
- ' var flowUpd = new GlideRecord("sys_hub_flow");',
121
- ' if (flowUpd.get(flowSysId)) {',
122
- ' flowUpd.setValue("latest_version", verSysId);',
123
- ' flowUpd.update();',
155
+ ' if (typeof sn_fd !== "undefined" && sn_fd.FlowDesigner && typeof sn_fd.FlowDesigner.createFlow === "function") {',
156
+ ' var fdResult = sn_fd.FlowDesigner.createFlow({ name: flowName, description: flowDesc, type: isSubflow ? "subflow" : "flow", category: flowCategory, run_as: runAs });',
157
+ ' if (fdResult) {',
158
+ ' flowSysId = (typeof fdResult === "object" ? fdResult.sys_id || fdResult.getValue("sys_id") : fdResult) + "";',
159
+ ' result.tier_used = "sn_fd_api";',
160
+ ' result.steps.tier1 = { success: true, api: "sn_fd.FlowDesigner.createFlow" };',
161
+ ' if (shouldActivate && typeof sn_fd.FlowDesigner.publishFlow === "function") {',
162
+ ' try { sn_fd.FlowDesigner.publishFlow(flowSysId); result.steps.tier1_publish = { success: true }; }',
163
+ ' catch(pe) { result.steps.tier1_publish = { success: false, error: pe + "" }; }',
164
+ ' }',
124
165
  ' }',
125
- ' } else {',
126
- ' result.steps.version = { success: false, error: "Insert returned empty sys_id" };',
127
166
  ' }',
128
- ' } catch (verErr) {',
129
- ' result.steps.version = { success: false, error: verErr.getMessage ? verErr.getMessage() : verErr + "" };',
130
- ' }',
167
+ ' } catch(t1e) { result.steps.tier1 = { success: false, error: t1e.getMessage ? t1e.getMessage() : t1e + "" }; }',
131
168
  '',
132
- ' // Step 3: Create trigger instance (non-manual, non-subflow only)',
133
- ' if (!isSubflow && triggerType !== "manual") {',
169
+ // ── TIER 2: GlideRecord INSERT as draft UPDATE to published ──
170
+ ' if (!flowSysId) {',
134
171
  ' try {',
135
- ' var triggerMap = {',
136
- ' "record_created": "sn_fd.trigger.record_created",',
137
- ' "record_updated": "sn_fd.trigger.record_updated",',
138
- ' "scheduled": "sn_fd.trigger.scheduled"',
139
- ' };',
140
- ' var triggerIntName = triggerMap[triggerType] || "";',
141
- ' if (triggerIntName) {',
142
- ' var trigDef = new GlideRecord("sys_hub_action_type_definition");',
143
- ' trigDef.addQuery("internal_name", triggerIntName);',
144
- ' trigDef.query();',
145
- ' if (trigDef.next()) {',
146
- ' var trigInst = new GlideRecord("sys_hub_trigger_instance");',
147
- ' trigInst.initialize();',
148
- ' trigInst.setValue("flow", flowSysId);',
149
- ' trigInst.setValue("action_type", trigDef.getUniqueValue());',
150
- ' trigInst.setValue("name", triggerType);',
151
- ' trigInst.setValue("order", 0);',
152
- ' trigInst.setValue("active", true);',
153
- ' if (triggerTable) trigInst.setValue("table", triggerTable);',
154
- ' if (triggerCondition) trigInst.setValue("condition", triggerCondition);',
155
- ' var trigSysId = trigInst.insert();',
156
- ' result.steps.trigger = { success: !!trigSysId, sys_id: trigSysId + "" };',
157
- ' } else {',
158
- ' result.steps.trigger = { success: false, error: "Trigger type definition not found: " + triggerIntName };',
172
+ ' var flow = new GlideRecord("sys_hub_flow");',
173
+ ' flow.initialize();',
174
+ ' flow.setValue("name", flowName);',
175
+ ' flow.setValue("description", flowDesc);',
176
+ ' flow.setValue("internal_name", intName);',
177
+ ' flow.setValue("category", flowCategory);',
178
+ ' flow.setValue("run_as", runAs);',
179
+ ' flow.setValue("active", false);',
180
+ ' flow.setValue("status", "draft");',
181
+ ' flow.setValue("validated", true);',
182
+ ' flow.setValue("type", isSubflow ? "subflow" : "flow");',
183
+ ' if (flowDefStr) { flow.setValue("flow_definition", flowDefStr); flow.setValue("latest_snapshot", flowDefStr); }',
184
+ ' flowSysId = flow.insert();',
185
+ ' result.steps.flow_insert = { success: !!flowSysId, sys_id: flowSysId + "", as_draft: true };',
186
+ '',
187
+ ' if (flowSysId) {',
188
+ ' var ver = new GlideRecord("sys_hub_flow_version");',
189
+ ' ver.initialize();',
190
+ ' ver.setValue("flow", flowSysId);',
191
+ ' ver.setValue("name", "1.0");',
192
+ ' ver.setValue("version", "1.0");',
193
+ ' ver.setValue("state", "draft");',
194
+ ' ver.setValue("active", true);',
195
+ ' ver.setValue("compile_state", "draft");',
196
+ ' ver.setValue("is_current", true);',
197
+ ' if (flowDefStr) ver.setValue("flow_definition", flowDefStr);',
198
+ ' verSysId = ver.insert();',
199
+ ' result.steps.version_insert = { success: !!verSysId, sys_id: verSysId + "", as_draft: true };',
200
+ '',
201
+ ' if (verSysId) {',
202
+ ' var linkUpd = new GlideRecord("sys_hub_flow");',
203
+ ' if (linkUpd.get(flowSysId)) { linkUpd.setValue("latest_version", verSysId); linkUpd.update(); }',
159
204
  ' }',
205
+ '',
206
+ ' if (shouldActivate) {',
207
+ ' var flowPub = new GlideRecord("sys_hub_flow");',
208
+ ' if (flowPub.get(flowSysId)) {',
209
+ ' flowPub.setValue("status", "published");',
210
+ ' flowPub.setValue("active", true);',
211
+ ' flowPub.update();',
212
+ ' result.steps.flow_publish_update = { success: true };',
213
+ ' }',
214
+ ' if (verSysId) {',
215
+ ' var verPub = new GlideRecord("sys_hub_flow_version");',
216
+ ' if (verPub.get(verSysId)) {',
217
+ ' verPub.setValue("state", "published");',
218
+ ' verPub.setValue("compile_state", "compiled");',
219
+ ' verPub.setValue("published_flow", flowSysId);',
220
+ ' verPub.update();',
221
+ ' result.steps.version_publish_update = { success: true };',
222
+ ' }',
223
+ ' }',
224
+ ' }',
225
+ ' result.tier_used = "gliderecord_draft_then_publish";',
226
+ ' result.success = true;',
160
227
  ' }',
161
- ' } catch (trigErr) {',
162
- ' result.steps.trigger = { success: false, error: trigErr.getMessage ? trigErr.getMessage() : trigErr + "" };',
163
- ' }',
228
+ ' } catch(t2e) { result.steps.tier2 = { success: false, error: t2e.getMessage ? t2e.getMessage() : t2e + "" }; }',
164
229
  ' }',
165
230
  '',
166
- ' // Step 4: Create action instances',
167
- ' var actionsCreated = 0;',
168
- ' for (var ai = 0; ai < activities.length; ai++) {',
231
+ // ── TIER 3: Raw GlideRecord INSERT (last resort) ──
232
+ ' if (!flowSysId) {',
169
233
  ' try {',
170
- ' var act = activities[ai];',
171
- ' var actTypeName = act.type || "script";',
172
- ' var actDef = new GlideRecord("sys_hub_action_type_definition");',
173
- ' actDef.addQuery("internal_name", "CONTAINS", actTypeName);',
174
- ' actDef.addOrCondition("name", "CONTAINS", actTypeName);',
175
- ' actDef.query();',
176
- ' var actInst = new GlideRecord("sys_hub_action_instance");',
177
- ' actInst.initialize();',
178
- ' actInst.setValue("flow", flowSysId);',
179
- ' actInst.setValue("name", act.name || ("Action " + (ai + 1)));',
180
- ' actInst.setValue("order", (ai + 1) * 100);',
181
- ' actInst.setValue("active", true);',
182
- ' if (actDef.next()) {',
183
- ' actInst.setValue("action_type", actDef.getUniqueValue());',
234
+ ' var rawFlow = new GlideRecord("sys_hub_flow");',
235
+ ' rawFlow.initialize();',
236
+ ' rawFlow.setValue("name", flowName);',
237
+ ' rawFlow.setValue("description", flowDesc);',
238
+ ' rawFlow.setValue("internal_name", intName);',
239
+ ' rawFlow.setValue("category", flowCategory);',
240
+ ' rawFlow.setValue("run_as", runAs);',
241
+ ' rawFlow.setValue("active", shouldActivate);',
242
+ ' rawFlow.setValue("status", shouldActivate ? "published" : "draft");',
243
+ ' rawFlow.setValue("validated", true);',
244
+ ' rawFlow.setValue("type", isSubflow ? "subflow" : "flow");',
245
+ ' if (flowDefStr) { rawFlow.setValue("flow_definition", flowDefStr); rawFlow.setValue("latest_snapshot", flowDefStr); }',
246
+ ' flowSysId = rawFlow.insert();',
247
+ ' if (flowSysId) {',
248
+ ' var rawVer = new GlideRecord("sys_hub_flow_version");',
249
+ ' rawVer.initialize();',
250
+ ' rawVer.setValue("flow", flowSysId);',
251
+ ' rawVer.setValue("name", "1.0"); rawVer.setValue("version", "1.0");',
252
+ ' rawVer.setValue("state", shouldActivate ? "published" : "draft");',
253
+ ' rawVer.setValue("active", true); rawVer.setValue("compile_state", "compiled");',
254
+ ' rawVer.setValue("is_current", true);',
255
+ ' if (shouldActivate) rawVer.setValue("published_flow", flowSysId);',
256
+ ' if (flowDefStr) rawVer.setValue("flow_definition", flowDefStr);',
257
+ ' verSysId = rawVer.insert();',
258
+ ' if (verSysId) {',
259
+ ' var rawLink = new GlideRecord("sys_hub_flow");',
260
+ ' if (rawLink.get(flowSysId)) { rawLink.setValue("latest_version", verSysId); rawLink.update(); }',
261
+ ' }',
262
+ ' result.tier_used = "gliderecord_raw";',
263
+ ' result.success = true;',
184
264
  ' }',
185
- ' if (actInst.insert()) actionsCreated++;',
186
- ' } catch (actErr) {',
187
- ' // Best-effort per action',
188
- ' }',
265
+ ' } catch(t3e) { result.steps.tier3 = { success: false, error: t3e.getMessage ? t3e.getMessage() : t3e + "" }; }',
189
266
  ' }',
190
- ' result.steps.actions = { success: true, created: actionsCreated, requested: activities.length };',
191
267
  '',
192
- ' // Step 5: Create flow variables (subflows)',
193
- ' var varsCreated = 0;',
194
- ' if (isSubflow) {',
195
- ' for (var vi = 0; vi < inputs.length; vi++) {',
268
+ // ── Common: trigger, actions, variables ──
269
+ ' if (flowSysId) {',
270
+ ' result.flow_sys_id = flowSysId + "";',
271
+ ' result.version_sys_id = verSysId ? verSysId + "" : null;',
272
+ ' result.version_created = !!verSysId;',
273
+ '',
274
+ ' if (!isSubflow && triggerType !== "manual") {',
196
275
  ' try {',
197
- ' var inp = inputs[vi];',
198
- ' var fv = new GlideRecord("sys_hub_flow_variable");',
199
- ' fv.initialize();',
200
- ' fv.setValue("flow", flowSysId);',
201
- ' fv.setValue("name", inp.name);',
202
- ' fv.setValue("label", inp.label || inp.name);',
203
- ' fv.setValue("type", inp.type || "string");',
204
- ' fv.setValue("mandatory", inp.mandatory || false);',
205
- ' fv.setValue("default_value", inp.default_value || "");',
206
- ' fv.setValue("variable_type", "input");',
207
- ' if (fv.insert()) varsCreated++;',
208
- ' } catch (vErr) { /* best-effort */ }',
276
+ ' var triggerMap = { "record_created": "sn_fd.trigger.record_created", "record_updated": "sn_fd.trigger.record_updated", "scheduled": "sn_fd.trigger.scheduled" };',
277
+ ' var trigIntName = triggerMap[triggerType] || "";',
278
+ ' if (trigIntName) {',
279
+ ' var trigDef = new GlideRecord("sys_hub_action_type_definition");',
280
+ ' trigDef.addQuery("internal_name", trigIntName); trigDef.query();',
281
+ ' if (trigDef.next()) {',
282
+ ' var trigInst = new GlideRecord("sys_hub_trigger_instance");',
283
+ ' trigInst.initialize();',
284
+ ' trigInst.setValue("flow", flowSysId); trigInst.setValue("action_type", trigDef.getUniqueValue());',
285
+ ' trigInst.setValue("name", triggerType); trigInst.setValue("order", 0); trigInst.setValue("active", true);',
286
+ ' if (triggerTable) trigInst.setValue("table", triggerTable);',
287
+ ' if (triggerCondition) trigInst.setValue("condition", triggerCondition);',
288
+ ' var trigSysId = trigInst.insert();',
289
+ ' result.steps.trigger = { success: !!trigSysId, sys_id: trigSysId + "" };',
290
+ ' } else { result.steps.trigger = { success: false, error: "Trigger def not found: " + trigIntName }; }',
291
+ ' }',
292
+ ' } catch(te) { result.steps.trigger = { success: false, error: te.getMessage ? te.getMessage() : te + "" }; }',
209
293
  ' }',
210
- ' for (var vo = 0; vo < outputs.length; vo++) {',
294
+ '',
295
+ ' var actionsCreated = 0;',
296
+ ' for (var ai = 0; ai < activities.length; ai++) {',
211
297
  ' try {',
212
- ' var out = outputs[vo];',
213
- ' var ov = new GlideRecord("sys_hub_flow_variable");',
214
- ' ov.initialize();',
215
- ' ov.setValue("flow", flowSysId);',
216
- ' ov.setValue("name", out.name);',
217
- ' ov.setValue("label", out.label || out.name);',
218
- ' ov.setValue("type", out.type || "string");',
219
- ' ov.setValue("variable_type", "output");',
220
- ' if (ov.insert()) varsCreated++;',
221
- ' } catch (vErr) { /* best-effort */ }',
298
+ ' var act = activities[ai];',
299
+ ' var actDef = new GlideRecord("sys_hub_action_type_definition");',
300
+ ' actDef.addQuery("internal_name", "CONTAINS", act.type || "script");',
301
+ ' actDef.addOrCondition("name", "CONTAINS", act.type || "script"); actDef.query();',
302
+ ' var actInst = new GlideRecord("sys_hub_action_instance");',
303
+ ' actInst.initialize();',
304
+ ' actInst.setValue("flow", flowSysId); actInst.setValue("name", act.name || "Action " + (ai + 1));',
305
+ ' actInst.setValue("order", (ai + 1) * 100); actInst.setValue("active", true);',
306
+ ' if (actDef.next()) actInst.setValue("action_type", actDef.getUniqueValue());',
307
+ ' if (actInst.insert()) actionsCreated++;',
308
+ ' } catch(ae) {}',
309
+ ' }',
310
+ ' result.steps.actions = { success: true, created: actionsCreated, requested: activities.length };',
311
+ '',
312
+ ' var varsCreated = 0;',
313
+ ' if (isSubflow) {',
314
+ ' for (var vi = 0; vi < inputs.length; vi++) {',
315
+ ' try { var inp = inputs[vi]; var fv = new GlideRecord("sys_hub_flow_variable"); fv.initialize();',
316
+ ' fv.setValue("flow",flowSysId); fv.setValue("name",inp.name); fv.setValue("label",inp.label||inp.name);',
317
+ ' fv.setValue("type",inp.type||"string"); fv.setValue("mandatory",inp.mandatory||false);',
318
+ ' fv.setValue("default_value",inp.default_value||""); fv.setValue("variable_type","input");',
319
+ ' if (fv.insert()) varsCreated++;',
320
+ ' } catch(ve) {}',
321
+ ' }',
322
+ ' for (var vo = 0; vo < outputs.length; vo++) {',
323
+ ' try { var out = outputs[vo]; var ov = new GlideRecord("sys_hub_flow_variable"); ov.initialize();',
324
+ ' ov.setValue("flow",flowSysId); ov.setValue("name",out.name); ov.setValue("label",out.label||out.name);',
325
+ ' ov.setValue("type",out.type||"string"); ov.setValue("variable_type","output");',
326
+ ' if (ov.insert()) varsCreated++;',
327
+ ' } catch(ve) {}',
328
+ ' }',
222
329
  ' }',
330
+ ' result.steps.variables = { success: true, created: varsCreated };',
223
331
  ' }',
224
- ' result.steps.variables = { success: true, created: varsCreated };',
225
332
  '',
226
- ' result.success = true;',
227
- ' result.flow_sys_id = flowSysId + "";',
228
- ' result.version_created = !!(result.steps.version && result.steps.version.success);',
229
- ' response.setStatus(201);',
333
+ ' if (result.success) response.setStatus(201);',
334
+ ' else response.setStatus(500);',
230
335
  '',
231
336
  ' } catch (e) {',
232
337
  ' result.success = false;',
233
338
  ' result.error = e.getMessage ? e.getMessage() : e + "";',
234
339
  ' response.setStatus(500);',
235
340
  ' }',
236
- '',
237
341
  ' response.setBody(result);',
238
342
  '})(request, response);'
239
343
  ].join('\n');
@@ -384,7 +488,7 @@ async function ensureFlowFactoryAPI(
384
488
  name: 'create',
385
489
  active: true,
386
490
  relative_path: '/create',
387
- short_description: 'Create a flow or subflow with GlideRecord (triggers all BRs + version record)',
491
+ short_description: 'Create a flow/subflow via 3-tier approach: sn_fd API draft+publish raw GlideRecord',
388
492
  operation_script: FLOW_FACTORY_SCRIPT,
389
493
  requires_authentication: true,
390
494
  enforce_acl: 'false'
@@ -395,6 +499,23 @@ async function ensureFlowFactoryAPI(
395
499
  throw new Error('Failed to create Scripted REST operation: ' + (opError.message || opError));
396
500
  }
397
501
 
502
+ // 5b. Deploy the GET /discover resource (API capability probing)
503
+ try {
504
+ await client.post('/api/now/table/sys_ws_operation', {
505
+ web_service_definition: apiSysId,
506
+ http_method: 'GET',
507
+ name: 'discover',
508
+ active: true,
509
+ relative_path: '/discover',
510
+ short_description: 'Probe available sn_fd APIs, methods and fields for this ServiceNow instance',
511
+ operation_script: FLOW_FACTORY_DISCOVER_SCRIPT,
512
+ requires_authentication: true,
513
+ enforce_acl: 'false'
514
+ });
515
+ } catch (_) {
516
+ // Non-fatal: create endpoint is more important than discover
517
+ }
518
+
398
519
  // 6. Probe to discover the namespace ServiceNow assigned
399
520
  var resolvedNs = await probeFlowFactoryNamespace(client, apiSysId, instanceUrl);
400
521
  if (!resolvedNs) {
@@ -417,6 +538,111 @@ async function ensureFlowFactoryAPI(
417
538
  */
418
539
  function invalidateFlowFactoryCache(): void {
419
540
  _flowFactoryCache = null;
541
+ _discoveryCache = null;
542
+ }
543
+
544
+ /**
545
+ * Call the /discover endpoint on the Flow Factory API to learn which
546
+ * sn_fd APIs and methods are available on the target ServiceNow instance.
547
+ * Results are cached alongside the factory cache.
548
+ */
549
+ var _discoveryCache: any = null;
550
+
551
+ async function discoverInstanceCapabilities(
552
+ client: any,
553
+ namespace: string
554
+ ): Promise<any> {
555
+ if (_discoveryCache) return _discoveryCache;
556
+ try {
557
+ var discoverEndpoint = '/api/' + namespace + '/' + FLOW_FACTORY_API_ID + '/discover';
558
+ var resp = await client.get(discoverEndpoint);
559
+ _discoveryCache = resp.data?.result || resp.data || null;
560
+ return _discoveryCache;
561
+ } catch (_) {
562
+ return null;
563
+ }
564
+ }
565
+
566
+ /**
567
+ * After creating a flow (via any method), try to register it with the
568
+ * Flow Designer engine by calling its built-in compile / publish / activate
569
+ * REST endpoints. Without this step the flow record exists in the DB but
570
+ * Flow Designer cannot open it ("Your flow cannot be found").
571
+ *
572
+ * Tries multiple known endpoint patterns because the namespace/path changed
573
+ * across ServiceNow releases. Returns diagnostics on which attempts were
574
+ * made and what succeeded.
575
+ */
576
+ async function registerFlowWithEngine(
577
+ client: any,
578
+ flowSysId: string,
579
+ shouldActivate: boolean
580
+ ): Promise<{ success: boolean; method: string; attempts: string[] }> {
581
+ var attempts: string[] = [];
582
+
583
+ // Helper: try a POST and classify the result
584
+ async function tryPost(label: string, url: string, body?: any): Promise<boolean> {
585
+ try {
586
+ await client.post(url, body || {});
587
+ attempts.push(label + ': success');
588
+ return true;
589
+ } catch (e: any) {
590
+ var s = e.response?.status || 'err';
591
+ attempts.push(label + ': ' + s);
592
+ return false;
593
+ }
594
+ }
595
+
596
+ // ── Strategy 1: Publish via Flow Designer REST API ───────────────
597
+ // This is the closest equivalent to clicking "Publish" in the UI.
598
+ var publishPaths = [
599
+ '/api/sn_fd/flow/' + flowSysId + '/publish',
600
+ '/api/sn_fd/designer/flow/' + flowSysId + '/publish',
601
+ '/api/sn_flow_designer/flow/' + flowSysId + '/publish',
602
+ ];
603
+ for (var pi = 0; pi < publishPaths.length; pi++) {
604
+ if (await tryPost('publish[' + pi + ']', publishPaths[pi])) {
605
+ return { success: true, method: 'publish', attempts: attempts };
606
+ }
607
+ }
608
+
609
+ // ── Strategy 2: Activate (registers the flow with the engine) ────
610
+ if (shouldActivate) {
611
+ var activatePaths = [
612
+ '/api/sn_fd/flow/' + flowSysId + '/activate',
613
+ '/api/sn_fd/designer/flow/' + flowSysId + '/activate',
614
+ '/api/sn_flow_designer/flow/' + flowSysId + '/activate',
615
+ ];
616
+ for (var ai = 0; ai < activatePaths.length; ai++) {
617
+ if (await tryPost('activate[' + ai + ']', activatePaths[ai])) {
618
+ return { success: true, method: 'activate', attempts: attempts };
619
+ }
620
+ }
621
+ }
622
+
623
+ // ── Strategy 3: Checkout + checkin (triggers internal compilation) ─
624
+ var checkoutPaths = [
625
+ '/api/sn_fd/flow/' + flowSysId + '/checkout',
626
+ '/api/sn_fd/designer/flow/' + flowSysId + '/checkout',
627
+ ];
628
+ for (var ci = 0; ci < checkoutPaths.length; ci++) {
629
+ if (await tryPost('checkout[' + ci + ']', checkoutPaths[ci])) {
630
+ var checkinPath = checkoutPaths[ci].replace('/checkout', '/checkin');
631
+ await tryPost('checkin[' + ci + ']', checkinPath);
632
+ return { success: true, method: 'checkout+checkin', attempts: attempts };
633
+ }
634
+ }
635
+
636
+ // ── Strategy 4: Snapshot (already existed — triggers version snapshot) ─
637
+ if (await tryPost('snapshot', '/api/sn_flow_designer/flow/snapshot', { flow_id: flowSysId })) {
638
+ return { success: true, method: 'snapshot', attempts: attempts };
639
+ }
640
+ // Try alternate snapshot path
641
+ if (await tryPost('snapshot-alt', '/api/sn_fd/flow/' + flowSysId + '/snapshot')) {
642
+ return { success: true, method: 'snapshot-alt', attempts: attempts };
643
+ }
644
+
645
+ return { success: false, method: 'none', attempts: attempts };
420
646
  }
421
647
 
422
648
  /**
@@ -675,7 +901,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
675
901
  version_created: false,
676
902
  version_method: null,
677
903
  version_fields_set: [] as string[],
678
- snapshot_result: null,
904
+ engine_registration: null,
679
905
  post_verify: null
680
906
  };
681
907
 
@@ -684,6 +910,14 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
684
910
  diagnostics.factory_bootstrap = 'success';
685
911
  diagnostics.factory_namespace = factory.namespace;
686
912
 
913
+ // Call /discover to learn what sn_fd APIs exist on this instance
914
+ var discovery = await discoverInstanceCapabilities(client, factory.namespace);
915
+ if (discovery) {
916
+ diagnostics.instance_build = discovery.build_tag || discovery.build_name || 'unknown';
917
+ diagnostics.available_apis = discovery.apis || {};
918
+ diagnostics.available_methods = discovery.methods || {};
919
+ }
920
+
687
921
  var factoryPayload = {
688
922
  name: flowName,
689
923
  description: flowDescription,
@@ -722,12 +956,15 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
722
956
 
723
957
  var factoryResult = factoryResp.data?.result || factoryResp.data;
724
958
  if (factoryResult && factoryResult.success && factoryResult.flow_sys_id) {
959
+ var tierUsed = factoryResult.tier_used || 'unknown';
725
960
  diagnostics.factory_call = 'success';
961
+ diagnostics.factory_tier = tierUsed;
962
+ diagnostics.factory_steps = factoryResult.steps || {};
726
963
  flowSysId = factoryResult.flow_sys_id;
727
964
  usedMethod = 'scripted_rest_api';
728
965
  versionCreated = !!factoryResult.version_created;
729
966
  diagnostics.version_created = versionCreated;
730
- diagnostics.version_method = 'factory';
967
+ diagnostics.version_method = 'factory (' + tierUsed + ')';
731
968
  diagnostics.version_fields_set = ['flow', 'name', 'version', 'state', 'active', 'compile_state', 'is_current', 'published_flow'];
732
969
 
733
970
  // Extract step details
@@ -919,12 +1156,20 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
919
1156
  }
920
1157
  }
921
1158
 
922
- // Best-effort snapshot
923
- try {
924
- await client.post('/api/sn_flow_designer/flow/snapshot', { flow_id: flowSysId });
925
- diagnostics.snapshot_result = 'success';
926
- } catch (snapError: any) {
927
- diagnostics.snapshot_result = 'error: ' + (snapError.response?.status || snapError.message || 'unknown');
1159
+ }
1160
+
1161
+ // ── Register flow with Flow Designer engine ─────────────────
1162
+ // This is the KEY step: without it, records exist but Flow Designer
1163
+ // shows "Your flow cannot be found" because the engine hasn't compiled it.
1164
+ if (flowSysId) {
1165
+ var engineResult = await registerFlowWithEngine(client, flowSysId, shouldActivate);
1166
+ diagnostics.engine_registration = {
1167
+ success: engineResult.success,
1168
+ method: engineResult.method,
1169
+ attempts: engineResult.attempts
1170
+ };
1171
+ if (!engineResult.success) {
1172
+ factoryWarnings.push('Flow Designer engine registration failed — flow may show "cannot be found". Attempts: ' + engineResult.attempts.join(', '));
928
1173
  }
929
1174
  }
930
1175
 
@@ -1034,12 +1279,29 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1034
1279
 
1035
1280
  // Diagnostics section
1036
1281
  createSummary.blank().line('Diagnostics:');
1282
+ if (diagnostics.instance_build) {
1283
+ createSummary.indented('Instance build: ' + diagnostics.instance_build);
1284
+ }
1037
1285
  createSummary.indented('Factory bootstrap: ' + (diagnostics.factory_bootstrap || 'not attempted'));
1038
1286
  createSummary.indented('Factory namespace: ' + (diagnostics.factory_namespace || 'n/a'));
1039
1287
  createSummary.indented('Factory call: ' + (diagnostics.factory_call || 'not attempted'));
1288
+ if (diagnostics.factory_tier) {
1289
+ createSummary.indented('Factory tier used: ' + diagnostics.factory_tier);
1290
+ }
1291
+ if (diagnostics.available_apis && Object.keys(diagnostics.available_apis).length > 0) {
1292
+ var apiStr = Object.keys(diagnostics.available_apis).map(function (k: string) { return k + '=' + diagnostics.available_apis[k]; }).join(', ');
1293
+ createSummary.indented('Available APIs: ' + apiStr);
1294
+ }
1040
1295
  createSummary.indented('Table API used: ' + diagnostics.table_api_used);
1041
1296
  createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
1042
- createSummary.indented('Snapshot: ' + (diagnostics.snapshot_result || 'n/a'));
1297
+ if (diagnostics.engine_registration) {
1298
+ createSummary.indented('Engine registration: ' + (diagnostics.engine_registration.success ? diagnostics.engine_registration.method : 'FAILED'));
1299
+ if (diagnostics.engine_registration.attempts) {
1300
+ for (var ea = 0; ea < diagnostics.engine_registration.attempts.length; ea++) {
1301
+ createSummary.indented(' ' + diagnostics.engine_registration.attempts[ea]);
1302
+ }
1303
+ }
1304
+ }
1043
1305
  if (diagnostics.post_verify) {
1044
1306
  if (diagnostics.post_verify.error) {
1045
1307
  createSummary.indented('Post-verify: error — ' + diagnostics.post_verify.error);
@@ -1425,5 +1687,5 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1425
1687
  }
1426
1688
  }
1427
1689
 
1428
- export const version = '3.0.0';
1690
+ export const version = '5.0.0';
1429
1691
  export const author = 'Snow-Flow Team';