snow-flow 3.0.13 → 3.0.15
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.
|
@@ -53,7 +53,7 @@ class ServiceNowDeploymentMCP {
|
|
|
53
53
|
inputSchema: {
|
|
54
54
|
type: 'object',
|
|
55
55
|
properties: {
|
|
56
|
-
type: { type: 'string', enum: ['widget', '
|
|
56
|
+
type: { type: 'string', enum: ['widget', 'application'] }, // removed 'workflow' - deprecated
|
|
57
57
|
artifact: { type: 'object', description: 'The artifact to validate' },
|
|
58
58
|
},
|
|
59
59
|
required: ['type', 'artifact'],
|
|
@@ -83,11 +83,11 @@ class ServiceNowDeploymentMCP {
|
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
name: 'snow_export_artifact',
|
|
86
|
-
description: 'Exports ServiceNow artifacts (widgets,
|
|
86
|
+
description: 'Exports ServiceNow artifacts (widgets, applications) to JSON/XML format for backup, version control, or migration purposes.', // removed workflows
|
|
87
87
|
inputSchema: {
|
|
88
88
|
type: 'object',
|
|
89
89
|
properties: {
|
|
90
|
-
type: { type: 'string', enum: ['widget', '
|
|
90
|
+
type: { type: 'string', enum: ['widget', 'application'] }, // removed 'workflow' - deprecated
|
|
91
91
|
sys_id: { type: 'string', description: 'Sys ID of the artifact' },
|
|
92
92
|
format: { type: 'string', enum: ['json', 'xml', 'update_set'], default: 'json' },
|
|
93
93
|
},
|
|
@@ -100,7 +100,7 @@ class ServiceNowDeploymentMCP {
|
|
|
100
100
|
inputSchema: {
|
|
101
101
|
type: 'object',
|
|
102
102
|
properties: {
|
|
103
|
-
type: { type: 'string', enum: ['widget', '
|
|
103
|
+
type: { type: 'string', enum: ['widget', 'application'] }, // removed 'workflow' - deprecated
|
|
104
104
|
file_path: { type: 'string', description: 'Path to the artifact file' },
|
|
105
105
|
format: { type: 'string', enum: ['json', 'xml', 'update_set'], default: 'json' },
|
|
106
106
|
},
|
|
@@ -115,7 +115,7 @@ class ServiceNowDeploymentMCP {
|
|
|
115
115
|
properties: {
|
|
116
116
|
source_instance: { type: 'string', description: 'Source instance URL' },
|
|
117
117
|
target_instance: { type: 'string', description: 'Target instance URL' },
|
|
118
|
-
type: { type: 'string', enum: ['widget', '
|
|
118
|
+
type: { type: 'string', enum: ['widget', 'application'] }, // removed 'workflow' - deprecated
|
|
119
119
|
sys_id: { type: 'string', description: 'Sys ID of the artifact to clone' },
|
|
120
120
|
},
|
|
121
121
|
required: ['source_instance', 'target_instance', 'type', 'sys_id'],
|
|
@@ -461,11 +461,11 @@ class ServiceNowDeploymentMCP {
|
|
|
461
461
|
*/
|
|
462
462
|
getTableForType(type) {
|
|
463
463
|
const tableMap = {
|
|
464
|
-
'flow': 'sys_hub_flow',
|
|
464
|
+
// 'flow': 'sys_hub_flow', // REMOVED - flows deprecated
|
|
465
465
|
'widget': 'sp_widget',
|
|
466
466
|
'script': 'sys_script_include',
|
|
467
467
|
'business_rule': 'sys_script',
|
|
468
|
-
'workflow': 'wf_workflow',
|
|
468
|
+
// 'workflow': 'wf_workflow', // REMOVED - workflows deprecated
|
|
469
469
|
'application': 'sys_app',
|
|
470
470
|
'ui_action': 'sys_ui_action',
|
|
471
471
|
'ui_page': 'sys_ui_page',
|
|
@@ -1675,435 +1675,474 @@ ${args.widgets && args.widgets.length > 0 ? args.widgets.map((w, i) => `
|
|
|
1675
1675
|
`;
|
|
1676
1676
|
return css;
|
|
1677
1677
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
validationText.includes('Auto-converted "actions" to "activities"') ||
|
|
1740
|
-
validationText.includes('Smart Auto-Corrections Applied')) {
|
|
1741
|
-
// Re-parse the corrected definition from the validation process
|
|
1742
|
-
const tempDef = typeof args.flow_definition === 'string' ? JSON.parse(args.flow_definition) : args.flow_definition;
|
|
1743
|
-
if (tempDef.steps && !tempDef.activities) {
|
|
1744
|
-
tempDef.activities = tempDef.steps;
|
|
1745
|
-
delete tempDef.steps;
|
|
1746
|
-
}
|
|
1747
|
-
else if (tempDef.actions && !tempDef.activities) {
|
|
1748
|
-
tempDef.activities = tempDef.actions;
|
|
1749
|
-
delete tempDef.actions;
|
|
1750
|
-
}
|
|
1751
|
-
validatedDefinition = JSON.stringify(tempDef);
|
|
1752
|
-
this.logger.info('Using auto-corrected flow definition for deployment');
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
// Ensure Update Set is active
|
|
1756
|
-
const { updateSetId, updateSetName } = await this.ensureUpdateSet('Flow', args.name);
|
|
1757
|
-
// Check if this is a master flow with linked artifacts
|
|
1758
|
-
const isComposedFlow = args.composed_flow || args.linked_artifacts;
|
|
1759
|
-
const linkedArtifacts = args.linked_artifacts || [];
|
|
1760
|
-
// Deploy linked artifacts first if this is a composed flow
|
|
1761
|
-
const deployedArtifacts = [];
|
|
1762
|
-
if (isComposedFlow && linkedArtifacts.length > 0) {
|
|
1763
|
-
this.logger.info('Deploying linked artifacts for composed flow', { count: linkedArtifacts.length });
|
|
1764
|
-
for (const artifact of linkedArtifacts) {
|
|
1765
|
-
try {
|
|
1766
|
-
const deployResult = await this.deployLinkedArtifact(artifact);
|
|
1767
|
-
deployedArtifacts.push(deployResult);
|
|
1768
|
-
}
|
|
1769
|
-
catch (error) {
|
|
1770
|
-
this.logger.error('Failed to deploy linked artifact', { artifact, error });
|
|
1771
|
-
throw new Error(`Failed to deploy linked artifact ${artifact.name}: ${error}`);
|
|
1772
|
-
}
|
|
1678
|
+
// DEPRECATED: Flow deployment is no longer supported - flows, workflows, and subflows have been removed
|
|
1679
|
+
/* private async deployFlow(args: any) {
|
|
1680
|
+
try {
|
|
1681
|
+
// Enhanced authentication check with token refresh for deployment
|
|
1682
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
1683
|
+
if (!authResult.isValid) {
|
|
1684
|
+
this.logger.error('Deployment authentication failed:', authResult.error);
|
|
1685
|
+
|
|
1686
|
+
// If auth failed, try to refresh token
|
|
1687
|
+
const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
|
|
1688
|
+
if (!refreshResult.success) {
|
|
1689
|
+
return {
|
|
1690
|
+
content: [
|
|
1691
|
+
{
|
|
1692
|
+
type: 'text',
|
|
1693
|
+
text: `❌ Deployment authentication failed.\n\nError: ${authResult.error || 'Unable to authenticate'}\n\nRecommendations:\n${(authResult.recommendations || ['Run: snow-flow auth login']).map(r => `• ${r}`).join('\n')}\n\nNote: Deployment requires valid OAuth tokens with write permissions.`,
|
|
1694
|
+
},
|
|
1695
|
+
],
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
// Warn if token may lack write permissions
|
|
1701
|
+
if (!authResult.hasWriteScope) {
|
|
1702
|
+
this.logger.warn('⚠️ Token may lack write permissions, deployment might fail with 403');
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
// Ensure we have a flow definition
|
|
1706
|
+
if (!args.flow_definition) {
|
|
1707
|
+
return {
|
|
1708
|
+
content: [
|
|
1709
|
+
{
|
|
1710
|
+
type: 'text',
|
|
1711
|
+
text: '❌ Flow deployment failed: No flow_definition provided.\n\n💡 You need to provide a flow_definition with activities.\n\nExample:\n```json\n{\n "name": "approval_flow",\n "flow_definition": {\n "activities": [\n {\n "id": "activity_1",\n "name": "Check Condition",\n "type": "condition"\n }\n ]\n }\n}\n```\n\nOr use snow_create_flow with natural language for easier flow creation.',
|
|
1712
|
+
},
|
|
1713
|
+
],
|
|
1714
|
+
};
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
const flowType = args.flow_type || 'flow';
|
|
1718
|
+
this.logger.info(`Deploying ${flowType} to ServiceNow`, { name: args.name, type: flowType });
|
|
1719
|
+
|
|
1720
|
+
// Validate flow definition first if requested
|
|
1721
|
+
let validatedDefinition = args.flow_definition;
|
|
1722
|
+
if (args.validate_before_deploy !== false) {
|
|
1723
|
+
const validationResult = await this.validateFlowDefinition({
|
|
1724
|
+
definition: args.flow_definition,
|
|
1725
|
+
flow_type: flowType,
|
|
1726
|
+
show_preview: false,
|
|
1727
|
+
test_mode: false,
|
|
1728
|
+
check_dependencies: true
|
|
1729
|
+
});
|
|
1730
|
+
|
|
1731
|
+
// Check if validation failed
|
|
1732
|
+
const validationText = validationResult.content?.[0]?.text || '';
|
|
1733
|
+
if (validationText.includes('❌') || validationText.includes('ERROR')) {
|
|
1734
|
+
return {
|
|
1735
|
+
content: [
|
|
1736
|
+
{
|
|
1737
|
+
type: 'text',
|
|
1738
|
+
text: `❌ Flow validation failed. Please fix the following issues:\n\n${validationText}\n\nUse snow_validate_flow_definition to preview and test your flow before deployment.`
|
|
1773
1739
|
}
|
|
1774
|
-
|
|
1775
|
-
// Parse flow definition to inject deployed artifact references
|
|
1776
|
-
// Use the validated and corrected definition
|
|
1777
|
-
let flowDefinition = validatedDefinition;
|
|
1778
|
-
if (typeof flowDefinition === 'string') {
|
|
1779
|
-
flowDefinition = JSON.parse(flowDefinition);
|
|
1780
|
-
}
|
|
1781
|
-
// Update flow activities with deployed artifact sys_ids
|
|
1782
|
-
if (flowDefinition.activities && deployedArtifacts.length > 0) {
|
|
1783
|
-
flowDefinition.activities = flowDefinition.activities.map((activity) => {
|
|
1784
|
-
if (activity.artifact_reference) {
|
|
1785
|
-
const deployed = deployedArtifacts.find(d => d.originalId === activity.artifact_reference.sys_id ||
|
|
1786
|
-
d.name === activity.artifact_reference.name);
|
|
1787
|
-
if (deployed) {
|
|
1788
|
-
activity.artifact_sys_id = deployed.sys_id;
|
|
1789
|
-
activity.artifact_api_name = deployed.api_name;
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
return activity;
|
|
1793
|
-
});
|
|
1794
|
-
}
|
|
1795
|
-
// Create flow data based on flow type
|
|
1796
|
-
const flowData = {
|
|
1797
|
-
name: args.name,
|
|
1798
|
-
description: args.description,
|
|
1799
|
-
active: args.active !== false,
|
|
1800
|
-
flow_definition: JSON.stringify(flowDefinition),
|
|
1801
|
-
category: args.category || 'automation',
|
|
1802
|
-
// Additional fields for composed flows
|
|
1803
|
-
is_composed: isComposedFlow,
|
|
1804
|
-
linked_artifact_count: linkedArtifacts.length,
|
|
1805
|
-
artifact_references: deployedArtifacts.map(a => a.sys_id).join(',')
|
|
1740
|
+
]
|
|
1806
1741
|
};
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// CRITICAL: Use the corrected definition from validation
|
|
1745
|
+
// The validateFlowDefinition method may have auto-corrected "steps" or "actions" to "activities"
|
|
1746
|
+
if (validationText.includes('Auto-converted "steps" to "activities"') ||
|
|
1747
|
+
validationText.includes('Auto-converted "actions" to "activities"') ||
|
|
1748
|
+
validationText.includes('Smart Auto-Corrections Applied')) {
|
|
1749
|
+
// Re-parse the corrected definition from the validation process
|
|
1750
|
+
const tempDef = typeof args.flow_definition === 'string' ? JSON.parse(args.flow_definition) : args.flow_definition;
|
|
1751
|
+
if (tempDef.steps && !tempDef.activities) {
|
|
1752
|
+
tempDef.activities = tempDef.steps;
|
|
1753
|
+
delete tempDef.steps;
|
|
1754
|
+
} else if (tempDef.actions && !tempDef.activities) {
|
|
1755
|
+
tempDef.activities = tempDef.actions;
|
|
1756
|
+
delete tempDef.actions;
|
|
1757
|
+
}
|
|
1758
|
+
validatedDefinition = JSON.stringify(tempDef);
|
|
1759
|
+
this.logger.info('Using auto-corrected flow definition for deployment');
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
// Ensure Update Set is active
|
|
1764
|
+
const { updateSetId, updateSetName } = await this.ensureUpdateSet('Flow', args.name);
|
|
1765
|
+
|
|
1766
|
+
// Check if this is a master flow with linked artifacts
|
|
1767
|
+
const isComposedFlow = args.composed_flow || args.linked_artifacts;
|
|
1768
|
+
const linkedArtifacts = args.linked_artifacts || [];
|
|
1769
|
+
|
|
1770
|
+
// Deploy linked artifacts first if this is a composed flow
|
|
1771
|
+
const deployedArtifacts: any[] = [];
|
|
1772
|
+
if (isComposedFlow && linkedArtifacts.length > 0) {
|
|
1773
|
+
this.logger.info('Deploying linked artifacts for composed flow', { count: linkedArtifacts.length });
|
|
1774
|
+
|
|
1775
|
+
for (const artifact of linkedArtifacts) {
|
|
1833
1776
|
try {
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
name: flowData.name || `flow_${Date.now()}`,
|
|
1840
|
-
description: flowData.description || 'Created by Snow-Flow',
|
|
1841
|
-
table: flowData.table || 'incident',
|
|
1842
|
-
active: flowData.active !== false,
|
|
1843
|
-
condition: flowData.condition || '',
|
|
1844
|
-
script: flowData.script || '// Flow logic here',
|
|
1845
|
-
order: flowData.order || 100
|
|
1846
|
-
});
|
|
1847
|
-
break;
|
|
1848
|
-
case 'subflow':
|
|
1849
|
-
// Create subflow as a workflow activity
|
|
1850
|
-
result = await this.client.create('wf_workflow', {
|
|
1851
|
-
name: flowData.name || `subflow_${Date.now()}`,
|
|
1852
|
-
description: `${flowData.description || 'Subflow created by Snow-Flow'} [SUBFLOW]`,
|
|
1853
|
-
table: flowData.table || 'incident',
|
|
1854
|
-
active: flowData.active !== false,
|
|
1855
|
-
condition: flowData.condition || '',
|
|
1856
|
-
script: flowData.script || '// Subflow logic here',
|
|
1857
|
-
order: flowData.order || 200
|
|
1858
|
-
});
|
|
1859
|
-
break;
|
|
1860
|
-
case 'action':
|
|
1861
|
-
// Create workflow activity/action
|
|
1862
|
-
if (!flowData.workflow_id) {
|
|
1863
|
-
throw new Error('workflow_id is required for flow actions');
|
|
1864
|
-
}
|
|
1865
|
-
result = await this.client.create('wf_activity', {
|
|
1866
|
-
workflow: flowData.workflow_id,
|
|
1867
|
-
name: flowData.name || `action_${Date.now()}`,
|
|
1868
|
-
script: flowData.script || '// Action script here',
|
|
1869
|
-
condition: flowData.condition || '',
|
|
1870
|
-
order: flowData.order || 100,
|
|
1871
|
-
active: flowData.active !== false
|
|
1872
|
-
});
|
|
1873
|
-
break;
|
|
1874
|
-
default:
|
|
1875
|
-
throw new Error(`Unknown flow type: ${flowType}. Supported types: flow, subflow, action`);
|
|
1876
|
-
}
|
|
1777
|
+
const deployResult = await this.deployLinkedArtifact(artifact);
|
|
1778
|
+
deployedArtifacts.push(deployResult);
|
|
1779
|
+
} catch (error) {
|
|
1780
|
+
this.logger.error('Failed to deploy linked artifact', { artifact, error });
|
|
1781
|
+
throw new Error(`Failed to deploy linked artifact ${artifact.name}: ${error}`);
|
|
1877
1782
|
}
|
|
1878
|
-
|
|
1879
|
-
this.logger.warn('Flow Designer deployment failed, attempting Business Rule fallback', {
|
|
1880
|
-
error: flowError,
|
|
1881
|
-
flowName: args.name
|
|
1882
|
-
});
|
|
1883
|
-
// Try to create equivalent Business Rule instead
|
|
1884
|
-
try {
|
|
1885
|
-
fallbackBusinessRule = await this.createBusinessRuleFallback(args, flowDefinition);
|
|
1886
|
-
result = {
|
|
1887
|
-
success: true,
|
|
1888
|
-
data: fallbackBusinessRule,
|
|
1889
|
-
fallback_used: true,
|
|
1890
|
-
original_error: flowError instanceof Error ? flowError.message : String(flowError)
|
|
1891
|
-
};
|
|
1892
|
-
usedFallback = true;
|
|
1893
|
-
this.logger.info('Successfully created Business Rule fallback', {
|
|
1894
|
-
businessRuleId: fallbackBusinessRule.sys_id,
|
|
1895
|
-
originalFlowName: args.name
|
|
1896
|
-
});
|
|
1897
|
-
}
|
|
1898
|
-
catch (fallbackError) {
|
|
1899
|
-
this.logger.error('Both Flow Designer and Business Rule fallback failed', {
|
|
1900
|
-
flowError,
|
|
1901
|
-
fallbackError
|
|
1902
|
-
});
|
|
1903
|
-
throw new Error(`🚨 Flow deployment failed and fallback unsuccessful:
|
|
1904
|
-
|
|
1905
|
-
📍 **Errors:**
|
|
1906
|
-
- Flow Designer Error: ${flowError instanceof Error ? flowError.message : String(flowError)}
|
|
1907
|
-
- Business Rule Fallback Error: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}
|
|
1908
|
-
|
|
1909
|
-
🔧 **Update Set Troubleshooting:**
|
|
1910
|
-
1. Check current Update Set: snow_smart_update_set with action="track"
|
|
1911
|
-
2. Verify Update Set is active for tracking
|
|
1912
|
-
3. Use mock testing: snow_test_flow_with_mock instead
|
|
1913
|
-
4. Check flow exists: snow_get_by_sysid
|
|
1914
|
-
|
|
1915
|
-
💡 **Alternative Solutions:**
|
|
1916
|
-
- Use snow_test_flow_with_mock for safe testing
|
|
1917
|
-
- Verify flow creation with snow_get_by_sysid
|
|
1918
|
-
- Check Update Set contains the flow artifact
|
|
1919
|
-
- Create Business Rule manually if needed
|
|
1920
|
-
|
|
1921
|
-
📚 Please check your flow definition JSON format or use manual deployment.`);
|
|
1922
|
-
}
|
|
1923
|
-
}
|
|
1924
|
-
const credentials = await this.oauth.loadCredentials();
|
|
1925
|
-
const flowUrl = result.success && result.data
|
|
1926
|
-
? (usedFallback
|
|
1927
|
-
? `https://${credentials?.instance}/sys_script.do?sys_id=${result.data.sys_id}`
|
|
1928
|
-
: `https://${credentials?.instance}/nav_to.do?uri=sys_hub_flow.do?sys_id=${result.data.sys_id}`)
|
|
1929
|
-
: `https://${credentials?.instance}/flow-designer.do`;
|
|
1930
|
-
const artifactSummary = deployedArtifacts.length > 0
|
|
1931
|
-
? `\n🔗 **Linked Artifacts Deployed:**\n${deployedArtifacts.map((a, i) => `${i + 1}. ${a.type}: ${a.name} (${a.sys_id})`).join('\n')}\n`
|
|
1932
|
-
: '';
|
|
1933
|
-
const activitySummary = flowDefinition.activities
|
|
1934
|
-
? `\n📊 **Flow Activities:**\n${flowDefinition.activities.map((a, i) => `${i + 1}. ${a.name} (${a.type})${a.artifact_reference ? ` - Uses: ${a.artifact_reference.name}` : ''}`).join('\n')}\n`
|
|
1935
|
-
: '';
|
|
1936
|
-
const successMessage = usedFallback
|
|
1937
|
-
? `🔄 **INTELLIGENT FALLBACK SUCCESSFUL!**
|
|
1938
|
-
|
|
1939
|
-
⚠️ Flow Designer deployment failed, but Snow-Flow automatically created a Business Rule that achieves the same result!
|
|
1940
|
-
|
|
1941
|
-
🛠️ **Business Rule Details:**
|
|
1942
|
-
- Name: ${args.name}
|
|
1943
|
-
- Type: 🔧 Business Rule (Fallback from Flow Designer)
|
|
1944
|
-
- Table: ${args.table || 'sys_user'}
|
|
1945
|
-
- When: ${this.getTriggerWhen(args.trigger_type)}
|
|
1946
|
-
- Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
1947
|
-
- Original Error: ${result.original_error}
|
|
1948
|
-
|
|
1949
|
-
✨ **Why This Works Better:**
|
|
1950
|
-
- ✅ More reliable than Flow Designer for simple automations
|
|
1951
|
-
- ✅ Faster execution (server-side JavaScript)
|
|
1952
|
-
- ✅ Better error handling and debugging
|
|
1953
|
-
- ✅ Direct database access capabilities`
|
|
1954
|
-
: `✅ Flow Designer flow deployed successfully!
|
|
1955
|
-
|
|
1956
|
-
🔄 **${flowType.charAt(0).toUpperCase() + flowType.slice(1)} Details:**
|
|
1957
|
-
- Name: ${args.name}
|
|
1958
|
-
- Flow Type: ${flowType === 'flow' ? '📋 Flow' : flowType === 'subflow' ? '🔄 Subflow' : '⚡ Action'}
|
|
1959
|
-
- Composed: ${isComposedFlow ? '🧠 Yes - Intelligent Composed Flow' : '❌ No - Standard'}
|
|
1960
|
-
${flowType === 'flow' ? `- Trigger Type: ${args.trigger_type}
|
|
1961
|
-
- Table: ${args.table || 'N/A'}` : ''}
|
|
1962
|
-
${flowType !== 'flow' ? `- Inputs: ${flowDefinition.inputs?.length || 0}
|
|
1963
|
-
- Outputs: ${flowDefinition.outputs?.length || 0}` : ''}
|
|
1964
|
-
- Category: ${args.category || 'automation'}
|
|
1965
|
-
- Active: ${args.active !== false ? 'Yes' : 'No'}`;
|
|
1966
|
-
const continuationMessage = usedFallback
|
|
1967
|
-
? `
|
|
1968
|
-
📦 **Update Set:**
|
|
1969
|
-
- Name: ${updateSetName}
|
|
1970
|
-
- ID: ${updateSetId}
|
|
1971
|
-
|
|
1972
|
-
🔗 **Direct Links:**
|
|
1973
|
-
- Business Rule: ${flowUrl}
|
|
1974
|
-
- Business Rules List: https://${credentials?.instance}/sys_script_list.do
|
|
1975
|
-
|
|
1976
|
-
📝 **Business Rule Components Created:**
|
|
1977
|
-
1. ✅ Trigger configured (${this.getTriggerWhen(args.trigger_type)})
|
|
1978
|
-
2. ✅ Condition logic applied
|
|
1979
|
-
3. ✅ Server-side script generated
|
|
1980
|
-
4. ✅ Error handling implemented
|
|
1981
|
-
5. ✅ Activation settings configured
|
|
1982
|
-
|
|
1983
|
-
📋 **Next Steps:**
|
|
1984
|
-
1. Test business rule execution by triggering the event
|
|
1985
|
-
2. Check logs in System Logs > Script Log Statements
|
|
1986
|
-
3. Modify the script if additional logic is needed
|
|
1987
|
-
4. Monitor performance and error handling
|
|
1988
|
-
|
|
1989
|
-
🔄 **Snow-Flow Intelligent Fallback:**
|
|
1990
|
-
Snow-Flow automatically detected Flow Designer issues and created a functionally equivalent Business Rule. This is often more reliable and performant for simple automation tasks.`
|
|
1991
|
-
: `
|
|
1992
|
-
📦 **Update Set:**
|
|
1993
|
-
- Name: ${updateSetName}
|
|
1994
|
-
- ID: ${updateSetId}
|
|
1995
|
-
${artifactSummary}${activitySummary}
|
|
1996
|
-
🔗 **Direct Links:**
|
|
1997
|
-
- Flow Designer: ${flowUrl}
|
|
1998
|
-
- Flow Designer Home: https://${credentials?.instance}/flow-designer.do?sysparm_nostack=true
|
|
1999
|
-
|
|
2000
|
-
📝 **Flow Components Created:**
|
|
2001
|
-
1. ✅ Trigger configured (${args.trigger_type})
|
|
2002
|
-
2. ✅ Condition logic applied
|
|
2003
|
-
3. ✅ Flow definition structured with ${flowDefinition.activities?.length || 0} activities
|
|
2004
|
-
4. ✅ ${linkedArtifacts.length} artifacts linked and deployed
|
|
2005
|
-
5. ✅ Activation settings configured
|
|
2006
|
-
|
|
2007
|
-
${isComposedFlow ? `
|
|
2008
|
-
🧠 **Intelligent Flow Features:**
|
|
2009
|
-
- ✅ Natural language instruction processed
|
|
2010
|
-
- ✅ Artifacts automatically discovered and linked
|
|
2011
|
-
- ✅ Dependencies resolved and deployed
|
|
2012
|
-
- ✅ Error handling configured
|
|
2013
|
-
- ✅ Variables and connections mapped
|
|
2014
|
-
` : ''}
|
|
2015
|
-
|
|
2016
|
-
📋 **Next Steps:**
|
|
2017
|
-
1. Test flow execution with sample data
|
|
2018
|
-
2. Monitor flow performance and logs
|
|
2019
|
-
3. Review artifact connections
|
|
2020
|
-
4. Customize error handling if needed
|
|
2021
|
-
|
|
2022
|
-
💡 **Composed Flow Capabilities:**
|
|
2023
|
-
- Automatic artifact orchestration
|
|
2024
|
-
- Intelligent output-to-input mapping
|
|
2025
|
-
- Multi-artifact dependency resolution
|
|
2026
|
-
- Natural language configuration`;
|
|
2027
|
-
// ENHANCED: Ensure artifact is tracked in Update Set
|
|
2028
|
-
if (result.success && result.data) {
|
|
2029
|
-
await this.ensureUpdateSetTracking({
|
|
2030
|
-
sys_id: result.data.sys_id,
|
|
2031
|
-
type: usedFallback ? 'Business Rule' : 'Flow',
|
|
2032
|
-
name: args.name,
|
|
2033
|
-
table: usedFallback ? 'sys_script' : 'sys_hub_flow'
|
|
2034
|
-
});
|
|
2035
|
-
}
|
|
2036
|
-
return {
|
|
2037
|
-
content: [
|
|
2038
|
-
{
|
|
2039
|
-
type: 'text',
|
|
2040
|
-
text: successMessage + continuationMessage,
|
|
2041
|
-
},
|
|
2042
|
-
],
|
|
2043
|
-
};
|
|
1783
|
+
}
|
|
2044
1784
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
1. Check authentication: snow_auth_diagnostics()
|
|
2052
|
-
2. Validate flow definition: snow_validate_flow_definition()
|
|
2053
|
-
3. Check Update Set: snow_update_set_current()
|
|
2054
|
-
4. Verify flow_designer role permissions
|
|
2055
|
-
|
|
2056
|
-
💡 Alternative Approaches:
|
|
2057
|
-
• Use snow_create_flow with natural language (recommended)
|
|
2058
|
-
• Test with snow_test_flow_with_mock() first
|
|
2059
|
-
• Use snow_flow_wizard for step-by-step creation
|
|
2060
|
-
• Try Business Rule fallback if flow creation fails
|
|
2061
|
-
|
|
2062
|
-
📚 Documentation: See CLAUDE.md for Flow Development Guidelines`;
|
|
2063
|
-
throw new Error(enhancedError);
|
|
1785
|
+
|
|
1786
|
+
// Parse flow definition to inject deployed artifact references
|
|
1787
|
+
// Use the validated and corrected definition
|
|
1788
|
+
let flowDefinition = validatedDefinition;
|
|
1789
|
+
if (typeof flowDefinition === 'string') {
|
|
1790
|
+
flowDefinition = JSON.parse(flowDefinition);
|
|
2064
1791
|
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
1792
|
+
|
|
1793
|
+
// Update flow activities with deployed artifact sys_ids
|
|
1794
|
+
if (flowDefinition.activities && deployedArtifacts.length > 0) {
|
|
1795
|
+
flowDefinition.activities = flowDefinition.activities.map((activity: any) => {
|
|
1796
|
+
if (activity.artifact_reference) {
|
|
1797
|
+
const deployed = deployedArtifacts.find(d =>
|
|
1798
|
+
d.originalId === activity.artifact_reference.sys_id ||
|
|
1799
|
+
d.name === activity.artifact_reference.name
|
|
1800
|
+
);
|
|
1801
|
+
if (deployed) {
|
|
1802
|
+
activity.artifact_sys_id = deployed.sys_id;
|
|
1803
|
+
activity.artifact_api_name = deployed.api_name;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
return activity;
|
|
1807
|
+
});
|
|
2080
1808
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
1809
|
+
|
|
1810
|
+
// Create flow data based on flow type
|
|
1811
|
+
const flowData: any = {
|
|
1812
|
+
name: args.name,
|
|
1813
|
+
description: args.description,
|
|
1814
|
+
active: args.active !== false,
|
|
1815
|
+
flow_definition: JSON.stringify(flowDefinition),
|
|
1816
|
+
category: args.category || 'automation',
|
|
1817
|
+
// Additional fields for composed flows
|
|
1818
|
+
is_composed: isComposedFlow,
|
|
1819
|
+
linked_artifact_count: linkedArtifacts.length,
|
|
1820
|
+
artifact_references: deployedArtifacts.map(a => a.sys_id).join(',')
|
|
2093
1821
|
};
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
1822
|
+
|
|
1823
|
+
// Configure based on flow type
|
|
1824
|
+
switch (flowType) {
|
|
1825
|
+
case 'flow':
|
|
1826
|
+
flowData.table = args.table || '';
|
|
1827
|
+
flowData.trigger_type = args.trigger_type;
|
|
1828
|
+
flowData.condition = args.condition || '';
|
|
1829
|
+
flowData.type = 'flow';
|
|
1830
|
+
break;
|
|
1831
|
+
case 'subflow':
|
|
1832
|
+
// Subflows don't have triggers, they're called by other flows
|
|
1833
|
+
flowData.type = 'subflow';
|
|
1834
|
+
flowData.inputs = flowDefinition.inputs || [];
|
|
1835
|
+
flowData.outputs = flowDefinition.outputs || [];
|
|
1836
|
+
break;
|
|
1837
|
+
case 'action':
|
|
1838
|
+
// Actions are reusable components
|
|
1839
|
+
flowData.type = 'action';
|
|
1840
|
+
flowData.action_type = args.action_type || 'custom';
|
|
1841
|
+
flowData.inputs = flowDefinition.inputs || [];
|
|
1842
|
+
flowData.outputs = flowDefinition.outputs || [];
|
|
1843
|
+
break;
|
|
2097
1844
|
}
|
|
2098
|
-
|
|
2099
|
-
|
|
1845
|
+
|
|
1846
|
+
// Deploy to ServiceNow using appropriate API based on flow type
|
|
1847
|
+
let result;
|
|
1848
|
+
let usedFallback = false;
|
|
1849
|
+
let fallbackBusinessRule = null;
|
|
1850
|
+
|
|
1851
|
+
try {
|
|
1852
|
+
// Real ServiceNow Flow deployment using proper API calls
|
|
1853
|
+
switch (flowType) {
|
|
1854
|
+
case 'flow':
|
|
1855
|
+
// Create workflow record in ServiceNow
|
|
1856
|
+
result = await (this.client as any).createRecord('wf_workflow', {
|
|
1857
|
+
name: flowData.name || `flow_${Date.now()}`,
|
|
1858
|
+
description: flowData.description || 'Created by Snow-Flow',
|
|
1859
|
+
table: flowData.table || 'incident',
|
|
1860
|
+
active: flowData.active !== false,
|
|
1861
|
+
condition: flowData.condition || '',
|
|
1862
|
+
script: flowData.script || '// Flow logic here',
|
|
1863
|
+
order: flowData.order || 100
|
|
1864
|
+
});
|
|
1865
|
+
break;
|
|
1866
|
+
case 'subflow':
|
|
1867
|
+
// Create subflow as a workflow activity
|
|
1868
|
+
result = await (this.client as any).createRecord('wf_workflow', {
|
|
1869
|
+
name: flowData.name || `subflow_${Date.now()}`,
|
|
1870
|
+
description: `${flowData.description || 'Subflow created by Snow-Flow'} [SUBFLOW]`,
|
|
1871
|
+
table: flowData.table || 'incident',
|
|
1872
|
+
active: flowData.active !== false,
|
|
1873
|
+
condition: flowData.condition || '',
|
|
1874
|
+
script: flowData.script || '// Subflow logic here',
|
|
1875
|
+
order: flowData.order || 200
|
|
1876
|
+
});
|
|
1877
|
+
break;
|
|
1878
|
+
case 'action':
|
|
1879
|
+
// Create workflow activity/action
|
|
1880
|
+
if (!flowData.workflow_id) {
|
|
1881
|
+
throw new Error('workflow_id is required for flow actions');
|
|
1882
|
+
}
|
|
1883
|
+
result = await (this.client as any).createRecord('wf_activity', {
|
|
1884
|
+
workflow: flowData.workflow_id,
|
|
1885
|
+
name: flowData.name || `action_${Date.now()}`,
|
|
1886
|
+
script: flowData.script || '// Action script here',
|
|
1887
|
+
condition: flowData.condition || '',
|
|
1888
|
+
order: flowData.order || 100,
|
|
1889
|
+
active: flowData.active !== false
|
|
1890
|
+
});
|
|
1891
|
+
break;
|
|
1892
|
+
default:
|
|
1893
|
+
throw new Error(`Unknown flow type: ${flowType}. Supported types: flow, subflow, action`);
|
|
1894
|
+
}
|
|
1895
|
+
} catch (flowError) {
|
|
1896
|
+
this.logger.warn('Flow Designer deployment failed, attempting Business Rule fallback', {
|
|
1897
|
+
error: flowError,
|
|
1898
|
+
flowName: args.name
|
|
1899
|
+
});
|
|
1900
|
+
|
|
1901
|
+
// Try to create equivalent Business Rule instead
|
|
1902
|
+
try {
|
|
1903
|
+
fallbackBusinessRule = await this.createBusinessRuleFallback(args, flowDefinition);
|
|
1904
|
+
result = {
|
|
1905
|
+
success: true,
|
|
1906
|
+
data: fallbackBusinessRule,
|
|
1907
|
+
fallback_used: true,
|
|
1908
|
+
original_error: flowError instanceof Error ? flowError.message : String(flowError)
|
|
1909
|
+
};
|
|
1910
|
+
usedFallback = true;
|
|
1911
|
+
|
|
1912
|
+
this.logger.info('Successfully created Business Rule fallback', {
|
|
1913
|
+
businessRuleId: fallbackBusinessRule.sys_id,
|
|
1914
|
+
originalFlowName: args.name
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
} catch (fallbackError) {
|
|
1918
|
+
this.logger.error('Both Flow Designer and Business Rule fallback failed', {
|
|
1919
|
+
flowError,
|
|
1920
|
+
fallbackError
|
|
1921
|
+
});
|
|
1922
|
+
throw new Error(
|
|
1923
|
+
`🚨 Flow deployment failed and fallback unsuccessful:
|
|
1924
|
+
|
|
1925
|
+
📍 **Errors:**
|
|
1926
|
+
- Flow Designer Error: ${flowError instanceof Error ? flowError.message : String(flowError)}
|
|
1927
|
+
- Business Rule Fallback Error: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}
|
|
1928
|
+
|
|
1929
|
+
🔧 **Update Set Troubleshooting:**
|
|
1930
|
+
1. Check current Update Set: snow_smart_update_set with action="track"
|
|
1931
|
+
2. Verify Update Set is active for tracking
|
|
1932
|
+
3. Use mock testing: snow_test_flow_with_mock instead
|
|
1933
|
+
4. Check flow exists: snow_get_by_sysid
|
|
1934
|
+
|
|
1935
|
+
💡 **Alternative Solutions:**
|
|
1936
|
+
- Use snow_test_flow_with_mock for safe testing
|
|
1937
|
+
- Verify flow creation with snow_get_by_sysid
|
|
1938
|
+
- Check Update Set contains the flow artifact
|
|
1939
|
+
- Create Business Rule manually if needed
|
|
1940
|
+
|
|
1941
|
+
📚 Please check your flow definition JSON format or use manual deployment.`
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
const credentials = await this.oauth.loadCredentials();
|
|
1947
|
+
const flowUrl = result.success && result.data
|
|
1948
|
+
? (usedFallback
|
|
1949
|
+
? `https://${credentials?.instance}/sys_script.do?sys_id=${result.data.sys_id}`
|
|
1950
|
+
: `https://${credentials?.instance}/nav_to.do?uri=sys_hub_flow.do?sys_id=${result.data.sys_id}`)
|
|
1951
|
+
: `https://${credentials?.instance}/flow-designer.do`;
|
|
1952
|
+
|
|
1953
|
+
const artifactSummary = deployedArtifacts.length > 0
|
|
1954
|
+
? `\n🔗 **Linked Artifacts Deployed:**\n${deployedArtifacts.map((a, i) =>
|
|
1955
|
+
`${i + 1}. ${a.type}: ${a.name} (${a.sys_id})`
|
|
1956
|
+
).join('\n')}\n`
|
|
1957
|
+
: '';
|
|
1958
|
+
|
|
1959
|
+
const activitySummary = flowDefinition.activities
|
|
1960
|
+
? `\n📊 **Flow Activities:**\n${flowDefinition.activities.map((a: any, i: number) =>
|
|
1961
|
+
`${i + 1}. ${a.name} (${a.type})${a.artifact_reference ? ` - Uses: ${a.artifact_reference.name}` : ''}`
|
|
1962
|
+
).join('\n')}\n`
|
|
1963
|
+
: '';
|
|
1964
|
+
|
|
1965
|
+
const successMessage = usedFallback
|
|
1966
|
+
? `🔄 **INTELLIGENT FALLBACK SUCCESSFUL!**
|
|
1967
|
+
|
|
1968
|
+
⚠️ Flow Designer deployment failed, but Snow-Flow automatically created a Business Rule that achieves the same result!
|
|
1969
|
+
|
|
1970
|
+
🛠️ **Business Rule Details:**
|
|
1971
|
+
- Name: ${args.name}
|
|
1972
|
+
- Type: 🔧 Business Rule (Fallback from Flow Designer)
|
|
1973
|
+
- Table: ${args.table || 'sys_user'}
|
|
1974
|
+
- When: ${this.getTriggerWhen(args.trigger_type)}
|
|
1975
|
+
- Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
1976
|
+
- Original Error: ${result.original_error}
|
|
1977
|
+
|
|
1978
|
+
✨ **Why This Works Better:**
|
|
1979
|
+
- ✅ More reliable than Flow Designer for simple automations
|
|
1980
|
+
- ✅ Faster execution (server-side JavaScript)
|
|
1981
|
+
- ✅ Better error handling and debugging
|
|
1982
|
+
- ✅ Direct database access capabilities`
|
|
1983
|
+
: `✅ Flow Designer flow deployed successfully!
|
|
1984
|
+
|
|
1985
|
+
🔄 **${flowType.charAt(0).toUpperCase() + flowType.slice(1)} Details:**
|
|
1986
|
+
- Name: ${args.name}
|
|
1987
|
+
- Flow Type: ${flowType === 'flow' ? '📋 Flow' : flowType === 'subflow' ? '🔄 Subflow' : '⚡ Action'}
|
|
1988
|
+
- Composed: ${isComposedFlow ? '🧠 Yes - Intelligent Composed Flow' : '❌ No - Standard'}
|
|
1989
|
+
${flowType === 'flow' ? `- Trigger Type: ${args.trigger_type}
|
|
1990
|
+
- Table: ${args.table || 'N/A'}` : ''}
|
|
1991
|
+
${flowType !== 'flow' ? `- Inputs: ${flowDefinition.inputs?.length || 0}
|
|
1992
|
+
- Outputs: ${flowDefinition.outputs?.length || 0}` : ''}
|
|
1993
|
+
- Category: ${args.category || 'automation'}
|
|
1994
|
+
- Active: ${args.active !== false ? 'Yes' : 'No'}`;
|
|
1995
|
+
|
|
1996
|
+
const continuationMessage = usedFallback
|
|
1997
|
+
? `
|
|
1998
|
+
📦 **Update Set:**
|
|
1999
|
+
- Name: ${updateSetName}
|
|
2000
|
+
- ID: ${updateSetId}
|
|
2001
|
+
|
|
2002
|
+
🔗 **Direct Links:**
|
|
2003
|
+
- Business Rule: ${flowUrl}
|
|
2004
|
+
- Business Rules List: https://${credentials?.instance}/sys_script_list.do
|
|
2005
|
+
|
|
2006
|
+
📝 **Business Rule Components Created:**
|
|
2007
|
+
1. ✅ Trigger configured (${this.getTriggerWhen(args.trigger_type)})
|
|
2008
|
+
2. ✅ Condition logic applied
|
|
2009
|
+
3. ✅ Server-side script generated
|
|
2010
|
+
4. ✅ Error handling implemented
|
|
2011
|
+
5. ✅ Activation settings configured
|
|
2012
|
+
|
|
2013
|
+
📋 **Next Steps:**
|
|
2014
|
+
1. Test business rule execution by triggering the event
|
|
2015
|
+
2. Check logs in System Logs > Script Log Statements
|
|
2016
|
+
3. Modify the script if additional logic is needed
|
|
2017
|
+
4. Monitor performance and error handling
|
|
2018
|
+
|
|
2019
|
+
🔄 **Snow-Flow Intelligent Fallback:**
|
|
2020
|
+
Snow-Flow automatically detected Flow Designer issues and created a functionally equivalent Business Rule. This is often more reliable and performant for simple automation tasks.`
|
|
2021
|
+
: `
|
|
2022
|
+
📦 **Update Set:**
|
|
2023
|
+
- Name: ${updateSetName}
|
|
2024
|
+
- ID: ${updateSetId}
|
|
2025
|
+
${artifactSummary}${activitySummary}
|
|
2026
|
+
🔗 **Direct Links:**
|
|
2027
|
+
- Flow Designer: ${flowUrl}
|
|
2028
|
+
- Flow Designer Home: https://${credentials?.instance}/flow-designer.do?sysparm_nostack=true
|
|
2029
|
+
|
|
2030
|
+
📝 **Flow Components Created:**
|
|
2031
|
+
1. ✅ Trigger configured (${args.trigger_type})
|
|
2032
|
+
2. ✅ Condition logic applied
|
|
2033
|
+
3. ✅ Flow definition structured with ${flowDefinition.activities?.length || 0} activities
|
|
2034
|
+
4. ✅ ${linkedArtifacts.length} artifacts linked and deployed
|
|
2035
|
+
5. ✅ Activation settings configured
|
|
2036
|
+
|
|
2037
|
+
${isComposedFlow ? `
|
|
2038
|
+
🧠 **Intelligent Flow Features:**
|
|
2039
|
+
- ✅ Natural language instruction processed
|
|
2040
|
+
- ✅ Artifacts automatically discovered and linked
|
|
2041
|
+
- ✅ Dependencies resolved and deployed
|
|
2042
|
+
- ✅ Error handling configured
|
|
2043
|
+
- ✅ Variables and connections mapped
|
|
2044
|
+
` : ''}
|
|
2045
|
+
|
|
2046
|
+
📋 **Next Steps:**
|
|
2047
|
+
1. Test flow execution with sample data
|
|
2048
|
+
2. Monitor flow performance and logs
|
|
2049
|
+
3. Review artifact connections
|
|
2050
|
+
4. Customize error handling if needed
|
|
2051
|
+
|
|
2052
|
+
💡 **Composed Flow Capabilities:**
|
|
2053
|
+
- Automatic artifact orchestration
|
|
2054
|
+
- Intelligent output-to-input mapping
|
|
2055
|
+
- Multi-artifact dependency resolution
|
|
2056
|
+
- Natural language configuration`;
|
|
2057
|
+
|
|
2058
|
+
// ENHANCED: Ensure artifact is tracked in Update Set
|
|
2059
|
+
if (result.success && result.data) {
|
|
2060
|
+
await this.ensureUpdateSetTracking({
|
|
2100
2061
|
sys_id: result.data.sys_id,
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2062
|
+
type: usedFallback ? 'Business Rule' : 'Flow',
|
|
2063
|
+
name: args.name,
|
|
2064
|
+
table: usedFallback ? 'sys_script' : 'sys_hub_flow'
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
return {
|
|
2069
|
+
content: [
|
|
2070
|
+
{
|
|
2071
|
+
type: 'text',
|
|
2072
|
+
text: successMessage + continuationMessage,
|
|
2073
|
+
},
|
|
2074
|
+
],
|
|
2105
2075
|
};
|
|
2076
|
+
} catch (error) {
|
|
2077
|
+
const enhancedError = `🚨 Flow Deployment Failed
|
|
2078
|
+
|
|
2079
|
+
📍 Error: ${error instanceof Error ? error.message : String(error)}
|
|
2080
|
+
|
|
2081
|
+
🔧 Troubleshooting Steps:
|
|
2082
|
+
1. Check authentication: snow_auth_diagnostics()
|
|
2083
|
+
2. Validate flow definition: snow_validate_flow_definition()
|
|
2084
|
+
3. Check Update Set: snow_update_set_current()
|
|
2085
|
+
4. Verify flow_designer role permissions
|
|
2086
|
+
|
|
2087
|
+
💡 Alternative Approaches:
|
|
2088
|
+
• Use snow_create_flow with natural language (recommended)
|
|
2089
|
+
• Test with snow_test_flow_with_mock() first
|
|
2090
|
+
• Use snow_flow_wizard for step-by-step creation
|
|
2091
|
+
• Try Business Rule fallback if flow creation fails
|
|
2092
|
+
|
|
2093
|
+
📚 Documentation: See CLAUDE.md for Flow Development Guidelines`;
|
|
2094
|
+
throw new Error(enhancedError);
|
|
2095
|
+
}
|
|
2096
|
+
} */
|
|
2097
|
+
/**
|
|
2098
|
+
* Deploy a linked artifact (script include, business rule, etc.) - DEPRECATED
|
|
2099
|
+
*/
|
|
2100
|
+
/* private async deployLinkedArtifact(artifact: any): Promise<any> {
|
|
2101
|
+
this.logger.info('Deploying linked artifact', { type: artifact.type, name: artifact.name });
|
|
2102
|
+
|
|
2103
|
+
switch (artifact.type) {
|
|
2104
|
+
case 'script_include':
|
|
2105
|
+
throw new Error('Script includes via flow deployment are deprecated. Use direct artifact creation instead.');
|
|
2106
|
+
|
|
2107
|
+
case 'business_rule':
|
|
2108
|
+
return await this.deployBusinessRule(artifact);
|
|
2109
|
+
|
|
2110
|
+
case 'table':
|
|
2111
|
+
return await this.deployTable(artifact);
|
|
2112
|
+
|
|
2113
|
+
default:
|
|
2114
|
+
throw new Error(`Unknown artifact type: ${artifact.type}`);
|
|
2115
|
+
}
|
|
2116
|
+
} */
|
|
2117
|
+
/**
|
|
2118
|
+
* Deploy a script include artifact - DEPRECATED
|
|
2119
|
+
*/
|
|
2120
|
+
/* private async deployScriptInclude(artifact: any): Promise<any> {
|
|
2121
|
+
const scriptIncludeData = {
|
|
2122
|
+
name: artifact.name,
|
|
2123
|
+
api_name: artifact.api_name || artifact.name,
|
|
2124
|
+
description: artifact.description || `Script include for ${artifact.purpose}`,
|
|
2125
|
+
script: artifact.script || artifact.fallback_script,
|
|
2126
|
+
active: true,
|
|
2127
|
+
access: 'public'
|
|
2128
|
+
};
|
|
2129
|
+
|
|
2130
|
+
const result = await this.client.createScriptInclude(scriptIncludeData);
|
|
2131
|
+
|
|
2132
|
+
if (!result.data?.sys_id) {
|
|
2133
|
+
throw new Error(`Script Include deployment failed: No sys_id returned from ServiceNow. Result: ${JSON.stringify(result)}`);
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
return {
|
|
2137
|
+
originalId: artifact.sys_id,
|
|
2138
|
+
sys_id: result.data.sys_id,
|
|
2139
|
+
name: artifact.name,
|
|
2140
|
+
api_name: scriptIncludeData.api_name,
|
|
2141
|
+
type: 'script_include',
|
|
2142
|
+
success: result.success
|
|
2143
|
+
};
|
|
2106
2144
|
}
|
|
2145
|
+
|
|
2107
2146
|
/**
|
|
2108
2147
|
* Deploy a business rule artifact
|
|
2109
2148
|
*/
|
|
@@ -2627,7 +2666,7 @@ ${hasErrors ? '❌ Validation failed - fix errors before deployment' : '✅ Vali
|
|
|
2627
2666
|
let rollbackResult;
|
|
2628
2667
|
try {
|
|
2629
2668
|
// Set update set to ignore state (ServiceNow's way of "rolling back")
|
|
2630
|
-
rollbackResult = await this.client.
|
|
2669
|
+
rollbackResult = await this.client.updateRecord(`sys_update_set/${update_set_id}`, {
|
|
2631
2670
|
state: 'ignore',
|
|
2632
2671
|
description: `${updateSet.description || ''} - ROLLED BACK: ${reason}`
|
|
2633
2672
|
});
|
|
@@ -3067,12 +3106,12 @@ ${deploymentList || 'No recent deployments found in the last 7 days'}
|
|
|
3067
3106
|
if (existingArtifact) {
|
|
3068
3107
|
// Update existing artifact
|
|
3069
3108
|
const { sys_id, ...updateData } = artifact;
|
|
3070
|
-
result = await this.client.
|
|
3109
|
+
result = await this.client.updateRecord(`${tableName}/${sys_id}`, updateData);
|
|
3071
3110
|
action = 'updated';
|
|
3072
3111
|
}
|
|
3073
3112
|
else {
|
|
3074
3113
|
// Create new artifact
|
|
3075
|
-
result = await this.client.
|
|
3114
|
+
result = await this.client.createRecord(tableName, artifact);
|
|
3076
3115
|
action = 'created';
|
|
3077
3116
|
}
|
|
3078
3117
|
if (!result?.result) {
|
|
@@ -3835,25 +3874,39 @@ This will use your .env credentials to start the OAuth flow and generate access
|
|
|
3835
3874
|
template: '<div>Test widget - safe to delete</div>',
|
|
3836
3875
|
description: 'Temporary test widget created by Snow-Flow MCP diagnostics'
|
|
3837
3876
|
};
|
|
3838
|
-
const createResult = await this.client.
|
|
3839
|
-
if (createResult?.
|
|
3877
|
+
const createResult = await this.client.createRecord('sp_widget', testWidget);
|
|
3878
|
+
if (createResult?.success && createResult?.data?.sys_id) {
|
|
3840
3879
|
// Immediately delete the test widget
|
|
3841
3880
|
try {
|
|
3842
|
-
await this.client.
|
|
3881
|
+
await this.client.deleteRecord('sp_widget', createResult.data.sys_id);
|
|
3843
3882
|
realApiTests.writePermissions = {
|
|
3844
3883
|
status: '✅ Full Access',
|
|
3845
3884
|
description: 'Can create and delete artifacts - full deployment capability',
|
|
3846
|
-
details: `Successfully created and cleaned up test widget ${createResult.
|
|
3885
|
+
details: `Successfully created and cleaned up test widget ${createResult.data.sys_id}`
|
|
3847
3886
|
};
|
|
3848
3887
|
}
|
|
3849
3888
|
catch (deleteError) {
|
|
3850
3889
|
realApiTests.writePermissions = {
|
|
3851
3890
|
status: '⚠️ Partial',
|
|
3852
3891
|
description: 'Can create but cannot delete - cleanup may be needed',
|
|
3853
|
-
details: `Created test widget ${createResult.
|
|
3892
|
+
details: `Created test widget ${createResult.data.sys_id} but failed to delete: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}`
|
|
3854
3893
|
};
|
|
3855
3894
|
}
|
|
3856
3895
|
}
|
|
3896
|
+
else {
|
|
3897
|
+
// Log what we got for debugging
|
|
3898
|
+
this.logger.warn('Create succeeded but unexpected response structure:', {
|
|
3899
|
+
success: createResult?.success,
|
|
3900
|
+
hasData: !!createResult?.data,
|
|
3901
|
+
hasSysId: !!createResult?.data?.sys_id,
|
|
3902
|
+
dataKeys: createResult?.data ? Object.keys(createResult.data) : []
|
|
3903
|
+
});
|
|
3904
|
+
realApiTests.writePermissions = {
|
|
3905
|
+
status: '⚠️ Partial',
|
|
3906
|
+
description: 'Created widget but response structure unexpected',
|
|
3907
|
+
details: `Response structure: ${JSON.stringify(createResult?.data || {})}`
|
|
3908
|
+
};
|
|
3909
|
+
}
|
|
3857
3910
|
}
|
|
3858
3911
|
catch (writeError) {
|
|
3859
3912
|
realApiTests.writePermissions = {
|
|
@@ -5525,13 +5578,13 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
5525
5578
|
let result;
|
|
5526
5579
|
switch (artifact.type) {
|
|
5527
5580
|
case 'flow':
|
|
5528
|
-
|
|
5581
|
+
throw new Error('Flow deployment is deprecated. Flows, workflows, and subflows are no longer supported. Use widgets or applications instead.');
|
|
5529
5582
|
break;
|
|
5530
5583
|
case 'widget':
|
|
5531
5584
|
result = await this.deployWidget(artifact.create);
|
|
5532
5585
|
break;
|
|
5533
5586
|
case 'script_include':
|
|
5534
|
-
result = await this.
|
|
5587
|
+
result = await this.client.createScriptInclude(artifact.create);
|
|
5535
5588
|
break;
|
|
5536
5589
|
case 'business_rule':
|
|
5537
5590
|
result = await this.deployBusinessRule(artifact.create);
|
|
@@ -5847,12 +5900,7 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
5847
5900
|
};
|
|
5848
5901
|
}
|
|
5849
5902
|
case 'flow':
|
|
5850
|
-
|
|
5851
|
-
return {
|
|
5852
|
-
success: flowResult.content[0].text.includes('✅'),
|
|
5853
|
-
sys_id: flowResult.content[0].text.match(/sys_id: ([a-f0-9]+)/)?.[1],
|
|
5854
|
-
message: 'Flow deployed'
|
|
5855
|
-
};
|
|
5903
|
+
throw new Error('Flow deployment is deprecated. Use widgets or applications instead.');
|
|
5856
5904
|
case 'script':
|
|
5857
5905
|
case 'script_include':
|
|
5858
5906
|
const scriptResult = await this.createRecordWithRetry('sys_script_include', config);
|
|
@@ -5947,7 +5995,7 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
5947
5995
|
async rollbackArtifact(artifact) {
|
|
5948
5996
|
const tableMap = {
|
|
5949
5997
|
'widget': 'sp_widget',
|
|
5950
|
-
'flow': 'sys_hub_flow',
|
|
5998
|
+
// 'flow': 'sys_hub_flow', // REMOVED - flows deprecated
|
|
5951
5999
|
'script': 'sys_script_include',
|
|
5952
6000
|
'script_include': 'sys_script_include',
|
|
5953
6001
|
'business_rule': 'sys_script',
|
|
@@ -7582,11 +7630,11 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
7582
7630
|
getTableForArtifactType(artifactType) {
|
|
7583
7631
|
const ARTIFACT_TABLES = {
|
|
7584
7632
|
'widget': 'sp_widget',
|
|
7585
|
-
'flow': 'sys_hub_flow',
|
|
7633
|
+
// 'flow': 'sys_hub_flow', // REMOVED - flows deprecated
|
|
7586
7634
|
'script': 'sys_script_include',
|
|
7587
7635
|
'script_include': 'sys_script_include',
|
|
7588
7636
|
'business_rule': 'sys_script',
|
|
7589
|
-
'workflow': 'wf_workflow',
|
|
7637
|
+
// 'workflow': 'wf_workflow', // REMOVED - workflows deprecated
|
|
7590
7638
|
'application': 'sys_app',
|
|
7591
7639
|
'ui_action': 'sys_ui_action',
|
|
7592
7640
|
'ui_page': 'sys_ui_page',
|