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.
- package/CLAUDE.md +9 -6
- package/dist/api/health-api.d.ts +1 -1
- package/dist/api/health-api.d.ts.map +1 -1
- package/dist/api/simple-health-api.d.ts +1 -1
- package/dist/api/simple-health-api.d.ts.map +1 -1
- package/dist/mcp/servicenow-automation-mcp.js +8 -8
- package/dist/mcp/servicenow-automation-mcp.js.map +1 -1
- package/dist/mcp/servicenow-mcp-server.js +4 -4
- package/dist/mcp/servicenow-mcp-server.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/index.d.ts +1 -2
- package/dist/mcp/servicenow-mcp-unified/tools/automation/index.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/index.js +6 -8
- package/dist/mcp/servicenow-mcp-unified/tools/automation/index.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_confirm_script_execution.d.ts +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_confirm_script_execution.js +2 -2
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_confirm_script_execution.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_schedule_script_job.d.ts +26 -0
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_schedule_script_job.d.ts.map +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/automation/{snow_execute_script.js → snow_schedule_script_job.js} +19 -13
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_schedule_script_job.js.map +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.d.ts +12 -4
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.js +69 -67
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_trace_execution.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_artifact.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_artifact.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_artifact.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_requirements.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_requirements.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_analyze_requirements.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_artifact.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_artifact.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_artifact.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_by_sysid.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_by_sysid.js +5 -2
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_edit_by_sysid.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_find_artifact.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_find_artifact.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_find_artifact.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_get_by_sysid.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_get_by_sysid.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_get_by_sysid.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_memory_search.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_memory_search.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_memory_search.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_orchestrate_development.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_orchestrate_development.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_orchestrate_development.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_sync_data_consistency.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_sync_data_consistency.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_sync_data_consistency.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_validate_live_connection.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_validate_live_connection.js +2 -1
- package/dist/mcp/servicenow-mcp-unified/tools/development/snow_validate_live_connection.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/integration/index.d.ts +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/integration/index.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/integration/index.js +4 -1
- package/dist/mcp/servicenow-mcp-unified/tools/integration/index.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.d.ts +26 -0
- package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.d.ts.map +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.js +1280 -0
- package/dist/mcp/servicenow-mcp-unified/tools/integration/snow_rest_message_manage.js.map +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/index.d.ts +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/index.js +4 -4
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.d.ts +19 -0
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.d.ts.map +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.js +142 -0
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_trigger_scheduled_job.js.map +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.d.ts +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.d.ts.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.js +4 -4
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/index.js.map +1 -1
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.d.ts +15 -0
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.d.ts.map +1 -0
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.js +136 -0
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_start_workflow.js.map +1 -0
- package/dist/templates/agents-md-template.d.ts +1 -1
- package/dist/templates/agents-md-template.d.ts.map +1 -1
- package/dist/templates/agents-md-template.js +5 -5
- package/dist/templates/claude-md-template.d.ts +1 -1
- package/dist/templates/claude-md-template.d.ts.map +1 -1
- package/dist/templates/claude-md-template.js +11 -9
- package/dist/templates/claude-md-template.js.map +1 -1
- package/dist/utils/auto-update-snow-code.d.ts +7 -0
- package/dist/utils/auto-update-snow-code.d.ts.map +1 -1
- package/dist/utils/auto-update-snow-code.js +62 -13
- package/dist/utils/auto-update-snow-code.js.map +1 -1
- package/package.json +1 -1
- package/.snow-code/opencode.json +0 -49
- package/dist/cli/enterprise-docs-generator.d.ts +0 -14
- package/dist/cli/enterprise-docs-generator.d.ts.map +0 -1
- package/dist/cli/enterprise-docs-generator.js +0 -875
- package/dist/cli/enterprise-docs-generator.js.map +0 -1
- package/dist/config/snowcode-config.d.ts +0 -76
- package/dist/config/snowcode-config.d.ts.map +0 -1
- package/dist/config/snowcode-config.js +0 -225
- package/dist/config/snowcode-config.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_execute_script.d.ts +0 -20
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_execute_script.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_execute_script.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.d.ts +0 -11
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.js +0 -142
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_test_rest_connection.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.d.ts +0 -8
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.js +0 -24
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/index.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.d.ts +0 -8
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.js +0 -44
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_execute_flow.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.d.ts +0 -8
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.js +0 -67
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_connectivity_test.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.d.ts +0 -15
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.js +0 -273
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_discover.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.d.ts +0 -14
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.js +0 -205
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_flow_query.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.d.ts +0 -8
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.js +0 -68
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_import_flow_from_xml.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.d.ts +0 -143
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.js +0 -2293
- package/dist/mcp/servicenow-mcp-unified/tools/flow-designer/snow_manage_flow.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.d.ts +0 -9
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.js +0 -57
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_create_rest_method.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.d.ts +0 -9
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.js +0 -58
- package/dist/mcp/servicenow-mcp-unified/tools/rest-api/snow_test_rest_message.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.d.ts +0 -9
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.js +0 -59
- package/dist/mcp/servicenow-mcp-unified/tools/scheduled-jobs/snow_execute_scheduled_job.js.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.d.ts +0 -9
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.d.ts.map +0 -1
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.js +0 -55
- package/dist/mcp/servicenow-mcp-unified/tools/workflow/snow_execute_workflow.js.map +0 -1
- package/dist/sdk/claude-agent-sdk-integration.d.ts +0 -103
- package/dist/sdk/claude-agent-sdk-integration.d.ts.map +0 -1
- package/dist/sdk/claude-agent-sdk-integration.js +0 -414
- 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
|