snow-flow 4.3.8 → 4.3.10

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.
@@ -798,7 +798,7 @@ ${args.recurring_price && args.recurring_price !== '0' ? `šŸ”„ Recurring: $${arg
798
798
  this.logger.error('āŒ Variable creation failed:', {
799
799
  error: response.error,
800
800
  payload: variableData,
801
- status: response.status
801
+ success: response.success
802
802
  });
803
803
  throw new Error(`Failed to create catalog variable: ${response.error}`);
804
804
  }
@@ -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
+ });
861
856
  }
862
- catch (error) {
863
- this.logger.error(`āŒ Failed to fetch variables for debugging:`, error);
857
+ else {
858
+ this.logger.warn(`āš ļø No variables found for catalog item ${args.cat_item} - this might be a problem!`);
864
859
  }
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;
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;
870
+ }
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
1131
- }
1132
- // STEP 5: Set optional value field
1133
- if (action.value !== undefined && action.value !== null && action.value !== '') {
1134
- actionData.value = String(action.value);
1229
+ if (!catalogVariableValue.startsWith('IO:')) {
1230
+ this.logger.warn(`āš ļø catalog_variable missing IO: prefix - Got: ${catalogVariableValue}`);
1135
1231
  }
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
+ success: actionResponse.success,
1256
+ error: actionResponse.error,
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,81 +1283,71 @@ ${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
- }
1318
- catch(error) { n; this.logger.error('Failed to create catalog UI policy:', error); n; return { content: [{ type: 'text', text: `āŒ Failed to create catalog UI policy: ${error.message || error}` }] }; n; }
1319
- }
1320
- n;
1321
- n;
1322
- formatActionDetails(action, any);
1323
- string;
1324
- {
1325
- const details = [];
1326
- if (action.mandatory !== undefined) {
1327
- details.push(`Mandatory: ${action.mandatory}`);
1328
- }
1329
- if (action.visible !== undefined) {
1330
- details.push(`Visible: ${action.visible}`);
1331
1307
  }
1332
- if (action.readonly !== undefined) {
1333
- details.push(`Read-only: ${action.readonly}`);
1334
- }
1335
- if (action.value !== undefined && action.value !== '') {
1336
- details.push(`Value: "${action.value}"`);
1337
- }
1338
- return details.length > 0 ? details.join(', ') : 'No specific action';
1339
- }
1340
- async;
1341
- createCatalogClientScript(args, any);
1342
- {
1343
- try {
1344
- this.logger.info('Creating catalog client script...');
1345
- const scriptData = {
1346
- cat_item: args.cat_item,
1347
- name: args.name,
1348
- script: args.script,
1349
- type: args.type,
1350
- applies_to: args.applies_to || 'item',
1351
- cat_variable: args.variable || '',
1352
- active: args.active !== false
1353
- };
1354
- const response = await this.client.createRecord('catalog_script_client', scriptData);
1355
- if (!response.success) {
1356
- throw new Error(`Failed to create catalog client script: ${response.error}`);
1308
+ /**
1309
+ * Helper function to format action details for display
1310
+ */
1311
+ formatActionDetails(action) {
1312
+ const details = [];
1313
+ if (action.mandatory !== undefined) {
1314
+ details.push(`Mandatory: ${action.mandatory}`);
1315
+ }
1316
+ if (action.visible !== undefined) {
1317
+ details.push(`Visible: ${action.visible}`);
1318
+ }
1319
+ if (action.readonly !== undefined) {
1320
+ details.push(`Read-only: ${action.readonly}`);
1357
1321
  }
1358
- return {
1359
- content: [{
1360
- type: 'text',
1361
- text: `āœ… Catalog Client Script created successfully!
1322
+ if (action.value !== undefined && action.value !== '') {
1323
+ details.push(`Value: "${action.value}"`);
1324
+ }
1325
+ return details.length > 0 ? details.join(', ') : 'No specific action';
1326
+ }
1327
+ /**
1328
+ * Create Catalog Client Script
1329
+ * Uses catalog_script_client table
1330
+ */
1331
+ async createCatalogClientScript(args) {
1332
+ try {
1333
+ this.logger.info('Creating catalog client script...');
1334
+ const scriptData = {
1335
+ cat_item: args.cat_item,
1336
+ name: args.name,
1337
+ script: args.script,
1338
+ type: args.type,
1339
+ applies_to: args.applies_to || 'item',
1340
+ cat_variable: args.variable || '',
1341
+ active: args.active !== false
1342
+ };
1343
+ const response = await this.client.createRecord('catalog_script_client', scriptData);
1344
+ if (!response.success) {
1345
+ throw new Error(`Failed to create catalog client script: ${response.error}`);
1346
+ }
1347
+ return {
1348
+ content: [{
1349
+ type: 'text',
1350
+ text: `āœ… Catalog Client Script created successfully!
1362
1351
 
1363
1352
  šŸ“œ **${args.name}**
1364
1353
  šŸ†” sys_id: ${response.data.sys_id}
@@ -1368,115 +1357,118 @@ ${args.variable ? `šŸ“ Variable: ${args.variable}` : ''}
1368
1357
  šŸ”„ Active: ${args.active !== false ? 'Yes' : 'No'}
1369
1358
 
1370
1359
  ✨ Client script added to catalog item!`
1371
- }]
1372
- };
1373
- }
1374
- catch (error) {
1375
- this.logger.error('Failed to create catalog client script:', error);
1376
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog client script: ${error}`);
1377
- }
1378
- }
1379
- async;
1380
- searchCatalog(args, any);
1381
- {
1382
- try {
1383
- this.logger.info('Searching service catalog...');
1384
- let query = args.query ? `nameLIKE${args.query}^ORshort_descriptionLIKE${args.query}` : '';
1385
- if (args.category) {
1386
- query += query ? '^' : '';
1387
- query += `category=${args.category}`;
1388
- }
1389
- if (args.catalog) {
1390
- query += query ? '^' : '';
1391
- query += `sc_catalogs=${args.catalog}`;
1392
- }
1393
- if (args.active_only) {
1394
- query += query ? '^' : '';
1395
- query += 'active=true';
1396
- }
1397
- const limit = args.limit || 20;
1398
- this.logger.trackAPICall('SEARCH', 'sc_cat_item', limit);
1399
- const response = await this.client.searchRecords('sc_cat_item', query, limit);
1400
- if (!response.success) {
1401
- throw new Error('Failed to search catalog');
1402
- }
1403
- const items = response.data.result;
1404
- if (!items.length) {
1405
- return {
1406
- content: [{
1407
- type: 'text',
1408
- text: `āŒ No catalog items found${args.query ? ` matching "${args.query}"` : ''}`
1409
1360
  }]
1410
1361
  };
1411
1362
  }
1412
- const itemList = items.map((item) => {
1413
- return `šŸ›ļø **${item.name}**
1363
+ catch (error) {
1364
+ this.logger.error('Failed to create catalog client script:', error);
1365
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to create catalog client script: ${error}`);
1366
+ }
1367
+ }
1368
+ /**
1369
+ * Search Service Catalog
1370
+ */
1371
+ async searchCatalog(args) {
1372
+ try {
1373
+ this.logger.info('Searching service catalog...');
1374
+ let query = args.query ? `nameLIKE${args.query}^ORshort_descriptionLIKE${args.query}` : '';
1375
+ if (args.category) {
1376
+ query += query ? '^' : '';
1377
+ query += `category=${args.category}`;
1378
+ }
1379
+ if (args.catalog) {
1380
+ query += query ? '^' : '';
1381
+ query += `sc_catalogs=${args.catalog}`;
1382
+ }
1383
+ if (args.active_only) {
1384
+ query += query ? '^' : '';
1385
+ query += 'active=true';
1386
+ }
1387
+ const limit = args.limit || 20;
1388
+ this.logger.trackAPICall('SEARCH', 'sc_cat_item', limit);
1389
+ const response = await this.client.searchRecords('sc_cat_item', query, limit);
1390
+ if (!response.success) {
1391
+ throw new Error('Failed to search catalog');
1392
+ }
1393
+ const items = response.data.result;
1394
+ if (!items.length) {
1395
+ return {
1396
+ content: [{
1397
+ type: 'text',
1398
+ text: `āŒ No catalog items found${args.query ? ` matching "${args.query}"` : ''}`
1399
+ }]
1400
+ };
1401
+ }
1402
+ const itemList = items.map((item) => {
1403
+ return `šŸ›ļø **${item.name}**
1414
1404
  šŸ†” ${item.sys_id}
1415
1405
  šŸ“ ${item.short_description}
1416
1406
  ${item.price && item.price !== '0' ? `šŸ’° Price: $${item.price}` : ''}
1417
1407
  šŸ”„ Active: ${item.active ? 'Yes' : 'No'}`;
1418
- }).join('\n\n');
1419
- return {
1420
- content: [{
1421
- type: 'text',
1422
- text: `šŸ” Catalog Search Results${args.query ? ` for "${args.query}"` : ''}:
1408
+ }).join('\n\n');
1409
+ return {
1410
+ content: [{
1411
+ type: 'text',
1412
+ text: `šŸ” Catalog Search Results${args.query ? ` for "${args.query}"` : ''}:
1423
1413
 
1424
1414
  ${itemList}
1425
1415
 
1426
1416
  ✨ Found ${items.length} catalog item(s)`
1427
- }]
1428
- };
1429
- }
1430
- catch (error) {
1431
- this.logger.error('Failed to search catalog:', error);
1432
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search catalog: ${error}`);
1433
- }
1434
- }
1435
- async;
1436
- orderCatalogItem(args, any);
1437
- {
1438
- try {
1439
- this.logger.info('Ordering catalog item...');
1440
- // Create service catalog request
1441
- const requestData = {
1442
- requested_for: args.requested_for,
1443
- opened_by: args.requested_for,
1444
- special_instructions: args.special_instructions || ''
1445
- };
1446
- const requestResponse = await this.client.createRecord('sc_request', requestData);
1447
- if (!requestResponse.success) {
1448
- throw new Error(`Failed to create request: ${requestResponse.error}`);
1417
+ }]
1418
+ };
1449
1419
  }
1450
- const requestId = requestResponse.data.sys_id;
1451
- // Create requested item (RITM)
1452
- const ritmData = {
1453
- request: requestId,
1454
- cat_item: args.cat_item,
1455
- requested_for: args.requested_for,
1456
- quantity: args.quantity || 1,
1457
- delivery_address: args.delivery_address || ''
1458
- };
1459
- const ritmResponse = await this.client.createRecord('sc_req_item', ritmData);
1460
- if (!ritmResponse.success) {
1461
- throw new Error(`Failed to create requested item: ${ritmResponse.error}`);
1420
+ catch (error) {
1421
+ this.logger.error('Failed to search catalog:', error);
1422
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to search catalog: ${error}`);
1462
1423
  }
1463
- const ritmId = ritmResponse.data.sys_id;
1464
- const ritmNumber = ritmResponse.data.number;
1465
- // Set variable values if provided
1466
- if (args.variables) {
1467
- for (const [varName, varValue] of Object.entries(args.variables)) {
1468
- const varData = {
1469
- request_item: ritmId,
1470
- name: varName,
1471
- value: varValue
1472
- };
1473
- await this.client.createRecord('sc_item_option_mtom', varData);
1424
+ }
1425
+ /**
1426
+ * Order Catalog Item
1427
+ * Creates sc_request and sc_req_item records
1428
+ */
1429
+ async orderCatalogItem(args) {
1430
+ try {
1431
+ this.logger.info('Ordering catalog item...');
1432
+ // Create service catalog request
1433
+ const requestData = {
1434
+ requested_for: args.requested_for,
1435
+ opened_by: args.requested_for,
1436
+ special_instructions: args.special_instructions || ''
1437
+ };
1438
+ const requestResponse = await this.client.createRecord('sc_request', requestData);
1439
+ if (!requestResponse.success) {
1440
+ throw new Error(`Failed to create request: ${requestResponse.error}`);
1474
1441
  }
1475
- }
1476
- return {
1477
- content: [{
1478
- type: 'text',
1479
- text: `āœ… Catalog Item ordered successfully!
1442
+ const requestId = requestResponse.data.sys_id;
1443
+ // Create requested item (RITM)
1444
+ const ritmData = {
1445
+ request: requestId,
1446
+ cat_item: args.cat_item,
1447
+ requested_for: args.requested_for,
1448
+ quantity: args.quantity || 1,
1449
+ delivery_address: args.delivery_address || ''
1450
+ };
1451
+ const ritmResponse = await this.client.createRecord('sc_req_item', ritmData);
1452
+ if (!ritmResponse.success) {
1453
+ throw new Error(`Failed to create requested item: ${ritmResponse.error}`);
1454
+ }
1455
+ const ritmId = ritmResponse.data.sys_id;
1456
+ const ritmNumber = ritmResponse.data.number;
1457
+ // Set variable values if provided
1458
+ if (args.variables) {
1459
+ for (const [varName, varValue] of Object.entries(args.variables)) {
1460
+ const varData = {
1461
+ request_item: ritmId,
1462
+ name: varName,
1463
+ value: varValue
1464
+ };
1465
+ await this.client.createRecord('sc_item_option_mtom', varData);
1466
+ }
1467
+ }
1468
+ return {
1469
+ content: [{
1470
+ type: 'text',
1471
+ text: `āœ… Catalog Item ordered successfully!
1480
1472
 
1481
1473
  šŸ›ļø **Order Placed**
1482
1474
  šŸ†” Request: ${requestId}
@@ -1487,51 +1479,52 @@ ${args.delivery_address ? `šŸ“ Delivery: ${args.delivery_address}` : ''}
1487
1479
  ${args.special_instructions ? `šŸ“ Instructions: ${args.special_instructions}` : ''}
1488
1480
 
1489
1481
  ✨ Order submitted for fulfillment!`
1490
- }]
1491
- };
1492
- }
1493
- catch (error) {
1494
- this.logger.error('Failed to order catalog item:', error);
1495
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to order catalog item: ${error}`);
1482
+ }]
1483
+ };
1484
+ }
1485
+ catch (error) {
1486
+ this.logger.error('Failed to order catalog item:', error);
1487
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to order catalog item: ${error}`);
1488
+ }
1496
1489
  }
1497
- }
1498
- async;
1499
- getCatalogItemDetails(args, any);
1500
- {
1501
- try {
1502
- this.logger.info('Getting catalog item details...', { sys_id: args.sys_id });
1503
- // šŸ” DEBUGGING: Try different methods to find the item
1504
- this.logger.info('šŸ” Step 1: Trying getRecord method...');
1505
- let itemResponse = await this.client.getRecord('sc_cat_item', args.sys_id);
1506
- if (!itemResponse.success) {
1507
- this.logger.warn('āŒ getRecord failed, trying searchRecords as fallback...');
1508
- this.logger.warn('getRecord error:', itemResponse.error);
1509
- // Fallback: try searchRecords method
1510
- itemResponse = await this.client.searchRecords('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1511
- if (!itemResponse.success || itemResponse.data.result.length === 0) {
1512
- this.logger.error('āŒ Both getRecord and searchRecords failed');
1513
- this.logger.error('searchRecords error:', itemResponse.error);
1514
- // Final fallback: try to query with different parameters
1515
- const queryResponse = await this.client.queryTable('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1516
- if (queryResponse.success && queryResponse.data.result.length > 0) {
1517
- this.logger.info('āœ… Found item using queryTable fallback');
1518
- itemResponse = { success: true, data: queryResponse.data.result[0] };
1490
+ /**
1491
+ * Get Catalog Item Details
1492
+ */
1493
+ async getCatalogItemDetails(args) {
1494
+ try {
1495
+ this.logger.info('Getting catalog item details...', { sys_id: args.sys_id });
1496
+ // šŸ” DEBUGGING: Try different methods to find the item
1497
+ this.logger.info('šŸ” Step 1: Trying getRecord method...');
1498
+ let itemResponse = await this.client.getRecord('sc_cat_item', args.sys_id);
1499
+ if (!itemResponse.success) {
1500
+ this.logger.warn('āŒ getRecord failed, trying searchRecords as fallback...');
1501
+ this.logger.warn('getRecord error:', itemResponse.error);
1502
+ // Fallback: try searchRecords method
1503
+ itemResponse = await this.client.searchRecords('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1504
+ if (!itemResponse.success || itemResponse.data.result.length === 0) {
1505
+ this.logger.error('āŒ Both getRecord and searchRecords failed');
1506
+ this.logger.error('searchRecords error:', itemResponse.error);
1507
+ // Final fallback: try to query with different parameters
1508
+ const queryResponse = await this.client.searchRecords('sc_cat_item', `sys_id=${args.sys_id}`, 1);
1509
+ if (queryResponse.success && queryResponse.data.result.length > 0) {
1510
+ this.logger.info('āœ… Found item using queryTable fallback');
1511
+ itemResponse = { success: true, data: queryResponse.data.result[0] };
1512
+ }
1513
+ else {
1514
+ this.logger.error('āŒ All methods failed to find catalog item');
1515
+ throw new Error(`Catalog item not found with sys_id: ${args.sys_id}. Tried getRecord, searchRecords, and queryTable.`);
1516
+ }
1519
1517
  }
1520
1518
  else {
1521
- this.logger.error('āŒ All methods failed to find catalog item');
1522
- throw new Error(`Catalog item not found with sys_id: ${args.sys_id}. Tried getRecord, searchRecords, and queryTable.`);
1519
+ this.logger.info('āœ… Found item using searchRecords fallback');
1520
+ itemResponse = { success: true, data: itemResponse.data.result[0] };
1523
1521
  }
1524
1522
  }
1525
1523
  else {
1526
- this.logger.info('āœ… Found item using searchRecords fallback');
1527
- itemResponse = { success: true, data: itemResponse.data.result[0] };
1524
+ this.logger.info('āœ… Found item using getRecord');
1528
1525
  }
1529
- }
1530
- else {
1531
- this.logger.info('āœ… Found item using getRecord');
1532
- }
1533
- const item = itemResponse.data;
1534
- let details = `šŸ›ļø **${item.name}**
1526
+ const item = itemResponse.data;
1527
+ let details = `šŸ›ļø **${item.name}**
1535
1528
  šŸ†” sys_id: ${item.sys_id}
1536
1529
  šŸ“ ${item.short_description}
1537
1530
  šŸ“„ ${item.description || 'No detailed description'}
@@ -1539,82 +1532,82 @@ ${item.price && item.price !== '0' ? `šŸ’° Price: $${item.price}` : ''}
1539
1532
  ${item.recurring_price && item.recurring_price !== '0' ? `šŸ”„ Recurring: $${item.recurring_price}` : ''}
1540
1533
  šŸ“¦ Delivery: ${item.delivery_time || '3 business days'}
1541
1534
  šŸ”„ Active: ${item.active ? 'Yes' : 'No'}`;
1542
- // Get variables if requested
1543
- if (args.include_variables) {
1544
- const varResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${args.sys_id}`, 50);
1545
- if (varResponse.success && varResponse.data.result.length) {
1546
- const variables = varResponse.data.result.map((v) => ` - ${v.question_text} (${v.type})${v.mandatory ? ' *Required' : ''}`).join('\n');
1547
- details += `\n\nšŸ“‹ **Variables:**\n${variables}`;
1535
+ // Get variables if requested
1536
+ if (args.include_variables) {
1537
+ const varResponse = await this.client.searchRecords('sc_cat_item_option', `cat_item=${args.sys_id}`, 50);
1538
+ if (varResponse.success && varResponse.data.result.length) {
1539
+ const variables = varResponse.data.result.map((v) => ` - ${v.question_text} (${v.type})${v.mandatory ? ' *Required' : ''}`).join('\n');
1540
+ details += `\n\nšŸ“‹ **Variables:**\n${variables}`;
1541
+ }
1548
1542
  }
1543
+ return {
1544
+ content: [{
1545
+ type: 'text',
1546
+ text: details + '\n\n✨ Catalog item details retrieved!'
1547
+ }]
1548
+ };
1549
1549
  }
1550
- return {
1551
- content: [{
1552
- type: 'text',
1553
- text: details + '\n\n✨ Catalog item details retrieved!'
1554
- }]
1555
- };
1556
- }
1557
- catch (error) {
1558
- this.logger.error('Failed to get catalog item details:', error);
1559
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get catalog item details: ${error}`);
1560
- }
1561
- }
1562
- async;
1563
- discoverCatalogs(args, any);
1564
- {
1565
- try {
1566
- this.logger.info('Discovering service catalogs...');
1567
- let query = '';
1568
- if (args.active_only) {
1569
- query = 'active=true';
1570
- }
1571
- const catalogResponse = await this.client.searchRecords('sc_catalog', query, 50);
1572
- if (!catalogResponse.success) {
1573
- throw new Error('Failed to discover catalogs');
1550
+ catch (error) {
1551
+ this.logger.error('Failed to get catalog item details:', error);
1552
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to get catalog item details: ${error}`);
1574
1553
  }
1575
- const catalogs = catalogResponse.data.result;
1576
- // Get categories if requested
1577
- const catalogsWithDetails = await Promise.all(catalogs.map(async (catalog) => {
1578
- if (args.include_categories) {
1579
- const catResponse = await this.client.searchRecords('sc_category', `sc_catalog=${catalog.sys_id}`, 20);
1580
- const categories = catResponse.success ? catResponse.data.result : [];
1581
- return { ...catalog, categories };
1554
+ }
1555
+ /**
1556
+ * Discover Service Catalogs
1557
+ */
1558
+ async discoverCatalogs(args) {
1559
+ try {
1560
+ this.logger.info('Discovering service catalogs...');
1561
+ let query = '';
1562
+ if (args.active_only) {
1563
+ query = 'active=true';
1582
1564
  }
1583
- return catalog;
1584
- }));
1585
- const catalogText = catalogsWithDetails.map((catalog) => {
1586
- let text = `šŸ›ļø **${catalog.title}** ${catalog.active ? 'āœ…' : 'āŒ'}
1565
+ const catalogResponse = await this.client.searchRecords('sc_catalog', query, 50);
1566
+ if (!catalogResponse.success) {
1567
+ throw new Error('Failed to discover catalogs');
1568
+ }
1569
+ const catalogs = catalogResponse.data.result;
1570
+ // Get categories if requested
1571
+ const catalogsWithDetails = await Promise.all(catalogs.map(async (catalog) => {
1572
+ if (args.include_categories) {
1573
+ const catResponse = await this.client.searchRecords('sc_category', `sc_catalog=${catalog.sys_id}`, 20);
1574
+ const categories = catResponse.success ? catResponse.data.result : [];
1575
+ return { ...catalog, categories };
1576
+ }
1577
+ return catalog;
1578
+ }));
1579
+ const catalogText = catalogsWithDetails.map((catalog) => {
1580
+ let text = `šŸ›ļø **${catalog.title}** ${catalog.active ? 'āœ…' : 'āŒ'}
1587
1581
  šŸ†” ${catalog.sys_id}
1588
1582
  šŸ“ ${catalog.description || 'No description'}`;
1589
- if (catalog.categories) {
1590
- const categoryList = catalog.categories.map((cat) => ` - ${cat.title}`).join('\n');
1591
- text += `\nšŸ“‚ Categories:\n${categoryList || ' No categories'}`;
1592
- }
1593
- return text;
1594
- }).join('\n\n');
1595
- return {
1596
- content: [{
1597
- type: 'text',
1598
- text: `šŸ” Discovered Service Catalogs:
1583
+ if (catalog.categories) {
1584
+ const categoryList = catalog.categories.map((cat) => ` - ${cat.title}`).join('\n');
1585
+ text += `\nšŸ“‚ Categories:\n${categoryList || ' No categories'}`;
1586
+ }
1587
+ return text;
1588
+ }).join('\n\n');
1589
+ return {
1590
+ content: [{
1591
+ type: 'text',
1592
+ text: `šŸ” Discovered Service Catalogs:
1599
1593
 
1600
1594
  ${catalogText}
1601
1595
 
1602
1596
  ✨ Found ${catalogs.length} catalog(s)`
1603
- }]
1604
- };
1597
+ }]
1598
+ };
1599
+ }
1600
+ catch (error) {
1601
+ this.logger.error('Failed to discover catalogs:', error);
1602
+ throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover catalogs: ${error}`);
1603
+ }
1605
1604
  }
1606
- catch (error) {
1607
- this.logger.error('Failed to discover catalogs:', error);
1608
- throw new types_js_1.McpError(types_js_1.ErrorCode.InternalError, `Failed to discover catalogs: ${error}`);
1605
+ async run() {
1606
+ const transport = new stdio_js_1.StdioServerTransport();
1607
+ await this.server.connect(transport);
1608
+ this.logger.info('ServiceNow Knowledge & Catalog MCP Server running on stdio');
1609
1609
  }
1610
1610
  }
1611
- async;
1612
- run();
1613
- {
1614
- const transport = new stdio_js_1.StdioServerTransport();
1615
- await this.server.connect(transport);
1616
- this.logger.info('ServiceNow Knowledge & Catalog MCP Server running on stdio');
1617
- }
1618
1611
  const server = new ServiceNowKnowledgeCatalogMCP();
1619
1612
  server.run().catch(console.error);
1620
1613
  //# sourceMappingURL=servicenow-knowledge-catalog-mcp.js.map