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

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.403",
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) {",
@@ -216,10 +223,57 @@ async function createFlowViaScheduledJob(
216
223
  " }",
217
224
  " }",
218
225
  " r.steps.variables = { success: true, created: varsCreated };",
226
+ "",
227
+ // ── Engine registration (server-side sn_fd APIs) ──
228
+ " r.steps.engine = { apis_found: [] };",
229
+ " try {",
230
+ " if (typeof sn_fd !== 'undefined') {",
231
+ " r.steps.engine.sn_fd = 'available';",
232
+ " if (sn_fd.FlowDesigner) {",
233
+ " r.steps.engine.apis_found.push('FlowDesigner');",
234
+ " if (typeof sn_fd.FlowDesigner.publishFlow === 'function') {",
235
+ " try {",
236
+ " sn_fd.FlowDesigner.publishFlow(flowSysId);",
237
+ " r.steps.engine.publish = 'success';",
238
+ " } catch(pe) { r.steps.engine.publish = pe.getMessage ? pe.getMessage() : pe + ''; }",
239
+ " }",
240
+ " }",
241
+ " if (sn_fd.FlowCompiler) {",
242
+ " r.steps.engine.apis_found.push('FlowCompiler');",
243
+ " if (typeof sn_fd.FlowCompiler.compile === 'function') {",
244
+ " try {",
245
+ " sn_fd.FlowCompiler.compile(flowSysId);",
246
+ " r.steps.engine.compile = 'success';",
247
+ " } catch(ce) { r.steps.engine.compile = ce.getMessage ? ce.getMessage() : ce + ''; }",
248
+ " }",
249
+ " }",
250
+ " var otherApis = ['FlowAPI', 'FlowPublisher'];",
251
+ " for (var oa = 0; oa < otherApis.length; oa++) {",
252
+ " if (sn_fd[otherApis[oa]]) {",
253
+ " r.steps.engine.apis_found.push(otherApis[oa]);",
254
+ " var apiObj = sn_fd[otherApis[oa]];",
255
+ " if (typeof apiObj.publish === 'function') {",
256
+ " try { apiObj.publish(flowSysId); r.steps.engine[otherApis[oa] + '_publish'] = 'success'; }",
257
+ " catch(e) { r.steps.engine[otherApis[oa] + '_publish'] = e + ''; }",
258
+ " }",
259
+ " }",
260
+ " }",
261
+ " } else {",
262
+ " r.steps.engine.sn_fd = 'unavailable';",
263
+ " }",
264
+ " try {",
265
+ " if (typeof GlideFlowDesigner !== 'undefined') {",
266
+ " r.steps.engine.apis_found.push('GlideFlowDesigner');",
267
+ " }",
268
+ " } catch(e) {}",
269
+ " } catch(engineErr) {",
270
+ " r.steps.engine.error = engineErr.getMessage ? engineErr.getMessage() : engineErr + '';",
271
+ " }",
219
272
  " }",
220
273
  "",
221
274
  " r.flow_sys_id = flowSysId ? flowSysId + '' : null;",
222
275
  " r.version_sys_id = verSysId ? verSysId + '' : null;",
276
+ // ── Post-engine check: re-read latest_version ──
223
277
  " if (flowSysId) {",
224
278
  " var cf = new GlideRecord('sys_hub_flow');",
225
279
  " if (cf.get(flowSysId)) {",
@@ -252,9 +306,10 @@ async function createFlowViaScheduledJob(
252
306
  });
253
307
  var jobSysId = jobResp.data.result?.sys_id;
254
308
 
255
- // 3. Poll for result (15 attempts * 2s = 30s max)
309
+ // 3. Poll for result (adaptive: 5×1s + 2s + 5×3s = 30s max)
256
310
  for (var i = 0; i < 15; i++) {
257
- await new Promise(resolve => setTimeout(resolve, 2000));
311
+ var delay = i < 5 ? 1000 : i < 10 ? 2000 : 3000;
312
+ await new Promise(resolve => setTimeout(resolve, delay));
258
313
  var propResp = await client.get('/api/now/table/sys_properties', {
259
314
  params: {
260
315
  sysparm_query: 'name=' + resultPropName,
@@ -1234,7 +1289,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1234
1289
  delete flowDefinition.trigger;
1235
1290
  }
1236
1291
 
1237
- // ── Try Scripted REST API (Flow Factory) first ──────────────
1292
+ // ── Pipeline: Scheduled Job (primary) → Table API (fallback) ──
1238
1293
  var flowSysId: string | null = null;
1239
1294
  var usedMethod = 'table_api';
1240
1295
  var versionCreated = false;
@@ -1245,7 +1300,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1245
1300
 
1246
1301
  // Diagnostics: track every step for debugging "flow cannot be found" issues
1247
1302
  var diagnostics: any = {
1248
- factory_bootstrap: null,
1303
+ factory_bootstrap: 'skipped (direct scheduled job)',
1249
1304
  factory_namespace: null,
1250
1305
  factory_call: null,
1251
1306
  table_api_used: false,
@@ -1256,154 +1311,65 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1256
1311
  post_verify: null
1257
1312
  };
1258
1313
 
1314
+ // ── Scheduled Job (primary — server-side GlideRecord) ───────
1315
+ // This runs inside ServiceNow as a system job, so it CAN set
1316
+ // computed fields like latest_version that Table API cannot.
1317
+ // Also attempts sn_fd engine registration server-side.
1259
1318
  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 = {
1319
+ var scheduledResult = await createFlowViaScheduledJob(client, {
1273
1320
  name: flowName,
1274
1321
  description: flowDescription,
1275
- type: isSubflow ? 'subflow' : 'flow',
1322
+ internalName: sanitizeInternalName(flowName),
1323
+ isSubflow: isSubflow,
1276
1324
  category: flowCategory,
1277
- run_as: flowRunAs,
1278
- activate: shouldActivate,
1279
- trigger_type: triggerType,
1280
- trigger_table: flowTable,
1281
- trigger_condition: triggerCondition,
1325
+ runAs: flowRunAs,
1326
+ shouldActivate: shouldActivate,
1327
+ triggerType: triggerType,
1328
+ triggerTable: flowTable,
1329
+ triggerCondition: triggerCondition,
1282
1330
  activities: activitiesArg.map(function (act: any, idx: number) {
1283
- return { name: act.name, type: act.type || 'script', inputs: act.inputs || {}, order: (idx + 1) * 100 };
1331
+ return { name: act.name, type: act.type || 'script', inputs: act.inputs || {} };
1284
1332
  }),
1285
1333
  inputs: inputsArg,
1286
1334
  outputs: outputsArg,
1287
- flow_definition: flowDefinition
1335
+ flowDefinition: flowDefinition
1336
+ });
1337
+ diagnostics.scheduled_job = {
1338
+ success: scheduledResult.success,
1339
+ tierUsed: scheduledResult.tierUsed,
1340
+ latestVersionSet: scheduledResult.latestVersionSet,
1341
+ latestVersionValue: scheduledResult.latestVersionValue,
1342
+ steps: scheduledResult.steps,
1343
+ error: scheduledResult.error
1288
1344
  };
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;
1345
+ if (scheduledResult.success && scheduledResult.flowSysId) {
1346
+ flowSysId = scheduledResult.flowSysId;
1347
+ usedMethod = 'scheduled_job (' + (scheduledResult.tierUsed || 'unknown') + ')';
1348
+ versionCreated = !!scheduledResult.versionSysId;
1317
1349
  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
- }
1328
- }
1329
- if (steps.actions) {
1330
- actionsCreated = steps.actions.created || 0;
1350
+ diagnostics.version_method = 'scheduled_job';
1351
+ diagnostics.latest_version_auto_set = scheduledResult.latestVersionSet;
1352
+ // Extract engine registration results from scheduled job
1353
+ if (scheduledResult.steps?.engine) {
1354
+ diagnostics.engine_registration = scheduledResult.steps.engine;
1331
1355
  }
1332
- if (steps.variables) {
1333
- varsCreated = steps.variables.created || 0;
1356
+ // Extract trigger/action/variable results from scheduled job
1357
+ if (scheduledResult.steps?.trigger) {
1358
+ triggerCreated = !!scheduledResult.steps.trigger.success;
1359
+ if (!scheduledResult.steps.trigger.success && scheduledResult.steps.trigger.error) {
1360
+ factoryWarnings.push('Trigger: ' + scheduledResult.steps.trigger.error);
1361
+ }
1334
1362
  }
1335
- if (steps.version && !steps.version.success) {
1336
- factoryWarnings.push('Version record: ' + (steps.version.error || 'creation failed'));
1363
+ if (scheduledResult.steps?.actions) {
1364
+ actionsCreated = scheduledResult.steps.actions.created || 0;
1337
1365
  }
1338
- }
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
- }
1366
+ if (scheduledResult.steps?.variables) {
1367
+ varsCreated = scheduledResult.steps.variables.created || 0;
1402
1368
  }
1403
- } catch (schedErr: any) {
1404
- diagnostics.scheduled_job = { error: schedErr.message || 'unknown' };
1405
- factoryWarnings.push('Scheduled job fallback failed: ' + (schedErr.message || schedErr));
1406
1369
  }
1370
+ } catch (schedErr: any) {
1371
+ diagnostics.scheduled_job = { error: schedErr.message || 'unknown' };
1372
+ factoryWarnings.push('Scheduled job failed: ' + (schedErr.message || schedErr));
1407
1373
  }
1408
1374
 
1409
1375
  // ── Table API fallback (last resort) ─────────────────────────
@@ -1604,10 +1570,11 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1604
1570
 
1605
1571
  }
1606
1572
 
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) {
1573
+ // ── Register flow with Flow Designer engine (Table API only) ──
1574
+ // For scheduled job path, engine registration is done server-side
1575
+ // inside the job script (via sn_fd APIs). Only call the external
1576
+ // REST-based registration for the Table API fallback path.
1577
+ if (flowSysId && usedMethod.startsWith('table_api')) {
1611
1578
  var engineResult = await registerFlowWithEngine(client, flowSysId, shouldActivate);
1612
1579
  diagnostics.engine_registration = {
1613
1580
  success: engineResult.success,
@@ -1722,29 +1689,38 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1722
1689
 
1723
1690
  // Diagnostics section
1724
1691
  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);
1692
+ if (diagnostics.scheduled_job) {
1693
+ var sj = diagnostics.scheduled_job;
1694
+ createSummary.indented('Scheduled job: ' + (sj.success ? 'success' : 'failed') + (sj.tierUsed ? ' (' + sj.tierUsed + ')' : ''));
1695
+ if (sj.error) createSummary.indented(' Error: ' + sj.error);
1737
1696
  }
1738
1697
  createSummary.indented('Table API used: ' + diagnostics.table_api_used);
1739
1698
  createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
1740
1699
  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]);
1700
+ var eng = diagnostics.engine_registration;
1701
+ if (eng.sn_fd) {
1702
+ // Server-side engine registration (from scheduled job)
1703
+ var engineLabel = 'sn_fd=' + eng.sn_fd;
1704
+ if (eng.apis_found && eng.apis_found.length > 0) {
1705
+ engineLabel += ', APIs=[' + eng.apis_found.join(', ') + ']';
1706
+ }
1707
+ if (eng.publish) engineLabel += ', publishFlow=' + eng.publish;
1708
+ if (eng.compile) engineLabel += ', compile=' + eng.compile;
1709
+ if (eng.error) engineLabel += ', error=' + eng.error;
1710
+ createSummary.indented('Engine (server-side): ' + engineLabel);
1711
+ } else if (eng.success !== undefined) {
1712
+ // REST-based engine registration (Table API path)
1713
+ createSummary.indented('Engine registration: ' + (eng.success ? eng.method : 'FAILED'));
1714
+ if (eng.attempts) {
1715
+ for (var ea = 0; ea < eng.attempts.length; ea++) {
1716
+ createSummary.indented(' ' + eng.attempts[ea]);
1717
+ }
1745
1718
  }
1746
1719
  }
1747
1720
  }
1721
+ if (diagnostics.latest_version_auto_set !== undefined) {
1722
+ createSummary.indented('latest_version: ' + (diagnostics.latest_version_auto_set ? 'set' : 'null'));
1723
+ }
1748
1724
  if (diagnostics.post_verify) {
1749
1725
  if (diagnostics.post_verify.error) {
1750
1726
  createSummary.indented('Post-verify: error — ' + diagnostics.post_verify.error);