snow-flow 4.3.6 → 4.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -754,28 +754,36 @@ Memory.store("swarm_session_${sessionId}", JSON.stringify({
754
754
  }));
755
755
  \`\`\`
756
756
 
757
- ### 2. Validate ServiceNow Connection
758
- **Execute these steps IN ORDER:**
757
+ ### 2. 🚨 MANDATORY: ServiceNow Auth & Update Set Setup
758
+ **CRITICAL: These steps are REQUIRED for ALL development work:**
759
759
 
760
760
  \`\`\`javascript
761
- // Step 2.1: Test ServiceNow authentication
761
+ // Step 2.1: MANDATORY - Test ServiceNow authentication
762
762
  const authCheck = await snow_auth_diagnostics();
763
763
  if (!authCheck.success) {
764
- throw new Error("Authentication failed! Run: snow-flow auth login");
764
+ throw new Error("āŒ CRITICAL: Authentication failed! Run: snow-flow auth login");
765
765
  }
766
766
 
767
- // Step 2.2: Create Update Set for tracking changes
767
+ // Step 2.2: MANDATORY - Create Update Set for tracking ALL changes
768
768
  const updateSetName = "Snow-Flow: ${objective.substring(0, 50)}... - ${new Date().toISOString().split('T')[0]}";
769
769
  const updateSet = await snow_update_set_create({
770
770
  name: updateSetName,
771
- description: "Automated creation for: ${objective}\\n\\nSession: ${sessionId}",
772
- auto_switch: true
771
+ description: "Automated Snow-Flow development for: ${objective}\\n\\nSession: ${sessionId}\\nAll changes tracked automatically",
772
+ auto_switch: true // 🚨 CRITICAL: Sets as current update set!
773
773
  });
774
774
 
775
- // Store Update Set info in memory
775
+ // Store Update Set info in memory for all agents
776
776
  Memory.store("update_set_${sessionId}", JSON.stringify(updateSet));
777
+ Memory.store("current_update_set", updateSet.sys_id);
777
778
  \`\`\`
778
779
 
780
+ **WHY THIS IS CRITICAL:**
781
+ - āœ… All ServiceNow changes are automatically tracked
782
+ - āœ… Enables deployment to other instances later
783
+ - āœ… Provides rollback capabilities
784
+ - āœ… Follows ServiceNow development best practices
785
+ - āŒ **WITHOUT UPDATE SET: Changes are untracked and risky!**
786
+
779
787
  ### 3. Create Master Task List
780
788
  After completing setup steps, create task breakdown:
781
789
  \`\`\`javascript
@@ -890,10 +898,11 @@ ${isAuthenticated ? 'āœ… Authentication detected - full deployment capabilities'
890
898
 
891
899
  Your agents MUST use these MCP tools IN THIS ORDER:
892
900
 
893
- šŸ” **PRE-FLIGHT CHECKS** (Always do first!):
901
+ 🚨 **MANDATORY PRE-FLIGHT CHECKS** (ALWAYS do first!):
894
902
  1. \`snow_auth_diagnostics\` - Test authentication and permissions
895
- 2. If auth fails, the tool provides specific instructions
896
- 3. Continue with appropriate strategy based on auth status
903
+ 2. \`snow_update_set_create\` - Create and activate update set for tracking
904
+ 3. If auth fails, STOP and provide instructions to run 'snow-flow auth login'
905
+ 4. If update set fails, STOP - development work is not safe without tracking
897
906
 
898
907
  šŸŽÆ **Core Development Tools**:
899
908
  1. **Universal Query Tool**: \`snow_query_table\` - Works with ALL ServiceNow tables
@@ -843,435 +843,434 @@ ${args.help_text ? `ā“ Help: ${args.help_text}` : ''}
843
843
  this.logger.info('šŸŽÆ SIMPLIFIED: Creating catalog UI policy with working approach...');
844
844
  // Simplified approach - focus on what actually works in ServiceNow
845
845
  try {
846
+ this.logger.info('Creating comprehensive catalog UI policy...');
847
+ // First, let's fetch ALL variables for this catalog item for debugging
848
+ this.logger.info(`šŸ“‹ Fetching all variables for catalog item ${args.cat_item} for debugging...`);
846
849
  try {
847
- this.logger.info('Creating comprehensive catalog UI policy...');
848
- // First, let's fetch ALL variables for this catalog item for debugging
849
- this.logger.info(`šŸ“‹ Fetching all variables for catalog item ${args.cat_item} for debugging...`);
850
- try {
851
- const allVarsResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${args.cat_item}`, 100);
852
- if (allVarsResponse.success && allVarsResponse.data.result.length > 0) {
853
- this.logger.info(`šŸ“Š Found ${allVarsResponse.data.result.length} variables for this catalog item:`);
854
- allVarsResponse.data.result.forEach((v) => {
855
- this.logger.info(` - Variable: name='${v.name}', sys_id='${v.sys_id}', question='${v.question_text}'`);
856
- });
857
- }
858
- else {
859
- this.logger.warn(`āš ļø No variables found for catalog item ${args.cat_item} - this might be a problem!`);
860
- }
850
+ const allVarsResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${args.cat_item}`, 100);
851
+ if (allVarsResponse.success && allVarsResponse.data.result.length > 0) {
852
+ this.logger.info(`šŸ“Š Found ${allVarsResponse.data.result.length} variables for this catalog item:`);
853
+ allVarsResponse.data.result.forEach((v) => {
854
+ this.logger.info(` - Variable: name='${v.name}', sys_id='${v.sys_id}', question='${v.question_text}'`);
855
+ });
856
+ }
857
+ else {
858
+ this.logger.warn(`āš ļø No variables found for catalog item ${args.cat_item} - this might be a problem!`);
861
859
  }
862
- catch (error) {
863
- this.logger.error(`āŒ Failed to fetch variables for debugging:`, error);
860
+ }
861
+ catch (error) {
862
+ this.logger.error(`āŒ Failed to fetch variables for debugging:`, error);
863
+ }
864
+ // Helper function to resolve variable names to sys_ids with MULTIPLE FALLBACKS
865
+ const resolveVariableId = async (variableName, catalogItem) => {
866
+ // If already a sys_id, return as-is
867
+ if (variableName && variableName.match(/^[a-f0-9]{32}$/)) {
868
+ this.logger.info(`āœ… Using sys_id directly: ${variableName}`);
869
+ return variableName;
864
870
  }
865
- // Helper function to resolve variable names to sys_ids with MULTIPLE FALLBACKS
866
- const resolveVariableId = async (variableName, catalogItem) => {
867
- // If already a sys_id, return as-is
868
- if (variableName && variableName.match(/^[a-f0-9]{32}$/)) {
869
- this.logger.info(`āœ… Using sys_id directly: ${variableName}`);
870
- return variableName;
871
+ this.logger.info(`šŸ” Resolving variable name '${variableName}' to sys_id for catalog item ${catalogItem}...`);
872
+ // Try multiple search strategies for maximum compatibility
873
+ const searchStrategies = [
874
+ // Strategy 1: Search by name field
875
+ {
876
+ query: `cat_item=${catalogItem}^name=${variableName}`,
877
+ description: 'by name field'
878
+ },
879
+ // Strategy 2: Search by name with LIKE operator
880
+ {
881
+ query: `cat_item=${catalogItem}^nameLIKE${variableName}`,
882
+ description: 'by name with LIKE'
883
+ },
884
+ // Strategy 3: Search by question_text
885
+ {
886
+ query: `cat_item=${catalogItem}^question_text=${variableName}`,
887
+ description: 'by question_text field'
888
+ },
889
+ // Strategy 4: Search all variables for this item and match manually
890
+ {
891
+ query: `cat_item=${catalogItem}`,
892
+ description: 'all variables for manual matching'
871
893
  }
872
- this.logger.info(`šŸ” Resolving variable name '${variableName}' to sys_id for catalog item ${catalogItem}...`);
873
- // Try multiple search strategies for maximum compatibility
874
- const searchStrategies = [
875
- // Strategy 1: Search by name field
876
- {
877
- query: `cat_item=${catalogItem}^name=${variableName}`,
878
- description: 'by name field'
879
- },
880
- // Strategy 2: Search by name with LIKE operator
881
- {
882
- query: `cat_item=${catalogItem}^nameLIKE${variableName}`,
883
- description: 'by name with LIKE'
884
- },
885
- // Strategy 3: Search by question_text
886
- {
887
- query: `cat_item=${catalogItem}^question_text=${variableName}`,
888
- description: 'by question_text field'
889
- },
890
- // Strategy 4: Search all variables for this item and match manually
891
- {
892
- query: `cat_item=${catalogItem}`,
893
- description: 'all variables for manual matching'
894
- }
895
- ];
896
- for (const strategy of searchStrategies) {
897
- this.logger.info(`šŸ” Trying strategy: ${strategy.description}`);
898
- try {
899
- const varResponse = await this.client.searchRecords('sc_cat_item_option', strategy.query, 50 // Get more results for manual matching
900
- );
901
- if (varResponse.success && varResponse.data.result.length > 0) {
902
- // For the "all variables" strategy, try to match manually
903
- if (strategy.description === 'all variables for manual matching') {
904
- const match = varResponse.data.result.find((v) => v.name === variableName ||
905
- v.question_text === variableName ||
906
- v.name?.toLowerCase() === variableName.toLowerCase());
907
- if (match) {
908
- this.logger.info(`āœ… Found variable through manual matching: ${match.sys_id}`);
909
- return match.sys_id;
910
- }
911
- }
912
- else {
913
- // Direct match found
914
- const sysId = varResponse.data.result[0].sys_id;
915
- this.logger.info(`āœ… Resolved '${variableName}' to sys_id: ${sysId} (${strategy.description})`);
916
- return sysId;
894
+ ];
895
+ for (const strategy of searchStrategies) {
896
+ this.logger.info(`šŸ” Trying strategy: ${strategy.description}`);
897
+ try {
898
+ const varResponse = await this.client.searchRecords('sc_cat_item_option', strategy.query, 50 // Get more results for manual matching
899
+ );
900
+ if (varResponse.success && varResponse.data.result.length > 0) {
901
+ // For the "all variables" strategy, try to match manually
902
+ if (strategy.description === 'all variables for manual matching') {
903
+ const match = varResponse.data.result.find((v) => v.name === variableName ||
904
+ v.question_text === variableName ||
905
+ v.name?.toLowerCase() === variableName.toLowerCase());
906
+ if (match) {
907
+ this.logger.info(`āœ… Found variable through manual matching: ${match.sys_id}`);
908
+ return match.sys_id;
917
909
  }
918
910
  }
919
- }
920
- catch (error) {
921
- this.logger.warn(`āš ļø Strategy failed: ${strategy.description}`, error);
922
- }
923
- }
924
- // If all strategies fail, try alternate table names (just in case)
925
- const alternateTables = ['item_option_new', 'io_set_item_option'];
926
- for (const table of alternateTables) {
927
- this.logger.info(`šŸ” Trying alternate table: ${table}`);
928
- try {
929
- const varResponse = await this.client.searchRecords(table, `cat_item=${catalogItem}^name=${variableName}`, 1);
930
- if (varResponse.success && varResponse.data.result.length > 0) {
911
+ else {
912
+ // Direct match found
931
913
  const sysId = varResponse.data.result[0].sys_id;
932
- this.logger.info(`āœ… Found in alternate table ${table}: ${sysId}`);
914
+ this.logger.info(`āœ… Resolved '${variableName}' to sys_id: ${sysId} (${strategy.description})`);
933
915
  return sysId;
934
916
  }
935
917
  }
936
- catch (error) {
937
- this.logger.warn(`āš ļø Alternate table ${table} failed:`, error);
938
- }
939
918
  }
940
- this.logger.error(`āŒ CRITICAL: Variable '${variableName}' not found in any table for catalog item ${catalogItem}`);
941
- this.logger.error(`āŒ This will cause the action to fail! Please use the sys_id directly instead of the name.`);
942
- // Return the name but log a warning that this will fail
943
- return variableName;
944
- };
945
- // Helper function to map operations to correct ServiceNow format
946
- const mapOperatorToServiceNow = (operator) => {
947
- const opMap = {
948
- 'is': '=',
949
- 'equals': '=',
950
- 'is_not': '!=',
951
- 'is not': '!=',
952
- 'not equals': '!=',
953
- 'contains': 'LIKE',
954
- 'does_not_contain': 'NOT LIKE',
955
- 'does not contain': 'NOT LIKE',
956
- 'greater_than': '>',
957
- 'greater than': '>',
958
- 'less_than': '<',
959
- 'less than': '<',
960
- 'is_empty': 'ISEMPTY',
961
- 'is empty': 'ISEMPTY',
962
- 'is_not_empty': 'ISNOTEMPTY', // āœ… CRITICAL FIX: was missing!
963
- 'is not empty': 'ISNOTEMPTY'
964
- };
965
- const normalizedOp = operator.toLowerCase().trim();
966
- const mapped = opMap[normalizedOp] || operator;
967
- this.logger.info(`šŸŽÆ Mapped operator '${operator}' -> '${mapped}'`);
968
- return mapped;
969
- };
970
- // Build condition string from conditions array
971
- let conditionString = '';
972
- this.logger.info('Checking conditions parameter:', {
973
- hasConditions: !!args.conditions,
974
- isArray: Array.isArray(args.conditions),
975
- length: args.conditions ? args.conditions.length : 0,
976
- conditionsData: args.conditions
977
- });
978
- if (args.conditions && Array.isArray(args.conditions)) {
979
- this.logger.info(`šŸŽÆ Building conditions string from ${args.conditions.length} conditions...`);
980
- const conditionParts = [];
981
- for (const condition of args.conditions) {
982
- // Resolve variable name to sys_id
983
- const variableId = await resolveVariableId(condition.catalog_variable, args.cat_item);
984
- // Get the correct ServiceNow operator
985
- const originalOperator = condition.operation || 'is';
986
- const serviceNowOperator = mapOperatorToServiceNow(originalOperator);
987
- // Safely get condition value (avoid undefined concatenation)
988
- const conditionValue = condition.value || '';
989
- // Build the condition part based on operator type
990
- // āœ… CRITICAL: Conditions must use IO:sys_id format for catalog variables!
991
- // BUT only if we successfully resolved the variable to a sys_id
992
- const isValidSysId = variableId && variableId.match(/^[a-f0-9]{32}$/);
993
- const variableWithIOPrefix = isValidSysId ? `IO:${variableId}` : variableId;
994
- if (!isValidSysId) {
995
- this.logger.warn(`āš ļø Variable '${condition.catalog_variable}' could not be resolved to sys_id - condition may fail!`);
996
- }
997
- let conditionPart = '';
998
- if (serviceNowOperator === 'ISEMPTY' || serviceNowOperator === 'ISNOTEMPTY') {
999
- // For empty/not empty checks, no value needed
1000
- conditionPart = `${variableWithIOPrefix}${serviceNowOperator}`;
1001
- }
1002
- else if (serviceNowOperator === 'LIKE' || serviceNowOperator === 'NOT LIKE') {
1003
- // For LIKE operations, ensure value is wrapped properly
1004
- conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
1005
- }
1006
- else {
1007
- // Standard operations (=, !=, >, <, etc.)
1008
- conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
919
+ catch (error) {
920
+ this.logger.warn(`āš ļø Strategy failed: ${strategy.description}`, error);
921
+ }
922
+ }
923
+ // If all strategies fail, try alternate table names (just in case)
924
+ const alternateTables = ['item_option_new', 'io_set_item_option'];
925
+ for (const table of alternateTables) {
926
+ this.logger.info(`šŸ” Trying alternate table: ${table}`);
927
+ try {
928
+ const varResponse = await this.client.searchRecords(table, `cat_item=${catalogItem}^name=${variableName}`, 1);
929
+ if (varResponse.success && varResponse.data.result.length > 0) {
930
+ const sysId = varResponse.data.result[0].sys_id;
931
+ this.logger.info(`āœ… Found in alternate table ${table}: ${sysId}`);
932
+ return sysId;
1009
933
  }
1010
- this.logger.info(`šŸ—ļø Built condition part: ${conditionPart} (from ${originalOperator}: '${conditionValue}')`);
1011
- conditionParts.push(conditionPart);
1012
934
  }
1013
- // Join all conditions with ^ separator (ServiceNow query format)
1014
- conditionString = conditionParts.join('^');
1015
- this.logger.info(`āœ… Built conditions string: "${conditionString}"`);
935
+ catch (error) {
936
+ this.logger.warn(`āš ļø Alternate table ${table} failed:`, error);
937
+ }
1016
938
  }
1017
- // āœ… CRITICAL FIX: Create in catalog_ui_policy table!
1018
- // The actions reference catalog_ui_policy, NOT sys_ui_policy!
1019
- const policyData = {
1020
- // catalog_ui_policy fields
1021
- short_description: args.short_description,
1022
- catalog_item: args.cat_item, // Reference to the catalog item
1023
- catalog_conditions: conditionString || args.condition || '', // Conditions as string
1024
- applies_catalog: true, // This is a catalog policy
1025
- active: args.active !== false,
1026
- applies_on: args.applies_on || 'true', // When to apply: 'true', 'false', or 'both'
1027
- reverse_if_false: args.reverse_if_false !== false,
1028
- // Optional script fields
1029
- script_true: args.script_true || '',
1030
- script_false: args.script_false || ''
939
+ this.logger.error(`āŒ CRITICAL: Variable '${variableName}' not found in any table for catalog item ${catalogItem}`);
940
+ this.logger.error(`āŒ This will cause the action to fail! Please use the sys_id directly instead of the name.`);
941
+ // Return the name but log a warning that this will fail
942
+ return variableName;
943
+ };
944
+ // Helper function to map operations to correct ServiceNow format
945
+ const mapOperatorToServiceNow = (operator) => {
946
+ const opMap = {
947
+ 'is': '=',
948
+ 'equals': '=',
949
+ 'is_not': '!=',
950
+ 'is not': '!=',
951
+ 'not equals': '!=',
952
+ 'contains': 'LIKE',
953
+ 'does_not_contain': 'NOT LIKE',
954
+ 'does not contain': 'NOT LIKE',
955
+ 'greater_than': '>',
956
+ 'greater than': '>',
957
+ 'less_than': '<',
958
+ 'less than': '<',
959
+ 'is_empty': 'ISEMPTY',
960
+ 'is empty': 'ISEMPTY',
961
+ 'is_not_empty': 'ISNOTEMPTY', // āœ… CRITICAL FIX: was missing!
962
+ 'is not empty': 'ISNOTEMPTY'
1031
963
  };
1032
- this.logger.info('šŸŽÆ Creating main policy in catalog_ui_policy table with data:', policyData);
1033
- const policyResponse = await this.client.createRecord('catalog_ui_policy', policyData);
1034
- if (!policyResponse.success) {
1035
- this.logger.error('āŒ Policy creation failed:', policyResponse.error);
1036
- throw new Error(`Failed to create catalog UI policy: ${policyResponse.error}`);
964
+ const normalizedOp = operator.toLowerCase().trim();
965
+ const mapped = opMap[normalizedOp] || operator;
966
+ this.logger.info(`šŸŽÆ Mapped operator '${operator}' -> '${mapped}'`);
967
+ return mapped;
968
+ };
969
+ // Build condition string from conditions array
970
+ let conditionString = '';
971
+ this.logger.info('Checking conditions parameter:', {
972
+ hasConditions: !!args.conditions,
973
+ isArray: Array.isArray(args.conditions),
974
+ length: args.conditions ? args.conditions.length : 0,
975
+ conditionsData: args.conditions
976
+ });
977
+ if (args.conditions && Array.isArray(args.conditions)) {
978
+ this.logger.info(`šŸŽÆ Building conditions string from ${args.conditions.length} conditions...`);
979
+ const conditionParts = [];
980
+ for (const condition of args.conditions) {
981
+ // Resolve variable name to sys_id
982
+ const variableId = await resolveVariableId(condition.catalog_variable, args.cat_item);
983
+ // Get the correct ServiceNow operator
984
+ const originalOperator = condition.operation || 'is';
985
+ const serviceNowOperator = mapOperatorToServiceNow(originalOperator);
986
+ // Safely get condition value (avoid undefined concatenation)
987
+ const conditionValue = condition.value || '';
988
+ // Build the condition part based on operator type
989
+ // āœ… CRITICAL: Conditions must use IO:sys_id format for catalog variables!
990
+ // BUT only if we successfully resolved the variable to a sys_id
991
+ const isValidSysId = variableId && variableId.match(/^[a-f0-9]{32}$/);
992
+ const variableWithIOPrefix = isValidSysId ? `IO:${variableId}` : variableId;
993
+ if (!isValidSysId) {
994
+ this.logger.warn(`āš ļø Variable '${condition.catalog_variable}' could not be resolved to sys_id - condition may fail!`);
995
+ }
996
+ let conditionPart = '';
997
+ if (serviceNowOperator === 'ISEMPTY' || serviceNowOperator === 'ISNOTEMPTY') {
998
+ // For empty/not empty checks, no value needed
999
+ conditionPart = `${variableWithIOPrefix}${serviceNowOperator}`;
1000
+ }
1001
+ else if (serviceNowOperator === 'LIKE' || serviceNowOperator === 'NOT LIKE') {
1002
+ // For LIKE operations, ensure value is wrapped properly
1003
+ conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
1004
+ }
1005
+ else {
1006
+ // Standard operations (=, !=, >, <, etc.)
1007
+ conditionPart = `${variableWithIOPrefix}${serviceNowOperator}${conditionValue}`;
1008
+ }
1009
+ this.logger.info(`šŸ—ļø Built condition part: ${conditionPart} (from ${originalOperator}: '${conditionValue}')`);
1010
+ conditionParts.push(conditionPart);
1037
1011
  }
1038
- const policyId = policyResponse.data.sys_id;
1039
- this.logger.info(`āœ… Created main policy with sys_id: ${policyId}`);
1040
- const createdActions = [];
1041
- // Step 2: Create action records (dit werkt wel met aparte tabel)
1042
- this.logger.info('Checking actions parameter:', {
1043
- hasActions: !!args.actions,
1044
- isArray: Array.isArray(args.actions),
1045
- length: args.actions ? args.actions.length : 0,
1046
- actionsData: args.actions
1047
- });
1048
- if (args.actions && Array.isArray(args.actions)) {
1049
- this.logger.info(`šŸŽÆ Starting to create ${args.actions.length} action records...`);
1050
- for (let i = 0; i < args.actions.length; i++) {
1051
- const action = args.actions[i];
1052
- // Resolve variable name to sys_id
1053
- this.logger.info(`šŸ” Resolving variable: ${action.catalog_variable} for catalog item: ${args.cat_item}`);
1054
- const variableId = await resolveVariableId(action.catalog_variable, args.cat_item);
1055
- // Check if resolution actually worked (should be a sys_id now)
1056
- const isValidSysId = variableId && variableId.match(/^[a-f0-9]{32}$/);
1057
- if (!isValidSysId) {
1058
- this.logger.error(`āŒ CRITICAL: Failed to resolve variable '${action.catalog_variable}' to sys_id!`);
1059
- this.logger.error(`āŒ Got: ${variableId} (this is not a valid sys_id)`);
1060
- // Continue anyway but it will likely fail
1061
- }
1062
- else {
1063
- this.logger.info(`āœ… Resolved to variable sys_id: ${variableId}`);
1064
- }
1065
- // āœ… CRITICAL FIX: Add "IO:" prefix as required by ServiceNow
1066
- // BUT ONLY if we have a valid sys_id!
1067
- const catalogVariableWithPrefix = isValidSysId ? `IO:${variableId}` : variableId;
1068
- this.logger.info(`šŸŽÆ Using catalog_variable value: ${catalogVariableWithPrefix}`);
1069
- // Actions structure in ServiceNow:
1070
- // - catalog_ui_policy_action.catalog_variable -> "IO:" + variable_sys_id (required format)
1071
- // - catalog_ui_policy_action.ui_policy -> catalog_ui_policy.sys_id
1072
- // āœ… CRITICAL: Build action data according to ServiceNow's exact structure
1073
- // catalog_ui_policy_action inherits from sys_ui_policy_action
1074
- // We must set fields in the correct way for ServiceNow to accept them
1075
- const actionData = {};
1076
- // STEP 1: ENHANCED VERIFICATION - Verify policy exists before creating actions
1077
- this.logger.info(`šŸ” ENHANCED DEBUG: Verifying policy ${policyId} exists before creating action...`);
1078
- if (!policyId) {
1079
- this.logger.error(`āŒ CRITICAL: No policyId available for action ${i + 1}!`);
1080
- throw new Error(`Cannot create action without valid policy ID`);
1081
- }
1082
- // āœ… NEW: Test policy existence before action creation
1083
- const policyExists = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
1084
- if (!policyExists.success || policyExists.data.result.length === 0) {
1085
- this.logger.error(`āŒ CRITICAL: Policy ${policyId} does not exist in catalog_ui_policy table!`);
1086
- throw new Error(`Policy verification failed - cannot create action without valid policy`);
1087
- }
1088
- const existingPolicy = policyExists.data.result[0];
1089
- this.logger.info(`āœ… Policy verification successful:`);
1090
- this.logger.info(` - Policy sys_id: ${existingPolicy.sys_id}`);
1091
- this.logger.info(` - Policy name: ${existingPolicy.short_description || 'N/A'}`);
1092
- this.logger.info(` - Policy active: ${existingPolicy.active}`);
1093
- // STEP 2: Set reference fields with enhanced validation
1094
- // āœ… ui_policy is a reference to catalog_ui_policy - test multiple formats
1095
- this.logger.info(`šŸ“ Setting ui_policy reference to: ${policyId}`);
1096
- // Try setting the reference in the most explicit way possible
1097
- actionData.ui_policy = policyId;
1098
- // āœ… catalog_item is a reference to sc_cat_item - use sys_id directly
1099
- if (!args.cat_item) {
1100
- this.logger.error(`āŒ CRITICAL: No catalog item ID provided!`);
1101
- throw new Error(`Cannot create action without catalog item ID`);
1012
+ // Join all conditions with ^ separator (ServiceNow query format)
1013
+ conditionString = conditionParts.join('^');
1014
+ this.logger.info(`āœ… Built conditions string: "${conditionString}"`);
1015
+ }
1016
+ // āœ… CRITICAL FIX: Create in catalog_ui_policy table!
1017
+ // The actions reference catalog_ui_policy, NOT sys_ui_policy!
1018
+ const policyData = {
1019
+ // catalog_ui_policy fields
1020
+ short_description: args.short_description,
1021
+ catalog_item: args.cat_item, // Reference to the catalog item
1022
+ catalog_conditions: conditionString || args.condition || '', // Conditions as string
1023
+ applies_catalog: true, // This is a catalog policy
1024
+ active: args.active !== false,
1025
+ applies_on: args.applies_on || 'true', // When to apply: 'true', 'false', or 'both'
1026
+ reverse_if_false: args.reverse_if_false !== false,
1027
+ // Optional script fields
1028
+ script_true: args.script_true || '',
1029
+ script_false: args.script_false || ''
1030
+ };
1031
+ this.logger.info('šŸŽÆ Creating main policy in catalog_ui_policy table with data:', policyData);
1032
+ const policyResponse = await this.client.createRecord('catalog_ui_policy', policyData);
1033
+ if (!policyResponse.success) {
1034
+ this.logger.error('āŒ Policy creation failed:', policyResponse.error);
1035
+ throw new Error(`Failed to create catalog UI policy: ${policyResponse.error}`);
1036
+ }
1037
+ const policyId = policyResponse.data.sys_id;
1038
+ this.logger.info(`āœ… Created main policy with sys_id: ${policyId}`);
1039
+ const createdActions = [];
1040
+ // Step 2: Create action records (dit werkt wel met aparte tabel)
1041
+ this.logger.info('Checking actions parameter:', {
1042
+ hasActions: !!args.actions,
1043
+ isArray: Array.isArray(args.actions),
1044
+ length: args.actions ? args.actions.length : 0,
1045
+ actionsData: args.actions
1046
+ });
1047
+ if (args.actions && Array.isArray(args.actions)) {
1048
+ this.logger.info(`šŸŽÆ Starting to create ${args.actions.length} action records...`);
1049
+ for (let i = 0; i < args.actions.length; i++) {
1050
+ const action = args.actions[i];
1051
+ // Resolve variable name to sys_id
1052
+ this.logger.info(`šŸ” Resolving variable: ${action.catalog_variable} for catalog item: ${args.cat_item}`);
1053
+ const variableId = await resolveVariableId(action.catalog_variable, args.cat_item);
1054
+ // Check if resolution actually worked (should be a sys_id now)
1055
+ const isValidSysId = variableId && variableId.match(/^[a-f0-9]{32}$/);
1056
+ if (!isValidSysId) {
1057
+ this.logger.error(`āŒ CRITICAL: Failed to resolve variable '${action.catalog_variable}' to sys_id!`);
1058
+ this.logger.error(`āŒ Got: ${variableId} (this is not a valid sys_id)`);
1059
+ // Continue anyway but it will likely fail
1060
+ }
1061
+ else {
1062
+ this.logger.info(`āœ… Resolved to variable sys_id: ${variableId}`);
1063
+ }
1064
+ // āœ… CRITICAL FIX: Add "IO:" prefix as required by ServiceNow
1065
+ // BUT ONLY if we have a valid sys_id!
1066
+ const catalogVariableWithPrefix = isValidSysId ? `IO:${variableId}` : variableId;
1067
+ this.logger.info(`šŸŽÆ Using catalog_variable value: ${catalogVariableWithPrefix}`);
1068
+ // Actions structure in ServiceNow:
1069
+ // - catalog_ui_policy_action.catalog_variable -> "IO:" + variable_sys_id (required format)
1070
+ // - catalog_ui_policy_action.ui_policy -> catalog_ui_policy.sys_id
1071
+ // āœ… CRITICAL: Build action data according to ServiceNow's exact structure
1072
+ // catalog_ui_policy_action inherits from sys_ui_policy_action
1073
+ // We must set fields in the correct way for ServiceNow to accept them
1074
+ const actionData = {};
1075
+ // STEP 1: ENHANCED VERIFICATION - Verify policy exists before creating actions
1076
+ this.logger.info(`šŸ” ENHANCED DEBUG: Verifying policy ${policyId} exists before creating action...`);
1077
+ if (!policyId) {
1078
+ this.logger.error(`āŒ CRITICAL: No policyId available for action ${i + 1}!`);
1079
+ throw new Error(`Cannot create action without valid policy ID`);
1080
+ }
1081
+ // āœ… NEW: Test policy existence before action creation
1082
+ const policyExists = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
1083
+ if (!policyExists.success || policyExists.data.result.length === 0) {
1084
+ this.logger.error(`āŒ CRITICAL: Policy ${policyId} does not exist in catalog_ui_policy table!`);
1085
+ throw new Error(`Policy verification failed - cannot create action without valid policy`);
1086
+ }
1087
+ const existingPolicy = policyExists.data.result[0];
1088
+ this.logger.info(`āœ… Policy verification successful:`);
1089
+ this.logger.info(` - Policy sys_id: ${existingPolicy.sys_id}`);
1090
+ this.logger.info(` - Policy name: ${existingPolicy.short_description || 'N/A'}`);
1091
+ this.logger.info(` - Policy active: ${existingPolicy.active}`);
1092
+ // STEP 2: Set reference fields with enhanced validation
1093
+ // āœ… ui_policy is a reference to catalog_ui_policy - test multiple formats
1094
+ this.logger.info(`šŸ“ Setting ui_policy reference to: ${policyId}`);
1095
+ // Try setting the reference in the most explicit way possible
1096
+ actionData.ui_policy = policyId;
1097
+ // āœ… catalog_item is a reference to sc_cat_item - use sys_id directly
1098
+ if (!args.cat_item) {
1099
+ this.logger.error(`āŒ CRITICAL: No catalog item ID provided!`);
1100
+ throw new Error(`Cannot create action without catalog item ID`);
1101
+ }
1102
+ this.logger.info(`šŸ“ Setting catalog_item reference to: ${args.cat_item}`);
1103
+ actionData.catalog_item = args.cat_item;
1104
+ // STEP 3: Set the catalog_variable with IO: prefix (STRING field, not reference)
1105
+ this.logger.info(`šŸ“ Setting catalog_variable to: ${catalogVariableWithPrefix}`);
1106
+ actionData.catalog_variable = catalogVariableWithPrefix;
1107
+ // STEP 4: Set action properties with correct values
1108
+ // āœ… CRITICAL: Use "ignore" instead of not setting or using false
1109
+ // This is how ServiceNow differentiates between "don't change" and "set to false"
1110
+ if (action.visible !== undefined) {
1111
+ actionData.visible = action.visible === true ? 'true' :
1112
+ action.visible === false ? 'false' : 'ignore';
1113
+ }
1114
+ else {
1115
+ actionData.visible = 'ignore'; // Default to ignore if not specified
1116
+ }
1117
+ if (action.mandatory !== undefined) {
1118
+ actionData.mandatory = action.mandatory === true ? 'true' :
1119
+ action.mandatory === false ? 'false' : 'ignore';
1120
+ }
1121
+ else {
1122
+ actionData.mandatory = 'ignore'; // Default to ignore if not specified
1123
+ }
1124
+ if (action.readonly !== undefined) {
1125
+ actionData.disabled = action.readonly === true ? 'true' :
1126
+ action.readonly === false ? 'false' : 'ignore';
1127
+ }
1128
+ else {
1129
+ actionData.disabled = 'ignore'; // Default to ignore if not specified
1130
+ }
1131
+ // STEP 5: Set optional value field
1132
+ if (action.value !== undefined && action.value !== null && action.value !== '') {
1133
+ actionData.value = String(action.value);
1134
+ }
1135
+ // STEP 6: Set other metadata
1136
+ actionData.order = (i + 1) * 100;
1137
+ actionData.active = true;
1138
+ this.logger.info(`šŸ”— Creating action with VALIDATED structure:`);
1139
+ this.logger.info(` - ui_policy (ref): ${policyId} [VERIFIED EXISTS]`);
1140
+ this.logger.info(` - catalog_item (ref): ${args.cat_item}`);
1141
+ this.logger.info(` - catalog_variable: ${catalogVariableWithPrefix}`);
1142
+ this.logger.info(` - visible: ${actionData.visible}`);
1143
+ this.logger.info(` - mandatory: ${actionData.mandatory}`);
1144
+ this.logger.info(` - disabled: ${actionData.disabled}`);
1145
+ // āœ… ENHANCED DEBUG: Log complete action data being sent
1146
+ this.logger.info(`šŸ“‹ Complete action data being sent to ServiceNow:`, JSON.stringify(actionData, null, 2));
1147
+ this.logger.info(`šŸŽÆ Attempting to create action ${i + 1} in catalog_ui_policy_action table...`);
1148
+ const actionResponse = await this.client.createRecord('catalog_ui_policy_action', actionData);
1149
+ if (actionResponse.success) {
1150
+ const createdActionId = actionResponse.data.sys_id;
1151
+ this.logger.info(`āœ… Created action with sys_id: ${createdActionId}`);
1152
+ // šŸ” VERIFICATION: Check if action was actually created AND fields are populated
1153
+ this.logger.info(`šŸ” Verifying action ${i + 1} creation and field population...`);
1154
+ const actionVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
1155
+ if (!actionVerification.success || actionVerification.data.result.length === 0) {
1156
+ this.logger.error('āŒ ACTION VERIFICATION FAILED: Action not found after creation!');
1157
+ throw new Error(`Action creation verification failed - action ${i + 1} not found in database`);
1102
1158
  }
1103
- this.logger.info(`šŸ“ Setting catalog_item reference to: ${args.cat_item}`);
1104
- actionData.catalog_item = args.cat_item;
1105
- // STEP 3: Set the catalog_variable with IO: prefix (STRING field, not reference)
1106
- this.logger.info(`šŸ“ Setting catalog_variable to: ${catalogVariableWithPrefix}`);
1107
- actionData.catalog_variable = catalogVariableWithPrefix;
1108
- // STEP 4: Set action properties with correct values
1109
- // āœ… CRITICAL: Use "ignore" instead of not setting or using false
1110
- // This is how ServiceNow differentiates between "don't change" and "set to false"
1111
- if (action.visible !== undefined) {
1112
- actionData.visible = action.visible === true ? 'true' :
1113
- action.visible === false ? 'false' : 'ignore';
1159
+ // āœ… NEW: Verify that critical fields are actually populated
1160
+ const createdAction = actionVerification.data.result[0];
1161
+ this.logger.info(`šŸ“‹ Created action data:`, {
1162
+ sys_id: createdAction.sys_id,
1163
+ ui_policy: createdAction.ui_policy || 'āŒ EMPTY',
1164
+ catalog_variable: createdAction.catalog_variable || 'āŒ EMPTY',
1165
+ catalog_item: createdAction.catalog_item || 'āŒ EMPTY',
1166
+ visible: createdAction.visible,
1167
+ mandatory: createdAction.mandatory,
1168
+ disabled: createdAction.disabled
1169
+ });
1170
+ // āœ… ENHANCED VERIFICATION: Check if critical fields are populated correctly
1171
+ const uiPolicyValue = createdAction.ui_policy;
1172
+ const catalogVariableValue = createdAction.catalog_variable;
1173
+ const catalogItemValue = createdAction.catalog_item;
1174
+ // Log the raw response to understand what ServiceNow returns
1175
+ this.logger.info(`šŸ“‹ Raw action verification response:`, JSON.stringify(createdAction, null, 2));
1176
+ // Check ui_policy reference - ServiceNow might return it as an object
1177
+ let actualUiPolicyId = uiPolicyValue;
1178
+ if (typeof uiPolicyValue === 'object' && uiPolicyValue !== null) {
1179
+ actualUiPolicyId = uiPolicyValue.value || uiPolicyValue.sys_id || '';
1180
+ this.logger.info(`šŸ“ ui_policy returned as object: ${JSON.stringify(uiPolicyValue)}`);
1114
1181
  }
1115
- else {
1116
- actionData.visible = 'ignore'; // Default to ignore if not specified
1182
+ if (!actualUiPolicyId || actualUiPolicyId === '' || actualUiPolicyId === '{}') {
1183
+ this.logger.error(`āŒ CRITICAL: ui_policy field is EMPTY for action ${i + 1}!`);
1184
+ this.logger.error(`āŒ Expected policy ID: ${policyId}`);
1185
+ this.logger.error(`āŒ Raw ui_policy value: ${JSON.stringify(uiPolicyValue)}`);
1186
+ this.logger.error(`āŒ Parsed ui_policy value: ${actualUiPolicyId}`);
1187
+ this.logger.error(`āŒ Action data sent:`, JSON.stringify(actionData, null, 2));
1188
+ this.logger.error(`āŒ Full action verification response:`, JSON.stringify(createdAction, null, 2));
1189
+ // āœ… ENHANCED ERROR: Try to understand ServiceNow's response pattern
1190
+ this.logger.error(`ā„¹ļø DIAGNOSTIC INFO:`);
1191
+ this.logger.error(` - Policy verified to exist: YES (${policyId})`);
1192
+ this.logger.error(` - Action created successfully: YES (${createdActionId})`);
1193
+ this.logger.error(` - ui_policy field type: ${typeof uiPolicyValue}`);
1194
+ this.logger.error(` - ui_policy field value length: ${String(uiPolicyValue || '').length}`);
1195
+ this.logger.error(` - All action fields:`, Object.keys(createdAction));
1196
+ // Check if it's a ServiceNow API timing issue
1197
+ this.logger.warn(`šŸ”„ ATTEMPTING SECONDARY VERIFICATION (possible timing issue)...`);
1198
+ // Wait a moment and try again
1199
+ await new Promise(resolve => setTimeout(resolve, 1000));
1200
+ const secondVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
1201
+ if (secondVerification.success && secondVerification.data.result.length > 0) {
1202
+ const reCheckedAction = secondVerification.data.result[0];
1203
+ const reCheckedUiPolicy = reCheckedAction.ui_policy;
1204
+ this.logger.error(`šŸ”„ Secondary verification ui_policy: ${JSON.stringify(reCheckedUiPolicy)}`);
1205
+ if (reCheckedUiPolicy && reCheckedUiPolicy !== '' && reCheckedUiPolicy !== '{}') {
1206
+ this.logger.warn(`āš ļø This was a timing issue - ui_policy populated after delay`);
1207
+ // Continue with the re-checked value
1208
+ return; // Skip the error throwing
1209
+ }
1210
+ }
1211
+ 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.`);
1117
1212
  }
1118
- if (action.mandatory !== undefined) {
1119
- actionData.mandatory = action.mandatory === true ? 'true' :
1120
- action.mandatory === false ? 'false' : 'ignore';
1213
+ // For reference fields, ServiceNow might return an object - extract the value
1214
+ const uiPolicySysId = typeof uiPolicyValue === 'object' && uiPolicyValue.value ?
1215
+ uiPolicyValue.value : uiPolicyValue;
1216
+ this.logger.info(`āœ… ui_policy field populated successfully: ${actualUiPolicyId}`);
1217
+ if (uiPolicySysId !== policyId) {
1218
+ this.logger.warn(`āš ļø ui_policy mismatch - Expected: ${policyId}, Got: ${uiPolicySysId}`);
1121
1219
  }
1122
1220
  else {
1123
- actionData.mandatory = 'ignore'; // Default to ignore if not specified
1221
+ this.logger.info(`āœ… ui_policy reference matches expected value`);
1124
1222
  }
1125
- if (action.readonly !== undefined) {
1126
- actionData.disabled = action.readonly === true ? 'true' :
1127
- action.readonly === false ? 'false' : 'ignore';
1223
+ // Check catalog_variable (should have IO: prefix)
1224
+ if (!catalogVariableValue || catalogVariableValue === '') {
1225
+ this.logger.error(`āŒ CRITICAL: catalog_variable field is EMPTY for action ${i + 1}!`);
1226
+ this.logger.error(`āŒ Expected: ${catalogVariableWithPrefix}, Got: ${catalogVariableValue}`);
1227
+ throw new Error(`Action ${i + 1} created but catalog_variable field is empty - action will not work!`);
1128
1228
  }
1129
- else {
1130
- actionData.disabled = 'ignore'; // Default to ignore if not specified
1229
+ if (!catalogVariableValue.startsWith('IO:')) {
1230
+ this.logger.warn(`āš ļø catalog_variable missing IO: prefix - Got: ${catalogVariableValue}`);
1131
1231
  }
1132
- // STEP 5: Set optional value field
1133
- if (action.value !== undefined && action.value !== null && action.value !== '') {
1134
- actionData.value = String(action.value);
1135
- }
1136
- // STEP 6: Set other metadata
1137
- actionData.order = (i + 1) * 100;
1138
- actionData.active = true;
1139
- this.logger.info(`šŸ”— Creating action with VALIDATED structure:`);
1140
- this.logger.info(` - ui_policy (ref): ${policyId} [VERIFIED EXISTS]`);
1141
- this.logger.info(` - catalog_item (ref): ${args.cat_item}`);
1142
- this.logger.info(` - catalog_variable: ${catalogVariableWithPrefix}`);
1143
- this.logger.info(` - visible: ${actionData.visible}`);
1144
- this.logger.info(` - mandatory: ${actionData.mandatory}`);
1145
- this.logger.info(` - disabled: ${actionData.disabled}`);
1146
- // āœ… ENHANCED DEBUG: Log complete action data being sent
1147
- this.logger.info(`šŸ“‹ Complete action data being sent to ServiceNow:`, JSON.stringify(actionData, null, 2));
1148
- this.logger.info(`šŸŽÆ Attempting to create action ${i + 1} in catalog_ui_policy_action table...`);
1149
- const actionResponse = await this.client.createRecord('catalog_ui_policy_action', actionData);
1150
- if (actionResponse.success) {
1151
- const createdActionId = actionResponse.data.sys_id;
1152
- this.logger.info(`āœ… Created action with sys_id: ${createdActionId}`);
1153
- // šŸ” VERIFICATION: Check if action was actually created AND fields are populated
1154
- this.logger.info(`šŸ” Verifying action ${i + 1} creation and field population...`);
1155
- const actionVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
1156
- if (!actionVerification.success || actionVerification.data.result.length === 0) {
1157
- this.logger.error('āŒ ACTION VERIFICATION FAILED: Action not found after creation!');
1158
- throw new Error(`Action creation verification failed - action ${i + 1} not found in database`);
1159
- }
1160
- // āœ… NEW: Verify that critical fields are actually populated
1161
- const createdAction = actionVerification.data.result[0];
1162
- this.logger.info(`šŸ“‹ Created action data:`, {
1163
- sys_id: createdAction.sys_id,
1164
- ui_policy: createdAction.ui_policy || 'āŒ EMPTY',
1165
- catalog_variable: createdAction.catalog_variable || 'āŒ EMPTY',
1166
- catalog_item: createdAction.catalog_item || 'āŒ EMPTY',
1167
- visible: createdAction.visible,
1168
- mandatory: createdAction.mandatory,
1169
- disabled: createdAction.disabled
1170
- });
1171
- // āœ… ENHANCED VERIFICATION: Check if critical fields are populated correctly
1172
- const uiPolicyValue = createdAction.ui_policy;
1173
- const catalogVariableValue = createdAction.catalog_variable;
1174
- const catalogItemValue = createdAction.catalog_item;
1175
- // Log the raw response to understand what ServiceNow returns
1176
- this.logger.info(`šŸ“‹ Raw action verification response:`, JSON.stringify(createdAction, null, 2));
1177
- // Check ui_policy reference - ServiceNow might return it as an object
1178
- let actualUiPolicyId = uiPolicyValue;
1179
- if (typeof uiPolicyValue === 'object' && uiPolicyValue !== null) {
1180
- actualUiPolicyId = uiPolicyValue.value || uiPolicyValue.sys_id || '';
1181
- this.logger.info(`šŸ“ ui_policy returned as object: ${JSON.stringify(uiPolicyValue)}`);
1182
- }
1183
- if (!actualUiPolicyId || actualUiPolicyId === '' || actualUiPolicyId === '{}') {
1184
- this.logger.error(`āŒ CRITICAL: ui_policy field is EMPTY for action ${i + 1}!`);
1185
- this.logger.error(`āŒ Expected policy ID: ${policyId}`);
1186
- this.logger.error(`āŒ Raw ui_policy value: ${JSON.stringify(uiPolicyValue)}`);
1187
- this.logger.error(`āŒ Parsed ui_policy value: ${actualUiPolicyId}`);
1188
- this.logger.error(`āŒ Action data sent:`, JSON.stringify(actionData, null, 2));
1189
- this.logger.error(`āŒ Full action verification response:`, JSON.stringify(createdAction, null, 2));
1190
- // āœ… ENHANCED ERROR: Try to understand ServiceNow's response pattern
1191
- this.logger.error(`ā„¹ļø DIAGNOSTIC INFO:`);
1192
- this.logger.error(` - Policy verified to exist: YES (${policyId})`);
1193
- this.logger.error(` - Action created successfully: YES (${createdActionId})`);
1194
- this.logger.error(` - ui_policy field type: ${typeof uiPolicyValue}`);
1195
- this.logger.error(` - ui_policy field value length: ${String(uiPolicyValue || '').length}`);
1196
- this.logger.error(` - All action fields:`, Object.keys(createdAction));
1197
- // Check if it's a ServiceNow API timing issue
1198
- this.logger.warn(`šŸ”„ ATTEMPTING SECONDARY VERIFICATION (possible timing issue)...`);
1199
- // Wait a moment and try again
1200
- await new Promise(resolve => setTimeout(resolve, 1000));
1201
- const secondVerification = await this.client.searchRecords('catalog_ui_policy_action', `sys_id=${createdActionId}`, 1);
1202
- if (secondVerification.success && secondVerification.data.result.length > 0) {
1203
- const reCheckedAction = secondVerification.data.result[0];
1204
- const reCheckedUiPolicy = reCheckedAction.ui_policy;
1205
- this.logger.error(`šŸ”„ Secondary verification ui_policy: ${JSON.stringify(reCheckedUiPolicy)}`);
1206
- if (reCheckedUiPolicy && reCheckedUiPolicy !== '' && reCheckedUiPolicy !== '{}') {
1207
- this.logger.warn(`āš ļø This was a timing issue - ui_policy populated after delay`);
1208
- // Continue with the re-checked value
1209
- return; // Skip the error throwing
1210
- }
1211
- }
1212
- 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.`);
1213
- }
1214
- // For reference fields, ServiceNow might return an object - extract the value
1215
- const uiPolicySysId = typeof uiPolicyValue === 'object' && uiPolicyValue.value ?
1216
- uiPolicyValue.value : uiPolicyValue;
1217
- this.logger.info(`āœ… ui_policy field populated successfully: ${actualUiPolicyId}`);
1218
- if (uiPolicySysId !== policyId) {
1219
- this.logger.warn(`āš ļø ui_policy mismatch - Expected: ${policyId}, Got: ${uiPolicySysId}`);
1220
- }
1221
- else {
1222
- this.logger.info(`āœ… ui_policy reference matches expected value`);
1223
- }
1224
- // Check catalog_variable (should have IO: prefix)
1225
- if (!catalogVariableValue || catalogVariableValue === '') {
1226
- this.logger.error(`āŒ CRITICAL: catalog_variable field is EMPTY for action ${i + 1}!`);
1227
- this.logger.error(`āŒ Expected: ${catalogVariableWithPrefix}, Got: ${catalogVariableValue}`);
1228
- throw new Error(`Action ${i + 1} created but catalog_variable field is empty - action will not work!`);
1229
- }
1230
- if (!catalogVariableValue.startsWith('IO:')) {
1231
- this.logger.warn(`āš ļø catalog_variable missing IO: prefix - Got: ${catalogVariableValue}`);
1232
- }
1233
- // Check catalog_item reference
1234
- if (!catalogItemValue || catalogItemValue === '' || catalogItemValue === '{}') {
1235
- this.logger.error(`āŒ CRITICAL: catalog_item field is EMPTY for action ${i + 1}!`);
1236
- this.logger.error(`āŒ Expected: ${args.cat_item}, Got: ${catalogItemValue}`);
1237
- throw new Error(`Action ${i + 1} created but catalog_item field is empty - action will not work!`);
1238
- }
1239
- const catalogItemSysId = typeof catalogItemValue === 'object' && catalogItemValue.value ?
1240
- catalogItemValue.value : catalogItemValue;
1241
- if (catalogItemSysId !== args.cat_item) {
1242
- this.logger.warn(`āš ļø catalog_item mismatch - Expected: ${args.cat_item}, Got: ${catalogItemSysId}`);
1243
- }
1244
- this.logger.info(`āœ… Action ${i + 1} verified in database with all fields populated`);
1245
- createdActions.push({
1246
- sys_id: createdActionId,
1247
- variable: action.catalog_variable,
1248
- details: this.formatActionDetails(action)
1249
- });
1232
+ // Check catalog_item reference
1233
+ if (!catalogItemValue || catalogItemValue === '' || catalogItemValue === '{}') {
1234
+ this.logger.error(`āŒ CRITICAL: catalog_item field is EMPTY for action ${i + 1}!`);
1235
+ this.logger.error(`āŒ Expected: ${args.cat_item}, Got: ${catalogItemValue}`);
1236
+ throw new Error(`Action ${i + 1} created but catalog_item field is empty - action will not work!`);
1250
1237
  }
1251
- else {
1252
- const errorMsg = `āŒ Failed to create action ${i + 1}: ${actionResponse.error || 'Unknown error'}`;
1253
- this.logger.error(errorMsg);
1254
- this.logger.error('āŒ Action data was:', actionData);
1255
- this.logger.error('āŒ Response details:', {
1256
- status: actionResponse.status,
1257
- headers: actionResponse.headers,
1258
- data: actionResponse.data
1259
- });
1260
- // BELANGRIJK: Gooi een error zodat de gebruiker weet dat het faalt!
1261
- throw new Error(errorMsg);
1238
+ const catalogItemSysId = typeof catalogItemValue === 'object' && catalogItemValue.value ?
1239
+ catalogItemValue.value : catalogItemValue;
1240
+ if (catalogItemSysId !== args.cat_item) {
1241
+ this.logger.warn(`āš ļø catalog_item mismatch - Expected: ${args.cat_item}, Got: ${catalogItemSysId}`);
1262
1242
  }
1243
+ this.logger.info(`āœ… Action ${i + 1} verified in database with all fields populated`);
1244
+ createdActions.push({
1245
+ sys_id: createdActionId,
1246
+ variable: action.catalog_variable,
1247
+ details: this.formatActionDetails(action)
1248
+ });
1249
+ }
1250
+ else {
1251
+ const errorMsg = `āŒ Failed to create action ${i + 1}: ${actionResponse.error || 'Unknown error'}`;
1252
+ this.logger.error(errorMsg);
1253
+ this.logger.error('āŒ Action data was:', actionData);
1254
+ this.logger.error('āŒ Response details:', {
1255
+ status: actionResponse.status,
1256
+ headers: actionResponse.headers,
1257
+ data: actionResponse.data
1258
+ });
1259
+ // BELANGRIJK: Gooi een error zodat de gebruiker weet dat het faalt!
1260
+ throw new Error(errorMsg);
1263
1261
  }
1264
1262
  }
1265
- // šŸ” FINAL VERIFICATION: Policy creation in catalog_ui_policy table
1266
- this.logger.info('šŸ” Final verification: Checking policy in catalog_ui_policy table...');
1267
- const policyVerification = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
1268
- if (!policyVerification.success || policyVerification.data.result.length === 0) {
1269
- this.logger.error('āŒ POLICY VERIFICATION FAILED: Policy not found in catalog_ui_policy table!');
1270
- throw new Error(`Policy creation verification failed - policy not found in catalog_ui_policy table`);
1271
- }
1272
- this.logger.info('āœ… Policy verified in catalog_ui_policy table');
1273
- // Build comprehensive response
1274
- let responseText = `āœ… Catalog UI Policy created successfully!
1263
+ }
1264
+ // šŸ” FINAL VERIFICATION: Policy creation in catalog_ui_policy table
1265
+ this.logger.info('šŸ” Final verification: Checking policy in catalog_ui_policy table...');
1266
+ const policyVerification = await this.client.searchRecords('catalog_ui_policy', `sys_id=${policyId}`, 1);
1267
+ if (!policyVerification.success || policyVerification.data.result.length === 0) {
1268
+ this.logger.error('āŒ POLICY VERIFICATION FAILED: Policy not found in catalog_ui_policy table!');
1269
+ throw new Error(`Policy creation verification failed - policy not found in catalog_ui_policy table`);
1270
+ }
1271
+ this.logger.info('āœ… Policy verified in catalog_ui_policy table');
1272
+ // Build comprehensive response
1273
+ let responseText = `āœ… Catalog UI Policy created successfully!
1275
1274
 
1276
1275
  šŸ“‹ **${args.short_description}**
1277
1276
  šŸ†” Policy sys_id: ${policyId}
@@ -1284,36 +1283,27 @@ ${args.help_text ? `ā“ Help: ${args.help_text}` : ''}
1284
1283
  šŸ” **Verification Results:**
1285
1284
  āœ… Policy record created in catalog_ui_policy table
1286
1285
  āœ… ${createdActions.length} actions created in catalog_ui_policy_action table`;
1287
- if (conditionString) {
1288
- responseText += `\n\nšŸ“ **Conditions:**\n${conditionString}`;
1289
- }
1290
- if (createdActions.length > 0) {
1291
- responseText += `\n\n⚔ **Actions Created (${createdActions.length}):**\n`;
1292
- createdActions.forEach((action, i) => {
1293
- responseText += ` ${i + 1}. ${action.details} on ${action.variable}\n`;
1294
- });
1295
- }
1296
- responseText += `\n\n✨ UI policy configured successfully with ${createdActions.length} actions!`;
1297
- return {
1298
- content: [{
1299
- type: 'text',
1300
- text: responseText
1301
- }]
1302
- };
1286
+ if (conditionString) {
1287
+ responseText += `\n\nšŸ“ **Conditions:**\n${conditionString}`;
1303
1288
  }
1304
- catch (error) {
1305
- this.logger.error('Failed to create catalog UI policy:', error);
1306
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog UI policy: ${error}`);
1289
+ if (createdActions.length > 0) {
1290
+ responseText += `\n\n⚔ **Actions Created (${createdActions.length}):**\n`;
1291
+ createdActions.forEach((action, i) => {
1292
+ responseText += ` ${i + 1}. ${action.details} on ${action.variable}\n`;
1293
+ });
1307
1294
  }
1295
+ responseText += `\n\n✨ UI policy configured successfully with ${createdActions.length} actions!`;
1296
+ return {
1297
+ content: [{
1298
+ type: 'text',
1299
+ text: responseText
1300
+ }]
1301
+ };
1308
1302
  }
1309
- /**
1310
- * Helper function to format action details for display
1311
- */
1312
- finally {
1303
+ catch (error) {
1304
+ this.logger.error('Failed to create catalog UI policy:', error);
1305
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog UI policy: ${error}`);
1313
1306
  }
1314
- /**
1315
- * Helper function to format action details for display
1316
- */
1317
1307
  }
1318
1308
  /**
1319
1309
  * Helper function to format action details for display
@@ -1515,7 +1505,7 @@ ${args.special_instructions ? `šŸ“ Instructions: ${args.special_instructions}`
1515
1505
  this.logger.error('āŒ Both getRecord and searchRecords failed');
1516
1506
  this.logger.error('searchRecords error:', itemResponse.error);
1517
1507
  // Final fallback: try to query with different parameters
1518
- const queryResponse = await this.client.queryTable('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1508
+ const queryResponse = await this.client.searchRecords('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1519
1509
  if (queryResponse.success && queryResponse.data.result.length > 0) {
1520
1510
  this.logger.info('āœ… Found item using queryTable fallback');
1521
1511
  itemResponse = { success: true, data: queryResponse.data.result[0] };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "4.3.6",
3
+ "version": "4.3.9",
4
4
  "description": "Conversational ServiceNow development platform using Claude Code. Multi-agent orchestration with 20+ MCP servers providing 200+ ServiceNow tools for comprehensive platform development.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
@@ -677,7 +677,7 @@ and historical patterns. Deploy as real-time API" \
677
677
  <section id="new-features" class="section" style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: var(--space-20) 0;">
678
678
  <div class="container">
679
679
  <h2 class="text-center font-black" style="font-size: var(--font-4xl); color: var(--white); margin-bottom: var(--space-4);">šŸš€ New in v4.2.0 ENTERPRISE</h2>
680
- <p class="text-center" style="color: rgba(255,255,255,0.9); margin-bottom: var(--space-12); font-size: var(--font-lg);">Revolutionary enterprise features, memory optimizations, and massive performance improvements</p>
680
+ <p class="text-center" style="color: rgba(255,255,255,0.9); margin-bottom: var(--space-12); font-size: var(--font-lg);">Enterprise-grade ServiceNow integration with proven MCP servers, real machine learning, and comprehensive tooling</p>
681
681
 
682
682
  <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3" style="gap: var(--space-6);">
683
683
  <!-- ITAM Enterprise Module -->
@@ -685,7 +685,7 @@ and historical patterns. Deploy as real-time API" \
685
685
  <div style="padding: var(--space-6);">
686
686
  <div style="font-size: var(--font-3xl); margin-bottom: var(--space-3);">šŸ¢</div>
687
687
  <h3 style="color: var(--white); font-size: var(--font-xl); font-weight: 700; margin-bottom: var(--space-3);">IT Asset Management</h3>
688
- <p style="color: rgba(255,255,255,0.8); margin-bottom: var(--space-4);">Complete enterprise asset lifecycle management with license optimization and compliance reporting.</p>
688
+ <p style="color: rgba(255,255,255,0.8); margin-bottom: var(--space-4);">IT Asset Management with 6 MCP tools for asset tracking, license management, and compliance workflows.</p>
689
689
  <div style="background: rgba(16, 185, 129, 0.2); padding: var(--space-2); border-radius: var(--radius-md); margin-bottom: var(--space-3);">
690
690
  <strong style="color: #10b981;">6 New Enterprise Tools</strong>
691
691
  </div>
@@ -705,7 +705,7 @@ and historical patterns. Deploy as real-time API" \
705
705
  <div style="padding: var(--space-6);">
706
706
  <div style="font-size: var(--font-3xl); margin-bottom: var(--space-3);">šŸ›”ļø</div>
707
707
  <h3 style="color: var(--white); font-size: var(--font-xl); font-weight: 700; margin-bottom: var(--space-3);">Security Operations</h3>
708
- <p style="color: rgba(255,255,255,0.8); margin-bottom: var(--space-4);">Advanced security incident response with threat intelligence and automated SOAR capabilities.</p>
708
+ <p style="color: rgba(255,255,255,0.8); margin-bottom: var(--space-4);">Security Operations with 6 MCP tools for incident response, threat analysis, and security playbook automation.</p>
709
709
  <div style="background: rgba(239, 68, 68, 0.2); padding: var(--space-2); border-radius: var(--radius-md); margin-bottom: var(--space-3);">
710
710
  <strong style="color: #ef4444;">6 Security Tools</strong>
711
711
  </div>
@@ -725,7 +725,7 @@ and historical patterns. Deploy as real-time API" \
725
725
  <div style="padding: var(--space-6);">
726
726
  <div style="font-size: var(--font-3xl); margin-bottom: var(--space-3);">šŸ“Ø</div>
727
727
  <h3 style="color: var(--white); font-size: var(--font-xl); font-weight: 700; margin-bottom: var(--space-3);">Notification Framework</h3>
728
- <p style="color: rgba(255,255,255,0.8); margin-bottom: var(--space-4);">Enterprise multi-channel notification system with templates, analytics, and preference management.</p>
728
+ <p style="color: rgba(255,255,255,0.8); margin-bottom: var(--space-4);">Multi-channel notification system supporting email, SMS, push, Slack, and Teams with template management.</p>
729
729
  <div style="background: rgba(59, 130, 246, 0.2); padding: var(--space-2); border-radius: var(--radius-md); margin-bottom: var(--space-3);">
730
730
  <strong style="color: #3b82f6;">6 Communication Tools</strong>
731
731
  </div>
@@ -740,20 +740,23 @@ and historical patterns. Deploy as real-time API" \
740
740
  </div>
741
741
  </div>
742
742
 
743
- <!-- Memory Optimization -->
743
+ <!-- Local Development Sync -->
744
744
  <div class="card" style="background: rgba(255,255,255,0.1); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.2);">
745
- <div class="card-icon" style="font-size: 2rem;">šŸŽÆ</div>
746
- <h3 style="color: var(--white); margin-bottom: var(--space-2);">Catalog UI Policy Deployment</h3>
747
- <p style="color: rgba(255,255,255,0.8); font-size: var(--font-sm); margin-bottom: var(--space-3);">Deploy catalog UI policies via unified snow_deploy tool</p>
748
- <div class="code-block" style="background: rgba(0,0,0,0.2); padding: var(--space-3);">
749
- <code style="font-size: var(--font-xs); color: var(--white);">snow_deploy({
750
- type: "catalog_ui_policy",
751
- config: {
752
- catalog_item: "id",
753
- conditions: [...],
754
- actions: [...]
755
- }
756
- })</code>
745
+ <div style="padding: var(--space-6);">
746
+ <div style="font-size: var(--font-3xl); margin-bottom: var(--space-3);">šŸ”„</div>
747
+ <h3 style="color: var(--white); font-size: var(--font-xl); font-weight: 700; margin-bottom: var(--space-3);">Local Development Sync</h3>
748
+ <p style="color: rgba(255,255,255,0.8); margin-bottom: var(--space-4);">Bridge ServiceNow with Claude Code native tools. Edit any ServiceNow artifact locally with full search, refactor, and debugging capabilities.</p>
749
+ <div style="background: rgba(16, 185, 129, 0.2); padding: var(--space-2); border-radius: var(--radius-md); margin-bottom: var(--space-3);">
750
+ <strong style="color: #10b981;">12+ Artifact Types</strong>
751
+ </div>
752
+ <ul style="color: rgba(255,255,255,0.7); font-size: var(--font-sm); list-style: none; padding: 0;">
753
+ <li>āœ… snow_pull_artifact</li>
754
+ <li>āœ… snow_push_artifact</li>
755
+ <li>āœ… snow_validate_artifact_coherence</li>
756
+ <li>āœ… Widgets, Flows, Scripts</li>
757
+ <li>āœ… Full Claude Code integration</li>
758
+ <li>āœ… ES5 validation & conversion</li>
759
+ </ul>
757
760
  </div>
758
761
  </div>
759
762