snow-flow 10.0.1-dev.445 → 10.0.1-dev.447

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.445",
3
+ "version": "10.0.1-dev.447",
4
4
  "name": "snow-flow",
5
5
  "description": "Snow-Flow - ServiceNow Multi-Agent Development Framework powered by AI",
6
6
  "license": "Elastic-2.0",
@@ -38,6 +38,25 @@ function generateUUID(): string {
38
38
  });
39
39
  }
40
40
 
41
+ async function getNextOrder(client: any, flowId: string): Promise<number> {
42
+ let maxOrder = 0;
43
+ // Query all element types that have an order field on this flow
44
+ for (const table of ['sys_hub_action_instance', 'sys_hub_flow_logic', 'sys_hub_sub_flow_instance']) {
45
+ try {
46
+ const resp = await client.get('/api/now/table/' + table, {
47
+ params: {
48
+ sysparm_query: 'flow=' + flowId + '^ORDERBYDESCorder',
49
+ sysparm_fields: 'order',
50
+ sysparm_limit: 1
51
+ }
52
+ });
53
+ const order = parseInt(resp.data.result?.[0]?.order || '0', 10);
54
+ if (order > maxOrder) maxOrder = order;
55
+ } catch (_) {}
56
+ }
57
+ return maxOrder + 1;
58
+ }
59
+
41
60
  async function executeFlowPatchMutation(
42
61
  client: any,
43
62
  flowPatch: any,
@@ -183,6 +202,7 @@ async function addActionViaGraphQL(
183
202
  actionType: string,
184
203
  actionName: string,
185
204
  inputs?: Record<string, string>,
205
+ parentUiId?: string,
186
206
  order?: number
187
207
  ): Promise<{ success: boolean; actionId?: string; steps?: any; error?: string }> {
188
208
  const steps: any = {};
@@ -265,6 +285,7 @@ async function addActionViaGraphQL(
265
285
  }
266
286
 
267
287
  const uuid = generateUUID();
288
+ const resolvedOrder = order || await getNextOrder(client, flowId);
268
289
  const actionResponseFields = 'actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
269
290
  try {
270
291
  const result = await executeFlowPatchMutation(client, {
@@ -275,11 +296,11 @@ async function addActionViaGraphQL(
275
296
  metadata: '{"predicates":[]}',
276
297
  flowSysId: flowId,
277
298
  generationSource: '',
278
- order: String(order || 1),
279
- parent: '',
299
+ order: String(resolvedOrder),
300
+ parent: parentUiId || '',
280
301
  uiUniqueIdentifier: uuid,
281
302
  type: 'action',
282
- parentUiId: '',
303
+ parentUiId: parentUiId || '',
283
304
  inputs: []
284
305
  }]
285
306
  }
@@ -365,13 +386,14 @@ async function addFlowLogicViaGraphQL(
365
386
  if (!defId) return { success: false, error: 'Flow logic definition not found for: ' + logicType, steps };
366
387
 
367
388
  const uuid = generateUUID();
389
+ const resolvedOrder = order || await getNextOrder(client, flowId);
368
390
  const logicResponseFields = 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
369
391
  try {
370
392
  const result = await executeFlowPatchMutation(client, {
371
393
  flowId: flowId,
372
394
  flowLogics: {
373
395
  insert: [{
374
- order: String(order || 1),
396
+ order: String(resolvedOrder),
375
397
  uiUniqueIdentifier: uuid,
376
398
  parent: '',
377
399
  metadata: '{"predicates":[]}',
@@ -472,6 +494,7 @@ async function addSubflowCallViaGraphQL(
472
494
  if (!subflowName) subflowName = subflowId;
473
495
 
474
496
  const uuid = generateUUID();
497
+ const resolvedOrder = order || await getNextOrder(client, flowId);
475
498
  const subflowResponseFields = 'subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }';
476
499
  try {
477
500
  const result = await executeFlowPatchMutation(client, {
@@ -482,7 +505,7 @@ async function addSubflowCallViaGraphQL(
482
505
  flowSysId: flowId,
483
506
  generationSource: '',
484
507
  name: subflowName,
485
- order: String(order || 1),
508
+ order: String(resolvedOrder),
486
509
  parent: parentUiId || '',
487
510
  subflowSysId: subflowSysId,
488
511
  uiUniqueIdentifier: uuid,
@@ -521,6 +544,61 @@ async function addSubflowCallViaGraphQL(
521
544
  }
522
545
  }
523
546
 
547
+ // ── GENERIC UPDATE/DELETE for any flow element ───────────────────────
548
+
549
+ const elementGraphQLMap: Record<string, { key: string; type: string; responseFields: string }> = {
550
+ action: { key: 'actions', type: 'action', responseFields: 'actions { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
551
+ trigger: { key: 'triggerInstances', type: 'trigger', responseFields: 'triggerInstances { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
552
+ flowlogic: { key: 'flowLogics', type: 'flowlogic', responseFields: 'flowLogics { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
553
+ subflow: { key: 'subflows', type: 'subflow', responseFields: 'subflows { inserts { sysId uiUniqueIdentifier __typename } updates deletes __typename }' },
554
+ };
555
+
556
+ async function updateElementViaGraphQL(
557
+ client: any,
558
+ flowId: string,
559
+ elementType: string,
560
+ elementId: string,
561
+ inputs: Record<string, string>
562
+ ): Promise<{ success: boolean; steps?: any; error?: string }> {
563
+ const config = elementGraphQLMap[elementType];
564
+ if (!config) return { success: false, error: 'Unknown element type: ' + elementType };
565
+
566
+ const updateInputs = Object.entries(inputs).map(([name, value]) => ({
567
+ name,
568
+ value: { schemaless: false, schemalessValue: '', value: String(value) }
569
+ }));
570
+
571
+ try {
572
+ await executeFlowPatchMutation(client, {
573
+ flowId,
574
+ [config.key]: { update: [{ uiUniqueIdentifier: elementId, type: config.type, inputs: updateInputs }] }
575
+ }, config.responseFields);
576
+ return { success: true, steps: { element: elementId, type: elementType, inputs: updateInputs.map(i => i.name) } };
577
+ } catch (e: any) {
578
+ return { success: false, error: e.message, steps: { element: elementId, type: elementType } };
579
+ }
580
+ }
581
+
582
+ async function deleteElementViaGraphQL(
583
+ client: any,
584
+ flowId: string,
585
+ elementType: string,
586
+ elementIds: string[]
587
+ ): Promise<{ success: boolean; steps?: any; error?: string }> {
588
+ const config = elementGraphQLMap[elementType];
589
+ if (!config) return { success: false, error: 'Unknown element type: ' + elementType };
590
+
591
+ try {
592
+ await executeFlowPatchMutation(client, {
593
+ flowId,
594
+ [config.key]: { delete: elementIds }
595
+ }, config.responseFields);
596
+ return { success: true, steps: { deleted: elementIds, type: elementType } };
597
+ } catch (e: any) {
598
+ return { success: false, error: e.message, steps: { elementIds, type: elementType } };
599
+ }
600
+ }
601
+
524
602
  async function createFlowViaProcessFlowAPI(
525
603
  client: any,
526
604
  params: {
@@ -629,8 +707,14 @@ export const toolDefinition: MCPToolDefinition = {
629
707
  properties: {
630
708
  action: {
631
709
  type: 'string',
632
- enum: ['create', 'create_subflow', 'list', 'get', 'update', 'activate', 'deactivate', 'delete', 'publish', 'add_trigger', 'update_trigger', 'add_action', 'add_flow_logic', 'add_subflow'],
633
- description: 'Action to perform. Use add_trigger/add_action/add_flow_logic/add_subflow to add elements, update_trigger to change an existing trigger. Flow logic types: IF, FOR_EACH, DO_UNTIL, SWITCH. add_subflow calls an existing subflow as a step.'
710
+ enum: [
711
+ 'create', 'create_subflow', 'list', 'get', 'update', 'activate', 'deactivate', 'delete', 'publish',
712
+ 'add_trigger', 'update_trigger', 'delete_trigger',
713
+ 'add_action', 'update_action', 'delete_action',
714
+ 'add_flow_logic', 'update_flow_logic', 'delete_flow_logic',
715
+ 'add_subflow', 'update_subflow', 'delete_subflow'
716
+ ],
717
+ description: 'Action to perform. add_*/update_*/delete_* for triggers, actions, flow_logic, subflows. update_trigger replaces the trigger type. update_action/update_flow_logic/update_subflow change input values. delete_* removes elements by element_id.'
634
718
  },
635
719
 
636
720
  flow_id: {
@@ -732,6 +816,14 @@ export const toolDefinition: MCPToolDefinition = {
732
816
  type: 'string',
733
817
  description: 'Subflow sys_id or name to call as a step (for add_subflow action). Looked up in sys_hub_flow where type=subflow.'
734
818
  },
819
+ element_id: {
820
+ type: 'string',
821
+ description: 'Element sys_id or uiUniqueIdentifier for update_*/delete_* actions. For delete_* this can also be a comma-separated list of IDs.'
822
+ },
823
+ order: {
824
+ type: 'number',
825
+ description: 'Position/order of the element in the flow (for add_* actions). Auto-detected if not provided (appends after last element).'
826
+ },
735
827
  type: {
736
828
  type: 'string',
737
829
  enum: ['flow', 'subflow', 'all'],
@@ -1651,7 +1743,7 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1651
1743
  var addActName = args.action_name || args.name || addActType;
1652
1744
  var addActInputs = args.action_inputs || args.inputs || {};
1653
1745
 
1654
- var addActResult = await addActionViaGraphQL(client, addActFlowId, addActType, addActName, addActInputs);
1746
+ var addActResult = await addActionViaGraphQL(client, addActFlowId, addActType, addActName, addActInputs, args.parent_ui_id, args.order);
1655
1747
 
1656
1748
  var addActSummary = summary();
1657
1749
  if (addActResult.success) {
@@ -1738,6 +1830,62 @@ export async function execute(args: any, context: ServiceNowContext): Promise<To
1738
1830
  : createErrorResult(addSubResult.error || 'Failed to add subflow call');
1739
1831
  }
1740
1832
 
1833
+ // ────────────────────────────────────────────────────────────────
1834
+ // UPDATE_ACTION / UPDATE_FLOW_LOGIC / UPDATE_SUBFLOW
1835
+ // ────────────────────────────────────────────────────────────────
1836
+ case 'update_action':
1837
+ case 'update_flow_logic':
1838
+ case 'update_subflow': {
1839
+ if (!args.flow_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required');
1840
+ if (!args.element_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'element_id is required (sys_id or uiUniqueIdentifier of the element)');
1841
+ var updElemFlowId = await resolveFlowId(client, args.flow_id);
1842
+ var updElemType = action === 'update_action' ? 'action' : action === 'update_flow_logic' ? 'flowlogic' : 'subflow';
1843
+ var updElemInputs = args.action_inputs || args.logic_inputs || args.inputs || {};
1844
+
1845
+ var updElemResult = await updateElementViaGraphQL(client, updElemFlowId, updElemType, args.element_id, updElemInputs);
1846
+
1847
+ var updElemSummary = summary();
1848
+ if (updElemResult.success) {
1849
+ updElemSummary.success('Element updated').field('Type', updElemType).field('Element', args.element_id);
1850
+ } else {
1851
+ updElemSummary.error('Failed to update element: ' + (updElemResult.error || 'unknown'));
1852
+ }
1853
+ return updElemResult.success
1854
+ ? createSuccessResult({ action, ...updElemResult }, {}, updElemSummary.build())
1855
+ : createErrorResult(updElemResult.error || 'Failed to update element');
1856
+ }
1857
+
1858
+ // ────────────────────────────────────────────────────────────────
1859
+ // DELETE_ACTION / DELETE_FLOW_LOGIC / DELETE_SUBFLOW / DELETE_TRIGGER
1860
+ // ────────────────────────────────────────────────────────────────
1861
+ case 'delete_action':
1862
+ case 'delete_flow_logic':
1863
+ case 'delete_subflow':
1864
+ case 'delete_trigger': {
1865
+ if (!args.flow_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'flow_id is required');
1866
+ if (!args.element_id) throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'element_id is required (sys_id(s) to delete, comma-separated for multiple)');
1867
+ var delElemFlowId = await resolveFlowId(client, args.flow_id);
1868
+ var delElemType = action === 'delete_action' ? 'action'
1869
+ : action === 'delete_flow_logic' ? 'flowlogic'
1870
+ : action === 'delete_subflow' ? 'subflow'
1871
+ : 'trigger';
1872
+ var delElemIds = String(args.element_id).split(',').map((id: string) => id.trim());
1873
+
1874
+ // Map 'trigger' to the correct GraphQL key
1875
+ var delGraphQLType = delElemType === 'trigger' ? 'trigger' : delElemType;
1876
+ var delResult = await deleteElementViaGraphQL(client, delElemFlowId, delGraphQLType, delElemIds);
1877
+
1878
+ var delSummary = summary();
1879
+ if (delResult.success) {
1880
+ delSummary.success('Element(s) deleted').field('Type', delElemType).field('Deleted', delElemIds.join(', '));
1881
+ } else {
1882
+ delSummary.error('Failed to delete element: ' + (delResult.error || 'unknown'));
1883
+ }
1884
+ return delResult.success
1885
+ ? createSuccessResult({ action, ...delResult }, {}, delSummary.build())
1886
+ : createErrorResult(delResult.error || 'Failed to delete element');
1887
+ }
1888
+
1741
1889
  default:
1742
1890
  throw new SnowFlowError(ErrorType.VALIDATION_ERROR, 'Unknown action: ' + action);
1743
1891
  }