snow-flow-test 10.0.1-test.111 → 10.0.1-test.113

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.111",
3
+ "version": "10.0.1-test.113",
4
4
  "name": "snow-flow-test",
5
5
  "description": "Snow-Flow Test - ServiceNow Multi-Agent Development Framework",
6
6
  "license": "Elastic-2.0",
@@ -45,8 +45,67 @@ 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) {',
108
+ // ── Body parsing (3-method cascade) ──
50
109
  ' var body = null;',
51
110
  ' var parseLog = [];',
52
111
  ' try {',
@@ -69,7 +128,7 @@ var FLOW_FACTORY_SCRIPT = [
69
128
  ' response.setBody({ success: false, error: "No parseable body", parseLog: parseLog, bodyType: typeof request.body });',
70
129
  ' return;',
71
130
  ' }',
72
- ' var result = { success: false, steps: {}, parseLog: parseLog };',
131
+ ' var result = { success: false, steps: {}, tier_used: null, parseLog: parseLog };',
73
132
  '',
74
133
  ' try {',
75
134
  ' var flowName = body.name || "Unnamed Flow";',
@@ -84,170 +143,201 @@ var FLOW_FACTORY_SCRIPT = [
84
143
  ' var activities = body.activities || [];',
85
144
  ' var inputs = body.inputs || [];',
86
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, "");',
87
149
  '',
88
- ' // Step 1: Create sys_hub_flow via GlideRecord (triggers all BRs)',
89
- ' var flow = new GlideRecord("sys_hub_flow");',
90
- ' flow.initialize();',
91
- ' flow.setValue("name", flowName);',
92
- ' flow.setValue("description", flowDesc);',
93
- ' flow.setValue("internal_name", flowName.toLowerCase().replace(/[^a-z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, ""));',
94
- ' flow.setValue("category", flowCategory);',
95
- ' flow.setValue("run_as", runAs);',
96
- ' flow.setValue("active", shouldActivate);',
97
- ' flow.setValue("status", shouldActivate ? "published" : "draft");',
98
- ' flow.setValue("validated", true);',
99
- ' flow.setValue("type", isSubflow ? "subflow" : "flow");',
100
- '',
101
- ' if (body.flow_definition) {',
102
- ' flow.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
103
- ' flow.setValue("latest_snapshot", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
104
- ' }',
150
+ ' var flowSysId = null;',
151
+ ' var verSysId = null;',
105
152
  '',
106
- ' var flowSysId = flow.insert();',
107
- ' if (!flowSysId) {',
108
- ' result.error = "Failed to insert sys_hub_flow record";',
109
- ' response.setStatus(500);',
110
- ' response.setBody(result);',
111
- ' return;',
112
- ' }',
113
- ' result.steps.flow = { success: true, sys_id: flowSysId + "" };',
114
- '',
115
- ' // Step 2: Create sys_hub_flow_version (this is what Table API misses!)',
153
+ // ── TIER 1: sn_fd.FlowDesigner API ──
116
154
  ' try {',
117
- ' var ver = new GlideRecord("sys_hub_flow_version");',
118
- ' ver.initialize();',
119
- ' ver.setValue("flow", flowSysId);',
120
- ' ver.setValue("name", "1.0");',
121
- ' ver.setValue("version", "1.0");',
122
- ' ver.setValue("state", shouldActivate ? "published" : "draft");',
123
- ' ver.setValue("active", true);',
124
- ' ver.setValue("compile_state", "compiled");',
125
- ' ver.setValue("is_current", true);',
126
- ' if (shouldActivate) ver.setValue("published_flow", flowSysId);',
127
- ' if (body.flow_definition) {',
128
- ' ver.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
129
- ' }',
130
- ' var verSysId = ver.insert();',
131
- ' if (verSysId) {',
132
- ' result.steps.version = { success: true, sys_id: verSysId + "" };',
133
- ' // Update flow to point to latest version',
134
- ' var flowUpd = new GlideRecord("sys_hub_flow");',
135
- ' if (flowUpd.get(flowSysId)) {',
136
- ' flowUpd.setValue("latest_version", verSysId);',
137
- ' 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
+ ' }',
138
165
  ' }',
139
- ' } else {',
140
- ' result.steps.version = { success: false, error: "Insert returned empty sys_id" };',
141
166
  ' }',
142
- ' } catch (verErr) {',
143
- ' result.steps.version = { success: false, error: verErr.getMessage ? verErr.getMessage() : verErr + "" };',
144
- ' }',
167
+ ' } catch(t1e) { result.steps.tier1 = { success: false, error: t1e.getMessage ? t1e.getMessage() : t1e + "" }; }',
145
168
  '',
146
- ' // Step 3: Create trigger instance (non-manual, non-subflow only)',
147
- ' if (!isSubflow && triggerType !== "manual") {',
169
+ // ── TIER 2: GlideRecord INSERT as draft UPDATE to published ──
170
+ ' if (!flowSysId) {',
148
171
  ' try {',
149
- ' var triggerMap = {',
150
- ' "record_created": "sn_fd.trigger.record_created",',
151
- ' "record_updated": "sn_fd.trigger.record_updated",',
152
- ' "scheduled": "sn_fd.trigger.scheduled"',
153
- ' };',
154
- ' var triggerIntName = triggerMap[triggerType] || "";',
155
- ' if (triggerIntName) {',
156
- ' var trigDef = new GlideRecord("sys_hub_action_type_definition");',
157
- ' trigDef.addQuery("internal_name", triggerIntName);',
158
- ' trigDef.query();',
159
- ' if (trigDef.next()) {',
160
- ' var trigInst = new GlideRecord("sys_hub_trigger_instance");',
161
- ' trigInst.initialize();',
162
- ' trigInst.setValue("flow", flowSysId);',
163
- ' trigInst.setValue("action_type", trigDef.getUniqueValue());',
164
- ' trigInst.setValue("name", triggerType);',
165
- ' trigInst.setValue("order", 0);',
166
- ' trigInst.setValue("active", true);',
167
- ' if (triggerTable) trigInst.setValue("table", triggerTable);',
168
- ' if (triggerCondition) trigInst.setValue("condition", triggerCondition);',
169
- ' var trigSysId = trigInst.insert();',
170
- ' result.steps.trigger = { success: !!trigSysId, sys_id: trigSysId + "" };',
171
- ' } else {',
172
- ' 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(); }',
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
+ ' }',
173
224
  ' }',
225
+ ' result.tier_used = "gliderecord_draft_then_publish";',
226
+ ' result.success = true;',
174
227
  ' }',
175
- ' } catch (trigErr) {',
176
- ' result.steps.trigger = { success: false, error: trigErr.getMessage ? trigErr.getMessage() : trigErr + "" };',
177
- ' }',
228
+ ' } catch(t2e) { result.steps.tier2 = { success: false, error: t2e.getMessage ? t2e.getMessage() : t2e + "" }; }',
178
229
  ' }',
179
230
  '',
180
- ' // Step 4: Create action instances',
181
- ' var actionsCreated = 0;',
182
- ' for (var ai = 0; ai < activities.length; ai++) {',
231
+ // ── TIER 3: Raw GlideRecord INSERT (last resort) ──
232
+ ' if (!flowSysId) {',
183
233
  ' try {',
184
- ' var act = activities[ai];',
185
- ' var actTypeName = act.type || "script";',
186
- ' var actDef = new GlideRecord("sys_hub_action_type_definition");',
187
- ' actDef.addQuery("internal_name", "CONTAINS", actTypeName);',
188
- ' actDef.addOrCondition("name", "CONTAINS", actTypeName);',
189
- ' actDef.query();',
190
- ' var actInst = new GlideRecord("sys_hub_action_instance");',
191
- ' actInst.initialize();',
192
- ' actInst.setValue("flow", flowSysId);',
193
- ' actInst.setValue("name", act.name || ("Action " + (ai + 1)));',
194
- ' actInst.setValue("order", (ai + 1) * 100);',
195
- ' actInst.setValue("active", true);',
196
- ' if (actDef.next()) {',
197
- ' 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;',
198
264
  ' }',
199
- ' if (actInst.insert()) actionsCreated++;',
200
- ' } catch (actErr) {',
201
- ' // Best-effort per action',
202
- ' }',
265
+ ' } catch(t3e) { result.steps.tier3 = { success: false, error: t3e.getMessage ? t3e.getMessage() : t3e + "" }; }',
203
266
  ' }',
204
- ' result.steps.actions = { success: true, created: actionsCreated, requested: activities.length };',
205
267
  '',
206
- ' // Step 5: Create flow variables (subflows)',
207
- ' var varsCreated = 0;',
208
- ' if (isSubflow) {',
209
- ' 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") {',
210
275
  ' try {',
211
- ' var inp = inputs[vi];',
212
- ' var fv = new GlideRecord("sys_hub_flow_variable");',
213
- ' fv.initialize();',
214
- ' fv.setValue("flow", flowSysId);',
215
- ' fv.setValue("name", inp.name);',
216
- ' fv.setValue("label", inp.label || inp.name);',
217
- ' fv.setValue("type", inp.type || "string");',
218
- ' fv.setValue("mandatory", inp.mandatory || false);',
219
- ' fv.setValue("default_value", inp.default_value || "");',
220
- ' fv.setValue("variable_type", "input");',
221
- ' if (fv.insert()) varsCreated++;',
222
- ' } 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 + "" }; }',
223
293
  ' }',
224
- ' for (var vo = 0; vo < outputs.length; vo++) {',
294
+ '',
295
+ ' var actionsCreated = 0;',
296
+ ' for (var ai = 0; ai < activities.length; ai++) {',
225
297
  ' try {',
226
- ' var out = outputs[vo];',
227
- ' var ov = new GlideRecord("sys_hub_flow_variable");',
228
- ' ov.initialize();',
229
- ' ov.setValue("flow", flowSysId);',
230
- ' ov.setValue("name", out.name);',
231
- ' ov.setValue("label", out.label || out.name);',
232
- ' ov.setValue("type", out.type || "string");',
233
- ' ov.setValue("variable_type", "output");',
234
- ' if (ov.insert()) varsCreated++;',
235
- ' } 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) {}',
236
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
+ ' }',
329
+ ' }',
330
+ ' result.steps.variables = { success: true, created: varsCreated };',
237
331
  ' }',
238
- ' result.steps.variables = { success: true, created: varsCreated };',
239
332
  '',
240
- ' result.success = true;',
241
- ' result.flow_sys_id = flowSysId + "";',
242
- ' result.version_created = !!(result.steps.version && result.steps.version.success);',
243
- ' response.setStatus(201);',
333
+ ' if (result.success) response.setStatus(201);',
334
+ ' else response.setStatus(500);',
244
335
  '',
245
336
  ' } catch (e) {',
246
337
  ' result.success = false;',
247
338
  ' result.error = e.getMessage ? e.getMessage() : e + "";',
248
339
  ' response.setStatus(500);',
249
340
  ' }',
250
- '',
251
341
  ' response.setBody(result);',
252
342
  '})(request, response);'
253
343
  ].join('\n');
@@ -317,17 +407,32 @@ async function probeFlowFactoryNamespace(
317
407
  }
318
408
  }
319
409
 
320
- // 5. Probe each candidate — GET on a POST-only endpoint
410
+ // 5. Probe each candidate — GET the /discover endpoint (a real GET handler)
411
+ // If /discover doesn't exist yet, fall back to /create (expects 405)
321
412
  for (var j = 0; j < unique.length; j++) {
413
+ // Try /discover first (GET endpoint, returns 200 when namespace is correct)
414
+ try {
415
+ var discoverResp = await client.get('/api/' + unique[j] + '/' + FLOW_FACTORY_API_ID + '/discover');
416
+ if (discoverResp.status === 200 || discoverResp.data) {
417
+ return unique[j]; // Namespace confirmed via /discover
418
+ }
419
+ } catch (discoverErr: any) {
420
+ var ds = discoverErr.response?.status;
421
+ if (ds === 401 || ds === 403) {
422
+ return unique[j]; // Namespace correct but auth issue
423
+ }
424
+ // 404 = wrong namespace OR /discover not deployed yet, try /create
425
+ }
426
+ // Fallback: try /create (POST-only, expect 405 for correct namespace)
322
427
  try {
323
428
  await client.get('/api/' + unique[j] + '/' + FLOW_FACTORY_API_ID + '/create');
324
- return unique[j]; // 200 = endpoint exists (unlikely but valid)
325
- } catch (probeError: any) {
326
- var status = probeError.response?.status;
327
- if (status === 405 || status === 401 || status === 403) {
429
+ return unique[j]; // 200 = unexpected but valid
430
+ } catch (createErr: any) {
431
+ var cs = createErr.response?.status;
432
+ if (cs === 405 || cs === 401 || cs === 403) {
328
433
  return unique[j]; // Namespace correct — method or auth rejected
329
434
  }
330
- // 404 = wrong namespace, try next
435
+ // 404 = wrong namespace, try next candidate
331
436
  }
332
437
  }
333
438
 
@@ -398,7 +503,7 @@ async function ensureFlowFactoryAPI(
398
503
  name: 'create',
399
504
  active: true,
400
505
  relative_path: '/create',
401
- short_description: 'Create a flow or subflow with GlideRecord (triggers all BRs + version record)',
506
+ short_description: 'Create a flow/subflow via 3-tier approach: sn_fd API draft+publish raw GlideRecord',
402
507
  operation_script: FLOW_FACTORY_SCRIPT,
403
508
  requires_authentication: true,
404
509
  enforce_acl: 'false'
@@ -409,6 +514,23 @@ async function ensureFlowFactoryAPI(
409
514
  throw new Error('Failed to create Scripted REST operation: ' + (opError.message || opError));
410
515
  }
411
516
 
517
+ // 5b. Deploy the GET /discover resource (API capability probing)
518
+ try {
519
+ await client.post('/api/now/table/sys_ws_operation', {
520
+ web_service_definition: apiSysId,
521
+ http_method: 'GET',
522
+ name: 'discover',
523
+ active: true,
524
+ relative_path: '/discover',
525
+ short_description: 'Probe available sn_fd APIs, methods and fields for this ServiceNow instance',
526
+ operation_script: FLOW_FACTORY_DISCOVER_SCRIPT,
527
+ requires_authentication: true,
528
+ enforce_acl: 'false'
529
+ });
530
+ } catch (_) {
531
+ // Non-fatal: create endpoint is more important than discover
532
+ }
533
+
412
534
  // 6. Probe to discover the namespace ServiceNow assigned
413
535
  var resolvedNs = await probeFlowFactoryNamespace(client, apiSysId, instanceUrl);
414
536
  if (!resolvedNs) {
@@ -431,6 +553,29 @@ async function ensureFlowFactoryAPI(
431
553
  */
432
554
  function invalidateFlowFactoryCache(): void {
433
555
  _flowFactoryCache = null;
556
+ _discoveryCache = null;
557
+ }
558
+
559
+ /**
560
+ * Call the /discover endpoint on the Flow Factory API to learn which
561
+ * sn_fd APIs and methods are available on the target ServiceNow instance.
562
+ * Results are cached alongside the factory cache.
563
+ */
564
+ var _discoveryCache: any = null;
565
+
566
+ async function discoverInstanceCapabilities(
567
+ client: any,
568
+ namespace: string
569
+ ): Promise<any> {
570
+ if (_discoveryCache) return _discoveryCache;
571
+ try {
572
+ var discoverEndpoint = '/api/' + namespace + '/' + FLOW_FACTORY_API_ID + '/discover';
573
+ var resp = await client.get(discoverEndpoint);
574
+ _discoveryCache = resp.data?.result || resp.data || null;
575
+ return _discoveryCache;
576
+ } catch (_) {
577
+ return null;
578
+ }
434
579
  }
435
580
 
436
581
  /**
@@ -780,6 +925,14 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
780
925
  diagnostics.factory_bootstrap = 'success';
781
926
  diagnostics.factory_namespace = factory.namespace;
782
927
 
928
+ // Call /discover to learn what sn_fd APIs exist on this instance
929
+ var discovery = await discoverInstanceCapabilities(client, factory.namespace);
930
+ if (discovery) {
931
+ diagnostics.instance_build = discovery.build_tag || discovery.build_name || 'unknown';
932
+ diagnostics.available_apis = discovery.apis || {};
933
+ diagnostics.available_methods = discovery.methods || {};
934
+ }
935
+
783
936
  var factoryPayload = {
784
937
  name: flowName,
785
938
  description: flowDescription,
@@ -818,12 +971,15 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
818
971
 
819
972
  var factoryResult = factoryResp.data?.result || factoryResp.data;
820
973
  if (factoryResult && factoryResult.success && factoryResult.flow_sys_id) {
974
+ var tierUsed = factoryResult.tier_used || 'unknown';
821
975
  diagnostics.factory_call = 'success';
976
+ diagnostics.factory_tier = tierUsed;
977
+ diagnostics.factory_steps = factoryResult.steps || {};
822
978
  flowSysId = factoryResult.flow_sys_id;
823
979
  usedMethod = 'scripted_rest_api';
824
980
  versionCreated = !!factoryResult.version_created;
825
981
  diagnostics.version_created = versionCreated;
826
- diagnostics.version_method = 'factory';
982
+ diagnostics.version_method = 'factory (' + tierUsed + ')';
827
983
  diagnostics.version_fields_set = ['flow', 'name', 'version', 'state', 'active', 'compile_state', 'is_current', 'published_flow'];
828
984
 
829
985
  // Extract step details
@@ -1138,9 +1294,19 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1138
1294
 
1139
1295
  // Diagnostics section
1140
1296
  createSummary.blank().line('Diagnostics:');
1297
+ if (diagnostics.instance_build) {
1298
+ createSummary.indented('Instance build: ' + diagnostics.instance_build);
1299
+ }
1141
1300
  createSummary.indented('Factory bootstrap: ' + (diagnostics.factory_bootstrap || 'not attempted'));
1142
1301
  createSummary.indented('Factory namespace: ' + (diagnostics.factory_namespace || 'n/a'));
1143
1302
  createSummary.indented('Factory call: ' + (diagnostics.factory_call || 'not attempted'));
1303
+ if (diagnostics.factory_tier) {
1304
+ createSummary.indented('Factory tier used: ' + diagnostics.factory_tier);
1305
+ }
1306
+ if (diagnostics.available_apis && Object.keys(diagnostics.available_apis).length > 0) {
1307
+ var apiStr = Object.keys(diagnostics.available_apis).map(function (k: string) { return k + '=' + diagnostics.available_apis[k]; }).join(', ');
1308
+ createSummary.indented('Available APIs: ' + apiStr);
1309
+ }
1144
1310
  createSummary.indented('Table API used: ' + diagnostics.table_api_used);
1145
1311
  createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
1146
1312
  if (diagnostics.engine_registration) {
@@ -1536,5 +1702,5 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1536
1702
  }
1537
1703
  }
1538
1704
 
1539
- export const version = '4.0.0';
1705
+ export const version = '5.0.0';
1540
1706
  export const author = 'Snow-Flow Team';