snow-flow 3.0.14 → 3.0.16
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',
|
|
@@ -637,7 +637,9 @@ Your widget is deployed and ready for testing in Service Portal.`
|
|
|
637
637
|
this.logger.info('✅ Direct deployment successful');
|
|
638
638
|
}
|
|
639
639
|
else {
|
|
640
|
-
|
|
640
|
+
// Include detailed error information
|
|
641
|
+
const errorDetails = result?.details ? JSON.stringify(result.details, null, 2) : '';
|
|
642
|
+
throw new Error(`Widget creation failed: ${result?.error || 'Unknown error'}${errorDetails ? '\nDetails: ' + errorDetails : ''}`);
|
|
641
643
|
}
|
|
642
644
|
}
|
|
643
645
|
catch (error) {
|
|
@@ -1675,435 +1677,474 @@ ${args.widgets && args.widgets.length > 0 ? args.widgets.map((w, i) => `
|
|
|
1675
1677
|
`;
|
|
1676
1678
|
return css;
|
|
1677
1679
|
}
|
|
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
|
-
}
|
|
1680
|
+
// DEPRECATED: Flow deployment is no longer supported - flows, workflows, and subflows have been removed
|
|
1681
|
+
/* private async deployFlow(args: any) {
|
|
1682
|
+
try {
|
|
1683
|
+
// Enhanced authentication check with token refresh for deployment
|
|
1684
|
+
const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
|
|
1685
|
+
if (!authResult.isValid) {
|
|
1686
|
+
this.logger.error('Deployment authentication failed:', authResult.error);
|
|
1687
|
+
|
|
1688
|
+
// If auth failed, try to refresh token
|
|
1689
|
+
const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
|
|
1690
|
+
if (!refreshResult.success) {
|
|
1691
|
+
return {
|
|
1692
|
+
content: [
|
|
1693
|
+
{
|
|
1694
|
+
type: 'text',
|
|
1695
|
+
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.`,
|
|
1696
|
+
},
|
|
1697
|
+
],
|
|
1698
|
+
};
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
// Warn if token may lack write permissions
|
|
1703
|
+
if (!authResult.hasWriteScope) {
|
|
1704
|
+
this.logger.warn('⚠️ Token may lack write permissions, deployment might fail with 403');
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
// Ensure we have a flow definition
|
|
1708
|
+
if (!args.flow_definition) {
|
|
1709
|
+
return {
|
|
1710
|
+
content: [
|
|
1711
|
+
{
|
|
1712
|
+
type: 'text',
|
|
1713
|
+
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.',
|
|
1714
|
+
},
|
|
1715
|
+
],
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
const flowType = args.flow_type || 'flow';
|
|
1720
|
+
this.logger.info(`Deploying ${flowType} to ServiceNow`, { name: args.name, type: flowType });
|
|
1721
|
+
|
|
1722
|
+
// Validate flow definition first if requested
|
|
1723
|
+
let validatedDefinition = args.flow_definition;
|
|
1724
|
+
if (args.validate_before_deploy !== false) {
|
|
1725
|
+
const validationResult = await this.validateFlowDefinition({
|
|
1726
|
+
definition: args.flow_definition,
|
|
1727
|
+
flow_type: flowType,
|
|
1728
|
+
show_preview: false,
|
|
1729
|
+
test_mode: false,
|
|
1730
|
+
check_dependencies: true
|
|
1731
|
+
});
|
|
1732
|
+
|
|
1733
|
+
// Check if validation failed
|
|
1734
|
+
const validationText = validationResult.content?.[0]?.text || '';
|
|
1735
|
+
if (validationText.includes('❌') || validationText.includes('ERROR')) {
|
|
1736
|
+
return {
|
|
1737
|
+
content: [
|
|
1738
|
+
{
|
|
1739
|
+
type: 'text',
|
|
1740
|
+
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
1741
|
}
|
|
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(',')
|
|
1742
|
+
]
|
|
1806
1743
|
};
|
|
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
|
-
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// CRITICAL: Use the corrected definition from validation
|
|
1747
|
+
// The validateFlowDefinition method may have auto-corrected "steps" or "actions" to "activities"
|
|
1748
|
+
if (validationText.includes('Auto-converted "steps" to "activities"') ||
|
|
1749
|
+
validationText.includes('Auto-converted "actions" to "activities"') ||
|
|
1750
|
+
validationText.includes('Smart Auto-Corrections Applied')) {
|
|
1751
|
+
// Re-parse the corrected definition from the validation process
|
|
1752
|
+
const tempDef = typeof args.flow_definition === 'string' ? JSON.parse(args.flow_definition) : args.flow_definition;
|
|
1753
|
+
if (tempDef.steps && !tempDef.activities) {
|
|
1754
|
+
tempDef.activities = tempDef.steps;
|
|
1755
|
+
delete tempDef.steps;
|
|
1756
|
+
} else if (tempDef.actions && !tempDef.activities) {
|
|
1757
|
+
tempDef.activities = tempDef.actions;
|
|
1758
|
+
delete tempDef.actions;
|
|
1759
|
+
}
|
|
1760
|
+
validatedDefinition = JSON.stringify(tempDef);
|
|
1761
|
+
this.logger.info('Using auto-corrected flow definition for deployment');
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
// Ensure Update Set is active
|
|
1766
|
+
const { updateSetId, updateSetName } = await this.ensureUpdateSet('Flow', args.name);
|
|
1767
|
+
|
|
1768
|
+
// Check if this is a master flow with linked artifacts
|
|
1769
|
+
const isComposedFlow = args.composed_flow || args.linked_artifacts;
|
|
1770
|
+
const linkedArtifacts = args.linked_artifacts || [];
|
|
1771
|
+
|
|
1772
|
+
// Deploy linked artifacts first if this is a composed flow
|
|
1773
|
+
const deployedArtifacts: any[] = [];
|
|
1774
|
+
if (isComposedFlow && linkedArtifacts.length > 0) {
|
|
1775
|
+
this.logger.info('Deploying linked artifacts for composed flow', { count: linkedArtifacts.length });
|
|
1776
|
+
|
|
1777
|
+
for (const artifact of linkedArtifacts) {
|
|
1833
1778
|
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.createRecord('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.createRecord('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
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
catch (flowError) {
|
|
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
|
-
});
|
|
1779
|
+
const deployResult = await this.deployLinkedArtifact(artifact);
|
|
1780
|
+
deployedArtifacts.push(deployResult);
|
|
1781
|
+
} catch (error) {
|
|
1782
|
+
this.logger.error('Failed to deploy linked artifact', { artifact, error });
|
|
1783
|
+
throw new Error(`Failed to deploy linked artifact ${artifact.name}: ${error}`);
|
|
2035
1784
|
}
|
|
2036
|
-
|
|
2037
|
-
content: [
|
|
2038
|
-
{
|
|
2039
|
-
type: 'text',
|
|
2040
|
-
text: successMessage + continuationMessage,
|
|
2041
|
-
},
|
|
2042
|
-
],
|
|
2043
|
-
};
|
|
1785
|
+
}
|
|
2044
1786
|
}
|
|
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);
|
|
1787
|
+
|
|
1788
|
+
// Parse flow definition to inject deployed artifact references
|
|
1789
|
+
// Use the validated and corrected definition
|
|
1790
|
+
let flowDefinition = validatedDefinition;
|
|
1791
|
+
if (typeof flowDefinition === 'string') {
|
|
1792
|
+
flowDefinition = JSON.parse(flowDefinition);
|
|
2064
1793
|
}
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
1794
|
+
|
|
1795
|
+
// Update flow activities with deployed artifact sys_ids
|
|
1796
|
+
if (flowDefinition.activities && deployedArtifacts.length > 0) {
|
|
1797
|
+
flowDefinition.activities = flowDefinition.activities.map((activity: any) => {
|
|
1798
|
+
if (activity.artifact_reference) {
|
|
1799
|
+
const deployed = deployedArtifacts.find(d =>
|
|
1800
|
+
d.originalId === activity.artifact_reference.sys_id ||
|
|
1801
|
+
d.name === activity.artifact_reference.name
|
|
1802
|
+
);
|
|
1803
|
+
if (deployed) {
|
|
1804
|
+
activity.artifact_sys_id = deployed.sys_id;
|
|
1805
|
+
activity.artifact_api_name = deployed.api_name;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return activity;
|
|
1809
|
+
});
|
|
2080
1810
|
}
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
1811
|
+
|
|
1812
|
+
// Create flow data based on flow type
|
|
1813
|
+
const flowData: any = {
|
|
1814
|
+
name: args.name,
|
|
1815
|
+
description: args.description,
|
|
1816
|
+
active: args.active !== false,
|
|
1817
|
+
flow_definition: JSON.stringify(flowDefinition),
|
|
1818
|
+
category: args.category || 'automation',
|
|
1819
|
+
// Additional fields for composed flows
|
|
1820
|
+
is_composed: isComposedFlow,
|
|
1821
|
+
linked_artifact_count: linkedArtifacts.length,
|
|
1822
|
+
artifact_references: deployedArtifacts.map(a => a.sys_id).join(',')
|
|
2093
1823
|
};
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
1824
|
+
|
|
1825
|
+
// Configure based on flow type
|
|
1826
|
+
switch (flowType) {
|
|
1827
|
+
case 'flow':
|
|
1828
|
+
flowData.table = args.table || '';
|
|
1829
|
+
flowData.trigger_type = args.trigger_type;
|
|
1830
|
+
flowData.condition = args.condition || '';
|
|
1831
|
+
flowData.type = 'flow';
|
|
1832
|
+
break;
|
|
1833
|
+
case 'subflow':
|
|
1834
|
+
// Subflows don't have triggers, they're called by other flows
|
|
1835
|
+
flowData.type = 'subflow';
|
|
1836
|
+
flowData.inputs = flowDefinition.inputs || [];
|
|
1837
|
+
flowData.outputs = flowDefinition.outputs || [];
|
|
1838
|
+
break;
|
|
1839
|
+
case 'action':
|
|
1840
|
+
// Actions are reusable components
|
|
1841
|
+
flowData.type = 'action';
|
|
1842
|
+
flowData.action_type = args.action_type || 'custom';
|
|
1843
|
+
flowData.inputs = flowDefinition.inputs || [];
|
|
1844
|
+
flowData.outputs = flowDefinition.outputs || [];
|
|
1845
|
+
break;
|
|
2097
1846
|
}
|
|
2098
|
-
|
|
2099
|
-
|
|
1847
|
+
|
|
1848
|
+
// Deploy to ServiceNow using appropriate API based on flow type
|
|
1849
|
+
let result;
|
|
1850
|
+
let usedFallback = false;
|
|
1851
|
+
let fallbackBusinessRule = null;
|
|
1852
|
+
|
|
1853
|
+
try {
|
|
1854
|
+
// Real ServiceNow Flow deployment using proper API calls
|
|
1855
|
+
switch (flowType) {
|
|
1856
|
+
case 'flow':
|
|
1857
|
+
// Create workflow record in ServiceNow
|
|
1858
|
+
result = await (this.client as any).createRecord('wf_workflow', {
|
|
1859
|
+
name: flowData.name || `flow_${Date.now()}`,
|
|
1860
|
+
description: flowData.description || 'Created by Snow-Flow',
|
|
1861
|
+
table: flowData.table || 'incident',
|
|
1862
|
+
active: flowData.active !== false,
|
|
1863
|
+
condition: flowData.condition || '',
|
|
1864
|
+
script: flowData.script || '// Flow logic here',
|
|
1865
|
+
order: flowData.order || 100
|
|
1866
|
+
});
|
|
1867
|
+
break;
|
|
1868
|
+
case 'subflow':
|
|
1869
|
+
// Create subflow as a workflow activity
|
|
1870
|
+
result = await (this.client as any).createRecord('wf_workflow', {
|
|
1871
|
+
name: flowData.name || `subflow_${Date.now()}`,
|
|
1872
|
+
description: `${flowData.description || 'Subflow created by Snow-Flow'} [SUBFLOW]`,
|
|
1873
|
+
table: flowData.table || 'incident',
|
|
1874
|
+
active: flowData.active !== false,
|
|
1875
|
+
condition: flowData.condition || '',
|
|
1876
|
+
script: flowData.script || '// Subflow logic here',
|
|
1877
|
+
order: flowData.order || 200
|
|
1878
|
+
});
|
|
1879
|
+
break;
|
|
1880
|
+
case 'action':
|
|
1881
|
+
// Create workflow activity/action
|
|
1882
|
+
if (!flowData.workflow_id) {
|
|
1883
|
+
throw new Error('workflow_id is required for flow actions');
|
|
1884
|
+
}
|
|
1885
|
+
result = await (this.client as any).createRecord('wf_activity', {
|
|
1886
|
+
workflow: flowData.workflow_id,
|
|
1887
|
+
name: flowData.name || `action_${Date.now()}`,
|
|
1888
|
+
script: flowData.script || '// Action script here',
|
|
1889
|
+
condition: flowData.condition || '',
|
|
1890
|
+
order: flowData.order || 100,
|
|
1891
|
+
active: flowData.active !== false
|
|
1892
|
+
});
|
|
1893
|
+
break;
|
|
1894
|
+
default:
|
|
1895
|
+
throw new Error(`Unknown flow type: ${flowType}. Supported types: flow, subflow, action`);
|
|
1896
|
+
}
|
|
1897
|
+
} catch (flowError) {
|
|
1898
|
+
this.logger.warn('Flow Designer deployment failed, attempting Business Rule fallback', {
|
|
1899
|
+
error: flowError,
|
|
1900
|
+
flowName: args.name
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
// Try to create equivalent Business Rule instead
|
|
1904
|
+
try {
|
|
1905
|
+
fallbackBusinessRule = await this.createBusinessRuleFallback(args, flowDefinition);
|
|
1906
|
+
result = {
|
|
1907
|
+
success: true,
|
|
1908
|
+
data: fallbackBusinessRule,
|
|
1909
|
+
fallback_used: true,
|
|
1910
|
+
original_error: flowError instanceof Error ? flowError.message : String(flowError)
|
|
1911
|
+
};
|
|
1912
|
+
usedFallback = true;
|
|
1913
|
+
|
|
1914
|
+
this.logger.info('Successfully created Business Rule fallback', {
|
|
1915
|
+
businessRuleId: fallbackBusinessRule.sys_id,
|
|
1916
|
+
originalFlowName: args.name
|
|
1917
|
+
});
|
|
1918
|
+
|
|
1919
|
+
} catch (fallbackError) {
|
|
1920
|
+
this.logger.error('Both Flow Designer and Business Rule fallback failed', {
|
|
1921
|
+
flowError,
|
|
1922
|
+
fallbackError
|
|
1923
|
+
});
|
|
1924
|
+
throw new Error(
|
|
1925
|
+
`🚨 Flow deployment failed and fallback unsuccessful:
|
|
1926
|
+
|
|
1927
|
+
📍 **Errors:**
|
|
1928
|
+
- Flow Designer Error: ${flowError instanceof Error ? flowError.message : String(flowError)}
|
|
1929
|
+
- Business Rule Fallback Error: ${fallbackError instanceof Error ? fallbackError.message : String(fallbackError)}
|
|
1930
|
+
|
|
1931
|
+
🔧 **Update Set Troubleshooting:**
|
|
1932
|
+
1. Check current Update Set: snow_smart_update_set with action="track"
|
|
1933
|
+
2. Verify Update Set is active for tracking
|
|
1934
|
+
3. Use mock testing: snow_test_flow_with_mock instead
|
|
1935
|
+
4. Check flow exists: snow_get_by_sysid
|
|
1936
|
+
|
|
1937
|
+
💡 **Alternative Solutions:**
|
|
1938
|
+
- Use snow_test_flow_with_mock for safe testing
|
|
1939
|
+
- Verify flow creation with snow_get_by_sysid
|
|
1940
|
+
- Check Update Set contains the flow artifact
|
|
1941
|
+
- Create Business Rule manually if needed
|
|
1942
|
+
|
|
1943
|
+
📚 Please check your flow definition JSON format or use manual deployment.`
|
|
1944
|
+
);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
const credentials = await this.oauth.loadCredentials();
|
|
1949
|
+
const flowUrl = result.success && result.data
|
|
1950
|
+
? (usedFallback
|
|
1951
|
+
? `https://${credentials?.instance}/sys_script.do?sys_id=${result.data.sys_id}`
|
|
1952
|
+
: `https://${credentials?.instance}/nav_to.do?uri=sys_hub_flow.do?sys_id=${result.data.sys_id}`)
|
|
1953
|
+
: `https://${credentials?.instance}/flow-designer.do`;
|
|
1954
|
+
|
|
1955
|
+
const artifactSummary = deployedArtifacts.length > 0
|
|
1956
|
+
? `\n🔗 **Linked Artifacts Deployed:**\n${deployedArtifacts.map((a, i) =>
|
|
1957
|
+
`${i + 1}. ${a.type}: ${a.name} (${a.sys_id})`
|
|
1958
|
+
).join('\n')}\n`
|
|
1959
|
+
: '';
|
|
1960
|
+
|
|
1961
|
+
const activitySummary = flowDefinition.activities
|
|
1962
|
+
? `\n📊 **Flow Activities:**\n${flowDefinition.activities.map((a: any, i: number) =>
|
|
1963
|
+
`${i + 1}. ${a.name} (${a.type})${a.artifact_reference ? ` - Uses: ${a.artifact_reference.name}` : ''}`
|
|
1964
|
+
).join('\n')}\n`
|
|
1965
|
+
: '';
|
|
1966
|
+
|
|
1967
|
+
const successMessage = usedFallback
|
|
1968
|
+
? `🔄 **INTELLIGENT FALLBACK SUCCESSFUL!**
|
|
1969
|
+
|
|
1970
|
+
⚠️ Flow Designer deployment failed, but Snow-Flow automatically created a Business Rule that achieves the same result!
|
|
1971
|
+
|
|
1972
|
+
🛠️ **Business Rule Details:**
|
|
1973
|
+
- Name: ${args.name}
|
|
1974
|
+
- Type: 🔧 Business Rule (Fallback from Flow Designer)
|
|
1975
|
+
- Table: ${args.table || 'sys_user'}
|
|
1976
|
+
- When: ${this.getTriggerWhen(args.trigger_type)}
|
|
1977
|
+
- Active: ${args.active !== false ? 'Yes' : 'No'}
|
|
1978
|
+
- Original Error: ${result.original_error}
|
|
1979
|
+
|
|
1980
|
+
✨ **Why This Works Better:**
|
|
1981
|
+
- ✅ More reliable than Flow Designer for simple automations
|
|
1982
|
+
- ✅ Faster execution (server-side JavaScript)
|
|
1983
|
+
- ✅ Better error handling and debugging
|
|
1984
|
+
- ✅ Direct database access capabilities`
|
|
1985
|
+
: `✅ Flow Designer flow deployed successfully!
|
|
1986
|
+
|
|
1987
|
+
🔄 **${flowType.charAt(0).toUpperCase() + flowType.slice(1)} Details:**
|
|
1988
|
+
- Name: ${args.name}
|
|
1989
|
+
- Flow Type: ${flowType === 'flow' ? '📋 Flow' : flowType === 'subflow' ? '🔄 Subflow' : '⚡ Action'}
|
|
1990
|
+
- Composed: ${isComposedFlow ? '🧠 Yes - Intelligent Composed Flow' : '❌ No - Standard'}
|
|
1991
|
+
${flowType === 'flow' ? `- Trigger Type: ${args.trigger_type}
|
|
1992
|
+
- Table: ${args.table || 'N/A'}` : ''}
|
|
1993
|
+
${flowType !== 'flow' ? `- Inputs: ${flowDefinition.inputs?.length || 0}
|
|
1994
|
+
- Outputs: ${flowDefinition.outputs?.length || 0}` : ''}
|
|
1995
|
+
- Category: ${args.category || 'automation'}
|
|
1996
|
+
- Active: ${args.active !== false ? 'Yes' : 'No'}`;
|
|
1997
|
+
|
|
1998
|
+
const continuationMessage = usedFallback
|
|
1999
|
+
? `
|
|
2000
|
+
📦 **Update Set:**
|
|
2001
|
+
- Name: ${updateSetName}
|
|
2002
|
+
- ID: ${updateSetId}
|
|
2003
|
+
|
|
2004
|
+
🔗 **Direct Links:**
|
|
2005
|
+
- Business Rule: ${flowUrl}
|
|
2006
|
+
- Business Rules List: https://${credentials?.instance}/sys_script_list.do
|
|
2007
|
+
|
|
2008
|
+
📝 **Business Rule Components Created:**
|
|
2009
|
+
1. ✅ Trigger configured (${this.getTriggerWhen(args.trigger_type)})
|
|
2010
|
+
2. ✅ Condition logic applied
|
|
2011
|
+
3. ✅ Server-side script generated
|
|
2012
|
+
4. ✅ Error handling implemented
|
|
2013
|
+
5. ✅ Activation settings configured
|
|
2014
|
+
|
|
2015
|
+
📋 **Next Steps:**
|
|
2016
|
+
1. Test business rule execution by triggering the event
|
|
2017
|
+
2. Check logs in System Logs > Script Log Statements
|
|
2018
|
+
3. Modify the script if additional logic is needed
|
|
2019
|
+
4. Monitor performance and error handling
|
|
2020
|
+
|
|
2021
|
+
🔄 **Snow-Flow Intelligent Fallback:**
|
|
2022
|
+
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.`
|
|
2023
|
+
: `
|
|
2024
|
+
📦 **Update Set:**
|
|
2025
|
+
- Name: ${updateSetName}
|
|
2026
|
+
- ID: ${updateSetId}
|
|
2027
|
+
${artifactSummary}${activitySummary}
|
|
2028
|
+
🔗 **Direct Links:**
|
|
2029
|
+
- Flow Designer: ${flowUrl}
|
|
2030
|
+
- Flow Designer Home: https://${credentials?.instance}/flow-designer.do?sysparm_nostack=true
|
|
2031
|
+
|
|
2032
|
+
📝 **Flow Components Created:**
|
|
2033
|
+
1. ✅ Trigger configured (${args.trigger_type})
|
|
2034
|
+
2. ✅ Condition logic applied
|
|
2035
|
+
3. ✅ Flow definition structured with ${flowDefinition.activities?.length || 0} activities
|
|
2036
|
+
4. ✅ ${linkedArtifacts.length} artifacts linked and deployed
|
|
2037
|
+
5. ✅ Activation settings configured
|
|
2038
|
+
|
|
2039
|
+
${isComposedFlow ? `
|
|
2040
|
+
🧠 **Intelligent Flow Features:**
|
|
2041
|
+
- ✅ Natural language instruction processed
|
|
2042
|
+
- ✅ Artifacts automatically discovered and linked
|
|
2043
|
+
- ✅ Dependencies resolved and deployed
|
|
2044
|
+
- ✅ Error handling configured
|
|
2045
|
+
- ✅ Variables and connections mapped
|
|
2046
|
+
` : ''}
|
|
2047
|
+
|
|
2048
|
+
📋 **Next Steps:**
|
|
2049
|
+
1. Test flow execution with sample data
|
|
2050
|
+
2. Monitor flow performance and logs
|
|
2051
|
+
3. Review artifact connections
|
|
2052
|
+
4. Customize error handling if needed
|
|
2053
|
+
|
|
2054
|
+
💡 **Composed Flow Capabilities:**
|
|
2055
|
+
- Automatic artifact orchestration
|
|
2056
|
+
- Intelligent output-to-input mapping
|
|
2057
|
+
- Multi-artifact dependency resolution
|
|
2058
|
+
- Natural language configuration`;
|
|
2059
|
+
|
|
2060
|
+
// ENHANCED: Ensure artifact is tracked in Update Set
|
|
2061
|
+
if (result.success && result.data) {
|
|
2062
|
+
await this.ensureUpdateSetTracking({
|
|
2100
2063
|
sys_id: result.data.sys_id,
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2064
|
+
type: usedFallback ? 'Business Rule' : 'Flow',
|
|
2065
|
+
name: args.name,
|
|
2066
|
+
table: usedFallback ? 'sys_script' : 'sys_hub_flow'
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
|
|
2070
|
+
return {
|
|
2071
|
+
content: [
|
|
2072
|
+
{
|
|
2073
|
+
type: 'text',
|
|
2074
|
+
text: successMessage + continuationMessage,
|
|
2075
|
+
},
|
|
2076
|
+
],
|
|
2105
2077
|
};
|
|
2078
|
+
} catch (error) {
|
|
2079
|
+
const enhancedError = `🚨 Flow Deployment Failed
|
|
2080
|
+
|
|
2081
|
+
📍 Error: ${error instanceof Error ? error.message : String(error)}
|
|
2082
|
+
|
|
2083
|
+
🔧 Troubleshooting Steps:
|
|
2084
|
+
1. Check authentication: snow_auth_diagnostics()
|
|
2085
|
+
2. Validate flow definition: snow_validate_flow_definition()
|
|
2086
|
+
3. Check Update Set: snow_update_set_current()
|
|
2087
|
+
4. Verify flow_designer role permissions
|
|
2088
|
+
|
|
2089
|
+
💡 Alternative Approaches:
|
|
2090
|
+
• Use snow_create_flow with natural language (recommended)
|
|
2091
|
+
• Test with snow_test_flow_with_mock() first
|
|
2092
|
+
• Use snow_flow_wizard for step-by-step creation
|
|
2093
|
+
• Try Business Rule fallback if flow creation fails
|
|
2094
|
+
|
|
2095
|
+
📚 Documentation: See CLAUDE.md for Flow Development Guidelines`;
|
|
2096
|
+
throw new Error(enhancedError);
|
|
2097
|
+
}
|
|
2098
|
+
} */
|
|
2099
|
+
/**
|
|
2100
|
+
* Deploy a linked artifact (script include, business rule, etc.) - DEPRECATED
|
|
2101
|
+
*/
|
|
2102
|
+
/* private async deployLinkedArtifact(artifact: any): Promise<any> {
|
|
2103
|
+
this.logger.info('Deploying linked artifact', { type: artifact.type, name: artifact.name });
|
|
2104
|
+
|
|
2105
|
+
switch (artifact.type) {
|
|
2106
|
+
case 'script_include':
|
|
2107
|
+
throw new Error('Script includes via flow deployment are deprecated. Use direct artifact creation instead.');
|
|
2108
|
+
|
|
2109
|
+
case 'business_rule':
|
|
2110
|
+
return await this.deployBusinessRule(artifact);
|
|
2111
|
+
|
|
2112
|
+
case 'table':
|
|
2113
|
+
return await this.deployTable(artifact);
|
|
2114
|
+
|
|
2115
|
+
default:
|
|
2116
|
+
throw new Error(`Unknown artifact type: ${artifact.type}`);
|
|
2117
|
+
}
|
|
2118
|
+
} */
|
|
2119
|
+
/**
|
|
2120
|
+
* Deploy a script include artifact - DEPRECATED
|
|
2121
|
+
*/
|
|
2122
|
+
/* private async deployScriptInclude(artifact: any): Promise<any> {
|
|
2123
|
+
const scriptIncludeData = {
|
|
2124
|
+
name: artifact.name,
|
|
2125
|
+
api_name: artifact.api_name || artifact.name,
|
|
2126
|
+
description: artifact.description || `Script include for ${artifact.purpose}`,
|
|
2127
|
+
script: artifact.script || artifact.fallback_script,
|
|
2128
|
+
active: true,
|
|
2129
|
+
access: 'public'
|
|
2130
|
+
};
|
|
2131
|
+
|
|
2132
|
+
const result = await this.client.createScriptInclude(scriptIncludeData);
|
|
2133
|
+
|
|
2134
|
+
if (!result.data?.sys_id) {
|
|
2135
|
+
throw new Error(`Script Include deployment failed: No sys_id returned from ServiceNow. Result: ${JSON.stringify(result)}`);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
return {
|
|
2139
|
+
originalId: artifact.sys_id,
|
|
2140
|
+
sys_id: result.data.sys_id,
|
|
2141
|
+
name: artifact.name,
|
|
2142
|
+
api_name: scriptIncludeData.api_name,
|
|
2143
|
+
type: 'script_include',
|
|
2144
|
+
success: result.success
|
|
2145
|
+
};
|
|
2106
2146
|
}
|
|
2147
|
+
|
|
2107
2148
|
/**
|
|
2108
2149
|
* Deploy a business rule artifact
|
|
2109
2150
|
*/
|
|
@@ -5539,13 +5580,13 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
5539
5580
|
let result;
|
|
5540
5581
|
switch (artifact.type) {
|
|
5541
5582
|
case 'flow':
|
|
5542
|
-
|
|
5583
|
+
throw new Error('Flow deployment is deprecated. Flows, workflows, and subflows are no longer supported. Use widgets or applications instead.');
|
|
5543
5584
|
break;
|
|
5544
5585
|
case 'widget':
|
|
5545
5586
|
result = await this.deployWidget(artifact.create);
|
|
5546
5587
|
break;
|
|
5547
5588
|
case 'script_include':
|
|
5548
|
-
result = await this.
|
|
5589
|
+
result = await this.client.createScriptInclude(artifact.create);
|
|
5549
5590
|
break;
|
|
5550
5591
|
case 'business_rule':
|
|
5551
5592
|
result = await this.deployBusinessRule(artifact.create);
|
|
@@ -5861,12 +5902,7 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
5861
5902
|
};
|
|
5862
5903
|
}
|
|
5863
5904
|
case 'flow':
|
|
5864
|
-
|
|
5865
|
-
return {
|
|
5866
|
-
success: flowResult.content[0].text.includes('✅'),
|
|
5867
|
-
sys_id: flowResult.content[0].text.match(/sys_id: ([a-f0-9]+)/)?.[1],
|
|
5868
|
-
message: 'Flow deployed'
|
|
5869
|
-
};
|
|
5905
|
+
throw new Error('Flow deployment is deprecated. Use widgets or applications instead.');
|
|
5870
5906
|
case 'script':
|
|
5871
5907
|
case 'script_include':
|
|
5872
5908
|
const scriptResult = await this.createRecordWithRetry('sys_script_include', config);
|
|
@@ -5961,7 +5997,7 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
5961
5997
|
async rollbackArtifact(artifact) {
|
|
5962
5998
|
const tableMap = {
|
|
5963
5999
|
'widget': 'sp_widget',
|
|
5964
|
-
'flow': 'sys_hub_flow',
|
|
6000
|
+
// 'flow': 'sys_hub_flow', // REMOVED - flows deprecated
|
|
5965
6001
|
'script': 'sys_script_include',
|
|
5966
6002
|
'script_include': 'sys_script_include',
|
|
5967
6003
|
'business_rule': 'sys_script',
|
|
@@ -7596,11 +7632,11 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
7596
7632
|
getTableForArtifactType(artifactType) {
|
|
7597
7633
|
const ARTIFACT_TABLES = {
|
|
7598
7634
|
'widget': 'sp_widget',
|
|
7599
|
-
'flow': 'sys_hub_flow',
|
|
7635
|
+
// 'flow': 'sys_hub_flow', // REMOVED - flows deprecated
|
|
7600
7636
|
'script': 'sys_script_include',
|
|
7601
7637
|
'script_include': 'sys_script_include',
|
|
7602
7638
|
'business_rule': 'sys_script',
|
|
7603
|
-
'workflow': 'wf_workflow',
|
|
7639
|
+
// 'workflow': 'wf_workflow', // REMOVED - workflows deprecated
|
|
7604
7640
|
'application': 'sys_app',
|
|
7605
7641
|
'ui_action': 'sys_ui_action',
|
|
7606
7642
|
'ui_page': 'sys_ui_page',
|
|
@@ -668,39 +668,91 @@ class ServiceNowClient {
|
|
|
668
668
|
}
|
|
669
669
|
// Ensure we have credentials before making the API call
|
|
670
670
|
await this.ensureAuthenticated();
|
|
671
|
-
|
|
671
|
+
// Log the request details for debugging
|
|
672
|
+
const widgetData = {
|
|
672
673
|
name: widget.name,
|
|
673
|
-
id: widget.id,
|
|
674
|
+
id: widget.id || widget.name, // Ensure id is set
|
|
674
675
|
title: widget.title,
|
|
675
|
-
description: widget.description,
|
|
676
|
+
description: widget.description || '',
|
|
676
677
|
template: widget.template,
|
|
677
|
-
css: widget.css,
|
|
678
|
-
client_script: widget.client_script,
|
|
679
|
-
script: widget.server_script, // Service Portal uses 'script' not 'server_script'
|
|
678
|
+
css: widget.css || '',
|
|
679
|
+
client_script: widget.client_script || '',
|
|
680
|
+
script: widget.server_script || '', // Service Portal uses 'script' not 'server_script'
|
|
680
681
|
option_schema: widget.option_schema || '[]',
|
|
681
682
|
demo_data: widget.demo_data || '{}',
|
|
682
|
-
has_preview: widget.has_preview
|
|
683
|
-
category: widget.category || 'custom'
|
|
684
|
-
|
|
683
|
+
has_preview: widget.has_preview !== false, // Default to true
|
|
684
|
+
category: widget.category || 'custom',
|
|
685
|
+
active: true // Ensure widget is active
|
|
686
|
+
};
|
|
687
|
+
this.logger.info('Widget data to be sent:', widgetData);
|
|
688
|
+
const response = await this.client.post(`${this.getBaseUrl()}/api/now/table/sp_widget`, widgetData, {
|
|
685
689
|
timeout: this.deploymentTimeout, // Use deployment-specific timeout
|
|
686
690
|
headers: {
|
|
687
691
|
'X-Operation-Type': 'deployment' // Mark as deployment operation
|
|
688
692
|
}
|
|
689
693
|
});
|
|
690
694
|
this.logger.info('✅ Widget created successfully!');
|
|
691
|
-
|
|
695
|
+
// Handle different response structures from ServiceNow
|
|
696
|
+
const widgetResult = response.data.result || response.data;
|
|
697
|
+
const sysId = widgetResult.sys_id;
|
|
698
|
+
if (!sysId) {
|
|
699
|
+
this.logger.warn('⚠️ Widget created but no sys_id returned. Response:', response.data);
|
|
700
|
+
throw new Error('Widget creation succeeded but no sys_id was returned');
|
|
701
|
+
}
|
|
702
|
+
this.logger.info(`🆔 Widget ID: ${sysId}`);
|
|
692
703
|
// Add post-deployment verification
|
|
693
|
-
await this.verifyDeployment(
|
|
704
|
+
await this.verifyDeployment(sysId, 'widget');
|
|
694
705
|
return {
|
|
695
706
|
success: true,
|
|
696
|
-
data:
|
|
707
|
+
data: widgetResult
|
|
697
708
|
};
|
|
698
709
|
}
|
|
699
710
|
catch (error) {
|
|
700
711
|
console.error('❌ Failed to create widget:', error);
|
|
712
|
+
// Better error handling for axios errors
|
|
713
|
+
let errorMessage = 'Unknown error';
|
|
714
|
+
let errorDetails = {};
|
|
715
|
+
if (error.response) {
|
|
716
|
+
// The request was made and the server responded with a status code
|
|
717
|
+
// that falls out of the range of 2xx
|
|
718
|
+
errorMessage = `HTTP ${error.response.status}: ${error.response.statusText || 'Request failed'}`;
|
|
719
|
+
errorDetails = {
|
|
720
|
+
status: error.response.status,
|
|
721
|
+
statusText: error.response.statusText,
|
|
722
|
+
data: error.response.data,
|
|
723
|
+
headers: error.response.headers
|
|
724
|
+
};
|
|
725
|
+
// Extract ServiceNow specific error message if available
|
|
726
|
+
if (error.response.data?.error?.message) {
|
|
727
|
+
errorMessage = `ServiceNow Error: ${error.response.data.error.message}`;
|
|
728
|
+
}
|
|
729
|
+
else if (error.response.data?.error) {
|
|
730
|
+
errorMessage = `ServiceNow Error: ${JSON.stringify(error.response.data.error)}`;
|
|
731
|
+
}
|
|
732
|
+
else if (error.response.status === 401) {
|
|
733
|
+
errorMessage = 'Authentication failed: Invalid or expired token. Run: snow-flow auth login';
|
|
734
|
+
}
|
|
735
|
+
else if (error.response.status === 403) {
|
|
736
|
+
errorMessage = 'Permission denied: User lacks sp_admin role or widget creation permissions';
|
|
737
|
+
}
|
|
738
|
+
else if (error.response.status === 404) {
|
|
739
|
+
errorMessage = 'API endpoint not found: ServiceNow instance may not have Service Portal installed';
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
else if (error.request) {
|
|
743
|
+
// The request was made but no response was received
|
|
744
|
+
errorMessage = 'No response from ServiceNow - check network connection and instance URL';
|
|
745
|
+
errorDetails = { request: error.config?.url };
|
|
746
|
+
}
|
|
747
|
+
else {
|
|
748
|
+
// Something happened in setting up the request that triggered an Error
|
|
749
|
+
errorMessage = error.message || String(error);
|
|
750
|
+
}
|
|
751
|
+
this.logger.error('Widget creation error details:', errorDetails);
|
|
701
752
|
return {
|
|
702
753
|
success: false,
|
|
703
|
-
error:
|
|
754
|
+
error: errorMessage,
|
|
755
|
+
details: errorDetails
|
|
704
756
|
};
|
|
705
757
|
}
|
|
706
758
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "3.0.
|
|
4
|
-
"description": "Snow-Flow v3.0.
|
|
3
|
+
"version": "3.0.16",
|
|
4
|
+
"description": "Snow-Flow v3.0.16: WIDGET DEPLOYMENT ERROR FIX! 🔧 Fixed 'error: null' issue in widget deployment. Enhanced error reporting for failed POST requests to /api/now/table/sp_widget. Now provides detailed HTTP status codes, ServiceNow-specific error messages, and better debugging information. Widget deployment failures now show exact cause (401 auth, 403 permissions, 404 endpoint, etc).",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"bin": {
|