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 +20 -11
- package/dist/mcp/servicenow-knowledge-catalog-mcp.js +418 -428
- package/package.json +1 -1
- package/website/index.html +20 -17
package/dist/cli.js
CHANGED
|
@@ -754,28 +754,36 @@ Memory.store("swarm_session_${sessionId}", JSON.stringify({
|
|
|
754
754
|
}));
|
|
755
755
|
\`\`\`
|
|
756
756
|
|
|
757
|
-
### 2.
|
|
758
|
-
**
|
|
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
|
|
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
|
-
|
|
901
|
+
šØ **MANDATORY PRE-FLIGHT CHECKS** (ALWAYS do first!):
|
|
894
902
|
1. \`snow_auth_diagnostics\` - Test authentication and permissions
|
|
895
|
-
2.
|
|
896
|
-
3.
|
|
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.
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
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
|
-
|
|
863
|
-
|
|
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
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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
|
-
|
|
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(`ā
|
|
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
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
const
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
935
|
+
catch (error) {
|
|
936
|
+
this.logger.warn(`ā ļø Alternate table ${table} failed:`, error);
|
|
937
|
+
}
|
|
1016
938
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
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
|
-
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
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
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
-
|
|
1116
|
-
|
|
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
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
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
|
-
|
|
1221
|
+
this.logger.info(`ā
ui_policy reference matches expected value`);
|
|
1124
1222
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
-
|
|
1130
|
-
|
|
1229
|
+
if (!catalogVariableValue.startsWith('IO:')) {
|
|
1230
|
+
this.logger.warn(`ā ļø catalog_variable missing IO: prefix - Got: ${catalogVariableValue}`);
|
|
1131
1231
|
}
|
|
1132
|
-
//
|
|
1133
|
-
if (
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
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
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
this.logger.
|
|
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
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
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
|
-
|
|
1288
|
-
|
|
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
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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",
|
package/website/index.html
CHANGED
|
@@ -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);">
|
|
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);">
|
|
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);">
|
|
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);">
|
|
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
|
-
<!--
|
|
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
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
<
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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
|
|