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', 'workflow', 'application'] },
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, workflows, applications) to JSON/XML format for backup, version control, or migration purposes.',
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', 'workflow', 'application'] },
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', 'workflow', 'application'] },
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', 'workflow', 'application'] },
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
- throw new Error(`Widget creation failed: ${result?.error || 'Unknown error'}`);
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
- async deployFlow(args) {
1679
- try {
1680
- // Enhanced authentication check with token refresh for deployment
1681
- const authResult = await this.deploymentAuthManager.ensureDeploymentAuth();
1682
- if (!authResult.isValid) {
1683
- this.logger.error('Deployment authentication failed:', authResult.error);
1684
- // If auth failed, try to refresh token
1685
- const refreshResult = await this.deploymentAuthManager.forceTokenRefresh();
1686
- if (!refreshResult.success) {
1687
- return {
1688
- content: [
1689
- {
1690
- type: 'text',
1691
- 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.`,
1692
- },
1693
- ],
1694
- };
1695
- }
1696
- }
1697
- // Warn if token may lack write permissions
1698
- if (!authResult.hasWriteScope) {
1699
- this.logger.warn('⚠️ Token may lack write permissions, deployment might fail with 403');
1700
- }
1701
- // Ensure we have a flow definition
1702
- if (!args.flow_definition) {
1703
- return {
1704
- content: [
1705
- {
1706
- type: 'text',
1707
- 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.',
1708
- },
1709
- ],
1710
- };
1711
- }
1712
- const flowType = args.flow_type || 'flow';
1713
- this.logger.info(`Deploying ${flowType} to ServiceNow`, { name: args.name, type: flowType });
1714
- // Validate flow definition first if requested
1715
- let validatedDefinition = args.flow_definition;
1716
- if (args.validate_before_deploy !== false) {
1717
- const validationResult = await this.validateFlowDefinition({
1718
- definition: args.flow_definition,
1719
- flow_type: flowType,
1720
- show_preview: false,
1721
- test_mode: false,
1722
- check_dependencies: true
1723
- });
1724
- // Check if validation failed
1725
- const validationText = validationResult.content?.[0]?.text || '';
1726
- if (validationText.includes('❌') || validationText.includes('ERROR')) {
1727
- return {
1728
- content: [
1729
- {
1730
- type: 'text',
1731
- 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.`
1732
- }
1733
- ]
1734
- };
1735
- }
1736
- // CRITICAL: Use the corrected definition from validation
1737
- // The validateFlowDefinition method may have auto-corrected "steps" or "actions" to "activities"
1738
- if (validationText.includes('Auto-converted "steps" to "activities"') ||
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
- // Configure based on flow type
1808
- switch (flowType) {
1809
- case 'flow':
1810
- flowData.table = args.table || '';
1811
- flowData.trigger_type = args.trigger_type;
1812
- flowData.condition = args.condition || '';
1813
- flowData.type = 'flow';
1814
- break;
1815
- case 'subflow':
1816
- // Subflows don't have triggers, they're called by other flows
1817
- flowData.type = 'subflow';
1818
- flowData.inputs = flowDefinition.inputs || [];
1819
- flowData.outputs = flowDefinition.outputs || [];
1820
- break;
1821
- case 'action':
1822
- // Actions are reusable components
1823
- flowData.type = 'action';
1824
- flowData.action_type = args.action_type || 'custom';
1825
- flowData.inputs = flowDefinition.inputs || [];
1826
- flowData.outputs = flowDefinition.outputs || [];
1827
- break;
1828
- }
1829
- // Deploy to ServiceNow using appropriate API based on flow type
1830
- let result;
1831
- let usedFallback = false;
1832
- let fallbackBusinessRule = null;
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
- // Real ServiceNow Flow deployment using proper API calls
1835
- switch (flowType) {
1836
- case 'flow':
1837
- // Create workflow record in ServiceNow
1838
- result = await this.client.createRecord('wf_workflow', {
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
- return {
2037
- content: [
2038
- {
2039
- type: 'text',
2040
- text: successMessage + continuationMessage,
2041
- },
2042
- ],
2043
- };
1785
+ }
2044
1786
  }
2045
- catch (error) {
2046
- const enhancedError = `🚨 Flow Deployment Failed
2047
-
2048
- 📍 Error: ${error instanceof Error ? error.message : String(error)}
2049
-
2050
- 🔧 Troubleshooting Steps:
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
- * Deploy a linked artifact (script include, business rule, etc.)
2068
- */
2069
- async deployLinkedArtifact(artifact) {
2070
- this.logger.info('Deploying linked artifact', { type: artifact.type, name: artifact.name });
2071
- switch (artifact.type) {
2072
- case 'script_include':
2073
- return await this.deployScriptInclude(artifact);
2074
- case 'business_rule':
2075
- return await this.deployBusinessRule(artifact);
2076
- case 'table':
2077
- return await this.deployTable(artifact);
2078
- default:
2079
- throw new Error(`Unknown artifact type: ${artifact.type}`);
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
- * Deploy a script include artifact
2084
- */
2085
- async deployScriptInclude(artifact) {
2086
- const scriptIncludeData = {
2087
- name: artifact.name,
2088
- api_name: artifact.api_name || artifact.name,
2089
- description: artifact.description || `Script include for ${artifact.purpose}`,
2090
- script: artifact.script || artifact.fallback_script,
2091
- active: true,
2092
- access: 'public'
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
- const result = await this.client.createScriptInclude(scriptIncludeData);
2095
- if (!result.data?.sys_id) {
2096
- throw new Error(`Script Include deployment failed: No sys_id returned from ServiceNow. Result: ${JSON.stringify(result)}`);
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
- return {
2099
- originalId: artifact.sys_id,
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
- name: artifact.name,
2102
- api_name: scriptIncludeData.api_name,
2103
- type: 'script_include',
2104
- success: result.success
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
- result = await this.deployFlow(artifact.create);
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.deployScriptInclude(artifact.create);
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
- const flowResult = await this.deployFlow(config);
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',
@@ -46,6 +46,7 @@ export interface ServiceNowAPIResponse<T> {
46
46
  data?: T;
47
47
  error?: string;
48
48
  result?: T[];
49
+ details?: any;
49
50
  }
50
51
  export declare class ServiceNowClient {
51
52
  private client;
@@ -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
- const response = await this.client.post(`${this.getBaseUrl()}/api/now/table/sp_widget`, {
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 || false,
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
- this.logger.info(`🆔 Widget ID: ${response.data.result.sys_id}`);
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(response.data.result.sys_id, 'widget');
704
+ await this.verifyDeployment(sysId, 'widget');
694
705
  return {
695
706
  success: true,
696
- data: response.data.result
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: error instanceof Error ? error.message : String(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.14",
4
- "description": "Snow-Flow v3.0.14: WRITE PERMISSIONS FIX! 🔧 Fixed 'this.client.create is not a function' error that was blocking all deployments. Corrected API method calls to use createRecord/deleteRecord. Write permissions diagnostic now works correctly. Plus all features: Zero Mock Data Guarantee, race condition fixes, intelligent reporting, and 100+ MCP tools.",
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": {