snow-flow 8.41.23 → 8.42.0

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.
Files changed (152) hide show
  1. package/CLAUDE.md +9 -6
  2. package/dist/api/health-api.d.ts +1 -1
  3. package/dist/api/health-api.d.ts.map +1 -1
  4. package/dist/api/simple-health-api.d.ts +1 -1
  5. package/dist/api/simple-health-api.d.ts.map +1 -1
  6. package/dist/mcp/servicenow-automation-mcp.js +8 -8
  7. package/dist/mcp/servicenow-automation-mcp.js.map +1 -1
  8. package/dist/mcp/servicenow-mcp-server.js +4 -4
  9. package/dist/mcp/servicenow-mcp-server.js.map +1 -1
  10. package/dist/mcp/servicenow-mcp-unified/tools/automation/index.d.ts +1 -2
  11. package/dist/mcp/servicenow-mcp-unified/tools/automation/index.d.ts.map +1 -1
  12. package/dist/mcp/servicenow-mcp-unified/tools/automation/index.js +6 -8
  13. package/dist/mcp/servicenow-mcp-unified/tools/automation/index.js.map +1 -1
  14. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_confirm_script_execution.d.ts +1 -1
  15. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_confirm_script_execution.js +2 -2
  16. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_confirm_script_execution.js.map +1 -1
  17. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_schedule_script_job.d.ts +26 -0
  18. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_schedule_script_job.d.ts.map +1 -0
  19. package/dist/mcp/servicenow-mcp-unified/tools/automation/{snow_execute_script.js → snow_schedule_script_job.js} +19 -13
  20. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_schedule_script_job.js.map +1 -0
  21. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.d.ts +12 -4
  22. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.d.ts.map +1 -1
  23. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.js +69 -67
  24. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.js.map +1 -1
  25. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_artifact.d.ts.map +1 -1
  26. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_artifact.js +2 -1
  27. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_artifact.js.map +1 -1
  28. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_requirements.d.ts.map +1 -1
  29. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_requirements.js +2 -1
  30. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_requirements.js.map +1 -1
  31. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_artifact.d.ts.map +1 -1
  32. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_artifact.js +2 -1
  33. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_artifact.js.map +1 -1
  34. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_by_sysid.d.ts.map +1 -1
  35. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_by_sysid.js +5 -2
  36. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_by_sysid.js.map +1 -1
  37. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_find_artifact.d.ts.map +1 -1
  38. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_find_artifact.js +2 -1
  39. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_find_artifact.js.map +1 -1
  40. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_get_by_sysid.d.ts.map +1 -1
  41. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_get_by_sysid.js +2 -1
  42. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_get_by_sysid.js.map +1 -1
  43. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_memory_search.d.ts.map +1 -1
  44. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_memory_search.js +2 -1
  45. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_memory_search.js.map +1 -1
  46. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_orchestrate_development.d.ts.map +1 -1
  47. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_orchestrate_development.js +2 -1
  48. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_orchestrate_development.js.map +1 -1
  49. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_sync_data_consistency.d.ts.map +1 -1
  50. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_sync_data_consistency.js +2 -1
  51. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_sync_data_consistency.js.map +1 -1
  52. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_validate_live_connection.d.ts.map +1 -1
  53. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_validate_live_connection.js +2 -1
  54. package/dist/mcp/servicenow-mcp-unified/tools/development/snow_validate_live_connection.js.map +1 -1
  55. package/dist/mcp/servicenow-mcp-unified/tools/integration/index.d.ts +1 -0
  56. package/dist/mcp/servicenow-mcp-unified/tools/integration/index.d.ts.map +1 -1
  57. package/dist/mcp/servicenow-mcp-unified/tools/integration/index.js +4 -1
  58. package/dist/mcp/servicenow-mcp-unified/tools/integration/index.js.map +1 -1
  59. package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.d.ts +26 -0
  60. package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.d.ts.map +1 -0
  61. package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.js +1280 -0
  62. package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.js.map +1 -0
  63. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/index.d.ts +1 -1
  64. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/index.js +4 -4
  65. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.d.ts +19 -0
  66. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.d.ts.map +1 -0
  67. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.js +142 -0
  68. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.js.map +1 -0
  69. package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.d.ts +1 -1
  70. package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.d.ts.map +1 -1
  71. package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.js +4 -4
  72. package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.js.map +1 -1
  73. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.d.ts +15 -0
  74. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.d.ts.map +1 -0
  75. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.js +136 -0
  76. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.js.map +1 -0
  77. package/dist/templates/agents-md-template.d.ts +1 -1
  78. package/dist/templates/agents-md-template.d.ts.map +1 -1
  79. package/dist/templates/agents-md-template.js +5 -5
  80. package/dist/templates/claude-md-template.d.ts +1 -1
  81. package/dist/templates/claude-md-template.d.ts.map +1 -1
  82. package/dist/templates/claude-md-template.js +11 -9
  83. package/dist/templates/claude-md-template.js.map +1 -1
  84. package/dist/utils/auto-update-snow-code.d.ts +7 -0
  85. package/dist/utils/auto-update-snow-code.d.ts.map +1 -1
  86. package/dist/utils/auto-update-snow-code.js +62 -13
  87. package/dist/utils/auto-update-snow-code.js.map +1 -1
  88. package/package.json +1 -1
  89. package/.snow-code/opencode.json +0 -49
  90. package/dist/cli/enterprise-docs-generator.d.ts +0 -14
  91. package/dist/cli/enterprise-docs-generator.d.ts.map +0 -1
  92. package/dist/cli/enterprise-docs-generator.js +0 -875
  93. package/dist/cli/enterprise-docs-generator.js.map +0 -1
  94. package/dist/config/snowcode-config.d.ts +0 -76
  95. package/dist/config/snowcode-config.d.ts.map +0 -1
  96. package/dist/config/snowcode-config.js +0 -225
  97. package/dist/config/snowcode-config.js.map +0 -1
  98. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_execute_script.d.ts +0 -20
  99. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_execute_script.d.ts.map +0 -1
  100. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_execute_script.js.map +0 -1
  101. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.d.ts +0 -11
  102. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.d.ts.map +0 -1
  103. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.js +0 -142
  104. package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.js.map +0 -1
  105. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.d.ts +0 -8
  106. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.d.ts.map +0 -1
  107. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.js +0 -24
  108. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.js.map +0 -1
  109. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.d.ts +0 -8
  110. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.d.ts.map +0 -1
  111. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.js +0 -44
  112. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.js.map +0 -1
  113. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.d.ts +0 -8
  114. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.d.ts.map +0 -1
  115. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.js +0 -67
  116. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.js.map +0 -1
  117. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.d.ts +0 -15
  118. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.d.ts.map +0 -1
  119. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.js +0 -273
  120. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.js.map +0 -1
  121. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.d.ts +0 -14
  122. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.d.ts.map +0 -1
  123. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.js +0 -205
  124. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.js.map +0 -1
  125. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.d.ts +0 -8
  126. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.d.ts.map +0 -1
  127. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.js +0 -68
  128. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.js.map +0 -1
  129. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.d.ts +0 -143
  130. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.d.ts.map +0 -1
  131. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.js +0 -2293
  132. package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.js.map +0 -1
  133. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.d.ts +0 -9
  134. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.d.ts.map +0 -1
  135. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.js +0 -57
  136. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.js.map +0 -1
  137. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.d.ts +0 -9
  138. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.d.ts.map +0 -1
  139. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.js +0 -58
  140. package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.js.map +0 -1
  141. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.d.ts +0 -9
  142. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.d.ts.map +0 -1
  143. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.js +0 -59
  144. package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.js.map +0 -1
  145. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.d.ts +0 -9
  146. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.d.ts.map +0 -1
  147. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.js +0 -55
  148. package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.js.map +0 -1
  149. package/dist/sdk/claude-agent-sdk-integration.d.ts +0 -103
  150. package/dist/sdk/claude-agent-sdk-integration.d.ts.map +0 -1
  151. package/dist/sdk/claude-agent-sdk-integration.js +0 -414
  152. package/dist/sdk/claude-agent-sdk-integration.js.map +0 -1
@@ -1,2293 +0,0 @@
1
- "use strict";
2
- /**
3
- * snow_manage_flow - Unified Flow Designer Management via Direct Table API
4
- *
5
- * ⚠️ EXPERIMENTAL: Manages flows by directly manipulating Flow Designer tables.
6
- * This approach is NOT officially supported by ServiceNow but enables programmatic flow management.
7
- *
8
- * Supported Actions:
9
- * - create: Create a new flow with triggers, actions, conditions, loops, variables
10
- * - update: Update an existing flow's properties
11
- * - delete: Delete a flow and all related records
12
- * - clone: Clone an existing flow
13
- * - activate: Activate a flow
14
- * - deactivate: Deactivate a flow
15
- * - add_action: Add an action to an existing flow
16
- * - remove_action: Remove an action from a flow
17
- * - add_condition: Add a conditional logic block (if/then/else)
18
- * - add_loop: Add a loop construct (for_each/do_until)
19
- * - get_details: Get detailed flow structure including decompressed values
20
- *
21
- * Features:
22
- * - Xanadu+ compression support (GlideCompressionUtil compatible)
23
- * - Nested actions within conditions and loops
24
- * - Parent-child relationship management
25
- * - Version detection for table compatibility
26
- *
27
- * Flow Designer Table Structure:
28
- * - sys_hub_flow: Main flow record
29
- * - sys_hub_flow_snapshot: Flow version/snapshot
30
- * - sys_hub_trigger_instance_v2: Trigger configuration
31
- * - sys_hub_action_instance_v2: Action instances (with compressed values)
32
- * - sys_hub_flow_logic_instance_v2: Conditions, loops (with parent_logic_instance)
33
- * - sys_hub_flow_variable: Input/output variables
34
- * - sys_hub_sub_flow_instance: Subflow references
35
- *
36
- * @version 2.0.0-experimental
37
- * @author Snow-Flow v8.3.0 - Flow Designer Direct Table API
38
- */
39
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
40
- if (k2 === undefined) k2 = k;
41
- var desc = Object.getOwnPropertyDescriptor(m, k);
42
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
43
- desc = { enumerable: true, get: function() { return m[k]; } };
44
- }
45
- Object.defineProperty(o, k2, desc);
46
- }) : (function(o, m, k, k2) {
47
- if (k2 === undefined) k2 = k;
48
- o[k2] = m[k];
49
- }));
50
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
51
- Object.defineProperty(o, "default", { enumerable: true, value: v });
52
- }) : function(o, v) {
53
- o["default"] = v;
54
- });
55
- var __importStar = (this && this.__importStar) || (function () {
56
- var ownKeys = function(o) {
57
- ownKeys = Object.getOwnPropertyNames || function (o) {
58
- var ar = [];
59
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
60
- return ar;
61
- };
62
- return ownKeys(o);
63
- };
64
- return function (mod) {
65
- if (mod && mod.__esModule) return mod;
66
- var result = {};
67
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
68
- __setModuleDefault(result, mod);
69
- return result;
70
- };
71
- })();
72
- Object.defineProperty(exports, "__esModule", { value: true });
73
- exports.author = exports.version = exports.toolDefinition = void 0;
74
- exports.execute = execute;
75
- const auth_js_1 = require("../../shared/auth.js");
76
- const error_handler_js_1 = require("../../shared/error-handler.js");
77
- const crypto = __importStar(require("crypto"));
78
- const zlib = __importStar(require("zlib"));
79
- // ==================== CONSTANTS ====================
80
- const COMPRESSED_HEADER = 'COMPRESSED:';
81
- /**
82
- * Flow Designer Table Structure
83
- *
84
- * CREATE ORDER (parent → children):
85
- * 1. sys_hub_flow - Main flow record
86
- * 2. sys_hub_trigger_* - Triggers (ref: flow)
87
- * 3. sys_hub_flow_variable - Variables (ref: flow)
88
- * 4. sys_hub_flow_logic_* - Conditions/Loops (ref: flow, parent_logic_instance)
89
- * 5. sys_hub_action_* - Actions (ref: flow, parent_logic_instance, branch)
90
- * 6. sys_hub_sub_flow_* - Subflow calls (ref: flow, parent_logic_instance)
91
- * 7. sys_hub_flow_snapshot - Snapshots (ref: flow)
92
- *
93
- * DELETE ORDER (children → parent, hierarchical):
94
- * 1. Actions - Children of logic
95
- * 2. Subflow instances - Can be nested in logic
96
- * 3. Logic instances - Deepest children first, then parents
97
- * 4. Triggers
98
- * 5. Variables
99
- * 6. Snapshots
100
- * 7. Flow - Main record last
101
- *
102
- * VERSION HISTORY:
103
- * - Pre-Washington DC: V1 tables (no _v2 suffix)
104
- * - Washington DC+: V2 tables (with _v2 suffix)
105
- * - Xanadu+: V2 tables + compressed values (GZIP + Base64)
106
- */
107
- const FLOW_TABLES = {
108
- // === CORE TABLES ===
109
- FLOW: 'sys_hub_flow', // Main flow/subflow records
110
- SNAPSHOT: 'sys_hub_flow_snapshot', // Version snapshots
111
- VARIABLE: 'sys_hub_flow_variable', // Input/output/scratch variables
112
- // === V2 TABLES (Washington DC+) ===
113
- TRIGGER_V2: 'sys_hub_trigger_instance_v2', // Trigger instances
114
- ACTION_V2: 'sys_hub_action_instance_v2', // Action instances (compressed values in Xanadu+)
115
- LOGIC_V2: 'sys_hub_flow_logic_instance_v2', // Conditions, loops (supports nesting)
116
- // === SUBFLOW TABLES ===
117
- SUBFLOW: 'sys_hub_sub_flow_instance', // Subflow call instances
118
- // === LEGACY V1 TABLES (Pre-Washington DC) ===
119
- TRIGGER_V1: 'sys_hub_trigger_instance',
120
- ACTION_V1: 'sys_hub_action_instance',
121
- LOGIC_V1: 'sys_hub_flow_logic_instance',
122
- // === DEFINITION TABLES (Read-only, for lookups) ===
123
- TRIGGER_TYPE: 'sys_hub_trigger_type', // Available trigger types
124
- ACTION_TYPE: 'sys_hub_action_type_base', // Available action types
125
- // === ADDITIONAL TABLES (May be needed for complex flows) ===
126
- STEP_INSTANCE: 'sys_hub_step_instance', // Step configurations
127
- FLOW_INPUT: 'sys_hub_flow_input', // Flow inputs (legacy?)
128
- FLOW_OUTPUT: 'sys_hub_flow_output', // Flow outputs (legacy?)
129
- FLOW_STAGE: 'sys_hub_flow_stage' // Flow stages
130
- };
131
- // ==================== TOOL DEFINITION ====================
132
- exports.toolDefinition = {
133
- name: 'snow_manage_flow',
134
- description: `Unified Flow Designer management via Direct Table API (EXPERIMENTAL).
135
-
136
- ⚠️ WARNING: This tool manipulates Flow Designer tables directly.
137
- This is NOT officially supported by ServiceNow.
138
-
139
- Actions:
140
- - create: Create a new flow with all components
141
- - update: Update flow properties
142
- - delete: Delete a flow and all related records
143
- - clone: Clone an existing flow
144
- - activate/deactivate: Change flow state
145
- - add_action: Add an action to a flow
146
- - remove_action: Remove an action
147
- - add_condition: Add if/then/else logic
148
- - add_loop: Add for_each/do_until loop
149
- - add_variable: Add flow variable
150
- - get_details: Get flow structure with decompressed values
151
- - publish: Publish flow changes
152
-
153
- Features:
154
- - Xanadu+ compression support
155
- - Nested actions in conditions/loops
156
- - Parent-child relationship management
157
- - Automatic version detection`,
158
- category: 'automation',
159
- subcategory: 'flow-designer',
160
- use_cases: ['flow-management', 'automation', 'workflow'],
161
- complexity: 'advanced',
162
- frequency: 'medium',
163
- permission: 'write',
164
- allowedRoles: ['developer', 'admin'],
165
- inputSchema: {
166
- type: 'object',
167
- properties: {
168
- action: {
169
- type: 'string',
170
- enum: ['create', 'update', 'delete', 'clone', 'activate', 'deactivate',
171
- 'add_action', 'remove_action', 'add_condition', 'add_loop',
172
- 'add_variable', 'get_details', 'publish'],
173
- description: 'Management action to perform'
174
- },
175
- flow: {
176
- type: 'object',
177
- description: '[create] Flow configuration',
178
- properties: {
179
- name: { type: 'string' },
180
- internal_name: { type: 'string' },
181
- description: { type: 'string' },
182
- active: { type: 'boolean', default: false },
183
- application_scope: { type: 'string' },
184
- run_as: { type: 'string', enum: ['system', 'user_who_triggers', 'specific_user'] },
185
- run_as_user: { type: 'string' }
186
- }
187
- },
188
- trigger: {
189
- type: 'object',
190
- description: `[create] Trigger configuration. Use specific trigger types:
191
- - record_created: Triggers when a record is created on the specified table
192
- - record_updated: Triggers when a record is updated
193
- - record_deleted: Triggers when a record is deleted
194
- - record_created_or_updated: Triggers on both create and update
195
- - scheduled: Time-based trigger (requires schedule config)
196
- - api: REST API trigger
197
- - inbound_email: Email trigger
198
- - service_catalog: Catalog item trigger`,
199
- properties: {
200
- type: {
201
- type: 'string',
202
- enum: ['record_created', 'record_updated', 'record_deleted', 'record_created_or_updated',
203
- 'scheduled', 'api', 'inbound_email', 'service_catalog'],
204
- description: 'Trigger type - use record_created/record_updated instead of generic record'
205
- },
206
- table: {
207
- type: 'string',
208
- description: 'Table name for record triggers (e.g., incident, task, sys_user)'
209
- },
210
- condition: {
211
- type: 'string',
212
- description: 'Filter condition (encoded query) for when the trigger should fire'
213
- }
214
- }
215
- },
216
- actions: {
217
- type: 'array',
218
- description: '[create] Actions to add',
219
- items: { type: 'object' }
220
- },
221
- conditions: {
222
- type: 'array',
223
- description: '[create] Conditional logic blocks',
224
- items: { type: 'object' }
225
- },
226
- loops: {
227
- type: 'array',
228
- description: '[create] Loop constructs',
229
- items: { type: 'object' }
230
- },
231
- variables: {
232
- type: 'array',
233
- description: '[create] Flow variables',
234
- items: { type: 'object' }
235
- },
236
- flow_id: {
237
- type: 'string',
238
- description: '[update/delete/clone/activate/deactivate/get_details/add_*] Flow sys_id'
239
- },
240
- flow_name: {
241
- type: 'string',
242
- description: '[update/delete/clone/activate/deactivate/get_details/add_*] Flow name (alternative to flow_id)'
243
- },
244
- updates: {
245
- type: 'object',
246
- description: '[update] Properties to update'
247
- },
248
- new_name: {
249
- type: 'string',
250
- description: '[clone] Name for cloned flow'
251
- },
252
- action_config: {
253
- type: 'object',
254
- description: '[add_action] Action to add'
255
- },
256
- condition_config: {
257
- type: 'object',
258
- description: '[add_condition] Condition to add'
259
- },
260
- loop_config: {
261
- type: 'object',
262
- description: '[add_loop] Loop to add'
263
- },
264
- variable_config: {
265
- type: 'object',
266
- description: '[add_variable] Variable to add'
267
- },
268
- action_id: {
269
- type: 'string',
270
- description: '[remove_action] Action sys_id to remove'
271
- },
272
- auto_activate: {
273
- type: 'boolean',
274
- description: 'Activate after create/update',
275
- default: false
276
- },
277
- create_snapshot: {
278
- type: 'boolean',
279
- description: 'Create snapshot after changes',
280
- default: true
281
- },
282
- use_compression: {
283
- type: 'boolean',
284
- description: 'Use compression for values (auto-detected if not specified)'
285
- },
286
- validate_only: {
287
- type: 'boolean',
288
- description: 'Validate without making changes',
289
- default: false
290
- }
291
- },
292
- required: ['action']
293
- }
294
- };
295
- // ==================== HELPER FUNCTIONS ====================
296
- /**
297
- * Generate ServiceNow-compatible sys_id
298
- */
299
- function generateSysId() {
300
- return crypto.randomBytes(16).toString('hex');
301
- }
302
- /**
303
- * Generate internal name from display name
304
- */
305
- function generateInternalName(name, prefix = 'x_snfl') {
306
- const sanitized = name
307
- .toLowerCase()
308
- .replace(/[^a-z0-9]+/g, '_')
309
- .replace(/^_+|_+$/g, '')
310
- .substring(0, 40);
311
- return `${prefix}_${sanitized}`;
312
- }
313
- /**
314
- * Compress value for Xanadu+ versions
315
- * Uses gzip compression + base64 encoding with COMPRESSED: header
316
- */
317
- function compressValue(value) {
318
- const jsonStr = typeof value === 'object' ? JSON.stringify(value) : value;
319
- try {
320
- // Compress with gzip
321
- const compressed = zlib.gzipSync(Buffer.from(jsonStr, 'utf-8'));
322
- // Encode as base64
323
- const encoded = compressed.toString('base64');
324
- // Add header
325
- return COMPRESSED_HEADER + encoded;
326
- }
327
- catch (error) {
328
- console.error('Compression failed, returning uncompressed:', error);
329
- return jsonStr;
330
- }
331
- }
332
- /**
333
- * Decompress value from Xanadu+ format
334
- */
335
- function decompressValue(value) {
336
- if (!value || !value.startsWith(COMPRESSED_HEADER)) {
337
- return value;
338
- }
339
- try {
340
- // Remove header
341
- const encoded = value.substring(COMPRESSED_HEADER.length);
342
- // Decode base64
343
- const compressed = Buffer.from(encoded, 'base64');
344
- // Decompress gzip
345
- const decompressed = zlib.gunzipSync(compressed);
346
- return decompressed.toString('utf-8');
347
- }
348
- catch (error) {
349
- console.error('Decompression failed:', error);
350
- return value;
351
- }
352
- }
353
- /**
354
- * Check if value is compressed
355
- */
356
- function isCompressed(value) {
357
- return value && value.startsWith(COMPRESSED_HEADER);
358
- }
359
- /**
360
- * Detect ServiceNow version and capabilities
361
- */
362
- async function detectVersion(client) {
363
- try {
364
- // Check if V2 tables exist by querying them
365
- const v2Response = await client.get('/api/now/table/sys_hub_action_instance_v2', {
366
- params: { sysparm_limit: 1 }
367
- });
368
- // Check if values are compressed by looking at a sample
369
- let useCompression = false;
370
- if (v2Response.data.result && v2Response.data.result.length > 0) {
371
- const sample = v2Response.data.result[0];
372
- if (sample.values && isCompressed(sample.values)) {
373
- useCompression = true;
374
- }
375
- }
376
- return {
377
- useV2Tables: true,
378
- useCompression,
379
- version: useCompression ? 'Xanadu+' : 'Washington DC+'
380
- };
381
- }
382
- catch (error) {
383
- // V2 tables don't exist, use V1
384
- return {
385
- useV2Tables: false,
386
- useCompression: false,
387
- version: 'Pre-Washington DC'
388
- };
389
- }
390
- }
391
- /**
392
- * Detect if a value is a data pill reference
393
- * Data pills use {{action_name.field}} or {{trigger.field}} syntax
394
- */
395
- function isDataPill(value) {
396
- if (typeof value !== 'string')
397
- return false;
398
- return /^\{\{[\w.]+\}\}$/.test(value) || /^\$\{[\w.]+\}$/.test(value);
399
- }
400
- /**
401
- * Build a data pill reference
402
- */
403
- function buildDataPill(reference, displayValue) {
404
- return {
405
- value: reference,
406
- displayValue: displayValue || reference,
407
- valueType: 'pill'
408
- };
409
- }
410
- /**
411
- * Build a reference field value
412
- */
413
- function buildReferenceValue(sysId, displayValue, table) {
414
- return {
415
- value: sysId,
416
- displayValue: displayValue,
417
- valueType: 'reference',
418
- parameter: {
419
- type: 'reference',
420
- label: displayValue,
421
- mandatory: false,
422
- reference: table
423
- }
424
- };
425
- }
426
- /**
427
- * Build a script value (for Run Script action)
428
- */
429
- function buildScriptValue(script) {
430
- return {
431
- value: script,
432
- valueType: 'script'
433
- };
434
- }
435
- /**
436
- * Build a condition value (for If/Loop conditions)
437
- */
438
- function buildConditionValue(condition) {
439
- return {
440
- value: condition,
441
- valueType: 'condition'
442
- };
443
- }
444
- /**
445
- * Build a glide list value (multi-select)
446
- */
447
- function buildGlideListValue(values) {
448
- return {
449
- value: values.join(','),
450
- valueType: 'glide_list'
451
- };
452
- }
453
- /**
454
- * Build values JSON for action instance - ENHANCED VERSION
455
- * Supports: data pills, references, arrays, scripts, conditions, transforms
456
- */
457
- function buildActionValues(inputs, useCompression) {
458
- if (!inputs || Object.keys(inputs).length === 0) {
459
- const emptyValue = '{}';
460
- return useCompression ? compressValue(emptyValue) : emptyValue;
461
- }
462
- const values = {};
463
- for (const [key, value] of Object.entries(inputs)) {
464
- // Already in Flow Designer format
465
- if (typeof value === 'object' && value !== null && 'value' in value && 'valueType' in value) {
466
- values[key] = value;
467
- continue;
468
- }
469
- // Data pill reference ({{...}} or ${...})
470
- if (isDataPill(value)) {
471
- values[key] = {
472
- value: value,
473
- displayValue: value,
474
- valueType: 'pill'
475
- };
476
- continue;
477
- }
478
- // Reference object with sys_id and display
479
- if (typeof value === 'object' && value !== null && 'sys_id' in value) {
480
- values[key] = {
481
- value: value.sys_id,
482
- displayValue: value.display_value || value.name || value.sys_id,
483
- valueType: 'reference',
484
- referenceName: value.table || ''
485
- };
486
- continue;
487
- }
488
- // Array value (e.g., for multi-select or list inputs)
489
- if (Array.isArray(value)) {
490
- if (value.every(v => typeof v === 'string')) {
491
- // String array - join as comma-separated
492
- values[key] = {
493
- value: value.join(','),
494
- valueType: 'glide_list'
495
- };
496
- }
497
- else if (value.every(v => typeof v === 'object' && 'sys_id' in v)) {
498
- // Array of references
499
- values[key] = {
500
- value: value.map(v => v.sys_id).join(','),
501
- displayValue: value.map(v => v.display_value || v.name || v.sys_id).join(', '),
502
- valueType: 'reference_list'
503
- };
504
- }
505
- else {
506
- // Complex array - serialize
507
- values[key] = {
508
- value: JSON.stringify(value),
509
- valueType: 'array'
510
- };
511
- }
512
- continue;
513
- }
514
- // Object with special type hints
515
- if (typeof value === 'object' && value !== null) {
516
- if ('_type' in value) {
517
- // Handle special types
518
- switch (value._type) {
519
- case 'pill':
520
- case 'data_pill':
521
- values[key] = buildDataPill(value.reference, value.display);
522
- break;
523
- case 'reference':
524
- values[key] = buildReferenceValue(value.sys_id, value.display || value.sys_id, value.table);
525
- break;
526
- case 'script':
527
- values[key] = buildScriptValue(value.script || value.value);
528
- break;
529
- case 'condition':
530
- values[key] = buildConditionValue(value.condition || value.value);
531
- break;
532
- case 'glide_list':
533
- values[key] = buildGlideListValue(value.values || []);
534
- break;
535
- case 'transform':
536
- // Transform function applied to a value
537
- values[key] = {
538
- value: value.source,
539
- displayValue: value.display || value.source,
540
- valueType: 'transform',
541
- transform: {
542
- function: value.function,
543
- params: value.params || []
544
- }
545
- };
546
- break;
547
- default:
548
- values[key] = {
549
- value: JSON.stringify(value),
550
- valueType: 'object'
551
- };
552
- }
553
- continue;
554
- }
555
- // Generic object
556
- values[key] = {
557
- value: JSON.stringify(value),
558
- valueType: 'object'
559
- };
560
- continue;
561
- }
562
- // Boolean value
563
- if (typeof value === 'boolean') {
564
- values[key] = {
565
- value: String(value),
566
- valueType: 'boolean'
567
- };
568
- continue;
569
- }
570
- // Number value
571
- if (typeof value === 'number') {
572
- values[key] = {
573
- value: String(value),
574
- valueType: Number.isInteger(value) ? 'integer' : 'decimal'
575
- };
576
- continue;
577
- }
578
- // String value (check for special patterns)
579
- if (typeof value === 'string') {
580
- // Check if it looks like a date/time
581
- if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
582
- values[key] = {
583
- value: value,
584
- valueType: value.includes('T') ? 'glide_date_time' : 'glide_date'
585
- };
586
- continue;
587
- }
588
- // Check if it looks like a duration (e.g., "1d 2h 30m")
589
- if (/^\d+[dhms]\s*/.test(value)) {
590
- values[key] = {
591
- value: value,
592
- valueType: 'duration'
593
- };
594
- continue;
595
- }
596
- // Regular string
597
- values[key] = {
598
- value: value,
599
- valueType: 'string'
600
- };
601
- continue;
602
- }
603
- // Fallback - convert to string
604
- values[key] = {
605
- value: String(value),
606
- valueType: 'string'
607
- };
608
- }
609
- const jsonStr = JSON.stringify(values);
610
- return useCompression ? compressValue(jsonStr) : jsonStr;
611
- }
612
- /**
613
- * Remap sys_ids in values JSON when cloning
614
- * Updates data pill references from old flow to new flow
615
- */
616
- function remapValuesForClone(valuesJson, sysIdMap, oldFlowId, newFlowId) {
617
- if (!valuesJson)
618
- return valuesJson;
619
- let values;
620
- try {
621
- values = JSON.parse(valuesJson);
622
- }
623
- catch (e) {
624
- return valuesJson;
625
- }
626
- // Recursively update sys_ids and references
627
- const updateRefs = (obj) => {
628
- if (!obj || typeof obj !== 'object')
629
- return obj;
630
- if (Array.isArray(obj)) {
631
- return obj.map(item => updateRefs(item));
632
- }
633
- const updated = {};
634
- for (const [key, value] of Object.entries(obj)) {
635
- if (typeof value === 'string') {
636
- // Check if it's a sys_id that needs remapping
637
- if (sysIdMap.has(value)) {
638
- updated[key] = sysIdMap.get(value);
639
- }
640
- // Check if it's a data pill reference
641
- else if (value.includes(oldFlowId)) {
642
- updated[key] = value.replace(oldFlowId, newFlowId);
643
- }
644
- else {
645
- updated[key] = value;
646
- }
647
- }
648
- else if (typeof value === 'object') {
649
- updated[key] = updateRefs(value);
650
- }
651
- else {
652
- updated[key] = value;
653
- }
654
- }
655
- return updated;
656
- };
657
- const remapped = updateRefs(values);
658
- return JSON.stringify(remapped);
659
- }
660
- /**
661
- * Get trigger type sys_id from name
662
- */
663
- async function getTriggerTypeSysId(client, triggerType, when) {
664
- const triggerMap = {
665
- 'record_created': 'Created',
666
- 'record_updated': 'Updated',
667
- 'record_deleted': 'Deleted',
668
- 'record_created_or_updated': 'Created or Updated',
669
- 'scheduled': 'Scheduled',
670
- 'api': 'REST API',
671
- 'inbound_email': 'Inbound Email',
672
- 'service_catalog': 'Service Catalog'
673
- };
674
- let searchName = triggerType;
675
- if (triggerType === 'record' && when) {
676
- searchName = triggerMap[`record_${when}`] || when;
677
- }
678
- else if (triggerMap[triggerType]) {
679
- searchName = triggerMap[triggerType];
680
- }
681
- try {
682
- const response = await client.get('/api/now/table/sys_hub_trigger_type', {
683
- params: {
684
- sysparm_query: `nameLIKE${searchName}^ORlabelLIKE${searchName}`,
685
- sysparm_limit: 1,
686
- sysparm_fields: 'sys_id,name,label'
687
- }
688
- });
689
- if (response.data.result && response.data.result.length > 0) {
690
- return response.data.result[0].sys_id;
691
- }
692
- }
693
- catch (error) {
694
- console.error('Failed to get trigger type:', error);
695
- }
696
- return null;
697
- }
698
- /**
699
- * Get action type sys_id from name
700
- */
701
- async function getActionTypeSysId(client, actionType) {
702
- if (/^[0-9a-f]{32}$/i.test(actionType)) {
703
- return { sys_id: actionType, name: actionType };
704
- }
705
- try {
706
- const response = await client.get('/api/now/table/sys_hub_action_type_base', {
707
- params: {
708
- sysparm_query: `nameLIKE${actionType}^ORlabelLIKE${actionType}^active=true`,
709
- sysparm_limit: 1,
710
- sysparm_fields: 'sys_id,name,label'
711
- }
712
- });
713
- if (response.data.result && response.data.result.length > 0) {
714
- const result = response.data.result[0];
715
- return { sys_id: result.sys_id, name: result.name || result.label };
716
- }
717
- }
718
- catch (error) {
719
- console.error('Failed to get action type:', error);
720
- }
721
- return null;
722
- }
723
- /**
724
- * Find flow by ID or name
725
- */
726
- async function findFlow(client, flowId, flowName) {
727
- if (!flowId && !flowName) {
728
- return null;
729
- }
730
- try {
731
- let query = '';
732
- if (flowId) {
733
- query = `sys_id=${flowId}`;
734
- }
735
- else if (flowName) {
736
- query = `name=${flowName}^ORinternal_name=${flowName}`;
737
- }
738
- const response = await client.get('/api/now/table/sys_hub_flow', {
739
- params: {
740
- sysparm_query: query,
741
- sysparm_limit: 1
742
- }
743
- });
744
- if (response.data.result && response.data.result.length > 0) {
745
- return response.data.result[0];
746
- }
747
- }
748
- catch (error) {
749
- console.error('Failed to find flow:', error);
750
- }
751
- return null;
752
- }
753
- /**
754
- * Get maximum order number for actions/logic in a flow
755
- */
756
- async function getMaxOrder(client, flowId, table) {
757
- try {
758
- const response = await client.get(`/api/now/table/${table}`, {
759
- params: {
760
- sysparm_query: `flow=${flowId}`,
761
- sysparm_fields: 'order',
762
- sysparm_orderby: 'DESCorder',
763
- sysparm_limit: 1
764
- }
765
- });
766
- if (response.data.result && response.data.result.length > 0) {
767
- return parseInt(response.data.result[0].order || '0', 10);
768
- }
769
- }
770
- catch (error) {
771
- console.error('Failed to get max order:', error);
772
- }
773
- return 0;
774
- }
775
- // ==================== ACTION HANDLERS ====================
776
- /**
777
- * Create a new flow
778
- */
779
- async function handleCreate(args, client, versionInfo) {
780
- const { flow: flowConfig, trigger: triggerConfig, actions: actionConfigs = [], conditions = [], loops = [], variables = [], auto_activate = false, create_snapshot = true, use_compression, validate_only = false } = args;
781
- if (!flowConfig?.name) {
782
- return (0, error_handler_js_1.createErrorResult)('flow.name is required for create action');
783
- }
784
- const useCompression = use_compression ?? versionInfo.useCompression;
785
- const flowSysId = generateSysId();
786
- const snapshotSysId = generateSysId();
787
- const triggerSysId = generateSysId();
788
- const internalName = flowConfig.internal_name || generateInternalName(flowConfig.name);
789
- const timestamp = new Date().toISOString();
790
- if (validate_only) {
791
- return (0, error_handler_js_1.createSuccessResult)({
792
- valid: true,
793
- would_create: {
794
- flow: { sys_id: flowSysId, name: flowConfig.name, internal_name: internalName },
795
- snapshot: create_snapshot ? { sys_id: snapshotSysId } : null,
796
- trigger: triggerConfig ? { sys_id: triggerSysId, type: triggerConfig.type } : null,
797
- actions: actionConfigs.length,
798
- conditions: conditions.length,
799
- loops: loops.length,
800
- variables: variables.length
801
- },
802
- version_info: versionInfo,
803
- compression: useCompression
804
- });
805
- }
806
- const createdRecords = [];
807
- const errors = [];
808
- // 1. CREATE FLOW RECORD
809
- console.log(`Creating flow: ${flowConfig.name}`);
810
- const flowData = {
811
- sys_id: flowSysId,
812
- name: flowConfig.name,
813
- internal_name: internalName,
814
- description: flowConfig.description || '',
815
- active: false,
816
- sys_class_name: 'sys_hub_flow',
817
- type: 'flow',
818
- flow_type: 'flow',
819
- status: 'draft',
820
- source_ui: 'flow_designer',
821
- access: 'public',
822
- run_as: flowConfig.run_as || 'system'
823
- };
824
- if (flowConfig.application_scope && flowConfig.application_scope !== 'global') {
825
- flowData.sys_scope = flowConfig.application_scope;
826
- }
827
- if (flowConfig.run_as_user) {
828
- flowData.run_as_user = flowConfig.run_as_user;
829
- }
830
- try {
831
- const flowResponse = await client.post(`/api/now/table/${FLOW_TABLES.FLOW}`, flowData);
832
- createdRecords.push({ table: FLOW_TABLES.FLOW, sys_id: flowSysId, name: flowConfig.name });
833
- console.log(`✅ Created flow: ${flowSysId}`);
834
- }
835
- catch (error) {
836
- return (0, error_handler_js_1.createErrorResult)(`Failed to create flow: ${error.response?.data?.error?.message || error.message}`);
837
- }
838
- // 2. CREATE TRIGGER
839
- if (triggerConfig) {
840
- console.log(`Creating trigger: ${triggerConfig.type}`);
841
- const triggerTypeSysId = await getTriggerTypeSysId(client, triggerConfig.type, triggerConfig.when);
842
- if (!triggerTypeSysId) {
843
- errors.push(`Trigger type not found: ${triggerConfig.type}`);
844
- }
845
- else {
846
- const triggerTable = versionInfo.useV2Tables ? FLOW_TABLES.TRIGGER_V2 : FLOW_TABLES.TRIGGER_V1;
847
- const triggerData = {
848
- sys_id: triggerSysId,
849
- flow: flowSysId,
850
- trigger_type: triggerTypeSysId,
851
- active: true,
852
- order: 0
853
- };
854
- // Handle record triggers (both legacy 'record' type and new specific types)
855
- const isRecordTrigger = triggerConfig.type === 'record' ||
856
- triggerConfig.type === 'record_created' ||
857
- triggerConfig.type === 'record_updated' ||
858
- triggerConfig.type === 'record_deleted' ||
859
- triggerConfig.type === 'record_created_or_updated';
860
- if (isRecordTrigger) {
861
- if (triggerConfig.table)
862
- triggerData.table = triggerConfig.table;
863
- if (triggerConfig.condition) {
864
- const conditionValue = { filter_condition: { value: triggerConfig.condition } };
865
- triggerData.values = useCompression ? compressValue(conditionValue) : JSON.stringify(conditionValue);
866
- }
867
- }
868
- if (triggerConfig.type === 'scheduled' && triggerConfig.schedule) {
869
- const scheduleValues = {};
870
- if (triggerConfig.schedule.frequency)
871
- scheduleValues.frequency = { value: triggerConfig.schedule.frequency };
872
- if (triggerConfig.schedule.time)
873
- scheduleValues.time_of_day = { value: triggerConfig.schedule.time };
874
- if (triggerConfig.schedule.cron)
875
- scheduleValues.cron_expression = { value: triggerConfig.schedule.cron };
876
- triggerData.values = useCompression ? compressValue(scheduleValues) : JSON.stringify(scheduleValues);
877
- }
878
- try {
879
- await client.post(`/api/now/table/${triggerTable}`, triggerData);
880
- createdRecords.push({ table: triggerTable, sys_id: triggerSysId, type: triggerConfig.type });
881
- console.log(`✅ Created trigger: ${triggerSysId}`);
882
- }
883
- catch (error) {
884
- errors.push(`Failed to create trigger: ${error.response?.data?.error?.message || error.message}`);
885
- }
886
- }
887
- }
888
- // 3. CREATE VARIABLES
889
- for (let i = 0; i < variables.length; i++) {
890
- const variable = variables[i];
891
- const variableSysId = generateSysId();
892
- console.log(`Creating variable: ${variable.name}`);
893
- const typeMap = {
894
- 'string': 'string', 'integer': 'integer', 'boolean': 'boolean',
895
- 'reference': 'reference', 'object': 'object', 'array': 'array',
896
- 'glide_date_time': 'glide_date_time'
897
- };
898
- const variableData = {
899
- sys_id: variableSysId,
900
- flow: flowSysId,
901
- name: variable.name,
902
- label: variable.label || variable.name,
903
- description: variable.description || '',
904
- type: typeMap[variable.type] || 'string',
905
- direction: variable.direction,
906
- mandatory: variable.mandatory || false,
907
- order: i * 100
908
- };
909
- if (variable.default_value)
910
- variableData.default_value = variable.default_value;
911
- if (variable.reference_table)
912
- variableData.reference_table = variable.reference_table;
913
- try {
914
- await client.post(`/api/now/table/${FLOW_TABLES.VARIABLE}`, variableData);
915
- createdRecords.push({ table: FLOW_TABLES.VARIABLE, sys_id: variableSysId, name: variable.name });
916
- console.log(`✅ Created variable: ${variable.name}`);
917
- }
918
- catch (error) {
919
- errors.push(`Failed to create variable ${variable.name}: ${error.response?.data?.error?.message || error.message}`);
920
- }
921
- }
922
- // 4. CREATE CONDITIONS (with nested actions)
923
- const logicTable = versionInfo.useV2Tables ? FLOW_TABLES.LOGIC_V2 : FLOW_TABLES.LOGIC_V1;
924
- const actionTable = versionInfo.useV2Tables ? FLOW_TABLES.ACTION_V2 : FLOW_TABLES.ACTION_V1;
925
- let currentOrder = 100;
926
- for (const condition of conditions) {
927
- const conditionSysId = generateSysId();
928
- console.log(`Creating condition: ${condition.name || 'Condition'}`);
929
- const conditionData = {
930
- sys_id: conditionSysId,
931
- flow: flowSysId,
932
- name: condition.name || 'If',
933
- logic_type: 'if',
934
- order: condition.order ?? currentOrder,
935
- active: true
936
- };
937
- // Build condition values
938
- const conditionValues = {
939
- condition: { value: condition.condition, valueType: 'string' }
940
- };
941
- conditionData.values = useCompression ? compressValue(conditionValues) : JSON.stringify(conditionValues);
942
- if (condition.parent_logic_instance) {
943
- conditionData.parent_logic_instance = condition.parent_logic_instance;
944
- }
945
- try {
946
- await client.post(`/api/now/table/${logicTable}`, conditionData);
947
- createdRecords.push({ table: logicTable, sys_id: conditionSysId, name: condition.name || 'If', type: 'condition' });
948
- console.log(`✅ Created condition: ${conditionSysId}`);
949
- // Create THEN branch actions
950
- if (condition.then_actions && condition.then_actions.length > 0) {
951
- let thenOrder = 100;
952
- for (const thenAction of condition.then_actions) {
953
- const actionSysId = generateSysId();
954
- const actionType = await getActionTypeSysId(client, thenAction.type);
955
- if (actionType) {
956
- const actionData = {
957
- sys_id: actionSysId,
958
- flow: flowSysId,
959
- action_type: actionType.sys_id,
960
- name: thenAction.name || actionType.name,
961
- description: thenAction.description || '',
962
- order: thenOrder,
963
- active: true,
964
- parent_logic_instance: conditionSysId,
965
- branch: 'then'
966
- };
967
- if (thenAction.inputs) {
968
- actionData.values = buildActionValues(thenAction.inputs, useCompression);
969
- }
970
- try {
971
- await client.post(`/api/now/table/${actionTable}`, actionData);
972
- createdRecords.push({
973
- table: actionTable,
974
- sys_id: actionSysId,
975
- name: thenAction.name || actionType.name,
976
- parent: conditionSysId,
977
- branch: 'then'
978
- });
979
- console.log(`✅ Created then-action: ${actionSysId}`);
980
- }
981
- catch (error) {
982
- errors.push(`Failed to create then-action: ${error.response?.data?.error?.message || error.message}`);
983
- }
984
- }
985
- thenOrder += 100;
986
- }
987
- }
988
- // Create ELSE branch actions
989
- if (condition.else_actions && condition.else_actions.length > 0) {
990
- let elseOrder = 100;
991
- for (const elseAction of condition.else_actions) {
992
- const actionSysId = generateSysId();
993
- const actionType = await getActionTypeSysId(client, elseAction.type);
994
- if (actionType) {
995
- const actionData = {
996
- sys_id: actionSysId,
997
- flow: flowSysId,
998
- action_type: actionType.sys_id,
999
- name: elseAction.name || actionType.name,
1000
- description: elseAction.description || '',
1001
- order: elseOrder,
1002
- active: true,
1003
- parent_logic_instance: conditionSysId,
1004
- branch: 'else'
1005
- };
1006
- if (elseAction.inputs) {
1007
- actionData.values = buildActionValues(elseAction.inputs, useCompression);
1008
- }
1009
- try {
1010
- await client.post(`/api/now/table/${actionTable}`, actionData);
1011
- createdRecords.push({
1012
- table: actionTable,
1013
- sys_id: actionSysId,
1014
- name: elseAction.name || actionType.name,
1015
- parent: conditionSysId,
1016
- branch: 'else'
1017
- });
1018
- console.log(`✅ Created else-action: ${actionSysId}`);
1019
- }
1020
- catch (error) {
1021
- errors.push(`Failed to create else-action: ${error.response?.data?.error?.message || error.message}`);
1022
- }
1023
- }
1024
- elseOrder += 100;
1025
- }
1026
- }
1027
- }
1028
- catch (error) {
1029
- errors.push(`Failed to create condition: ${error.response?.data?.error?.message || error.message}`);
1030
- }
1031
- currentOrder += 100;
1032
- }
1033
- // 5. CREATE LOOPS (with nested actions)
1034
- for (const loop of loops) {
1035
- const loopSysId = generateSysId();
1036
- console.log(`Creating loop: ${loop.name || loop.type}`);
1037
- const loopData = {
1038
- sys_id: loopSysId,
1039
- flow: flowSysId,
1040
- name: loop.name || (loop.type === 'for_each' ? 'For Each' : 'Do Until'),
1041
- logic_type: loop.type,
1042
- order: loop.order ?? currentOrder,
1043
- active: true
1044
- };
1045
- // Build loop values
1046
- const loopValues = {};
1047
- if (loop.data_source)
1048
- loopValues.data_source = { value: loop.data_source, valueType: 'reference' };
1049
- if (loop.condition)
1050
- loopValues.condition = { value: loop.condition, valueType: 'string' };
1051
- if (loop.max_iterations)
1052
- loopValues.max_iterations = { value: String(loop.max_iterations), valueType: 'integer' };
1053
- if (Object.keys(loopValues).length > 0) {
1054
- loopData.values = useCompression ? compressValue(loopValues) : JSON.stringify(loopValues);
1055
- }
1056
- if (loop.parent_logic_instance) {
1057
- loopData.parent_logic_instance = loop.parent_logic_instance;
1058
- }
1059
- try {
1060
- await client.post(`/api/now/table/${logicTable}`, loopData);
1061
- createdRecords.push({ table: logicTable, sys_id: loopSysId, name: loop.name || loop.type, type: 'loop' });
1062
- console.log(`✅ Created loop: ${loopSysId}`);
1063
- // Create nested actions within loop
1064
- if (loop.actions && loop.actions.length > 0) {
1065
- let loopActionOrder = 100;
1066
- for (const loopAction of loop.actions) {
1067
- const actionSysId = generateSysId();
1068
- const actionType = await getActionTypeSysId(client, loopAction.type);
1069
- if (actionType) {
1070
- const actionData = {
1071
- sys_id: actionSysId,
1072
- flow: flowSysId,
1073
- action_type: actionType.sys_id,
1074
- name: loopAction.name || actionType.name,
1075
- description: loopAction.description || '',
1076
- order: loopActionOrder,
1077
- active: true,
1078
- parent_logic_instance: loopSysId
1079
- };
1080
- if (loopAction.inputs) {
1081
- actionData.values = buildActionValues(loopAction.inputs, useCompression);
1082
- }
1083
- try {
1084
- await client.post(`/api/now/table/${actionTable}`, actionData);
1085
- createdRecords.push({
1086
- table: actionTable,
1087
- sys_id: actionSysId,
1088
- name: loopAction.name || actionType.name,
1089
- parent: loopSysId
1090
- });
1091
- console.log(`✅ Created loop-action: ${actionSysId}`);
1092
- }
1093
- catch (error) {
1094
- errors.push(`Failed to create loop-action: ${error.response?.data?.error?.message || error.message}`);
1095
- }
1096
- }
1097
- loopActionOrder += 100;
1098
- }
1099
- }
1100
- }
1101
- catch (error) {
1102
- errors.push(`Failed to create loop: ${error.response?.data?.error?.message || error.message}`);
1103
- }
1104
- currentOrder += 100;
1105
- }
1106
- // 6. CREATE TOP-LEVEL ACTIONS (not nested in conditions/loops)
1107
- for (let i = 0; i < actionConfigs.length; i++) {
1108
- const actionConfig = actionConfigs[i];
1109
- // Skip if this action is meant to be nested (has parent)
1110
- if (actionConfig.parent_logic_instance)
1111
- continue;
1112
- const actionSysId = generateSysId();
1113
- console.log(`Creating action: ${actionConfig.type}`);
1114
- const actionType = await getActionTypeSysId(client, actionConfig.type);
1115
- if (!actionType) {
1116
- errors.push(`Action type not found: ${actionConfig.type}`);
1117
- continue;
1118
- }
1119
- const actionData = {
1120
- sys_id: actionSysId,
1121
- flow: flowSysId,
1122
- action_type: actionType.sys_id,
1123
- name: actionConfig.name || actionType.name,
1124
- description: actionConfig.description || '',
1125
- order: actionConfig.order ?? currentOrder,
1126
- active: true
1127
- };
1128
- if (actionConfig.inputs) {
1129
- actionData.values = buildActionValues(actionConfig.inputs, useCompression);
1130
- }
1131
- try {
1132
- await client.post(`/api/now/table/${actionTable}`, actionData);
1133
- createdRecords.push({ table: actionTable, sys_id: actionSysId, name: actionConfig.name || actionType.name });
1134
- console.log(`✅ Created action: ${actionSysId}`);
1135
- }
1136
- catch (error) {
1137
- errors.push(`Failed to create action ${actionConfig.type}: ${error.response?.data?.error?.message || error.message}`);
1138
- }
1139
- currentOrder += 100;
1140
- }
1141
- // 7. CREATE SNAPSHOT
1142
- if (create_snapshot) {
1143
- console.log('Creating snapshot...');
1144
- const snapshotData = {
1145
- sys_id: snapshotSysId,
1146
- flow: flowSysId,
1147
- name: `${flowConfig.name} - Initial`,
1148
- version: '1.0.0',
1149
- active: true,
1150
- published: false
1151
- };
1152
- try {
1153
- await client.post(`/api/now/table/${FLOW_TABLES.SNAPSHOT}`, snapshotData);
1154
- createdRecords.push({ table: FLOW_TABLES.SNAPSHOT, sys_id: snapshotSysId, name: `${flowConfig.name} - Initial` });
1155
- console.log(`✅ Created snapshot: ${snapshotSysId}`);
1156
- }
1157
- catch (error) {
1158
- errors.push(`Failed to create snapshot: ${error.response?.data?.error?.message || error.message}`);
1159
- }
1160
- }
1161
- // 8. ACTIVATE IF REQUESTED
1162
- if (auto_activate && errors.length === 0) {
1163
- console.log('Activating flow...');
1164
- try {
1165
- await client.patch(`/api/now/table/${FLOW_TABLES.FLOW}/${flowSysId}`, {
1166
- active: true,
1167
- status: 'published'
1168
- });
1169
- console.log(`✅ Flow activated`);
1170
- }
1171
- catch (error) {
1172
- errors.push(`Failed to activate: ${error.response?.data?.error?.message || error.message}`);
1173
- }
1174
- }
1175
- const instanceUrl = (client.defaults?.baseURL || '').replace('/api/now', '');
1176
- return (0, error_handler_js_1.createSuccessResult)({
1177
- action: 'create',
1178
- flow: {
1179
- sys_id: flowSysId,
1180
- name: flowConfig.name,
1181
- internal_name: internalName,
1182
- active: auto_activate && errors.length === 0,
1183
- url: `${instanceUrl}/sys_hub_flow.do?sys_id=${flowSysId}`,
1184
- flow_designer_url: `${instanceUrl}/nav_to.do?uri=/$flow-designer.do#/flow/${flowSysId}`
1185
- },
1186
- created_records: createdRecords,
1187
- version_info: versionInfo,
1188
- compression_used: useCompression,
1189
- errors: errors.length > 0 ? errors : undefined
1190
- });
1191
- }
1192
- /**
1193
- * Update an existing flow
1194
- */
1195
- async function handleUpdate(args, client, versionInfo) {
1196
- const { flow_id, flow_name, updates } = args;
1197
- const flow = await findFlow(client, flow_id, flow_name);
1198
- if (!flow) {
1199
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1200
- }
1201
- if (!updates || Object.keys(updates).length === 0) {
1202
- return (0, error_handler_js_1.createErrorResult)('No updates provided');
1203
- }
1204
- try {
1205
- await client.patch(`/api/now/table/${FLOW_TABLES.FLOW}/${flow.sys_id}`, updates);
1206
- return (0, error_handler_js_1.createSuccessResult)({
1207
- action: 'update',
1208
- flow_id: flow.sys_id,
1209
- updated_fields: Object.keys(updates)
1210
- });
1211
- }
1212
- catch (error) {
1213
- return (0, error_handler_js_1.createErrorResult)(`Failed to update flow: ${error.response?.data?.error?.message || error.message}`);
1214
- }
1215
- }
1216
- /**
1217
- * Delete a flow and all related records
1218
- *
1219
- * IMPORTANT: Delete order respects parent-child hierarchy:
1220
- * 1. Actions (children of logic)
1221
- * 2. Subflow instances (can be nested in logic)
1222
- * 3. Logic instances - hierarchically (children before parents)
1223
- * 4. Triggers
1224
- * 5. Variables
1225
- * 6. Snapshots
1226
- * 7. Flow record (last)
1227
- */
1228
- async function handleDelete(args, client, versionInfo) {
1229
- const { flow_id, flow_name } = args;
1230
- const flow = await findFlow(client, flow_id, flow_name);
1231
- if (!flow) {
1232
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1233
- }
1234
- const deletedRecords = [];
1235
- const errors = [];
1236
- const actionTable = versionInfo.useV2Tables ? FLOW_TABLES.ACTION_V2 : FLOW_TABLES.ACTION_V1;
1237
- const logicTable = versionInfo.useV2Tables ? FLOW_TABLES.LOGIC_V2 : FLOW_TABLES.LOGIC_V1;
1238
- const triggerTable = versionInfo.useV2Tables ? FLOW_TABLES.TRIGGER_V2 : FLOW_TABLES.TRIGGER_V1;
1239
- // Helper function to delete records from a table
1240
- const deleteFromTable = async (table, query) => {
1241
- try {
1242
- const response = await client.get(`/api/now/table/${table}`, {
1243
- params: { sysparm_query: query, sysparm_fields: 'sys_id' }
1244
- });
1245
- if (response.data.result) {
1246
- for (const record of response.data.result) {
1247
- try {
1248
- await client.delete(`/api/now/table/${table}/${record.sys_id}`);
1249
- deletedRecords.push({ table, sys_id: record.sys_id });
1250
- }
1251
- catch (e) {
1252
- errors.push(`Failed to delete ${table}/${record.sys_id}: ${e.message}`);
1253
- }
1254
- }
1255
- }
1256
- }
1257
- catch (e) {
1258
- // Table might not exist or be empty - this is OK
1259
- }
1260
- };
1261
- console.log(`Deleting flow: ${flow.name} (${flow.sys_id})`);
1262
- // 1. DELETE ACTIONS (children of logic instances)
1263
- console.log('Deleting actions...');
1264
- await deleteFromTable(actionTable, `flow=${flow.sys_id}`);
1265
- // 2. DELETE SUBFLOW INSTANCES (can be nested in logic)
1266
- console.log('Deleting subflow instances...');
1267
- await deleteFromTable(FLOW_TABLES.SUBFLOW, `flow=${flow.sys_id}`);
1268
- // 3. DELETE LOGIC INSTANCES - HIERARCHICALLY
1269
- // First delete children (items WITH parent_logic_instance), then parents
1270
- console.log('Deleting logic instances (hierarchically)...');
1271
- try {
1272
- const logicResponse = await client.get(`/api/now/table/${logicTable}`, {
1273
- params: {
1274
- sysparm_query: `flow=${flow.sys_id}`,
1275
- sysparm_fields: 'sys_id,parent_logic_instance'
1276
- }
1277
- });
1278
- if (logicResponse.data.result) {
1279
- const logicItems = logicResponse.data.result;
1280
- // Separate into children (with parent) and parents (without parent)
1281
- const children = logicItems.filter((item) => item.parent_logic_instance);
1282
- const parents = logicItems.filter((item) => !item.parent_logic_instance);
1283
- // Build hierarchy depth map for proper ordering
1284
- const depthMap = new Map();
1285
- const getDepth = (item, visited = new Set()) => {
1286
- if (!item.parent_logic_instance)
1287
- return 0;
1288
- if (visited.has(item.sys_id))
1289
- return 0; // Prevent infinite loops
1290
- visited.add(item.sys_id);
1291
- const parent = logicItems.find((p) => p.sys_id === item.parent_logic_instance);
1292
- if (!parent)
1293
- return 1;
1294
- return 1 + getDepth(parent, visited);
1295
- };
1296
- // Calculate depth for each item
1297
- for (const item of logicItems) {
1298
- depthMap.set(item.sys_id, getDepth(item));
1299
- }
1300
- // Sort by depth descending (deepest children first)
1301
- const sortedLogic = [...logicItems].sort((a, b) => {
1302
- const depthA = depthMap.get(a.sys_id) || 0;
1303
- const depthB = depthMap.get(b.sys_id) || 0;
1304
- return depthB - depthA; // Deepest first
1305
- });
1306
- // Delete in sorted order (deepest children first)
1307
- for (const logic of sortedLogic) {
1308
- try {
1309
- await client.delete(`/api/now/table/${logicTable}/${logic.sys_id}`);
1310
- deletedRecords.push({ table: logicTable, sys_id: logic.sys_id });
1311
- }
1312
- catch (e) {
1313
- errors.push(`Failed to delete logic ${logic.sys_id}: ${e.message}`);
1314
- }
1315
- }
1316
- }
1317
- }
1318
- catch (e) {
1319
- errors.push(`Failed to fetch logic instances: ${e.message}`);
1320
- }
1321
- // 4. DELETE TRIGGERS
1322
- console.log('Deleting triggers...');
1323
- await deleteFromTable(triggerTable, `flow=${flow.sys_id}`);
1324
- // 5. DELETE VARIABLES
1325
- console.log('Deleting variables...');
1326
- await deleteFromTable(FLOW_TABLES.VARIABLE, `flow=${flow.sys_id}`);
1327
- // 6. DELETE SNAPSHOTS
1328
- console.log('Deleting snapshots...');
1329
- await deleteFromTable(FLOW_TABLES.SNAPSHOT, `flow=${flow.sys_id}`);
1330
- // 7. DELETE FLOW RECORD (last)
1331
- console.log('Deleting flow record...');
1332
- try {
1333
- await client.delete(`/api/now/table/${FLOW_TABLES.FLOW}/${flow.sys_id}`);
1334
- deletedRecords.push({ table: FLOW_TABLES.FLOW, sys_id: flow.sys_id });
1335
- console.log(`✅ Flow deleted: ${flow.sys_id}`);
1336
- }
1337
- catch (error) {
1338
- return (0, error_handler_js_1.createErrorResult)(`Failed to delete flow: ${error.response?.data?.error?.message || error.message}`);
1339
- }
1340
- return (0, error_handler_js_1.createSuccessResult)({
1341
- action: 'delete',
1342
- flow_id: flow.sys_id,
1343
- flow_name: flow.name,
1344
- deleted_records: deletedRecords,
1345
- delete_order: [
1346
- 'actions',
1347
- 'subflow_instances',
1348
- 'logic_instances (hierarchical)',
1349
- 'triggers',
1350
- 'variables',
1351
- 'snapshots',
1352
- 'flow'
1353
- ],
1354
- errors: errors.length > 0 ? errors : undefined
1355
- });
1356
- }
1357
- /**
1358
- * Clone a flow - FULL IMPLEMENTATION
1359
- * Copies ALL components: flow, triggers, actions, logic, variables, subflows
1360
- * Maintains parent-child relationships and remaps sys_ids
1361
- */
1362
- async function handleClone(args, client, versionInfo) {
1363
- const { flow_id, flow_name, new_name, use_compression, create_snapshot = true } = args;
1364
- const sourceFlow = await findFlow(client, flow_id, flow_name);
1365
- if (!sourceFlow) {
1366
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1367
- }
1368
- if (!new_name) {
1369
- return (0, error_handler_js_1.createErrorResult)('new_name is required for clone action');
1370
- }
1371
- const useCompression = use_compression ?? versionInfo.useCompression;
1372
- const triggerTable = versionInfo.useV2Tables ? FLOW_TABLES.TRIGGER_V2 : FLOW_TABLES.TRIGGER_V1;
1373
- const actionTable = versionInfo.useV2Tables ? FLOW_TABLES.ACTION_V2 : FLOW_TABLES.ACTION_V1;
1374
- const logicTable = versionInfo.useV2Tables ? FLOW_TABLES.LOGIC_V2 : FLOW_TABLES.LOGIC_V1;
1375
- // Map old sys_ids to new sys_ids for remapping references
1376
- const sysIdMap = new Map();
1377
- const createdRecords = [];
1378
- const errors = [];
1379
- // Generate new IDs
1380
- const newFlowId = generateSysId();
1381
- const newInternalName = generateInternalName(new_name);
1382
- sysIdMap.set(sourceFlow.sys_id, newFlowId);
1383
- console.log(`Cloning flow: ${sourceFlow.name} -> ${new_name}`);
1384
- // ==================== 1. CLONE FLOW RECORD ====================
1385
- const newFlowData = {
1386
- sys_id: newFlowId,
1387
- name: new_name,
1388
- internal_name: newInternalName,
1389
- description: `Cloned from: ${sourceFlow.name}\n\n${sourceFlow.description || ''}`,
1390
- active: false,
1391
- sys_class_name: 'sys_hub_flow',
1392
- type: sourceFlow.type || 'flow',
1393
- flow_type: sourceFlow.flow_type || 'flow',
1394
- status: 'draft',
1395
- source_ui: 'flow_designer',
1396
- access: sourceFlow.access || 'public',
1397
- run_as: sourceFlow.run_as || 'system'
1398
- };
1399
- if (sourceFlow.sys_scope) {
1400
- newFlowData.sys_scope = sourceFlow.sys_scope;
1401
- }
1402
- if (sourceFlow.run_as_user) {
1403
- newFlowData.run_as_user = sourceFlow.run_as_user;
1404
- }
1405
- try {
1406
- await client.post(`/api/now/table/${FLOW_TABLES.FLOW}`, newFlowData);
1407
- createdRecords.push({ table: FLOW_TABLES.FLOW, sys_id: newFlowId, name: new_name, type: 'flow' });
1408
- console.log(`✅ Cloned flow record: ${newFlowId}`);
1409
- }
1410
- catch (error) {
1411
- return (0, error_handler_js_1.createErrorResult)(`Failed to create cloned flow: ${error.response?.data?.error?.message || error.message}`);
1412
- }
1413
- // ==================== 2. CLONE VARIABLES ====================
1414
- try {
1415
- const variablesResponse = await client.get(`/api/now/table/${FLOW_TABLES.VARIABLE}`, {
1416
- params: { sysparm_query: `flow=${sourceFlow.sys_id}`, sysparm_orderby: 'order' }
1417
- });
1418
- if (variablesResponse.data.result) {
1419
- for (const variable of variablesResponse.data.result) {
1420
- const newVariableId = generateSysId();
1421
- sysIdMap.set(variable.sys_id, newVariableId);
1422
- const newVariable = {
1423
- sys_id: newVariableId,
1424
- flow: newFlowId,
1425
- name: variable.name,
1426
- label: variable.label,
1427
- description: variable.description,
1428
- type: variable.type,
1429
- direction: variable.direction,
1430
- mandatory: variable.mandatory,
1431
- order: variable.order,
1432
- default_value: variable.default_value,
1433
- reference_table: variable.reference_table
1434
- };
1435
- try {
1436
- await client.post(`/api/now/table/${FLOW_TABLES.VARIABLE}`, newVariable);
1437
- createdRecords.push({
1438
- table: FLOW_TABLES.VARIABLE,
1439
- sys_id: newVariableId,
1440
- name: variable.name,
1441
- type: 'variable'
1442
- });
1443
- console.log(`✅ Cloned variable: ${variable.name}`);
1444
- }
1445
- catch (e) {
1446
- errors.push(`Failed to clone variable ${variable.name}: ${e.message}`);
1447
- }
1448
- }
1449
- }
1450
- }
1451
- catch (e) {
1452
- errors.push(`Failed to fetch variables: ${e.message}`);
1453
- }
1454
- // ==================== 3. CLONE TRIGGERS ====================
1455
- try {
1456
- const triggersResponse = await client.get(`/api/now/table/${triggerTable}`, {
1457
- params: { sysparm_query: `flow=${sourceFlow.sys_id}` }
1458
- });
1459
- if (triggersResponse.data.result) {
1460
- for (const trigger of triggersResponse.data.result) {
1461
- const newTriggerId = generateSysId();
1462
- sysIdMap.set(trigger.sys_id, newTriggerId);
1463
- // Decompress and remap values if needed
1464
- let newValues = trigger.values;
1465
- if (newValues) {
1466
- if (isCompressed(newValues)) {
1467
- const decompressed = decompressValue(newValues);
1468
- const remapped = remapValuesForClone(decompressed, sysIdMap, sourceFlow.sys_id, newFlowId);
1469
- newValues = useCompression ? compressValue(remapped) : remapped;
1470
- }
1471
- else {
1472
- newValues = remapValuesForClone(newValues, sysIdMap, sourceFlow.sys_id, newFlowId);
1473
- if (useCompression)
1474
- newValues = compressValue(newValues);
1475
- }
1476
- }
1477
- const newTrigger = {
1478
- sys_id: newTriggerId,
1479
- flow: newFlowId,
1480
- trigger_type: trigger.trigger_type,
1481
- table: trigger.table,
1482
- active: trigger.active,
1483
- order: trigger.order,
1484
- values: newValues
1485
- };
1486
- try {
1487
- await client.post(`/api/now/table/${triggerTable}`, newTrigger);
1488
- createdRecords.push({
1489
- table: triggerTable,
1490
- sys_id: newTriggerId,
1491
- type: 'trigger',
1492
- trigger_type: trigger.trigger_type
1493
- });
1494
- console.log(`✅ Cloned trigger: ${newTriggerId}`);
1495
- }
1496
- catch (e) {
1497
- errors.push(`Failed to clone trigger: ${e.message}`);
1498
- }
1499
- }
1500
- }
1501
- }
1502
- catch (e) {
1503
- errors.push(`Failed to fetch triggers: ${e.message}`);
1504
- }
1505
- // ==================== 4. CLONE LOGIC (CONDITIONS/LOOPS) ====================
1506
- // First pass: create all logic instances and map sys_ids
1507
- const logicItems = [];
1508
- try {
1509
- const logicResponse = await client.get(`/api/now/table/${logicTable}`, {
1510
- params: { sysparm_query: `flow=${sourceFlow.sys_id}`, sysparm_orderby: 'order' }
1511
- });
1512
- if (logicResponse.data.result) {
1513
- // First pass: generate new IDs for all logic items
1514
- for (const logic of logicResponse.data.result) {
1515
- const newLogicId = generateSysId();
1516
- sysIdMap.set(logic.sys_id, newLogicId);
1517
- logicItems.push({ ...logic, newSysId: newLogicId });
1518
- }
1519
- // Second pass: create logic items with remapped parent references
1520
- for (const logic of logicItems) {
1521
- // Decompress and remap values
1522
- let newValues = logic.values;
1523
- if (newValues) {
1524
- if (isCompressed(newValues)) {
1525
- const decompressed = decompressValue(newValues);
1526
- const remapped = remapValuesForClone(decompressed, sysIdMap, sourceFlow.sys_id, newFlowId);
1527
- newValues = useCompression ? compressValue(remapped) : remapped;
1528
- }
1529
- else {
1530
- newValues = remapValuesForClone(newValues, sysIdMap, sourceFlow.sys_id, newFlowId);
1531
- if (useCompression)
1532
- newValues = compressValue(newValues);
1533
- }
1534
- }
1535
- // Remap parent_logic_instance if it exists
1536
- let newParentLogic = null;
1537
- if (logic.parent_logic_instance) {
1538
- newParentLogic = sysIdMap.get(logic.parent_logic_instance) || logic.parent_logic_instance;
1539
- }
1540
- const newLogic = {
1541
- sys_id: logic.newSysId,
1542
- flow: newFlowId,
1543
- name: logic.name,
1544
- logic_type: logic.logic_type,
1545
- order: logic.order,
1546
- active: logic.active,
1547
- values: newValues,
1548
- condition: logic.condition
1549
- };
1550
- if (newParentLogic) {
1551
- newLogic.parent_logic_instance = newParentLogic;
1552
- }
1553
- if (logic.branch) {
1554
- newLogic.branch = logic.branch;
1555
- }
1556
- try {
1557
- await client.post(`/api/now/table/${logicTable}`, newLogic);
1558
- createdRecords.push({
1559
- table: logicTable,
1560
- sys_id: logic.newSysId,
1561
- name: logic.name,
1562
- type: 'logic',
1563
- logic_type: logic.logic_type
1564
- });
1565
- console.log(`✅ Cloned logic: ${logic.name} (${logic.logic_type})`);
1566
- }
1567
- catch (e) {
1568
- errors.push(`Failed to clone logic ${logic.name}: ${e.message}`);
1569
- }
1570
- }
1571
- }
1572
- }
1573
- catch (e) {
1574
- errors.push(`Failed to fetch logic: ${e.message}`);
1575
- }
1576
- // ==================== 5. CLONE ACTIONS ====================
1577
- try {
1578
- const actionsResponse = await client.get(`/api/now/table/${actionTable}`, {
1579
- params: { sysparm_query: `flow=${sourceFlow.sys_id}`, sysparm_orderby: 'order' }
1580
- });
1581
- if (actionsResponse.data.result) {
1582
- // First pass: generate new IDs
1583
- const actionItems = [];
1584
- for (const action of actionsResponse.data.result) {
1585
- const newActionId = generateSysId();
1586
- sysIdMap.set(action.sys_id, newActionId);
1587
- actionItems.push({ ...action, newSysId: newActionId });
1588
- }
1589
- // Second pass: create actions with remapped references
1590
- for (const action of actionItems) {
1591
- // Decompress and remap values
1592
- let newValues = action.values;
1593
- if (newValues) {
1594
- if (isCompressed(newValues)) {
1595
- const decompressed = decompressValue(newValues);
1596
- const remapped = remapValuesForClone(decompressed, sysIdMap, sourceFlow.sys_id, newFlowId);
1597
- newValues = useCompression ? compressValue(remapped) : remapped;
1598
- }
1599
- else {
1600
- newValues = remapValuesForClone(newValues, sysIdMap, sourceFlow.sys_id, newFlowId);
1601
- if (useCompression)
1602
- newValues = compressValue(newValues);
1603
- }
1604
- }
1605
- // Remap parent_logic_instance if it exists
1606
- let newParentLogic = null;
1607
- if (action.parent_logic_instance) {
1608
- newParentLogic = sysIdMap.get(action.parent_logic_instance) || action.parent_logic_instance;
1609
- }
1610
- const newAction = {
1611
- sys_id: action.newSysId,
1612
- flow: newFlowId,
1613
- action_type: action.action_type,
1614
- name: action.name,
1615
- description: action.description,
1616
- order: action.order,
1617
- active: action.active,
1618
- values: newValues
1619
- };
1620
- if (newParentLogic) {
1621
- newAction.parent_logic_instance = newParentLogic;
1622
- }
1623
- if (action.branch) {
1624
- newAction.branch = action.branch;
1625
- }
1626
- try {
1627
- await client.post(`/api/now/table/${actionTable}`, newAction);
1628
- createdRecords.push({
1629
- table: actionTable,
1630
- sys_id: action.newSysId,
1631
- name: action.name,
1632
- type: 'action',
1633
- action_type: action.action_type
1634
- });
1635
- console.log(`✅ Cloned action: ${action.name}`);
1636
- }
1637
- catch (e) {
1638
- errors.push(`Failed to clone action ${action.name}: ${e.message}`);
1639
- }
1640
- }
1641
- }
1642
- }
1643
- catch (e) {
1644
- errors.push(`Failed to fetch actions: ${e.message}`);
1645
- }
1646
- // ==================== 6. CLONE SUBFLOW INSTANCES ====================
1647
- try {
1648
- const subflowsResponse = await client.get(`/api/now/table/${FLOW_TABLES.SUBFLOW}`, {
1649
- params: { sysparm_query: `flow=${sourceFlow.sys_id}` }
1650
- });
1651
- if (subflowsResponse.data.result) {
1652
- for (const subflow of subflowsResponse.data.result) {
1653
- const newSubflowId = generateSysId();
1654
- sysIdMap.set(subflow.sys_id, newSubflowId);
1655
- // Decompress and remap values
1656
- let newValues = subflow.values;
1657
- if (newValues) {
1658
- if (isCompressed(newValues)) {
1659
- const decompressed = decompressValue(newValues);
1660
- const remapped = remapValuesForClone(decompressed, sysIdMap, sourceFlow.sys_id, newFlowId);
1661
- newValues = useCompression ? compressValue(remapped) : remapped;
1662
- }
1663
- else {
1664
- newValues = remapValuesForClone(newValues, sysIdMap, sourceFlow.sys_id, newFlowId);
1665
- if (useCompression)
1666
- newValues = compressValue(newValues);
1667
- }
1668
- }
1669
- // Remap parent_logic_instance if it exists
1670
- let newParentLogic = null;
1671
- if (subflow.parent_logic_instance) {
1672
- newParentLogic = sysIdMap.get(subflow.parent_logic_instance) || subflow.parent_logic_instance;
1673
- }
1674
- const newSubflow = {
1675
- sys_id: newSubflowId,
1676
- flow: newFlowId,
1677
- sub_flow: subflow.sub_flow, // Reference to the actual subflow definition
1678
- name: subflow.name,
1679
- description: subflow.description,
1680
- order: subflow.order,
1681
- active: subflow.active,
1682
- values: newValues
1683
- };
1684
- if (newParentLogic) {
1685
- newSubflow.parent_logic_instance = newParentLogic;
1686
- }
1687
- if (subflow.branch) {
1688
- newSubflow.branch = subflow.branch;
1689
- }
1690
- try {
1691
- await client.post(`/api/now/table/${FLOW_TABLES.SUBFLOW}`, newSubflow);
1692
- createdRecords.push({
1693
- table: FLOW_TABLES.SUBFLOW,
1694
- sys_id: newSubflowId,
1695
- name: subflow.name,
1696
- type: 'subflow_instance',
1697
- sub_flow: subflow.sub_flow
1698
- });
1699
- console.log(`✅ Cloned subflow instance: ${subflow.name}`);
1700
- }
1701
- catch (e) {
1702
- errors.push(`Failed to clone subflow instance ${subflow.name}: ${e.message}`);
1703
- }
1704
- }
1705
- }
1706
- }
1707
- catch (e) {
1708
- // Subflow table might not exist or be empty
1709
- }
1710
- // ==================== 7. CREATE SNAPSHOT ====================
1711
- if (create_snapshot) {
1712
- const snapshotId = generateSysId();
1713
- const snapshotData = {
1714
- sys_id: snapshotId,
1715
- flow: newFlowId,
1716
- name: `${new_name} - Initial (Cloned)`,
1717
- version: '1.0.0',
1718
- active: true,
1719
- published: false
1720
- };
1721
- try {
1722
- await client.post(`/api/now/table/${FLOW_TABLES.SNAPSHOT}`, snapshotData);
1723
- createdRecords.push({
1724
- table: FLOW_TABLES.SNAPSHOT,
1725
- sys_id: snapshotId,
1726
- name: snapshotData.name,
1727
- type: 'snapshot'
1728
- });
1729
- console.log(`✅ Created snapshot: ${snapshotId}`);
1730
- }
1731
- catch (e) {
1732
- errors.push(`Failed to create snapshot: ${e.message}`);
1733
- }
1734
- }
1735
- // ==================== BUILD RESULT ====================
1736
- const instanceUrl = (client.defaults?.baseURL || '').replace('/api/now', '');
1737
- // Summary statistics
1738
- const summary = {
1739
- flow: 1,
1740
- variables: createdRecords.filter(r => r.type === 'variable').length,
1741
- triggers: createdRecords.filter(r => r.type === 'trigger').length,
1742
- logic: createdRecords.filter(r => r.type === 'logic').length,
1743
- actions: createdRecords.filter(r => r.type === 'action').length,
1744
- subflow_instances: createdRecords.filter(r => r.type === 'subflow_instance').length,
1745
- snapshots: createdRecords.filter(r => r.type === 'snapshot').length,
1746
- total: createdRecords.length
1747
- };
1748
- return (0, error_handler_js_1.createSuccessResult)({
1749
- action: 'clone',
1750
- source_flow: {
1751
- sys_id: sourceFlow.sys_id,
1752
- name: sourceFlow.name
1753
- },
1754
- cloned_flow: {
1755
- sys_id: newFlowId,
1756
- name: new_name,
1757
- internal_name: newInternalName,
1758
- active: false,
1759
- url: `${instanceUrl}/sys_hub_flow.do?sys_id=${newFlowId}`,
1760
- flow_designer_url: `${instanceUrl}/nav_to.do?uri=/$flow-designer.do#/flow/${newFlowId}`
1761
- },
1762
- summary,
1763
- created_records: createdRecords,
1764
- sys_id_mapping: Object.fromEntries(sysIdMap),
1765
- version_info: versionInfo,
1766
- compression_used: useCompression,
1767
- errors: errors.length > 0 ? errors : undefined
1768
- });
1769
- }
1770
- /**
1771
- * Activate a flow
1772
- */
1773
- async function handleActivate(args, client) {
1774
- const { flow_id, flow_name } = args;
1775
- const flow = await findFlow(client, flow_id, flow_name);
1776
- if (!flow) {
1777
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1778
- }
1779
- try {
1780
- await client.patch(`/api/now/table/${FLOW_TABLES.FLOW}/${flow.sys_id}`, {
1781
- active: true,
1782
- status: 'published'
1783
- });
1784
- return (0, error_handler_js_1.createSuccessResult)({
1785
- action: 'activate',
1786
- flow_id: flow.sys_id,
1787
- flow_name: flow.name,
1788
- active: true
1789
- });
1790
- }
1791
- catch (error) {
1792
- return (0, error_handler_js_1.createErrorResult)(`Failed to activate: ${error.response?.data?.error?.message || error.message}`);
1793
- }
1794
- }
1795
- /**
1796
- * Deactivate a flow
1797
- */
1798
- async function handleDeactivate(args, client) {
1799
- const { flow_id, flow_name } = args;
1800
- const flow = await findFlow(client, flow_id, flow_name);
1801
- if (!flow) {
1802
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1803
- }
1804
- try {
1805
- await client.patch(`/api/now/table/${FLOW_TABLES.FLOW}/${flow.sys_id}`, {
1806
- active: false
1807
- });
1808
- return (0, error_handler_js_1.createSuccessResult)({
1809
- action: 'deactivate',
1810
- flow_id: flow.sys_id,
1811
- flow_name: flow.name,
1812
- active: false
1813
- });
1814
- }
1815
- catch (error) {
1816
- return (0, error_handler_js_1.createErrorResult)(`Failed to deactivate: ${error.response?.data?.error?.message || error.message}`);
1817
- }
1818
- }
1819
- /**
1820
- * Add an action to an existing flow
1821
- */
1822
- async function handleAddAction(args, client, versionInfo) {
1823
- const { flow_id, flow_name, action_config, use_compression } = args;
1824
- const flow = await findFlow(client, flow_id, flow_name);
1825
- if (!flow) {
1826
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1827
- }
1828
- if (!action_config?.type) {
1829
- return (0, error_handler_js_1.createErrorResult)('action_config.type is required');
1830
- }
1831
- const useCompression = use_compression ?? versionInfo.useCompression;
1832
- const actionTable = versionInfo.useV2Tables ? FLOW_TABLES.ACTION_V2 : FLOW_TABLES.ACTION_V1;
1833
- const actionSysId = generateSysId();
1834
- const actionType = await getActionTypeSysId(client, action_config.type);
1835
- if (!actionType) {
1836
- return (0, error_handler_js_1.createErrorResult)(`Action type not found: ${action_config.type}`);
1837
- }
1838
- const maxOrder = await getMaxOrder(client, flow.sys_id, actionTable);
1839
- const actionData = {
1840
- sys_id: actionSysId,
1841
- flow: flow.sys_id,
1842
- action_type: actionType.sys_id,
1843
- name: action_config.name || actionType.name,
1844
- description: action_config.description || '',
1845
- order: action_config.order ?? (maxOrder + 100),
1846
- active: true
1847
- };
1848
- if (action_config.parent_logic_instance) {
1849
- actionData.parent_logic_instance = action_config.parent_logic_instance;
1850
- }
1851
- if (action_config.branch) {
1852
- actionData.branch = action_config.branch;
1853
- }
1854
- if (action_config.inputs) {
1855
- actionData.values = buildActionValues(action_config.inputs, useCompression);
1856
- }
1857
- try {
1858
- await client.post(`/api/now/table/${actionTable}`, actionData);
1859
- return (0, error_handler_js_1.createSuccessResult)({
1860
- action: 'add_action',
1861
- flow_id: flow.sys_id,
1862
- added_action: {
1863
- sys_id: actionSysId,
1864
- name: action_config.name || actionType.name,
1865
- type: action_config.type,
1866
- order: actionData.order
1867
- }
1868
- });
1869
- }
1870
- catch (error) {
1871
- return (0, error_handler_js_1.createErrorResult)(`Failed to add action: ${error.response?.data?.error?.message || error.message}`);
1872
- }
1873
- }
1874
- /**
1875
- * Remove an action from a flow
1876
- */
1877
- async function handleRemoveAction(args, client, versionInfo) {
1878
- const { action_id } = args;
1879
- if (!action_id) {
1880
- return (0, error_handler_js_1.createErrorResult)('action_id is required');
1881
- }
1882
- const actionTable = versionInfo.useV2Tables ? FLOW_TABLES.ACTION_V2 : FLOW_TABLES.ACTION_V1;
1883
- try {
1884
- await client.delete(`/api/now/table/${actionTable}/${action_id}`);
1885
- return (0, error_handler_js_1.createSuccessResult)({
1886
- action: 'remove_action',
1887
- action_id,
1888
- removed: true
1889
- });
1890
- }
1891
- catch (error) {
1892
- return (0, error_handler_js_1.createErrorResult)(`Failed to remove action: ${error.response?.data?.error?.message || error.message}`);
1893
- }
1894
- }
1895
- /**
1896
- * Add a condition to an existing flow
1897
- */
1898
- async function handleAddCondition(args, client, versionInfo) {
1899
- const { flow_id, flow_name, condition_config, use_compression } = args;
1900
- const flow = await findFlow(client, flow_id, flow_name);
1901
- if (!flow) {
1902
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1903
- }
1904
- if (!condition_config?.condition) {
1905
- return (0, error_handler_js_1.createErrorResult)('condition_config.condition is required');
1906
- }
1907
- const useCompression = use_compression ?? versionInfo.useCompression;
1908
- const logicTable = versionInfo.useV2Tables ? FLOW_TABLES.LOGIC_V2 : FLOW_TABLES.LOGIC_V1;
1909
- const conditionSysId = generateSysId();
1910
- const maxOrder = await getMaxOrder(client, flow.sys_id, logicTable);
1911
- const conditionData = {
1912
- sys_id: conditionSysId,
1913
- flow: flow.sys_id,
1914
- name: condition_config.name || 'If',
1915
- logic_type: 'if',
1916
- order: condition_config.order ?? (maxOrder + 100),
1917
- active: true
1918
- };
1919
- const conditionValues = { condition: { value: condition_config.condition, valueType: 'string' } };
1920
- conditionData.values = useCompression ? compressValue(conditionValues) : JSON.stringify(conditionValues);
1921
- if (condition_config.parent_logic_instance) {
1922
- conditionData.parent_logic_instance = condition_config.parent_logic_instance;
1923
- }
1924
- try {
1925
- await client.post(`/api/now/table/${logicTable}`, conditionData);
1926
- return (0, error_handler_js_1.createSuccessResult)({
1927
- action: 'add_condition',
1928
- flow_id: flow.sys_id,
1929
- condition: {
1930
- sys_id: conditionSysId,
1931
- name: condition_config.name || 'If',
1932
- order: conditionData.order
1933
- }
1934
- });
1935
- }
1936
- catch (error) {
1937
- return (0, error_handler_js_1.createErrorResult)(`Failed to add condition: ${error.response?.data?.error?.message || error.message}`);
1938
- }
1939
- }
1940
- /**
1941
- * Add a loop to an existing flow
1942
- */
1943
- async function handleAddLoop(args, client, versionInfo) {
1944
- const { flow_id, flow_name, loop_config, use_compression } = args;
1945
- const flow = await findFlow(client, flow_id, flow_name);
1946
- if (!flow) {
1947
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
1948
- }
1949
- if (!loop_config?.type) {
1950
- return (0, error_handler_js_1.createErrorResult)('loop_config.type is required');
1951
- }
1952
- const useCompression = use_compression ?? versionInfo.useCompression;
1953
- const logicTable = versionInfo.useV2Tables ? FLOW_TABLES.LOGIC_V2 : FLOW_TABLES.LOGIC_V1;
1954
- const loopSysId = generateSysId();
1955
- const maxOrder = await getMaxOrder(client, flow.sys_id, logicTable);
1956
- const loopData = {
1957
- sys_id: loopSysId,
1958
- flow: flow.sys_id,
1959
- name: loop_config.name || (loop_config.type === 'for_each' ? 'For Each' : 'Do Until'),
1960
- logic_type: loop_config.type,
1961
- order: loop_config.order ?? (maxOrder + 100),
1962
- active: true
1963
- };
1964
- const loopValues = {};
1965
- if (loop_config.data_source)
1966
- loopValues.data_source = { value: loop_config.data_source, valueType: 'reference' };
1967
- if (loop_config.condition)
1968
- loopValues.condition = { value: loop_config.condition, valueType: 'string' };
1969
- if (loop_config.max_iterations)
1970
- loopValues.max_iterations = { value: String(loop_config.max_iterations), valueType: 'integer' };
1971
- if (Object.keys(loopValues).length > 0) {
1972
- loopData.values = useCompression ? compressValue(loopValues) : JSON.stringify(loopValues);
1973
- }
1974
- if (loop_config.parent_logic_instance) {
1975
- loopData.parent_logic_instance = loop_config.parent_logic_instance;
1976
- }
1977
- try {
1978
- await client.post(`/api/now/table/${logicTable}`, loopData);
1979
- return (0, error_handler_js_1.createSuccessResult)({
1980
- action: 'add_loop',
1981
- flow_id: flow.sys_id,
1982
- loop: {
1983
- sys_id: loopSysId,
1984
- name: loopData.name,
1985
- type: loop_config.type,
1986
- order: loopData.order
1987
- }
1988
- });
1989
- }
1990
- catch (error) {
1991
- return (0, error_handler_js_1.createErrorResult)(`Failed to add loop: ${error.response?.data?.error?.message || error.message}`);
1992
- }
1993
- }
1994
- /**
1995
- * Add a variable to an existing flow
1996
- */
1997
- async function handleAddVariable(args, client) {
1998
- const { flow_id, flow_name, variable_config } = args;
1999
- const flow = await findFlow(client, flow_id, flow_name);
2000
- if (!flow) {
2001
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
2002
- }
2003
- if (!variable_config?.name || !variable_config?.type || !variable_config?.direction) {
2004
- return (0, error_handler_js_1.createErrorResult)('variable_config requires name, type, and direction');
2005
- }
2006
- const variableSysId = generateSysId();
2007
- // Get max order for variables
2008
- let maxOrder = 0;
2009
- try {
2010
- const response = await client.get(`/api/now/table/${FLOW_TABLES.VARIABLE}`, {
2011
- params: {
2012
- sysparm_query: `flow=${flow.sys_id}`,
2013
- sysparm_fields: 'order',
2014
- sysparm_orderby: 'DESCorder',
2015
- sysparm_limit: 1
2016
- }
2017
- });
2018
- if (response.data.result && response.data.result.length > 0) {
2019
- maxOrder = parseInt(response.data.result[0].order || '0', 10);
2020
- }
2021
- }
2022
- catch (e) { }
2023
- const variableData = {
2024
- sys_id: variableSysId,
2025
- flow: flow.sys_id,
2026
- name: variable_config.name,
2027
- label: variable_config.label || variable_config.name,
2028
- description: variable_config.description || '',
2029
- type: variable_config.type,
2030
- direction: variable_config.direction,
2031
- mandatory: variable_config.mandatory || false,
2032
- order: maxOrder + 100
2033
- };
2034
- if (variable_config.default_value)
2035
- variableData.default_value = variable_config.default_value;
2036
- if (variable_config.reference_table)
2037
- variableData.reference_table = variable_config.reference_table;
2038
- try {
2039
- await client.post(`/api/now/table/${FLOW_TABLES.VARIABLE}`, variableData);
2040
- return (0, error_handler_js_1.createSuccessResult)({
2041
- action: 'add_variable',
2042
- flow_id: flow.sys_id,
2043
- variable: {
2044
- sys_id: variableSysId,
2045
- name: variable_config.name,
2046
- type: variable_config.type,
2047
- direction: variable_config.direction
2048
- }
2049
- });
2050
- }
2051
- catch (error) {
2052
- return (0, error_handler_js_1.createErrorResult)(`Failed to add variable: ${error.response?.data?.error?.message || error.message}`);
2053
- }
2054
- }
2055
- /**
2056
- * Get detailed flow structure with decompressed values
2057
- */
2058
- async function handleGetDetails(args, client, versionInfo) {
2059
- const { flow_id, flow_name } = args;
2060
- const flow = await findFlow(client, flow_id, flow_name);
2061
- if (!flow) {
2062
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
2063
- }
2064
- const details = {
2065
- flow: flow,
2066
- trigger: null,
2067
- actions: [],
2068
- logic: [],
2069
- variables: [],
2070
- snapshots: []
2071
- };
2072
- // Get trigger
2073
- const triggerTable = versionInfo.useV2Tables ? FLOW_TABLES.TRIGGER_V2 : FLOW_TABLES.TRIGGER_V1;
2074
- try {
2075
- const response = await client.get(`/api/now/table/${triggerTable}`, {
2076
- params: { sysparm_query: `flow=${flow.sys_id}` }
2077
- });
2078
- if (response.data.result && response.data.result.length > 0) {
2079
- const trigger = response.data.result[0];
2080
- if (trigger.values && isCompressed(trigger.values)) {
2081
- trigger.values_decompressed = JSON.parse(decompressValue(trigger.values));
2082
- }
2083
- else if (trigger.values) {
2084
- try {
2085
- trigger.values_parsed = JSON.parse(trigger.values);
2086
- }
2087
- catch (e) { }
2088
- }
2089
- details.trigger = trigger;
2090
- }
2091
- }
2092
- catch (e) { }
2093
- // Get actions
2094
- const actionTable = versionInfo.useV2Tables ? FLOW_TABLES.ACTION_V2 : FLOW_TABLES.ACTION_V1;
2095
- try {
2096
- const response = await client.get(`/api/now/table/${actionTable}`, {
2097
- params: { sysparm_query: `flow=${flow.sys_id}`, sysparm_orderby: 'order' }
2098
- });
2099
- if (response.data.result) {
2100
- details.actions = response.data.result.map((action) => {
2101
- if (action.values && isCompressed(action.values)) {
2102
- action.values_decompressed = JSON.parse(decompressValue(action.values));
2103
- }
2104
- else if (action.values) {
2105
- try {
2106
- action.values_parsed = JSON.parse(action.values);
2107
- }
2108
- catch (e) { }
2109
- }
2110
- return action;
2111
- });
2112
- }
2113
- }
2114
- catch (e) { }
2115
- // Get logic (conditions, loops)
2116
- const logicTable = versionInfo.useV2Tables ? FLOW_TABLES.LOGIC_V2 : FLOW_TABLES.LOGIC_V1;
2117
- try {
2118
- const response = await client.get(`/api/now/table/${logicTable}`, {
2119
- params: { sysparm_query: `flow=${flow.sys_id}`, sysparm_orderby: 'order' }
2120
- });
2121
- if (response.data.result) {
2122
- details.logic = response.data.result.map((logic) => {
2123
- if (logic.values && isCompressed(logic.values)) {
2124
- logic.values_decompressed = JSON.parse(decompressValue(logic.values));
2125
- }
2126
- else if (logic.values) {
2127
- try {
2128
- logic.values_parsed = JSON.parse(logic.values);
2129
- }
2130
- catch (e) { }
2131
- }
2132
- return logic;
2133
- });
2134
- }
2135
- }
2136
- catch (e) { }
2137
- // Get variables
2138
- try {
2139
- const response = await client.get(`/api/now/table/${FLOW_TABLES.VARIABLE}`, {
2140
- params: { sysparm_query: `flow=${flow.sys_id}`, sysparm_orderby: 'order' }
2141
- });
2142
- if (response.data.result) {
2143
- details.variables = response.data.result;
2144
- }
2145
- }
2146
- catch (e) { }
2147
- // Get snapshots
2148
- try {
2149
- const response = await client.get(`/api/now/table/${FLOW_TABLES.SNAPSHOT}`, {
2150
- params: { sysparm_query: `flow=${flow.sys_id}`, sysparm_orderby: 'DESCsys_created_on', sysparm_limit: 5 }
2151
- });
2152
- if (response.data.result) {
2153
- details.snapshots = response.data.result;
2154
- }
2155
- }
2156
- catch (e) { }
2157
- // Build tree structure for nested actions
2158
- const buildTree = () => {
2159
- const tree = [];
2160
- const itemMap = new Map();
2161
- // Index all items
2162
- for (const action of details.actions) {
2163
- itemMap.set(action.sys_id, { ...action, _type: 'action', _children: [] });
2164
- }
2165
- for (const logic of details.logic) {
2166
- itemMap.set(logic.sys_id, { ...logic, _type: 'logic', _children: [] });
2167
- }
2168
- // Build hierarchy
2169
- itemMap.forEach((item, id) => {
2170
- if (item.parent_logic_instance) {
2171
- const parent = itemMap.get(item.parent_logic_instance);
2172
- if (parent) {
2173
- parent._children.push(item);
2174
- }
2175
- else {
2176
- tree.push(item);
2177
- }
2178
- }
2179
- else {
2180
- tree.push(item);
2181
- }
2182
- });
2183
- // Sort by order
2184
- const sortByOrder = (items) => {
2185
- items.sort((a, b) => (a.order || 0) - (b.order || 0));
2186
- for (const item of items) {
2187
- if (item._children && item._children.length > 0) {
2188
- sortByOrder(item._children);
2189
- }
2190
- }
2191
- };
2192
- sortByOrder(tree);
2193
- return tree;
2194
- };
2195
- details.tree_structure = buildTree();
2196
- details.version_info = versionInfo;
2197
- return (0, error_handler_js_1.createSuccessResult)({
2198
- action: 'get_details',
2199
- ...details
2200
- });
2201
- }
2202
- /**
2203
- * Publish flow changes (create new snapshot and activate)
2204
- */
2205
- async function handlePublish(args, client, versionInfo) {
2206
- const { flow_id, flow_name } = args;
2207
- const flow = await findFlow(client, flow_id, flow_name);
2208
- if (!flow) {
2209
- return (0, error_handler_js_1.createErrorResult)(`Flow not found: ${flow_id || flow_name}`);
2210
- }
2211
- const snapshotSysId = generateSysId();
2212
- // Create new snapshot
2213
- const snapshotData = {
2214
- sys_id: snapshotSysId,
2215
- flow: flow.sys_id,
2216
- name: `${flow.name} - Published ${new Date().toISOString()}`,
2217
- version: '1.0.0',
2218
- active: true,
2219
- published: true
2220
- };
2221
- try {
2222
- await client.post(`/api/now/table/${FLOW_TABLES.SNAPSHOT}`, snapshotData);
2223
- }
2224
- catch (error) {
2225
- return (0, error_handler_js_1.createErrorResult)(`Failed to create snapshot: ${error.response?.data?.error?.message || error.message}`);
2226
- }
2227
- // Activate flow
2228
- try {
2229
- await client.patch(`/api/now/table/${FLOW_TABLES.FLOW}/${flow.sys_id}`, {
2230
- active: true,
2231
- status: 'published'
2232
- });
2233
- }
2234
- catch (error) {
2235
- return (0, error_handler_js_1.createErrorResult)(`Failed to activate flow: ${error.response?.data?.error?.message || error.message}`);
2236
- }
2237
- return (0, error_handler_js_1.createSuccessResult)({
2238
- action: 'publish',
2239
- flow_id: flow.sys_id,
2240
- flow_name: flow.name,
2241
- snapshot_id: snapshotSysId,
2242
- active: true
2243
- });
2244
- }
2245
- // ==================== MAIN EXECUTOR ====================
2246
- async function execute(args, context) {
2247
- const { action } = args;
2248
- if (!action) {
2249
- return (0, error_handler_js_1.createErrorResult)('action is required');
2250
- }
2251
- try {
2252
- const client = await (0, auth_js_1.getAuthenticatedClient)(context);
2253
- const versionInfo = await detectVersion(client);
2254
- console.log(`Executing flow management action: ${action}`);
2255
- console.log(`Detected version: ${versionInfo.version}, compression: ${versionInfo.useCompression}`);
2256
- switch (action) {
2257
- case 'create':
2258
- return handleCreate(args, client, versionInfo);
2259
- case 'update':
2260
- return handleUpdate(args, client, versionInfo);
2261
- case 'delete':
2262
- return handleDelete(args, client, versionInfo);
2263
- case 'clone':
2264
- return handleClone(args, client, versionInfo);
2265
- case 'activate':
2266
- return handleActivate(args, client);
2267
- case 'deactivate':
2268
- return handleDeactivate(args, client);
2269
- case 'add_action':
2270
- return handleAddAction(args, client, versionInfo);
2271
- case 'remove_action':
2272
- return handleRemoveAction(args, client, versionInfo);
2273
- case 'add_condition':
2274
- return handleAddCondition(args, client, versionInfo);
2275
- case 'add_loop':
2276
- return handleAddLoop(args, client, versionInfo);
2277
- case 'add_variable':
2278
- return handleAddVariable(args, client);
2279
- case 'get_details':
2280
- return handleGetDetails(args, client, versionInfo);
2281
- case 'publish':
2282
- return handlePublish(args, client, versionInfo);
2283
- default:
2284
- return (0, error_handler_js_1.createErrorResult)(`Unknown action: ${action}`);
2285
- }
2286
- }
2287
- catch (error) {
2288
- return (0, error_handler_js_1.createErrorResult)(`Flow management failed: ${error.message}`);
2289
- }
2290
- }
2291
- exports.version = '2.1.0-experimental';
2292
- exports.author = 'Snow-Flow v8.41.17 - Flow Designer Management';
2293
- //# sourceMappingURL=snow_manage_flow.js.map