snow-flow 10.0.1-dev.402 → 10.0.1-dev.404

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-dev.402",
3
+ "version": "10.0.1-dev.404",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -4,10 +4,10 @@
4
4
  * Create, list, get, update, activate, deactivate, delete and publish
5
5
  * Flow Designer flows and subflows.
6
6
  *
7
- * For create/create_subflow: uses a bootstrapped Scripted REST API
8
- * ("Flow Factory") that runs GlideRecord server-side, ensuring
9
- * sys_hub_flow_version records are created and all Business Rules fire.
10
- * Falls back to Table API if the factory is unavailable.
7
+ * For create/create_subflow: uses a Scheduled Job (sysauto_script) that
8
+ * runs GlideRecord server-side, ensuring sys_hub_flow_version records are
9
+ * created, flow_definition is set, and sn_fd engine registration is
10
+ * attempted. Falls back to Table API if the scheduled job fails.
11
11
  *
12
12
  * All other actions (list, get, update, activate, etc.) use the Table API.
13
13
  */
@@ -65,6 +65,7 @@ async function createFlowViaScheduledJob(
65
65
  activities?: Array<{ name: string; type?: string; inputs?: any }>;
66
66
  inputs?: Array<{ name: string; label?: string; type?: string; mandatory?: boolean; default_value?: string }>;
67
67
  outputs?: Array<{ name: string; label?: string; type?: string }>;
68
+ flowDefinition?: any;
68
69
  }
69
70
  ): Promise<{
70
71
  success: boolean;
@@ -77,6 +78,7 @@ async function createFlowViaScheduledJob(
77
78
  error?: string;
78
79
  }> {
79
80
  var resultPropName = 'snow_flow.factory_result.' + Date.now();
81
+ var flowDefStr = params.flowDefinition ? JSON.stringify(params.flowDefinition) : '';
80
82
 
81
83
  // Build the server-side ES5 script
82
84
  var script = [
@@ -94,6 +96,7 @@ async function createFlowViaScheduledJob(
94
96
  " var trigType = '" + escForScript(params.triggerType || 'manual') + "';",
95
97
  " var trigTable = '" + escForScript(params.triggerTable || '') + "';",
96
98
  " var trigCondition = '" + escForScript(params.triggerCondition || '') + "';",
99
+ " var flowDefStr = '" + escForScript(flowDefStr) + "';",
97
100
  " var activitiesJson = '" + escForScript(JSON.stringify(params.activities || [])) + "';",
98
101
  " var inputsJson = '" + escForScript(JSON.stringify(params.inputs || [])) + "';",
99
102
  " var outputsJson = '" + escForScript(JSON.stringify(params.outputs || [])) + "';",
@@ -103,6 +106,7 @@ async function createFlowViaScheduledJob(
103
106
  " var flowSysId = null;",
104
107
  " var verSysId = null;",
105
108
  "",
109
+ // ── TIER 1: sn_fd.FlowDesigner API ──
106
110
  " try {",
107
111
  " if (typeof sn_fd !== 'undefined' && sn_fd.FlowDesigner && typeof sn_fd.FlowDesigner.createFlow === 'function') {",
108
112
  " var fdR = sn_fd.FlowDesigner.createFlow({ name: flowName, description: flowDesc, type: isSubflow ? 'subflow' : 'flow', category: flowCat, run_as: runAs });",
@@ -119,6 +123,7 @@ async function createFlowViaScheduledJob(
119
123
  " }",
120
124
  " } catch(t1e) { r.steps.tier1 = { success: false, error: t1e.getMessage ? t1e.getMessage() : t1e + '' }; }",
121
125
  "",
126
+ // ── TIER 2: GlideRecord with flow_definition ──
122
127
  " if (!flowSysId) {",
123
128
  " try {",
124
129
  " var f = new GlideRecord('sys_hub_flow');",
@@ -128,6 +133,7 @@ async function createFlowViaScheduledJob(
128
133
  " f.setValue('run_as', runAs); f.setValue('active', false);",
129
134
  " f.setValue('status', 'draft'); f.setValue('validated', true);",
130
135
  " f.setValue('type', isSubflow ? 'subflow' : 'flow');",
136
+ " if (flowDefStr) { f.setValue('flow_definition', flowDefStr); f.setValue('latest_snapshot', flowDefStr); }",
131
137
  " flowSysId = f.insert();",
132
138
  " r.steps.flow_insert = { success: !!flowSysId, sys_id: flowSysId + '' };",
133
139
  " if (flowSysId) {",
@@ -137,6 +143,7 @@ async function createFlowViaScheduledJob(
137
143
  " v.setValue('version', '1.0'); v.setValue('state', 'draft');",
138
144
  " v.setValue('active', true); v.setValue('compile_state', 'draft');",
139
145
  " v.setValue('is_current', true);",
146
+ " if (flowDefStr) v.setValue('flow_definition', flowDefStr);",
140
147
  " verSysId = v.insert();",
141
148
  " r.steps.version_insert = { success: !!verSysId, sys_id: verSysId + '' };",
142
149
  " if (verSysId) {",
@@ -158,24 +165,45 @@ async function createFlowViaScheduledJob(
158
165
  "",
159
166
  // ── Trigger, action, variable creation (runs for any tier) ──
160
167
  " if (flowSysId) {",
161
- // Trigger
168
+ // Trigger — search broadly for trigger definition (internal_name varies per instance)
162
169
  " if (!isSubflow && trigType !== 'manual') {",
163
170
  " try {",
164
- " var triggerMap = { 'record_created': 'sn_fd.trigger.record_created', 'record_updated': 'sn_fd.trigger.record_updated', 'scheduled': 'sn_fd.trigger.scheduled' };",
165
- " var trigIntName = triggerMap[trigType] || '';",
166
- " if (trigIntName) {",
167
- " var trigDef = new GlideRecord('sys_hub_action_type_definition');",
168
- " trigDef.addQuery('internal_name', trigIntName); trigDef.query();",
169
- " if (trigDef.next()) {",
170
- " var trigInst = new GlideRecord('sys_hub_trigger_instance');",
171
- " trigInst.initialize();",
172
- " trigInst.setValue('flow', flowSysId); trigInst.setValue('action_type', trigDef.getUniqueValue());",
173
- " trigInst.setValue('name', trigType); trigInst.setValue('order', 0); trigInst.setValue('active', true);",
174
- " if (trigTable) trigInst.setValue('table', trigTable);",
175
- " if (trigCondition) trigInst.setValue('condition', trigCondition);",
176
- " var trigId = trigInst.insert();",
177
- " r.steps.trigger = { success: !!trigId, sys_id: trigId + '' };",
178
- " } else { r.steps.trigger = { success: false, error: 'Trigger def not found: ' + trigIntName }; }",
171
+ " var trigSearchTerms = {",
172
+ " 'record_created': ['record_created', 'record_insert', 'created'],",
173
+ " 'record_updated': ['record_updated', 'record_update', 'updated'],",
174
+ " 'scheduled': ['scheduled', 'timer', 'schedule']",
175
+ " };",
176
+ " var terms = trigSearchTerms[trigType] || [trigType];",
177
+ " var trigDef = null;",
178
+ " var trigDefId = null;",
179
+ " var trigSearchLog = [];",
180
+ " for (var ts = 0; ts < terms.length; ts++) {",
181
+ " var tg = new GlideRecord('sys_hub_action_type_definition');",
182
+ " tg.addQuery('internal_name', 'CONTAINS', terms[ts]);",
183
+ " tg.addQuery('type', 'trigger');",
184
+ " tg.setLimit(1); tg.query();",
185
+ " if (tg.next()) { trigDef = tg; trigDefId = tg.getUniqueValue(); trigSearchLog.push(terms[ts] + ':found'); break; }",
186
+ " trigSearchLog.push(terms[ts] + ':not_found');",
187
+ " }",
188
+ " if (!trigDefId) {",
189
+ " var tg2 = new GlideRecord('sys_hub_action_type_definition');",
190
+ " tg2.addQuery('name', 'CONTAINS', trigType.replace('_', ' '));",
191
+ " tg2.addQuery('type', 'trigger');",
192
+ " tg2.setLimit(1); tg2.query();",
193
+ " if (tg2.next()) { trigDef = tg2; trigDefId = tg2.getUniqueValue(); trigSearchLog.push('name_search:found'); }",
194
+ " else { trigSearchLog.push('name_search:not_found'); }",
195
+ " }",
196
+ " if (trigDefId) {",
197
+ " var trigInst = new GlideRecord('sys_hub_trigger_instance');",
198
+ " trigInst.initialize();",
199
+ " trigInst.setValue('flow', flowSysId); trigInst.setValue('action_type', trigDefId);",
200
+ " trigInst.setValue('name', trigType); trigInst.setValue('order', 0); trigInst.setValue('active', true);",
201
+ " if (trigTable) trigInst.setValue('table', trigTable);",
202
+ " if (trigCondition) trigInst.setValue('condition', trigCondition);",
203
+ " var trigId = trigInst.insert();",
204
+ " r.steps.trigger = { success: !!trigId, sys_id: trigId + '', def_name: trigDef.getValue('name'), search: trigSearchLog };",
205
+ " } else {",
206
+ " r.steps.trigger = { success: false, error: 'No trigger def found', search: trigSearchLog };",
179
207
  " }",
180
208
  " } catch(te) { r.steps.trigger = { success: false, error: te.getMessage ? te.getMessage() : te + '' }; }",
181
209
  " }",
@@ -216,10 +244,46 @@ async function createFlowViaScheduledJob(
216
244
  " }",
217
245
  " }",
218
246
  " r.steps.variables = { success: true, created: varsCreated };",
247
+ "",
248
+ // ── Engine probe (discovery only — do NOT call publish/compile) ──
249
+ // Previous versions called FlowAPI.publish() which caused StackOverflowError
250
+ // and corrupted the flow. Now we only probe which APIs exist for diagnostics.
251
+ // The flow is left as a clean draft; user publishes via Flow Designer UI.
252
+ " r.steps.engine = { apis_found: [], mode: 'probe_only' };",
253
+ " try {",
254
+ " if (typeof sn_fd !== 'undefined') {",
255
+ " r.steps.engine.sn_fd = 'available';",
256
+ " var probeNames = ['FlowDesigner', 'FlowCompiler', 'FlowAPI', 'FlowPublisher'];",
257
+ " for (var pn = 0; pn < probeNames.length; pn++) {",
258
+ " try {",
259
+ " if (sn_fd[probeNames[pn]]) {",
260
+ " r.steps.engine.apis_found.push(probeNames[pn]);",
261
+ " var methods = [];",
262
+ " var pObj = sn_fd[probeNames[pn]];",
263
+ " var checkMethods = ['createFlow','publishFlow','activateFlow','compileFlow','compile','publish','getFlow'];",
264
+ " for (var cm = 0; cm < checkMethods.length; cm++) {",
265
+ " if (typeof pObj[checkMethods[cm]] === 'function') methods.push(checkMethods[cm]);",
266
+ " }",
267
+ " if (methods.length > 0) r.steps.engine[probeNames[pn] + '_methods'] = methods;",
268
+ " }",
269
+ " } catch(e) {}",
270
+ " }",
271
+ " } else {",
272
+ " r.steps.engine.sn_fd = 'unavailable';",
273
+ " }",
274
+ " try {",
275
+ " if (typeof GlideFlowDesigner !== 'undefined') {",
276
+ " r.steps.engine.apis_found.push('GlideFlowDesigner');",
277
+ " }",
278
+ " } catch(e) {}",
279
+ " } catch(engineErr) {",
280
+ " r.steps.engine.error = engineErr.getMessage ? engineErr.getMessage() : engineErr + '';",
281
+ " }",
219
282
  " }",
220
283
  "",
221
284
  " r.flow_sys_id = flowSysId ? flowSysId + '' : null;",
222
285
  " r.version_sys_id = verSysId ? verSysId + '' : null;",
286
+ // ── Post-engine check: re-read latest_version ──
223
287
  " if (flowSysId) {",
224
288
  " var cf = new GlideRecord('sys_hub_flow');",
225
289
  " if (cf.get(flowSysId)) {",
@@ -252,9 +316,10 @@ async function createFlowViaScheduledJob(
252
316
  });
253
317
  var jobSysId = jobResp.data.result?.sys_id;
254
318
 
255
- // 3. Poll for result (15 attempts * 2s = 30s max)
319
+ // 3. Poll for result (adaptive: 5×1s + 2s + 5×3s = 30s max)
256
320
  for (var i = 0; i < 15; i++) {
257
- await new Promise(resolve => setTimeout(resolve, 2000));
321
+ var delay = i < 5 ? 1000 : i < 10 ? 2000 : 3000;
322
+ await new Promise(resolve => setTimeout(resolve, delay));
258
323
  var propResp = await client.get('/api/now/table/sys_properties', {
259
324
  params: {
260
325
  sysparm_query: 'name=' + resultPropName,
@@ -1234,7 +1299,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1234
1299
  delete flowDefinition.trigger;
1235
1300
  }
1236
1301
 
1237
- // ── Try Scripted REST API (Flow Factory) first ──────────────
1302
+ // ── Pipeline: Scheduled Job (primary) → Table API (fallback) ──
1238
1303
  var flowSysId: string | null = null;
1239
1304
  var usedMethod = 'table_api';
1240
1305
  var versionCreated = false;
@@ -1245,7 +1310,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1245
1310
 
1246
1311
  // Diagnostics: track every step for debugging "flow cannot be found" issues
1247
1312
  var diagnostics: any = {
1248
- factory_bootstrap: null,
1313
+ factory_bootstrap: 'skipped (direct scheduled job)',
1249
1314
  factory_namespace: null,
1250
1315
  factory_call: null,
1251
1316
  table_api_used: false,
@@ -1256,154 +1321,65 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1256
1321
  post_verify: null
1257
1322
  };
1258
1323
 
1324
+ // ── Scheduled Job (primary — server-side GlideRecord) ───────
1325
+ // This runs inside ServiceNow as a system job, so it CAN set
1326
+ // computed fields like latest_version that Table API cannot.
1327
+ // Also attempts sn_fd engine registration server-side.
1259
1328
  try {
1260
- var factory = await ensureFlowFactoryAPI(client, context.instanceUrl);
1261
- diagnostics.factory_bootstrap = 'success';
1262
- diagnostics.factory_namespace = factory.namespace;
1263
-
1264
- // Call /discover to learn what sn_fd APIs exist on this instance
1265
- var discovery = await discoverInstanceCapabilities(client, factory.namespace);
1266
- if (discovery) {
1267
- diagnostics.instance_build = discovery.build_tag || discovery.build_name || 'unknown';
1268
- diagnostics.available_apis = discovery.apis || {};
1269
- diagnostics.available_methods = discovery.methods || {};
1270
- }
1271
-
1272
- var factoryPayload = {
1329
+ var scheduledResult = await createFlowViaScheduledJob(client, {
1273
1330
  name: flowName,
1274
1331
  description: flowDescription,
1275
- type: isSubflow ? 'subflow' : 'flow',
1332
+ internalName: sanitizeInternalName(flowName),
1333
+ isSubflow: isSubflow,
1276
1334
  category: flowCategory,
1277
- run_as: flowRunAs,
1278
- activate: shouldActivate,
1279
- trigger_type: triggerType,
1280
- trigger_table: flowTable,
1281
- trigger_condition: triggerCondition,
1335
+ runAs: flowRunAs,
1336
+ shouldActivate: shouldActivate,
1337
+ triggerType: triggerType,
1338
+ triggerTable: flowTable,
1339
+ triggerCondition: triggerCondition,
1282
1340
  activities: activitiesArg.map(function (act: any, idx: number) {
1283
- return { name: act.name, type: act.type || 'script', inputs: act.inputs || {}, order: (idx + 1) * 100 };
1341
+ return { name: act.name, type: act.type || 'script', inputs: act.inputs || {} };
1284
1342
  }),
1285
1343
  inputs: inputsArg,
1286
1344
  outputs: outputsArg,
1287
- flow_definition: flowDefinition
1345
+ flowDefinition: flowDefinition
1346
+ });
1347
+ diagnostics.scheduled_job = {
1348
+ success: scheduledResult.success,
1349
+ tierUsed: scheduledResult.tierUsed,
1350
+ latestVersionSet: scheduledResult.latestVersionSet,
1351
+ latestVersionValue: scheduledResult.latestVersionValue,
1352
+ steps: scheduledResult.steps,
1353
+ error: scheduledResult.error
1288
1354
  };
1289
-
1290
- var factoryEndpoint = '/api/' + factory.namespace + '/' + FLOW_FACTORY_API_ID + '/create';
1291
- var factoryResp: any;
1292
-
1293
- try {
1294
- factoryResp = await client.post(factoryEndpoint, factoryPayload);
1295
- } catch (callError: any) {
1296
- // 404 = API was deleted externally → invalidate cache, retry once
1297
- if (callError.response?.status === 404) {
1298
- invalidateFlowFactoryCache();
1299
- var retryFactory = await ensureFlowFactoryAPI(client, context.instanceUrl);
1300
- diagnostics.factory_namespace = retryFactory.namespace;
1301
- var retryEndpoint = '/api/' + retryFactory.namespace + '/' + FLOW_FACTORY_API_ID + '/create';
1302
- factoryResp = await client.post(retryEndpoint, factoryPayload);
1303
- } else {
1304
- throw callError;
1305
- }
1306
- }
1307
-
1308
- var factoryResult = factoryResp.data?.result || factoryResp.data;
1309
- if (factoryResult && factoryResult.success && factoryResult.flow_sys_id) {
1310
- var tierUsed = factoryResult.tier_used || 'unknown';
1311
- diagnostics.factory_call = 'success';
1312
- diagnostics.factory_tier = tierUsed;
1313
- diagnostics.factory_steps = factoryResult.steps || {};
1314
- flowSysId = factoryResult.flow_sys_id;
1315
- usedMethod = 'scripted_rest_api';
1316
- versionCreated = !!factoryResult.version_created;
1355
+ if (scheduledResult.success && scheduledResult.flowSysId) {
1356
+ flowSysId = scheduledResult.flowSysId;
1357
+ usedMethod = 'scheduled_job (' + (scheduledResult.tierUsed || 'unknown') + ')';
1358
+ versionCreated = !!scheduledResult.versionSysId;
1317
1359
  diagnostics.version_created = versionCreated;
1318
- diagnostics.version_method = 'factory (' + tierUsed + ')';
1319
- diagnostics.version_fields_set = ['flow', 'name', 'version', 'state', 'active', 'compile_state', 'is_current', 'published_flow'];
1320
-
1321
- // Extract step details
1322
- var steps = factoryResult.steps || {};
1323
- if (steps.trigger) {
1324
- triggerCreated = !!steps.trigger.success;
1325
- if (!steps.trigger.success && steps.trigger.error) {
1326
- factoryWarnings.push('Trigger: ' + steps.trigger.error);
1327
- }
1360
+ diagnostics.version_method = 'scheduled_job';
1361
+ diagnostics.latest_version_auto_set = scheduledResult.latestVersionSet;
1362
+ // Extract engine registration results from scheduled job
1363
+ if (scheduledResult.steps?.engine) {
1364
+ diagnostics.engine_registration = scheduledResult.steps.engine;
1328
1365
  }
1329
- if (steps.actions) {
1330
- actionsCreated = steps.actions.created || 0;
1366
+ // Extract trigger/action/variable results from scheduled job
1367
+ if (scheduledResult.steps?.trigger) {
1368
+ triggerCreated = !!scheduledResult.steps.trigger.success;
1369
+ if (!scheduledResult.steps.trigger.success && scheduledResult.steps.trigger.error) {
1370
+ factoryWarnings.push('Trigger: ' + scheduledResult.steps.trigger.error);
1371
+ }
1331
1372
  }
1332
- if (steps.variables) {
1333
- varsCreated = steps.variables.created || 0;
1373
+ if (scheduledResult.steps?.actions) {
1374
+ actionsCreated = scheduledResult.steps.actions.created || 0;
1334
1375
  }
1335
- if (steps.version && !steps.version.success) {
1336
- factoryWarnings.push('Version record: ' + (steps.version.error || 'creation failed'));
1376
+ if (scheduledResult.steps?.variables) {
1377
+ varsCreated = scheduledResult.steps.variables.created || 0;
1337
1378
  }
1338
1379
  }
1339
- } catch (factoryError: any) {
1340
- // Flow Factory unavailable fall through to Table API
1341
- var statusCode = factoryError.response?.status;
1342
- var factoryErrMsg = statusCode
1343
- ? 'HTTP ' + statusCode + ': ' + (factoryError.response?.data?.error?.message || factoryError.message || 'unknown')
1344
- : (factoryError.message || 'unknown');
1345
- diagnostics.factory_bootstrap = diagnostics.factory_bootstrap || ('error: ' + factoryErrMsg);
1346
- diagnostics.factory_call = diagnostics.factory_call || ('error: ' + factoryErrMsg);
1347
- if (statusCode !== 403) {
1348
- factoryWarnings.push('Flow Factory unavailable (' + factoryErrMsg + '), using Table API fallback');
1349
- }
1350
- }
1351
-
1352
- // ── Scheduled Job fallback (server-side GlideRecord) ────────
1353
- // This runs inside ServiceNow as a system job, so it CAN set
1354
- // computed fields like latest_version that Table API cannot.
1355
- if (!flowSysId) {
1356
- try {
1357
- var scheduledResult = await createFlowViaScheduledJob(client, {
1358
- name: flowName,
1359
- description: flowDescription,
1360
- internalName: sanitizeInternalName(flowName),
1361
- isSubflow: isSubflow,
1362
- category: flowCategory,
1363
- runAs: flowRunAs,
1364
- shouldActivate: shouldActivate,
1365
- triggerType: triggerType,
1366
- triggerTable: flowTable,
1367
- triggerCondition: triggerCondition,
1368
- activities: activitiesArg.map(function (act: any, idx: number) {
1369
- return { name: act.name, type: act.type || 'script', inputs: act.inputs || {} };
1370
- }),
1371
- inputs: inputsArg,
1372
- outputs: outputsArg
1373
- });
1374
- diagnostics.scheduled_job = {
1375
- success: scheduledResult.success,
1376
- tierUsed: scheduledResult.tierUsed,
1377
- latestVersionSet: scheduledResult.latestVersionSet,
1378
- latestVersionValue: scheduledResult.latestVersionValue,
1379
- steps: scheduledResult.steps,
1380
- error: scheduledResult.error
1381
- };
1382
- if (scheduledResult.success && scheduledResult.flowSysId) {
1383
- flowSysId = scheduledResult.flowSysId;
1384
- usedMethod = 'scheduled_job (' + (scheduledResult.tierUsed || 'unknown') + ')';
1385
- versionCreated = !!scheduledResult.versionSysId;
1386
- diagnostics.version_created = versionCreated;
1387
- diagnostics.version_method = 'scheduled_job';
1388
- diagnostics.latest_version_auto_set = scheduledResult.latestVersionSet;
1389
- // Extract trigger/action/variable results from scheduled job
1390
- if (scheduledResult.steps?.trigger) {
1391
- triggerCreated = !!scheduledResult.steps.trigger.success;
1392
- if (!scheduledResult.steps.trigger.success && scheduledResult.steps.trigger.error) {
1393
- factoryWarnings.push('Trigger: ' + scheduledResult.steps.trigger.error);
1394
- }
1395
- }
1396
- if (scheduledResult.steps?.actions) {
1397
- actionsCreated = scheduledResult.steps.actions.created || 0;
1398
- }
1399
- if (scheduledResult.steps?.variables) {
1400
- varsCreated = scheduledResult.steps.variables.created || 0;
1401
- }
1402
- }
1403
- } catch (schedErr: any) {
1404
- diagnostics.scheduled_job = { error: schedErr.message || 'unknown' };
1405
- factoryWarnings.push('Scheduled job fallback failed: ' + (schedErr.message || schedErr));
1406
- }
1380
+ } catch (schedErr: any) {
1381
+ diagnostics.scheduled_job = { error: schedErr.message || 'unknown' };
1382
+ factoryWarnings.push('Scheduled job failed: ' + (schedErr.message || schedErr));
1407
1383
  }
1408
1384
 
1409
1385
  // ── Table API fallback (last resort) ─────────────────────────
@@ -1500,39 +1476,53 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1500
1476
  }
1501
1477
 
1502
1478
  // Create trigger instance (non-manual flows only)
1479
+ // Search broadly — internal_name varies across ServiceNow instances
1503
1480
  if (!isSubflow && triggerType !== 'manual') {
1504
1481
  try {
1505
- var triggerTypeLookup: Record<string, string> = {
1506
- 'record_created': 'sn_fd.trigger.record_created',
1507
- 'record_updated': 'sn_fd.trigger.record_updated',
1508
- 'scheduled': 'sn_fd.trigger.scheduled'
1482
+ var trigSearchTerms: Record<string, string[]> = {
1483
+ 'record_created': ['record_created', 'record_insert', 'created'],
1484
+ 'record_updated': ['record_updated', 'record_update', 'updated'],
1485
+ 'scheduled': ['scheduled', 'timer', 'schedule']
1509
1486
  };
1510
- var triggerInternalName = triggerTypeLookup[triggerType] || '';
1487
+ var searchTerms = trigSearchTerms[triggerType] || [triggerType];
1488
+ var triggerDefId: string | null = null;
1511
1489
 
1512
- if (triggerInternalName) {
1490
+ for (var tsi = 0; tsi < searchTerms.length && !triggerDefId; tsi++) {
1513
1491
  var triggerDefResp = await client.get('/api/now/table/sys_hub_action_type_definition', {
1514
1492
  params: {
1515
- sysparm_query: 'internal_name=' + triggerInternalName,
1493
+ sysparm_query: 'internal_nameLIKE' + searchTerms[tsi] + '^type=trigger',
1516
1494
  sysparm_fields: 'sys_id',
1517
1495
  sysparm_limit: 1
1518
1496
  }
1519
1497
  });
1498
+ triggerDefId = triggerDefResp.data.result?.[0]?.sys_id || null;
1499
+ }
1520
1500
 
1521
- var triggerDefId = triggerDefResp.data.result?.[0]?.sys_id;
1522
- if (triggerDefId) {
1523
- var triggerData: any = {
1524
- flow: flowSysId,
1525
- action_type: triggerDefId,
1526
- name: triggerType,
1527
- order: 0,
1528
- active: true
1529
- };
1530
- if (flowTable) triggerData.table = flowTable;
1531
- if (triggerCondition) triggerData.condition = triggerCondition;
1532
-
1533
- await client.post('/api/now/table/sys_hub_trigger_instance', triggerData);
1534
- triggerCreated = true;
1535
- }
1501
+ // Fallback: search by display name
1502
+ if (!triggerDefId) {
1503
+ var trigNameResp = await client.get('/api/now/table/sys_hub_action_type_definition', {
1504
+ params: {
1505
+ sysparm_query: 'nameLIKE' + triggerType.replace(/_/g, ' ') + '^type=trigger',
1506
+ sysparm_fields: 'sys_id',
1507
+ sysparm_limit: 1
1508
+ }
1509
+ });
1510
+ triggerDefId = trigNameResp.data.result?.[0]?.sys_id || null;
1511
+ }
1512
+
1513
+ if (triggerDefId) {
1514
+ var triggerData: any = {
1515
+ flow: flowSysId,
1516
+ action_type: triggerDefId,
1517
+ name: triggerType,
1518
+ order: 0,
1519
+ active: true
1520
+ };
1521
+ if (flowTable) triggerData.table = flowTable;
1522
+ if (triggerCondition) triggerData.condition = triggerCondition;
1523
+
1524
+ await client.post('/api/now/table/sys_hub_trigger_instance', triggerData);
1525
+ triggerCreated = true;
1536
1526
  }
1537
1527
  } catch (triggerError) {
1538
1528
  // Best-effort
@@ -1604,10 +1594,11 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1604
1594
 
1605
1595
  }
1606
1596
 
1607
- // ── Register flow with Flow Designer engine ─────────────────
1608
- // This is the KEY step: without it, records exist but Flow Designer
1609
- // shows "Your flow cannot be found" because the engine hasn't compiled it.
1610
- if (flowSysId) {
1597
+ // ── Register flow with Flow Designer engine (Table API only) ──
1598
+ // For scheduled job path, engine registration is done server-side
1599
+ // inside the job script (via sn_fd APIs). Only call the external
1600
+ // REST-based registration for the Table API fallback path.
1601
+ if (flowSysId && usedMethod.startsWith('table_api')) {
1611
1602
  var engineResult = await registerFlowWithEngine(client, flowSysId, shouldActivate);
1612
1603
  diagnostics.engine_registration = {
1613
1604
  success: engineResult.success,
@@ -1722,29 +1713,38 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1722
1713
 
1723
1714
  // Diagnostics section
1724
1715
  createSummary.blank().line('Diagnostics:');
1725
- if (diagnostics.instance_build) {
1726
- createSummary.indented('Instance build: ' + diagnostics.instance_build);
1727
- }
1728
- createSummary.indented('Factory bootstrap: ' + (diagnostics.factory_bootstrap || 'not attempted'));
1729
- createSummary.indented('Factory namespace: ' + (diagnostics.factory_namespace || 'n/a'));
1730
- createSummary.indented('Factory call: ' + (diagnostics.factory_call || 'not attempted'));
1731
- if (diagnostics.factory_tier) {
1732
- createSummary.indented('Factory tier used: ' + diagnostics.factory_tier);
1733
- }
1734
- if (diagnostics.available_apis && Object.keys(diagnostics.available_apis).length > 0) {
1735
- var apiStr = Object.keys(diagnostics.available_apis).map(function (k: string) { return k + '=' + diagnostics.available_apis[k]; }).join(', ');
1736
- createSummary.indented('Available APIs: ' + apiStr);
1716
+ if (diagnostics.scheduled_job) {
1717
+ var sj = diagnostics.scheduled_job;
1718
+ createSummary.indented('Scheduled job: ' + (sj.success ? 'success' : 'failed') + (sj.tierUsed ? ' (' + sj.tierUsed + ')' : ''));
1719
+ if (sj.error) createSummary.indented(' Error: ' + sj.error);
1737
1720
  }
1738
1721
  createSummary.indented('Table API used: ' + diagnostics.table_api_used);
1739
1722
  createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
1740
1723
  if (diagnostics.engine_registration) {
1741
- createSummary.indented('Engine registration: ' + (diagnostics.engine_registration.success ? diagnostics.engine_registration.method : 'FAILED'));
1742
- if (diagnostics.engine_registration.attempts) {
1743
- for (var ea = 0; ea < diagnostics.engine_registration.attempts.length; ea++) {
1744
- createSummary.indented(' ' + diagnostics.engine_registration.attempts[ea]);
1724
+ var eng = diagnostics.engine_registration;
1725
+ if (eng.sn_fd) {
1726
+ // Server-side engine registration (from scheduled job)
1727
+ var engineLabel = 'sn_fd=' + eng.sn_fd;
1728
+ if (eng.apis_found && eng.apis_found.length > 0) {
1729
+ engineLabel += ', APIs=[' + eng.apis_found.join(', ') + ']';
1730
+ }
1731
+ if (eng.publish) engineLabel += ', publishFlow=' + eng.publish;
1732
+ if (eng.compile) engineLabel += ', compile=' + eng.compile;
1733
+ if (eng.error) engineLabel += ', error=' + eng.error;
1734
+ createSummary.indented('Engine (server-side): ' + engineLabel);
1735
+ } else if (eng.success !== undefined) {
1736
+ // REST-based engine registration (Table API path)
1737
+ createSummary.indented('Engine registration: ' + (eng.success ? eng.method : 'FAILED'));
1738
+ if (eng.attempts) {
1739
+ for (var ea = 0; ea < eng.attempts.length; ea++) {
1740
+ createSummary.indented(' ' + eng.attempts[ea]);
1741
+ }
1745
1742
  }
1746
1743
  }
1747
1744
  }
1745
+ if (diagnostics.latest_version_auto_set !== undefined) {
1746
+ createSummary.indented('latest_version: ' + (diagnostics.latest_version_auto_set ? 'set' : 'null'));
1747
+ }
1748
1748
  if (diagnostics.post_verify) {
1749
1749
  if (diagnostics.post_verify.error) {
1750
1750
  createSummary.indented('Post-verify: error — ' + diagnostics.post_verify.error);