snow-flow 3.6.21 → 3.6.24

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.
@@ -36,7 +36,7 @@ function getDynamicVersion() {
36
36
  console.warn('Warning: Could not read version from package.json:', error);
37
37
  }
38
38
  // Fallback to hardcoded version
39
- return '3.4.4';
39
+ return '3.6.24';
40
40
  }
41
41
  // Export a constant that uses the dynamic version
42
42
  exports.VERSION = getDynamicVersion();
@@ -319,7 +319,7 @@ class ServiceNowDeploymentMCP {
319
319
  enum: [
320
320
  'widget', 'portal_page', 'application', 'script', 'business_rule', 'table',
321
321
  'script_include', 'ui_page', 'client_script', 'ui_action', 'ui_policy',
322
- 'acl', 'field', 'workflow', 'flow', 'notification', 'scheduled_job'
322
+ 'catalog_ui_policy', 'acl', 'field', 'workflow', 'flow', 'notification', 'scheduled_job'
323
323
  ],
324
324
  description: 'Type of artifact to deploy'
325
325
  },
@@ -7448,6 +7448,8 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
7448
7448
  return await this.deployApplication(scopedConfig);
7449
7449
  case 'xml_update_set':
7450
7450
  return await this.deployXMLUpdateSet(scopedConfig);
7451
+ case 'catalog_ui_policy':
7452
+ return await this.deployCatalogUIPolicy(scopedConfig);
7451
7453
  default:
7452
7454
  throw new Error(`Unsupported artifact type for unified deployment: ${type}`);
7453
7455
  }
@@ -7585,6 +7587,176 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
7585
7587
  throw new Error(`XML deployment failed: ${errorMsg}`);
7586
7588
  }
7587
7589
  }
7590
+ /**
7591
+ * Deploy Catalog UI Policy to ServiceNow
7592
+ */
7593
+ async deployCatalogUIPolicy(config) {
7594
+ const { catalog_item, cat_item, catalog_conditions, short_description, applies_to = 'all', active = true, on_load = true, reverse_if_false = true, conditions = [], actions = [] } = config;
7595
+ const catalogItemId = catalog_item || cat_item;
7596
+ if (!catalogItemId) {
7597
+ throw new Error('catalog_item or cat_item is required for catalog UI policy deployment');
7598
+ }
7599
+ if (!short_description) {
7600
+ throw new Error('short_description is required for catalog UI policy deployment');
7601
+ }
7602
+ this.logger.info('🚀 Deploying Catalog UI Policy', {
7603
+ catalogItem: catalogItemId,
7604
+ description: short_description,
7605
+ conditions: conditions.length,
7606
+ actions: actions.length
7607
+ });
7608
+ try {
7609
+ // If catalog_conditions is provided, convert it to conditions array
7610
+ let policyConditions = conditions;
7611
+ if (catalog_conditions && policyConditions.length === 0) {
7612
+ // Parse catalog_conditions string like "employee_type=contractor"
7613
+ const conditionParts = catalog_conditions.split('=');
7614
+ if (conditionParts.length === 2) {
7615
+ policyConditions = [{
7616
+ catalog_variable: conditionParts[0].trim(),
7617
+ operation: 'is',
7618
+ value: conditionParts[1].trim(),
7619
+ and_or: 'AND'
7620
+ }];
7621
+ this.logger.info('📝 Converted catalog_conditions to conditions array:', policyConditions);
7622
+ }
7623
+ }
7624
+ // Build policy data for catalog_ui_policy table
7625
+ const policyData = {
7626
+ cat_item: catalogItemId,
7627
+ short_description: short_description,
7628
+ applies_to: applies_to,
7629
+ active: active,
7630
+ on_load: on_load,
7631
+ reverse_if_false: reverse_if_false,
7632
+ order: 100
7633
+ };
7634
+ this.logger.info('đŸŽ¯ Creating main policy in catalog_ui_policy table with data:', policyData);
7635
+ const policyResponse = await this.client.createRecord('catalog_ui_policy', policyData);
7636
+ if (!policyResponse.success) {
7637
+ throw new Error(`Failed to create catalog UI policy: ${policyResponse.error}`);
7638
+ }
7639
+ const policyId = policyResponse.data.sys_id;
7640
+ this.logger.info(`✅ Created policy with sys_id: ${policyId}`);
7641
+ // Create conditions if provided
7642
+ const createdConditions = [];
7643
+ for (let i = 0; i < policyConditions.length; i++) {
7644
+ const condition = policyConditions[i];
7645
+ // Resolve variable name to sys_id if needed
7646
+ let variableSysId = condition.catalog_variable;
7647
+ if (condition.catalog_variable && !condition.catalog_variable.match(/^[a-f0-9]{32}$/)) {
7648
+ // Search for variable by name
7649
+ const variableSearch = await this.client.searchRecords('item_option_new', `cat_item=${catalogItemId}^name=${condition.catalog_variable}`, 1);
7650
+ if (variableSearch.success && variableSearch.data.result.length > 0) {
7651
+ variableSysId = variableSearch.data.result[0].sys_id;
7652
+ this.logger.info(`✅ Resolved variable '${condition.catalog_variable}' to sys_id: ${variableSysId}`);
7653
+ }
7654
+ else {
7655
+ this.logger.warn(`âš ī¸ Could not resolve variable '${condition.catalog_variable}', using as-is`);
7656
+ }
7657
+ }
7658
+ const conditionData = {
7659
+ ui_policy: policyId,
7660
+ catalog_variable: `IO:${variableSysId}`, // IO: prefix required
7661
+ operation: condition.operation || 'is',
7662
+ value: condition.value || '',
7663
+ and_or: condition.and_or || 'AND',
7664
+ order: (i + 1) * 100,
7665
+ active: true
7666
+ };
7667
+ this.logger.info(`🔗 Creating condition ${i + 1}:`, conditionData);
7668
+ const conditionResponse = await this.client.createRecord('catalog_ui_policy_condition', conditionData);
7669
+ if (conditionResponse.success) {
7670
+ createdConditions.push(conditionResponse.data.sys_id);
7671
+ this.logger.info(`✅ Created condition ${i + 1} with sys_id: ${conditionResponse.data.sys_id}`);
7672
+ }
7673
+ else {
7674
+ this.logger.error(`❌ Failed to create condition ${i + 1}:`, conditionResponse.error);
7675
+ }
7676
+ }
7677
+ // Create actions if provided
7678
+ const createdActions = [];
7679
+ for (let i = 0; i < actions.length; i++) {
7680
+ const action = actions[i];
7681
+ // Resolve variable name to sys_id if needed
7682
+ let actionVariableSysId = action.catalog_variable;
7683
+ if (action.catalog_variable && !action.catalog_variable.match(/^[a-f0-9]{32}$/)) {
7684
+ const actionVariableSearch = await this.client.searchRecords('item_option_new', `cat_item=${catalogItemId}^name=${action.catalog_variable}`, 1);
7685
+ if (actionVariableSearch.success && actionVariableSearch.data.result.length > 0) {
7686
+ actionVariableSysId = actionVariableSearch.data.result[0].sys_id;
7687
+ this.logger.info(`✅ Resolved action variable '${action.catalog_variable}' to sys_id: ${actionVariableSysId}`);
7688
+ }
7689
+ else {
7690
+ this.logger.warn(`âš ī¸ Could not resolve action variable '${action.catalog_variable}', using as-is`);
7691
+ }
7692
+ }
7693
+ const actionData = {
7694
+ ui_policy: policyId,
7695
+ catalog_item: catalogItemId,
7696
+ catalog_variable: `IO:${actionVariableSysId}`, // IO: prefix required
7697
+ order: (i + 1) * 100,
7698
+ active: true
7699
+ };
7700
+ // Set action type fields based on action.type or individual properties
7701
+ if (action.type === 'set_mandatory' || action.mandatory !== undefined) {
7702
+ actionData.mandatory = action.mandatory === true ? 'true' :
7703
+ action.mandatory === false ? 'false' : 'ignore';
7704
+ }
7705
+ else {
7706
+ actionData.mandatory = 'ignore';
7707
+ }
7708
+ if (action.type === 'set_visible' || action.visible !== undefined) {
7709
+ actionData.visible = action.visible === true ? 'true' :
7710
+ action.visible === false ? 'false' : 'ignore';
7711
+ }
7712
+ else {
7713
+ actionData.visible = 'ignore';
7714
+ }
7715
+ if (action.type === 'set_readonly' || action.readonly !== undefined) {
7716
+ actionData.disabled = action.readonly === true ? 'true' :
7717
+ action.readonly === false ? 'false' : 'ignore';
7718
+ }
7719
+ else {
7720
+ actionData.disabled = 'ignore';
7721
+ }
7722
+ if (action.value !== undefined && action.value !== null && action.value !== '') {
7723
+ actionData.value = String(action.value);
7724
+ }
7725
+ this.logger.info(`đŸŽŦ Creating action ${i + 1}:`, actionData);
7726
+ const actionResponse = await this.client.createRecord('catalog_ui_policy_action', actionData);
7727
+ if (actionResponse.success) {
7728
+ createdActions.push(actionResponse.data.sys_id);
7729
+ this.logger.info(`✅ Created action ${i + 1} with sys_id: ${actionResponse.data.sys_id}`);
7730
+ }
7731
+ else {
7732
+ this.logger.error(`❌ Failed to create action ${i + 1}:`, actionResponse.error);
7733
+ }
7734
+ }
7735
+ return {
7736
+ success: true,
7737
+ policy_id: policyId,
7738
+ conditions_created: createdConditions.length,
7739
+ actions_created: createdActions.length,
7740
+ message: `✅ Catalog UI Policy '${short_description}' deployed successfully`,
7741
+ details: {
7742
+ policy_sys_id: policyId,
7743
+ catalog_item: catalogItemId,
7744
+ conditions: createdConditions,
7745
+ actions: createdActions,
7746
+ next_steps: [
7747
+ '1. Test the catalog item form to verify policy behavior',
7748
+ '2. Check that conditions trigger actions correctly',
7749
+ '3. Verify variable visibility/mandatory/readonly changes'
7750
+ ]
7751
+ }
7752
+ };
7753
+ }
7754
+ catch (error) {
7755
+ const errorMsg = error instanceof Error ? error.message : String(error);
7756
+ this.logger.error('Catalog UI Policy deployment failed', { error: errorMsg, config });
7757
+ throw new Error(`Catalog UI Policy deployment failed: ${errorMsg}`);
7758
+ }
7759
+ }
7588
7760
  /**
7589
7761
  * Check if error is permission-related
7590
7762
  */
@@ -752,8 +752,8 @@ ${args.recurring_price && args.recurring_price !== '0' ? `🔄 Recurring: $${arg
752
752
  tooltip: args.tooltip || ''
753
753
  };
754
754
  this.logger.info('đŸŽ¯ Creating variable with payload:', variableData);
755
- // ✅ CORRECT TABLE NAME: Use sc_cat_item_option instead of item_option_new
756
- const response = await this.client.createRecord('sc_cat_item_option', variableData);
755
+ // ✅ FIXED TABLE NAME: Use item_option_new (the correct table for catalog variables)
756
+ const response = await this.client.createRecord('item_option_new', variableData);
757
757
  if (!response.success) {
758
758
  this.logger.error('❌ Variable creation failed:', {
759
759
  error: response.error,
@@ -766,7 +766,7 @@ ${args.recurring_price && args.recurring_price !== '0' ? `🔄 Recurring: $${arg
766
766
  this.logger.info(`✅ Variable created with sys_id: ${createdSysId}`);
767
767
  // 🔍 VERIFICATION: Check if variable was actually created
768
768
  this.logger.info('🔍 Verifying variable creation...');
769
- const verification = await this.client.searchRecords('sc_cat_item_option', `sys_id=${createdSysId}`, 1);
769
+ const verification = await this.client.searchRecords('item_option_new', `sys_id=${createdSysId}`, 1);
770
770
  if (!verification.success || verification.data.result.length === 0) {
771
771
  this.logger.error('❌ VERIFICATION FAILED: Variable not found after creation!');
772
772
  throw new Error(`Variable creation verification failed - not found in database`);
@@ -1040,14 +1040,39 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
1040
1040
  // catalog_ui_policy_action inherits from sys_ui_policy_action
1041
1041
  // We must set fields in the correct way for ServiceNow to accept them
1042
1042
  const actionData = {};
1043
- // STEP 1: Set reference fields (MUST be set first for ServiceNow)
1044
- // ✅ ui_policy is a reference to sys_ui_policy - use sys_id directly
1043
+ // STEP 1: ENHANCED VERIFICATION - Verify policy exists before creating actions
1044
+ this.logger.info(`🔍 ENHANCED DEBUG: Verifying policy ${policyId} exists before creating action...`);
1045
+ if (!policyId) {
1046
+ this.logger.error(`❌ CRITICAL: No policyId available for action ${i + 1}!`);
1047
+ throw new Error(`Cannot create action without valid policy ID`);
1048
+ }
1049
+ // ✅ NEW: Test policy existence before action creation
1050
+ const policyExists = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
1051
+ if (!policyExists.success || policyExists.data.result.length === 0) {
1052
+ this.logger.error(`❌ CRITICAL: Policy ${policyId} does not exist in catalog_ui_policy table!`);
1053
+ throw new Error(`Policy verification failed - cannot create action without valid policy`);
1054
+ }
1055
+ const existingPolicy = policyExists.data.result[0];
1056
+ this.logger.info(`✅ Policy verification successful:`);
1057
+ this.logger.info(` - Policy sys_id: ${existingPolicy.sys_id}`);
1058
+ this.logger.info(` - Policy name: ${existingPolicy.short_description || 'N/A'}`);
1059
+ this.logger.info(` - Policy active: ${existingPolicy.active}`);
1060
+ // STEP 2: Set reference fields with enhanced validation
1061
+ // ✅ ui_policy is a reference to catalog_ui_policy - test multiple formats
1062
+ this.logger.info(`📝 Setting ui_policy reference to: ${policyId}`);
1063
+ // Try setting the reference in the most explicit way possible
1045
1064
  actionData.ui_policy = policyId;
1046
1065
  // ✅ catalog_item is a reference to sc_cat_item - use sys_id directly
1066
+ if (!args.cat_item) {
1067
+ this.logger.error(`❌ CRITICAL: No catalog item ID provided!`);
1068
+ throw new Error(`Cannot create action without catalog item ID`);
1069
+ }
1070
+ this.logger.info(`📝 Setting catalog_item reference to: ${args.cat_item}`);
1047
1071
  actionData.catalog_item = args.cat_item;
1048
- // STEP 2: Set the catalog_variable with IO: prefix (STRING field, not reference)
1072
+ // STEP 3: Set the catalog_variable with IO: prefix (STRING field, not reference)
1073
+ this.logger.info(`📝 Setting catalog_variable to: ${catalogVariableWithPrefix}`);
1049
1074
  actionData.catalog_variable = catalogVariableWithPrefix;
1050
- // STEP 3: Set action properties with correct values
1075
+ // STEP 4: Set action properties with correct values
1051
1076
  // ✅ CRITICAL: Use "ignore" instead of not setting or using false
1052
1077
  // This is how ServiceNow differentiates between "don't change" and "set to false"
1053
1078
  if (action.visible !== undefined) {
@@ -1071,20 +1096,22 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
1071
1096
  else {
1072
1097
  actionData.disabled = 'ignore'; // Default to ignore if not specified
1073
1098
  }
1074
- // STEP 4: Set optional value field
1099
+ // STEP 5: Set optional value field
1075
1100
  if (action.value !== undefined && action.value !== null && action.value !== '') {
1076
1101
  actionData.value = String(action.value);
1077
1102
  }
1078
- // STEP 5: Set other metadata
1103
+ // STEP 6: Set other metadata
1079
1104
  actionData.order = (i + 1) * 100;
1080
1105
  actionData.active = true;
1081
- this.logger.info(`🔗 Creating action with proper structure:`);
1082
- this.logger.info(` - ui_policy (ref): ${policyId}`);
1106
+ this.logger.info(`🔗 Creating action with VALIDATED structure:`);
1107
+ this.logger.info(` - ui_policy (ref): ${policyId} [VERIFIED EXISTS]`);
1083
1108
  this.logger.info(` - catalog_item (ref): ${args.cat_item}`);
1084
1109
  this.logger.info(` - catalog_variable: ${catalogVariableWithPrefix}`);
1085
1110
  this.logger.info(` - visible: ${actionData.visible}`);
1086
1111
  this.logger.info(` - mandatory: ${actionData.mandatory}`);
1087
1112
  this.logger.info(` - disabled: ${actionData.disabled}`);
1113
+ // ✅ ENHANCED DEBUG: Log complete action data being sent
1114
+ this.logger.info(`📋 Complete action data being sent to ServiceNow:`, JSON.stringify(actionData, null, 2));
1088
1115
  this.logger.info(`đŸŽ¯ Attempting to create action ${i + 1} in catalog_ui_policy_action table...`);
1089
1116
  const actionResponse = await this.client.createRecord('catalog_ui_policy_action', actionData);
1090
1117
  if (actionResponse.success) {
@@ -1112,18 +1139,55 @@ ${args.help_text ? `❓ Help: ${args.help_text}` : ''}
1112
1139
  const uiPolicyValue = createdAction.ui_policy;
1113
1140
  const catalogVariableValue = createdAction.catalog_variable;
1114
1141
  const catalogItemValue = createdAction.catalog_item;
1115
- // Check ui_policy reference
1116
- if (!uiPolicyValue || uiPolicyValue === '' || uiPolicyValue === '{}') {
1142
+ // Log the raw response to understand what ServiceNow returns
1143
+ this.logger.info(`📋 Raw action verification response:`, JSON.stringify(createdAction, null, 2));
1144
+ // Check ui_policy reference - ServiceNow might return it as an object
1145
+ let actualUiPolicyId = uiPolicyValue;
1146
+ if (typeof uiPolicyValue === 'object' && uiPolicyValue !== null) {
1147
+ actualUiPolicyId = uiPolicyValue.value || uiPolicyValue.sys_id || '';
1148
+ this.logger.info(`📝 ui_policy returned as object: ${JSON.stringify(uiPolicyValue)}`);
1149
+ }
1150
+ if (!actualUiPolicyId || actualUiPolicyId === '' || actualUiPolicyId === '{}') {
1117
1151
  this.logger.error(`❌ CRITICAL: ui_policy field is EMPTY for action ${i + 1}!`);
1118
- this.logger.error(`❌ Expected: ${policyId}, Got: ${uiPolicyValue}`);
1119
- throw new Error(`Action ${i + 1} created but ui_policy field is empty - action will not work!`);
1152
+ this.logger.error(`❌ Expected policy ID: ${policyId}`);
1153
+ this.logger.error(`❌ Raw ui_policy value: ${JSON.stringify(uiPolicyValue)}`);
1154
+ this.logger.error(`❌ Parsed ui_policy value: ${actualUiPolicyId}`);
1155
+ this.logger.error(`❌ Action data sent:`, JSON.stringify(actionData, null, 2));
1156
+ this.logger.error(`❌ Full action verification response:`, JSON.stringify(createdAction, null, 2));
1157
+ // ✅ ENHANCED ERROR: Try to understand ServiceNow's response pattern
1158
+ this.logger.error(`â„šī¸ DIAGNOSTIC INFO:`);
1159
+ this.logger.error(` - Policy verified to exist: YES (${policyId})`);
1160
+ this.logger.error(` - Action created successfully: YES (${createdActionId})`);
1161
+ this.logger.error(` - ui_policy field type: ${typeof uiPolicyValue}`);
1162
+ this.logger.error(` - ui_policy field value length: ${String(uiPolicyValue || '').length}`);
1163
+ this.logger.error(` - All action fields:`, Object.keys(createdAction));
1164
+ // Check if it's a ServiceNow API timing issue
1165
+ this.logger.warn(`🔄 ATTEMPTING SECONDARY VERIFICATION (possible timing issue)...`);
1166
+ // Wait a moment and try again
1167
+ await new Promise(resolve => setTimeout(resolve, 1000));
1168
+ const secondVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
1169
+ if (secondVerification.success && secondVerification.data.result.length > 0) {
1170
+ const reCheckedAction = secondVerification.data.result[0];
1171
+ const reCheckedUiPolicy = reCheckedAction.ui_policy;
1172
+ this.logger.error(`🔄 Secondary verification ui_policy: ${JSON.stringify(reCheckedUiPolicy)}`);
1173
+ if (reCheckedUiPolicy && reCheckedUiPolicy !== '' && reCheckedUiPolicy !== '{}') {
1174
+ this.logger.warn(`âš ī¸ This was a timing issue - ui_policy populated after delay`);
1175
+ // Continue with the re-checked value
1176
+ return; // Skip the error throwing
1177
+ }
1178
+ }
1179
+ throw new Error(`Action ${i + 1} created but ui_policy field is empty - action will not work! This may be a ServiceNow API or table structure issue.`);
1120
1180
  }
1121
1181
  // For reference fields, ServiceNow might return an object - extract the value
1122
1182
  const uiPolicySysId = typeof uiPolicyValue === 'object' && uiPolicyValue.value ?
1123
1183
  uiPolicyValue.value : uiPolicyValue;
1184
+ this.logger.info(`✅ ui_policy field populated successfully: ${actualUiPolicyId}`);
1124
1185
  if (uiPolicySysId !== policyId) {
1125
1186
  this.logger.warn(`âš ī¸ ui_policy mismatch - Expected: ${policyId}, Got: ${uiPolicySysId}`);
1126
1187
  }
1188
+ else {
1189
+ this.logger.info(`✅ ui_policy reference matches expected value`);
1190
+ }
1127
1191
  // Check catalog_variable (should have IO: prefix)
1128
1192
  if (!catalogVariableValue || catalogVariableValue === '') {
1129
1193
  this.logger.error(`❌ CRITICAL: catalog_variable field is EMPTY for action ${i + 1}!`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "3.6.21",
4
- "description": "CRITICAL TABLE FIX - v3.6.21 fixes fundamental issue: policies now created in catalog_ui_policy table (NOT sys_ui_policy!). Actions correctly reference catalog_ui_policy records. This matches ServiceNow's actual structure where catalog_ui_policy_action.ui_policy references catalog_ui_policy table, not sys_ui_policy.",
3
+ "version": "3.6.24",
4
+ "description": "CATALOG UI POLICY DEPLOYMENT SUPPORT - v3.6.24 adds catalog_ui_policy support to snow_deploy tool. You can now deploy catalog UI policies via unified deployment with automatic variable resolution, condition/action creation, and comprehensive error handling.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {