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

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.109",
3
+ "version": "10.0.1-test.111",
4
4
  "name": "snow-flow-test",
5
5
  "description": "Snow-Flow Test - ServiceNow Multi-Agent Development Framework",
6
6
  "license": "Elastic-2.0",
@@ -35,7 +35,6 @@ function isSysId(value: string): boolean {
35
35
 
36
36
  var FLOW_FACTORY_API_NAME = 'Snow-Flow Flow Factory';
37
37
  var FLOW_FACTORY_API_ID = 'flow_factory';
38
- var FLOW_FACTORY_NAMESPACE = 'x_snflw';
39
38
  var FLOW_FACTORY_CACHE_TTL = 300000; // 5 minutes
40
39
 
41
40
  var _flowFactoryCache: { apiSysId: string; namespace: string; timestamp: number } | null = null;
@@ -48,8 +47,29 @@ var _bootstrapPromise: Promise<{ namespace: string; apiSysId: string }> | null =
48
47
  */
49
48
  var FLOW_FACTORY_SCRIPT = [
50
49
  '(function process(/*RESTAPIRequest*/ request, /*RESTAPIResponse*/ response) {',
51
- ' var body = request.body.data;',
52
- ' var result = { success: false, steps: {} };',
50
+ ' var body = null;',
51
+ ' var parseLog = [];',
52
+ ' try {',
53
+ ' var ds = request.body.dataString;',
54
+ ' if (ds) { body = JSON.parse(ds + ""); parseLog.push("dataString:ok"); }',
55
+ ' } catch(e1) { parseLog.push("dataString:" + e1); }',
56
+ ' if (!body) {',
57
+ ' try {',
58
+ ' var d = request.body.data;',
59
+ ' if (d && typeof d === "object") { body = d; parseLog.push("data:ok"); }',
60
+ ' else if (d) { body = JSON.parse(d + ""); parseLog.push("data-parse:ok"); }',
61
+ ' } catch(e2) { parseLog.push("data:" + e2); }',
62
+ ' }',
63
+ ' if (!body) {',
64
+ ' try { body = JSON.parse(request.body + ""); parseLog.push("body-direct:ok"); }',
65
+ ' catch(e3) { parseLog.push("body-direct:" + e3); }',
66
+ ' }',
67
+ ' if (!body || typeof body !== "object") {',
68
+ ' response.setStatus(400);',
69
+ ' response.setBody({ success: false, error: "No parseable body", parseLog: parseLog, bodyType: typeof request.body });',
70
+ ' return;',
71
+ ' }',
72
+ ' var result = { success: false, steps: {}, parseLog: parseLog };',
53
73
  '',
54
74
  ' try {',
55
75
  ' var flowName = body.name || "Unnamed Flow";',
@@ -101,6 +121,9 @@ var FLOW_FACTORY_SCRIPT = [
101
121
  ' ver.setValue("version", "1.0");',
102
122
  ' ver.setValue("state", shouldActivate ? "published" : "draft");',
103
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);',
104
127
  ' if (body.flow_definition) {',
105
128
  ' ver.setValue("flow_definition", typeof body.flow_definition === "string" ? body.flow_definition : JSON.stringify(body.flow_definition));',
106
129
  ' }',
@@ -229,12 +252,97 @@ var FLOW_FACTORY_SCRIPT = [
229
252
  '})(request, response);'
230
253
  ].join('\n');
231
254
 
255
+ /**
256
+ * Probe the ServiceNow instance to discover the correct URL namespace for
257
+ * the Flow Factory Scripted REST API. Sends GET requests to candidate
258
+ * namespaces — a 405 (Method Not Allowed) or 401/403 confirms the namespace
259
+ * exists, while 404 means wrong namespace.
260
+ */
261
+ async function probeFlowFactoryNamespace(
262
+ client: any,
263
+ apiSysId: string,
264
+ instanceUrl: string
265
+ ): Promise<string | null> {
266
+ // Build candidate list from multiple sources
267
+ var candidates: string[] = [];
268
+
269
+ // 1. Read back the record's namespace field
270
+ try {
271
+ var nsResp = await client.get('/api/now/table/sys_ws_definition/' + apiSysId, {
272
+ params: { sysparm_fields: 'sys_id,api_id,namespace', sysparm_display_value: 'all' }
273
+ });
274
+ var record = nsResp.data.result || {};
275
+ var ns = record.namespace;
276
+ if (ns) {
277
+ if (typeof ns === 'object') {
278
+ var dv = ns.display_value || '';
279
+ var val = ns.value || '';
280
+ if (dv && /^(x_|sn_)/.test(dv)) candidates.push(dv);
281
+ if (val && /^(x_|sn_)/.test(val)) candidates.push(val);
282
+ } else if (typeof ns === 'string' && ns.length > 0 && ns !== 'Global' && ns !== 'global') {
283
+ candidates.push(ns);
284
+ }
285
+ }
286
+ } catch (_) {}
287
+
288
+ // 2. Company code from sys_properties
289
+ try {
290
+ var compResp = await client.get('/api/now/table/sys_properties', {
291
+ params: {
292
+ sysparm_query: 'name=glide.appcreator.company.code',
293
+ sysparm_fields: 'value',
294
+ sysparm_limit: 1
295
+ }
296
+ });
297
+ var companyCode = compResp.data.result?.[0]?.value;
298
+ if (companyCode) candidates.push(companyCode);
299
+ } catch (_) {}
300
+
301
+ // 3. Instance subdomain (e.g. "dev351277" from "https://dev351277.service-now.com")
302
+ try {
303
+ var match = instanceUrl.match(/https?:\/\/([^.]+)\./);
304
+ if (match && match[1]) candidates.push(match[1]);
305
+ } catch (_) {}
306
+
307
+ // 4. Fixed fallbacks
308
+ candidates.push('now', 'global');
309
+
310
+ // Deduplicate
311
+ var seen: Record<string, boolean> = {};
312
+ var unique: string[] = [];
313
+ for (var i = 0; i < candidates.length; i++) {
314
+ if (!seen[candidates[i]]) {
315
+ seen[candidates[i]] = true;
316
+ unique.push(candidates[i]);
317
+ }
318
+ }
319
+
320
+ // 5. Probe each candidate — GET on a POST-only endpoint
321
+ for (var j = 0; j < unique.length; j++) {
322
+ try {
323
+ 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) {
328
+ return unique[j]; // Namespace correct — method or auth rejected
329
+ }
330
+ // 404 = wrong namespace, try next
331
+ }
332
+ }
333
+
334
+ return null; // No namespace matched — factory unreachable
335
+ }
336
+
232
337
  /**
233
338
  * Ensure the Flow Factory Scripted REST API exists on the ServiceNow instance.
234
339
  * Idempotent — checks cache first, then instance, deploys only if missing.
235
- * Uses a concurrency lock to prevent duplicate bootstrap calls.
340
+ * Uses namespace probing (HTTP GET) instead of record field parsing.
236
341
  */
237
- async function ensureFlowFactoryAPI(client: any): Promise<{ namespace: string; apiSysId: string }> {
342
+ async function ensureFlowFactoryAPI(
343
+ client: any,
344
+ instanceUrl: string
345
+ ): Promise<{ namespace: string; apiSysId: string }> {
238
346
  // 1. Check in-memory cache
239
347
  if (_flowFactoryCache && (Date.now() - _flowFactoryCache.timestamp) < FLOW_FACTORY_CACHE_TTL) {
240
348
  return { namespace: _flowFactoryCache.namespace, apiSysId: _flowFactoryCache.apiSysId };
@@ -258,7 +366,10 @@ async function ensureFlowFactoryAPI(client: any): Promise<{ namespace: string; a
258
366
 
259
367
  if (checkResp.data.result && checkResp.data.result.length > 0) {
260
368
  var existing = checkResp.data.result[0];
261
- var ns = resolveNamespaceFromRecord(existing);
369
+ var ns = await probeFlowFactoryNamespace(client, existing.sys_id, instanceUrl);
370
+ if (!ns) {
371
+ throw new Error('Flow Factory API exists (sys_id=' + existing.sys_id + ') but namespace could not be resolved via HTTP probing');
372
+ }
262
373
  _flowFactoryCache = { apiSysId: existing.sys_id, namespace: ns, timestamp: Date.now() };
263
374
  return { namespace: ns, apiSysId: existing.sys_id };
264
375
  }
@@ -270,7 +381,7 @@ async function ensureFlowFactoryAPI(client: any): Promise<{ namespace: string; a
270
381
  active: true,
271
382
  short_description: 'Bootstrapped by Snow-Flow MCP for reliable Flow Designer creation via GlideRecord',
272
383
  is_versioned: false,
273
- enforce_acl: 'no',
384
+ enforce_acl: 'false',
274
385
  requires_authentication: true
275
386
  });
276
387
 
@@ -290,7 +401,7 @@ async function ensureFlowFactoryAPI(client: any): Promise<{ namespace: string; a
290
401
  short_description: 'Create a flow or subflow with GlideRecord (triggers all BRs + version record)',
291
402
  operation_script: FLOW_FACTORY_SCRIPT,
292
403
  requires_authentication: true,
293
- enforce_acl: 'no'
404
+ enforce_acl: 'false'
294
405
  });
295
406
  } catch (opError: any) {
296
407
  // Cleanup the API definition if operation creation fails
@@ -298,11 +409,11 @@ async function ensureFlowFactoryAPI(client: any): Promise<{ namespace: string; a
298
409
  throw new Error('Failed to create Scripted REST operation: ' + (opError.message || opError));
299
410
  }
300
411
 
301
- // 6. Read back the actual namespace ServiceNow assigned
302
- var nsResp = await client.get('/api/now/table/sys_ws_definition/' + apiSysId, {
303
- params: { sysparm_fields: 'sys_id,api_id,namespace', sysparm_display_value: 'all' }
304
- });
305
- var resolvedNs = resolveNamespaceFromRecord(nsResp.data.result || {});
412
+ // 6. Probe to discover the namespace ServiceNow assigned
413
+ var resolvedNs = await probeFlowFactoryNamespace(client, apiSysId, instanceUrl);
414
+ if (!resolvedNs) {
415
+ throw new Error('Flow Factory API created (sys_id=' + apiSysId + ') but namespace could not be resolved via HTTP probing');
416
+ }
306
417
 
307
418
  _flowFactoryCache = { apiSysId: apiSysId, namespace: resolvedNs, timestamp: Date.now() };
308
419
  return { namespace: resolvedNs, apiSysId: apiSysId };
@@ -316,33 +427,92 @@ async function ensureFlowFactoryAPI(client: any): Promise<{ namespace: string; a
316
427
  }
317
428
 
318
429
  /**
319
- * Extract the URL namespace from a sys_ws_definition record.
320
- * The namespace field can be a string, a reference object, or empty.
321
- * For global scope APIs the namespace is typically the instance company prefix or 'now'.
430
+ * Invalidate the Flow Factory cache (e.g. on 404 when API was deleted externally).
322
431
  */
323
- function resolveNamespaceFromRecord(record: any): string {
324
- var ns = record.namespace;
325
- if (!ns) return FLOW_FACTORY_NAMESPACE;
326
-
327
- // display_value / value pair (sysparm_display_value=all)
328
- if (typeof ns === 'object') {
329
- // display_value is the scope name like "Global" or "x_snflw" — use value for sys_id, display_value for name
330
- var dv = ns.display_value || '';
331
- // If display_value looks like a scope namespace (x_something, sn_something, now, global), use it
332
- if (dv && dv !== 'Global' && dv !== 'global') return dv;
333
- // For Global scope, fall through to default
334
- }
335
- if (typeof ns === 'string' && ns.length > 0 && ns.length < 100) {
336
- return ns;
337
- }
338
- return FLOW_FACTORY_NAMESPACE;
432
+ function invalidateFlowFactoryCache(): void {
433
+ _flowFactoryCache = null;
339
434
  }
340
435
 
341
436
  /**
342
- * Invalidate the Flow Factory cache (e.g. on 404 when API was deleted externally).
437
+ * After creating a flow (via any method), try to register it with the
438
+ * Flow Designer engine by calling its built-in compile / publish / activate
439
+ * REST endpoints. Without this step the flow record exists in the DB but
440
+ * Flow Designer cannot open it ("Your flow cannot be found").
441
+ *
442
+ * Tries multiple known endpoint patterns because the namespace/path changed
443
+ * across ServiceNow releases. Returns diagnostics on which attempts were
444
+ * made and what succeeded.
343
445
  */
344
- function invalidateFlowFactoryCache(): void {
345
- _flowFactoryCache = null;
446
+ async function registerFlowWithEngine(
447
+ client: any,
448
+ flowSysId: string,
449
+ shouldActivate: boolean
450
+ ): Promise<{ success: boolean; method: string; attempts: string[] }> {
451
+ var attempts: string[] = [];
452
+
453
+ // Helper: try a POST and classify the result
454
+ async function tryPost(label: string, url: string, body?: any): Promise<boolean> {
455
+ try {
456
+ await client.post(url, body || {});
457
+ attempts.push(label + ': success');
458
+ return true;
459
+ } catch (e: any) {
460
+ var s = e.response?.status || 'err';
461
+ attempts.push(label + ': ' + s);
462
+ return false;
463
+ }
464
+ }
465
+
466
+ // ── Strategy 1: Publish via Flow Designer REST API ───────────────
467
+ // This is the closest equivalent to clicking "Publish" in the UI.
468
+ var publishPaths = [
469
+ '/api/sn_fd/flow/' + flowSysId + '/publish',
470
+ '/api/sn_fd/designer/flow/' + flowSysId + '/publish',
471
+ '/api/sn_flow_designer/flow/' + flowSysId + '/publish',
472
+ ];
473
+ for (var pi = 0; pi < publishPaths.length; pi++) {
474
+ if (await tryPost('publish[' + pi + ']', publishPaths[pi])) {
475
+ return { success: true, method: 'publish', attempts: attempts };
476
+ }
477
+ }
478
+
479
+ // ── Strategy 2: Activate (registers the flow with the engine) ────
480
+ if (shouldActivate) {
481
+ var activatePaths = [
482
+ '/api/sn_fd/flow/' + flowSysId + '/activate',
483
+ '/api/sn_fd/designer/flow/' + flowSysId + '/activate',
484
+ '/api/sn_flow_designer/flow/' + flowSysId + '/activate',
485
+ ];
486
+ for (var ai = 0; ai < activatePaths.length; ai++) {
487
+ if (await tryPost('activate[' + ai + ']', activatePaths[ai])) {
488
+ return { success: true, method: 'activate', attempts: attempts };
489
+ }
490
+ }
491
+ }
492
+
493
+ // ── Strategy 3: Checkout + checkin (triggers internal compilation) ─
494
+ var checkoutPaths = [
495
+ '/api/sn_fd/flow/' + flowSysId + '/checkout',
496
+ '/api/sn_fd/designer/flow/' + flowSysId + '/checkout',
497
+ ];
498
+ for (var ci = 0; ci < checkoutPaths.length; ci++) {
499
+ if (await tryPost('checkout[' + ci + ']', checkoutPaths[ci])) {
500
+ var checkinPath = checkoutPaths[ci].replace('/checkout', '/checkin');
501
+ await tryPost('checkin[' + ci + ']', checkinPath);
502
+ return { success: true, method: 'checkout+checkin', attempts: attempts };
503
+ }
504
+ }
505
+
506
+ // ── Strategy 4: Snapshot (already existed — triggers version snapshot) ─
507
+ if (await tryPost('snapshot', '/api/sn_flow_designer/flow/snapshot', { flow_id: flowSysId })) {
508
+ return { success: true, method: 'snapshot', attempts: attempts };
509
+ }
510
+ // Try alternate snapshot path
511
+ if (await tryPost('snapshot-alt', '/api/sn_fd/flow/' + flowSysId + '/snapshot')) {
512
+ return { success: true, method: 'snapshot-alt', attempts: attempts };
513
+ }
514
+
515
+ return { success: false, method: 'none', attempts: attempts };
346
516
  }
347
517
 
348
518
  /**
@@ -592,8 +762,23 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
592
762
  var actionsCreated = 0;
593
763
  var varsCreated = 0;
594
764
 
765
+ // Diagnostics: track every step for debugging "flow cannot be found" issues
766
+ var diagnostics: any = {
767
+ factory_bootstrap: null,
768
+ factory_namespace: null,
769
+ factory_call: null,
770
+ table_api_used: false,
771
+ version_created: false,
772
+ version_method: null,
773
+ version_fields_set: [] as string[],
774
+ engine_registration: null,
775
+ post_verify: null
776
+ };
777
+
595
778
  try {
596
- var factory = await ensureFlowFactoryAPI(client);
779
+ var factory = await ensureFlowFactoryAPI(client, context.instanceUrl);
780
+ diagnostics.factory_bootstrap = 'success';
781
+ diagnostics.factory_namespace = factory.namespace;
597
782
 
598
783
  var factoryPayload = {
599
784
  name: flowName,
@@ -622,7 +807,8 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
622
807
  // 404 = API was deleted externally → invalidate cache, retry once
623
808
  if (callError.response?.status === 404) {
624
809
  invalidateFlowFactoryCache();
625
- var retryFactory = await ensureFlowFactoryAPI(client);
810
+ var retryFactory = await ensureFlowFactoryAPI(client, context.instanceUrl);
811
+ diagnostics.factory_namespace = retryFactory.namespace;
626
812
  var retryEndpoint = '/api/' + retryFactory.namespace + '/' + FLOW_FACTORY_API_ID + '/create';
627
813
  factoryResp = await client.post(retryEndpoint, factoryPayload);
628
814
  } else {
@@ -632,9 +818,13 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
632
818
 
633
819
  var factoryResult = factoryResp.data?.result || factoryResp.data;
634
820
  if (factoryResult && factoryResult.success && factoryResult.flow_sys_id) {
821
+ diagnostics.factory_call = 'success';
635
822
  flowSysId = factoryResult.flow_sys_id;
636
823
  usedMethod = 'scripted_rest_api';
637
824
  versionCreated = !!factoryResult.version_created;
825
+ diagnostics.version_created = versionCreated;
826
+ diagnostics.version_method = 'factory';
827
+ diagnostics.version_fields_set = ['flow', 'name', 'version', 'state', 'active', 'compile_state', 'is_current', 'published_flow'];
638
828
 
639
829
  // Extract step details
640
830
  var steps = factoryResult.steps || {};
@@ -657,14 +847,19 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
657
847
  } catch (factoryError: any) {
658
848
  // Flow Factory unavailable — fall through to Table API
659
849
  var statusCode = factoryError.response?.status;
850
+ var factoryErrMsg = statusCode
851
+ ? 'HTTP ' + statusCode + ': ' + (factoryError.response?.data?.error?.message || factoryError.message || 'unknown')
852
+ : (factoryError.message || 'unknown');
853
+ diagnostics.factory_bootstrap = diagnostics.factory_bootstrap || ('error: ' + factoryErrMsg);
854
+ diagnostics.factory_call = diagnostics.factory_call || ('error: ' + factoryErrMsg);
660
855
  if (statusCode !== 403) {
661
- // Log non-permission errors as warnings (403 = silently skip)
662
- factoryWarnings.push('Flow Factory unavailable (' + (statusCode || factoryError.message || 'unknown') + '), using Table API fallback');
856
+ factoryWarnings.push('Flow Factory unavailable (' + factoryErrMsg + '), using Table API fallback');
663
857
  }
664
858
  }
665
859
 
666
860
  // ── Table API fallback (existing logic) ─────────────────────
667
861
  if (!flowSysId) {
862
+ diagnostics.table_api_used = true;
668
863
  var flowData: any = {
669
864
  name: flowName,
670
865
  description: flowDescription,
@@ -691,12 +886,18 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
691
886
  version: '1.0',
692
887
  state: shouldActivate ? 'published' : 'draft',
693
888
  active: true,
889
+ compile_state: 'compiled',
890
+ is_current: true,
694
891
  flow_definition: JSON.stringify(flowDefinition)
695
892
  };
893
+ if (shouldActivate) versionData.published_flow = flowSysId;
696
894
  var versionResp = await client.post('/api/now/table/sys_hub_flow_version', versionData);
697
895
  var versionSysId = versionResp.data.result?.sys_id;
698
896
  if (versionSysId) {
699
897
  versionCreated = true;
898
+ diagnostics.version_created = true;
899
+ diagnostics.version_method = 'table_api';
900
+ diagnostics.version_fields_set = ['flow', 'name', 'version', 'state', 'active', 'compile_state', 'is_current', 'published_flow'];
700
901
  // Link flow to its version
701
902
  try {
702
903
  await client.patch('/api/now/table/sys_hub_flow/' + flowSysId, {
@@ -814,10 +1015,88 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
814
1015
  }
815
1016
  }
816
1017
 
817
- // Best-effort snapshot
1018
+ }
1019
+
1020
+ // ── Register flow with Flow Designer engine ─────────────────
1021
+ // This is the KEY step: without it, records exist but Flow Designer
1022
+ // shows "Your flow cannot be found" because the engine hasn't compiled it.
1023
+ if (flowSysId) {
1024
+ var engineResult = await registerFlowWithEngine(client, flowSysId, shouldActivate);
1025
+ diagnostics.engine_registration = {
1026
+ success: engineResult.success,
1027
+ method: engineResult.method,
1028
+ attempts: engineResult.attempts
1029
+ };
1030
+ if (!engineResult.success) {
1031
+ factoryWarnings.push('Flow Designer engine registration failed — flow may show "cannot be found". Attempts: ' + engineResult.attempts.join(', '));
1032
+ }
1033
+ }
1034
+
1035
+ // ── Post-creation verification ─────────────────────────────
1036
+ if (flowSysId) {
818
1037
  try {
819
- await client.post('/api/sn_flow_designer/flow/snapshot', { flow_id: flowSysId });
820
- } catch (snapError) { /* may not exist */ }
1038
+ var verifyResp = await client.get('/api/now/table/sys_hub_flow/' + flowSysId, {
1039
+ params: { sysparm_fields: 'sys_id,name,latest_version' }
1040
+ });
1041
+ var verifyFlow = verifyResp.data.result;
1042
+ var hasLatestVersion = !!(verifyFlow && verifyFlow.latest_version);
1043
+
1044
+ // Check if version record exists
1045
+ var verCheckResp = await client.get('/api/now/table/sys_hub_flow_version', {
1046
+ params: {
1047
+ sysparm_query: 'flow=' + flowSysId,
1048
+ sysparm_fields: 'sys_id,name,state,compile_state,is_current',
1049
+ sysparm_limit: 1
1050
+ }
1051
+ });
1052
+ var verRecords = verCheckResp.data.result || [];
1053
+ var hasVersionRecord = verRecords.length > 0;
1054
+
1055
+ diagnostics.post_verify = {
1056
+ flow_exists: true,
1057
+ has_latest_version_ref: hasLatestVersion,
1058
+ version_record_exists: hasVersionRecord,
1059
+ version_details: hasVersionRecord ? {
1060
+ sys_id: verRecords[0].sys_id,
1061
+ state: verRecords[0].state,
1062
+ compile_state: verRecords[0].compile_state,
1063
+ is_current: verRecords[0].is_current
1064
+ } : null
1065
+ };
1066
+
1067
+ // If version record is missing, retry creation
1068
+ if (!hasVersionRecord && !versionCreated) {
1069
+ try {
1070
+ var retryVersionData: any = {
1071
+ flow: flowSysId,
1072
+ name: '1.0',
1073
+ version: '1.0',
1074
+ state: shouldActivate ? 'published' : 'draft',
1075
+ active: true,
1076
+ compile_state: 'compiled',
1077
+ is_current: true,
1078
+ flow_definition: JSON.stringify(flowDefinition)
1079
+ };
1080
+ if (shouldActivate) retryVersionData.published_flow = flowSysId;
1081
+
1082
+ var retryVerResp = await client.post('/api/now/table/sys_hub_flow_version', retryVersionData);
1083
+ var retryVerSysId = retryVerResp.data.result?.sys_id;
1084
+ if (retryVerSysId) {
1085
+ versionCreated = true;
1086
+ diagnostics.version_created = true;
1087
+ diagnostics.version_method = (diagnostics.version_method || 'table_api') + ' (retry)';
1088
+ try {
1089
+ await client.patch('/api/now/table/sys_hub_flow/' + flowSysId, { latest_version: retryVerSysId });
1090
+ } catch (_) {}
1091
+ factoryWarnings.push('Version record was missing — created via post-verify retry');
1092
+ }
1093
+ } catch (retryErr: any) {
1094
+ factoryWarnings.push('Post-verify version retry failed: ' + (retryErr.message || retryErr));
1095
+ }
1096
+ }
1097
+ } catch (verifyErr: any) {
1098
+ diagnostics.post_verify = { error: verifyErr.message || 'verification failed' };
1099
+ }
821
1100
  }
822
1101
 
823
1102
  // ── Build summary ───────────────────────────────────────────
@@ -833,8 +1112,14 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
833
1112
  .field('Status', shouldActivate ? 'Published (active)' : 'Draft')
834
1113
  .field('Method', methodLabel);
835
1114
 
1115
+ if (diagnostics.factory_namespace) {
1116
+ createSummary.field('Namespace', diagnostics.factory_namespace);
1117
+ }
1118
+
836
1119
  if (versionCreated) {
837
- createSummary.field('Version', 'v1.0 created');
1120
+ createSummary.field('Version', 'v1.0 created' + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
1121
+ } else {
1122
+ createSummary.warning('Version record NOT created — flow may show "cannot be found" in Flow Designer');
838
1123
  }
839
1124
 
840
1125
  if (!isSubflow && triggerType !== 'manual') {
@@ -851,6 +1136,31 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
851
1136
  createSummary.warning(factoryWarnings[wi]);
852
1137
  }
853
1138
 
1139
+ // Diagnostics section
1140
+ createSummary.blank().line('Diagnostics:');
1141
+ createSummary.indented('Factory bootstrap: ' + (diagnostics.factory_bootstrap || 'not attempted'));
1142
+ createSummary.indented('Factory namespace: ' + (diagnostics.factory_namespace || 'n/a'));
1143
+ createSummary.indented('Factory call: ' + (diagnostics.factory_call || 'not attempted'));
1144
+ createSummary.indented('Table API used: ' + diagnostics.table_api_used);
1145
+ createSummary.indented('Version created: ' + diagnostics.version_created + (diagnostics.version_method ? ' (' + diagnostics.version_method + ')' : ''));
1146
+ if (diagnostics.engine_registration) {
1147
+ createSummary.indented('Engine registration: ' + (diagnostics.engine_registration.success ? diagnostics.engine_registration.method : 'FAILED'));
1148
+ if (diagnostics.engine_registration.attempts) {
1149
+ for (var ea = 0; ea < diagnostics.engine_registration.attempts.length; ea++) {
1150
+ createSummary.indented(' ' + diagnostics.engine_registration.attempts[ea]);
1151
+ }
1152
+ }
1153
+ }
1154
+ if (diagnostics.post_verify) {
1155
+ if (diagnostics.post_verify.error) {
1156
+ createSummary.indented('Post-verify: error — ' + diagnostics.post_verify.error);
1157
+ } else {
1158
+ createSummary.indented('Post-verify: flow=' + diagnostics.post_verify.flow_exists +
1159
+ ', version_record=' + diagnostics.post_verify.version_record_exists +
1160
+ ', latest_version_ref=' + diagnostics.post_verify.has_latest_version_ref);
1161
+ }
1162
+ }
1163
+
854
1164
  return createSuccessResult({
855
1165
  created: true,
856
1166
  method: usedMethod,
@@ -872,7 +1182,8 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
872
1182
  activities_created: actionsCreated,
873
1183
  activities_requested: activitiesArg.length,
874
1184
  variables_created: varsCreated,
875
- warnings: factoryWarnings.length > 0 ? factoryWarnings : undefined
1185
+ warnings: factoryWarnings.length > 0 ? factoryWarnings : undefined,
1186
+ diagnostics: diagnostics
876
1187
  }, {}, createSummary.build());
877
1188
  }
878
1189
 
@@ -1225,5 +1536,5 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1225
1536
  }
1226
1537
  }
1227
1538
 
1228
- export const version = '2.0.0';
1539
+ export const version = '4.0.0';
1229
1540
  export const author = 'Snow-Flow Team';