snow-flow 1.3.0 → 1.3.2
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/.claude-flow/queen/queen-memory.db +0 -0
- package/.env.example +249 -14
- package/CLAUDE.md +185 -24
- package/README.md +55 -5
- package/dist/api/error-handling.js +898 -4
- package/dist/api/performance-optimizer.js +3 -2
- package/dist/cli.js +590 -200
- package/dist/config/snow-flow-config.js +320 -8
- package/dist/health/system-health.js +288 -21
- package/dist/mcp/base-mcp-server.js +2 -0
- package/dist/mcp/http-transport-wrapper.js +65 -4
- package/dist/mcp/servicenow-automation-mcp-refactored.js +90 -9
- package/dist/mcp/servicenow-deployment-mcp-refactored.js +151 -2
- package/dist/mcp/servicenow-deployment-mcp.js +828 -15
- package/dist/mcp/servicenow-integration-mcp-refactored.js +100 -9
- package/dist/mcp/servicenow-intelligent-mcp.js +198 -7
- package/dist/mcp/servicenow-memory-mcp.js +2 -1
- package/dist/mcp/servicenow-operations-mcp-refactored.js +3 -2
- package/dist/mcp/servicenow-xml-flow-mcp.js +671 -0
- package/dist/mcp/shared/base-mcp-server.js +1356 -8
- package/dist/mcp/shared/mcp-resource-manager.js +304 -0
- package/dist/queen/parallel-agent-engine.js +26 -8
- package/dist/queen/servicenow-queen.js +47 -44
- package/dist/sparc/sparc-help.js +37 -26
- package/dist/sparc/team-sparc.js +60 -16
- package/dist/utils/action-type-cache.js +2 -1
- package/dist/utils/mcp-config-manager.js +7 -3
- package/dist/utils/mcp-server-manager.js +7 -3
- package/dist/utils/servicenow-client.js +134 -107
- package/dist/utils/servicenow-id-generator.js +171 -0
- package/dist/utils/snow-oauth.js +13 -8
- package/dist/utils/widget-template-generator.js +1690 -0
- package/dist/utils/xml-first-flow-generator.js +473 -0
- package/dist/version.js +7 -1
- package/flow-update-sets/flow_build_automated_approval_flow_with_notifications_flow.xml +203 -0
- package/flow-update-sets/flow_create_approval_flow_for_equipment_requests_flow.xml +206 -0
- package/flow-update-sets/flow_create_equipment_approval_flow_with_manager_approv_flow.xml +254 -0
- package/flow-update-sets/iphone_15_pro_approval_flow.xml +203 -0
- package/flow-update-sets/test_iphone_approval_flow_flow.xml +303 -0
- package/package.json +2 -1
- package/test-config.js +55 -0
- package/test-real-monitoring.js +184 -0
|
@@ -44,6 +44,7 @@ const snow_oauth_js_1 = require("../../utils/snow-oauth.js");
|
|
|
44
44
|
const logger_js_1 = require("../../utils/logger.js");
|
|
45
45
|
const agent_context_provider_js_1 = require("./agent-context-provider.js");
|
|
46
46
|
const mcp_memory_manager_js_1 = require("./mcp-memory-manager.js");
|
|
47
|
+
const mcp_resource_manager_js_1 = require("./mcp-resource-manager.js");
|
|
47
48
|
class BaseMCPServer {
|
|
48
49
|
constructor(name, version = '1.0.0') {
|
|
49
50
|
this.server = new index_js_1.Server({
|
|
@@ -52,6 +53,7 @@ class BaseMCPServer {
|
|
|
52
53
|
}, {
|
|
53
54
|
capabilities: {
|
|
54
55
|
tools: {},
|
|
56
|
+
resources: {},
|
|
55
57
|
},
|
|
56
58
|
});
|
|
57
59
|
this.client = new servicenow_client_js_1.ServiceNowClient();
|
|
@@ -59,6 +61,7 @@ class BaseMCPServer {
|
|
|
59
61
|
this.logger = new logger_js_1.Logger(name);
|
|
60
62
|
this.contextProvider = new agent_context_provider_js_1.AgentContextProvider();
|
|
61
63
|
this.memory = mcp_memory_manager_js_1.MCPMemoryManager.getInstance();
|
|
64
|
+
this.resourceManager = new mcp_resource_manager_js_1.MCPResourceManager(name);
|
|
62
65
|
}
|
|
63
66
|
/**
|
|
64
67
|
* Execute a tool with agent context tracking
|
|
@@ -202,7 +205,7 @@ ${errorText}
|
|
|
202
205
|
|
|
203
206
|
💡 To get OAuth credentials:
|
|
204
207
|
• ServiceNow: System OAuth > Application Registry > New OAuth Application
|
|
205
|
-
• Redirect URI: http://localhost:3005/callback
|
|
208
|
+
• Redirect URI: http://localhost:3005/callback (configurable via SNOW_REDIRECT_* env vars)
|
|
206
209
|
• Scopes: useraccount write admin`
|
|
207
210
|
}
|
|
208
211
|
]
|
|
@@ -689,15 +692,1306 @@ ${errorText}
|
|
|
689
692
|
return options;
|
|
690
693
|
}
|
|
691
694
|
async escalateToGlobalScope(operation, context) {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
695
|
+
this.logger.info(`🔝 Attempting permission escalation for ${operation}`);
|
|
696
|
+
try {
|
|
697
|
+
// Enhanced authentication validation
|
|
698
|
+
const authValidation = await this.validateServiceNowConnection();
|
|
699
|
+
if (!authValidation.success) {
|
|
700
|
+
return {
|
|
701
|
+
success: false,
|
|
702
|
+
reason: 'Authentication validation failed before escalation',
|
|
703
|
+
recovery_action: 'Run: snow-flow auth login',
|
|
704
|
+
auth_error: authValidation.error
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
// Get current user information with enhanced OAuth scope handling
|
|
708
|
+
this.logger.info('🔍 Analyzing current user permissions and OAuth scopes...');
|
|
709
|
+
let currentUser = null;
|
|
710
|
+
let currentRoles = [];
|
|
711
|
+
let oAuthScopes = [];
|
|
712
|
+
try {
|
|
713
|
+
// Get current user details
|
|
714
|
+
const userResponse = await this.client.makeRequest({
|
|
715
|
+
method: 'GET',
|
|
716
|
+
endpoint: '/api/now/v2/table/sys_user',
|
|
717
|
+
params: {
|
|
718
|
+
sysparm_query: 'active=true^user_name=javascript:gs.getUserName()',
|
|
719
|
+
sysparm_fields: 'sys_id,user_name,name,email,active,locked_out,roles',
|
|
720
|
+
sysparm_limit: 1
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
if (userResponse.data?.result?.[0]) {
|
|
724
|
+
currentUser = userResponse.data.result[0];
|
|
725
|
+
this.logger.info(`👤 Current user: ${currentUser.name} (${currentUser.user_name})`);
|
|
726
|
+
}
|
|
727
|
+
// Get detailed role information
|
|
728
|
+
const rolesResponse = await this.client.makeRequest({
|
|
729
|
+
method: 'GET',
|
|
730
|
+
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
731
|
+
params: {
|
|
732
|
+
sysparm_query: `user.user_name=${currentUser?.user_name || 'javascript:gs.getUserName()'}`,
|
|
733
|
+
sysparm_fields: 'role.name,role.description,state,inherited',
|
|
734
|
+
sysparm_limit: 100
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
if (rolesResponse.data?.result) {
|
|
738
|
+
currentRoles = rolesResponse.data.result
|
|
739
|
+
.filter((r) => r.state === 'active')
|
|
740
|
+
.map((r) => r['role.name'])
|
|
741
|
+
.filter(Boolean);
|
|
742
|
+
this.logger.info(`🛡️ Active roles: ${currentRoles.join(', ')}`);
|
|
743
|
+
}
|
|
744
|
+
// Check OAuth token scopes if available
|
|
745
|
+
try {
|
|
746
|
+
// Attempt to get token info if method exists
|
|
747
|
+
if (typeof this.oauth.getTokenInfo === 'function') {
|
|
748
|
+
const tokenInfo = await this.oauth.getTokenInfo();
|
|
749
|
+
if (tokenInfo?.scope) {
|
|
750
|
+
oAuthScopes = tokenInfo.scope.split(' ');
|
|
751
|
+
this.logger.info(`🔑 OAuth scopes: ${oAuthScopes.join(', ')}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
this.logger.debug('OAuth getTokenInfo method not available');
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
catch (scopeError) {
|
|
759
|
+
this.logger.debug('Could not retrieve OAuth scope information', { error: scopeError });
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
catch (roleError) {
|
|
763
|
+
this.logger.warn('Could not fetch complete user information', { error: roleError });
|
|
764
|
+
// Continue with limited information
|
|
765
|
+
}
|
|
766
|
+
// Enhanced role analysis based on operation type
|
|
767
|
+
const requiredRoles = this.determineRequiredRoles(operation);
|
|
768
|
+
const requiredScopes = this.determineRequiredOAuthScopes(operation);
|
|
769
|
+
const missingRoles = requiredRoles.filter(role => !currentRoles.includes(role));
|
|
770
|
+
const missingScopes = requiredScopes.filter(scope => !oAuthScopes.includes(scope));
|
|
771
|
+
this.logger.info(`📊 Permission Analysis:`, {
|
|
772
|
+
required_roles: requiredRoles,
|
|
773
|
+
current_roles: currentRoles,
|
|
774
|
+
missing_roles: missingRoles,
|
|
775
|
+
required_scopes: requiredScopes,
|
|
776
|
+
current_scopes: oAuthScopes,
|
|
777
|
+
missing_scopes: missingScopes
|
|
778
|
+
});
|
|
779
|
+
// Check if user has sufficient permissions
|
|
780
|
+
if (missingRoles.length === 0 && missingScopes.length === 0) {
|
|
781
|
+
// User has all required permissions, issue might be elsewhere
|
|
782
|
+
return {
|
|
783
|
+
success: true,
|
|
784
|
+
reason: 'User has all required roles and scopes - permission issue may be configuration related',
|
|
785
|
+
current_roles: currentRoles,
|
|
786
|
+
current_scopes: oAuthScopes,
|
|
787
|
+
diagnostic_actions: [
|
|
788
|
+
'Check ACL rules for the specific table/operation',
|
|
789
|
+
'Verify application scope permissions',
|
|
790
|
+
'Check domain separation settings',
|
|
791
|
+
'Review field-level security',
|
|
792
|
+
'Validate write permissions for target table'
|
|
793
|
+
]
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
// Enhanced automatic role assignment with better error handling
|
|
797
|
+
let autoAssignmentAttempted = false;
|
|
798
|
+
const canAutoAssign = currentRoles.includes('admin') ||
|
|
799
|
+
currentRoles.includes('user_admin') ||
|
|
800
|
+
currentRoles.includes('system_administrator');
|
|
801
|
+
if (canAutoAssign && missingRoles.length > 0) {
|
|
802
|
+
this.logger.info('🤖 Attempting automatic role assignment...');
|
|
803
|
+
try {
|
|
804
|
+
const assignmentResult = await this.attemptEnhancedRoleAssignment(currentUser?.sys_id, missingRoles, context);
|
|
805
|
+
if (assignmentResult.success) {
|
|
806
|
+
autoAssignmentAttempted = true;
|
|
807
|
+
// Update context with successful assignment
|
|
808
|
+
await this.memory.updateSharedContext({
|
|
809
|
+
session_id: context.session_id,
|
|
810
|
+
context_key: `permission_escalation_${operation}`,
|
|
811
|
+
context_value: JSON.stringify({
|
|
812
|
+
escalation_type: 'automatic_role_assignment',
|
|
813
|
+
assigned_roles: assignmentResult.assigned_roles,
|
|
814
|
+
timestamp: new Date().toISOString(),
|
|
815
|
+
operation: operation
|
|
816
|
+
}),
|
|
817
|
+
created_by_agent: context.agent_id
|
|
818
|
+
});
|
|
819
|
+
return {
|
|
820
|
+
success: true,
|
|
821
|
+
reason: 'Roles automatically assigned successfully',
|
|
822
|
+
assigned_roles: assignmentResult.assigned_roles,
|
|
823
|
+
assignment_details: assignmentResult.details,
|
|
824
|
+
recommended_action: 'Retry the original operation - new permissions are active'
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
catch (assignError) {
|
|
829
|
+
this.logger.warn('Automatic role assignment failed', { error: assignError });
|
|
830
|
+
autoAssignmentAttempted = true; // We tried
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
// Enhanced OAuth scope elevation attempt
|
|
834
|
+
if (missingScopes.length > 0) {
|
|
835
|
+
this.logger.info('🔑 Attempting OAuth scope elevation...');
|
|
836
|
+
try {
|
|
837
|
+
const scopeElevationResult = await this.attemptOAuthScopeElevation(missingScopes, operation, context);
|
|
838
|
+
if (scopeElevationResult.success) {
|
|
839
|
+
return {
|
|
840
|
+
success: true,
|
|
841
|
+
reason: 'OAuth scopes elevated successfully',
|
|
842
|
+
elevated_scopes: scopeElevationResult.elevated_scopes,
|
|
843
|
+
recommended_action: 'Retry the original operation with elevated OAuth permissions'
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
catch (scopeError) {
|
|
848
|
+
this.logger.warn('OAuth scope elevation failed', { error: scopeError });
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
// Request Queen intervention for complex permission issues
|
|
852
|
+
await this.requestQueenIntervention(context, {
|
|
853
|
+
type: 'permission_escalation_required',
|
|
854
|
+
priority: 'high',
|
|
855
|
+
description: `Permission escalation needed for ${operation}. Missing roles: ${missingRoles.join(', ')}. Missing OAuth scopes: ${missingScopes.join(', ')}.`,
|
|
856
|
+
attempted_solutions: [
|
|
857
|
+
...(autoAssignmentAttempted ? ['automatic_role_assignment'] : []),
|
|
858
|
+
...(missingScopes.length > 0 ? ['oauth_scope_elevation'] : [])
|
|
859
|
+
]
|
|
860
|
+
});
|
|
861
|
+
// Comprehensive manual escalation instructions
|
|
862
|
+
const manualSteps = [
|
|
863
|
+
`1. ServiceNow Administrator Actions Required:`,
|
|
864
|
+
` • Navigate to User Administration > Users`,
|
|
865
|
+
` • Find user: ${currentUser?.name || 'Current User'} (${currentUser?.user_name || 'unknown'})`,
|
|
866
|
+
` • Add missing roles: ${missingRoles.join(', ')}`,
|
|
867
|
+
...missingScopes.length > 0 ? [
|
|
868
|
+
`2. OAuth Application Configuration:`,
|
|
869
|
+
` • Update OAuth application scopes to include: ${missingScopes.join(', ')}`,
|
|
870
|
+
` • Re-authorize application with elevated permissions`
|
|
871
|
+
] : [],
|
|
872
|
+
`${missingScopes.length > 0 ? '3' : '2'}. Verification Steps:`,
|
|
873
|
+
` • Test permissions: snow_validate_live_connection({ test_level: "permissions" })`,
|
|
874
|
+
` • Retry operation: ${operation}`
|
|
875
|
+
];
|
|
876
|
+
return {
|
|
877
|
+
success: false,
|
|
878
|
+
reason: 'Manual permission escalation required',
|
|
879
|
+
current_user: currentUser,
|
|
880
|
+
current_roles: currentRoles,
|
|
881
|
+
missing_roles: missingRoles,
|
|
882
|
+
current_oauth_scopes: oAuthScopes,
|
|
883
|
+
missing_oauth_scopes: missingScopes,
|
|
884
|
+
escalation_required: true,
|
|
885
|
+
auto_assignment_attempted: autoAssignmentAttempted,
|
|
886
|
+
manual_steps: manualSteps,
|
|
887
|
+
estimated_time: `${5 + (missingRoles.length * 2)}–${10 + (missingRoles.length * 3)} minutes with admin access`,
|
|
888
|
+
priority: missingRoles.includes('admin') || missingRoles.includes('system_administrator') ? 'critical' : 'high'
|
|
889
|
+
};
|
|
890
|
+
}
|
|
891
|
+
catch (error) {
|
|
892
|
+
this.logger.error('🚨 Permission escalation process failed', { error, operation, context });
|
|
893
|
+
return {
|
|
894
|
+
success: false,
|
|
895
|
+
reason: 'Critical error during permission escalation',
|
|
896
|
+
error: error instanceof Error ? error.message : String(error),
|
|
897
|
+
error_type: this.categorizeError(error instanceof Error ? error.message : String(error)),
|
|
898
|
+
recovery_actions: [
|
|
899
|
+
'1. Verify ServiceNow connection: snow_validate_live_connection()',
|
|
900
|
+
'2. Check authentication status: snow_auth_diagnostics()',
|
|
901
|
+
'3. Confirm API access is enabled in ServiceNow',
|
|
902
|
+
'4. Contact ServiceNow administrator for manual role assignment',
|
|
903
|
+
'5. Review system logs for additional error details'
|
|
904
|
+
],
|
|
905
|
+
escalation_required: true,
|
|
906
|
+
contact_admin: true
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Determine required roles based on operation type
|
|
912
|
+
*/
|
|
913
|
+
determineRequiredRoles(operation) {
|
|
914
|
+
const roles = [];
|
|
915
|
+
// Base roles for most operations
|
|
916
|
+
if (operation.includes('deploy') || operation.includes('create') || operation.includes('update')) {
|
|
917
|
+
roles.push('rest_service', 'web_service_admin');
|
|
918
|
+
}
|
|
919
|
+
// Widget operations
|
|
920
|
+
if (operation.includes('widget')) {
|
|
921
|
+
roles.push('sp_admin', 'sp_portal_admin');
|
|
922
|
+
}
|
|
923
|
+
// Flow operations
|
|
924
|
+
if (operation.includes('flow') || operation.includes('workflow')) {
|
|
925
|
+
roles.push('flow_designer', 'admin');
|
|
926
|
+
}
|
|
927
|
+
// Application operations
|
|
928
|
+
if (operation.includes('application') || operation.includes('app')) {
|
|
929
|
+
roles.push('app_creator', 'admin');
|
|
930
|
+
}
|
|
931
|
+
// Update set operations
|
|
932
|
+
if (operation.includes('update_set')) {
|
|
933
|
+
roles.push('admin');
|
|
934
|
+
}
|
|
935
|
+
// Global scope operations
|
|
936
|
+
if (operation.includes('global')) {
|
|
937
|
+
roles.push('admin', 'system_administrator');
|
|
938
|
+
}
|
|
939
|
+
return Array.from(new Set(roles)); // Remove duplicates
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* Determine required OAuth scopes based on operation type
|
|
943
|
+
*/
|
|
944
|
+
determineRequiredOAuthScopes(operation) {
|
|
945
|
+
const scopes = ['useraccount']; // Base scope for most operations
|
|
946
|
+
// Operations that require write access
|
|
947
|
+
if (operation.includes('deploy') || operation.includes('create') || operation.includes('update')) {
|
|
948
|
+
scopes.push('write');
|
|
949
|
+
}
|
|
950
|
+
// Administrative operations
|
|
951
|
+
if (operation.includes('admin') || operation.includes('global') || operation.includes('system')) {
|
|
952
|
+
scopes.push('admin');
|
|
953
|
+
}
|
|
954
|
+
// Flow Designer operations may need specific scopes
|
|
955
|
+
if (operation.includes('flow') || operation.includes('workflow')) {
|
|
956
|
+
scopes.push('write', 'admin');
|
|
957
|
+
}
|
|
958
|
+
return Array.from(new Set(scopes)); // Remove duplicates
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Enhanced role assignment with better validation
|
|
962
|
+
*/
|
|
963
|
+
async attemptEnhancedRoleAssignment(userSysId, roles, context) {
|
|
964
|
+
try {
|
|
965
|
+
const assignedRoles = [];
|
|
966
|
+
const assignmentDetails = [];
|
|
967
|
+
for (const roleName of roles) {
|
|
968
|
+
try {
|
|
969
|
+
// Get the role sys_id with enhanced validation
|
|
970
|
+
const roleResponse = await this.client.makeRequest({
|
|
971
|
+
method: 'GET',
|
|
972
|
+
endpoint: '/api/now/v2/table/sys_user_role',
|
|
973
|
+
params: {
|
|
974
|
+
sysparm_query: `name=${roleName}^active=true`,
|
|
975
|
+
sysparm_fields: 'sys_id,name,description,can_delegate',
|
|
976
|
+
sysparm_limit: 1
|
|
977
|
+
}
|
|
978
|
+
});
|
|
979
|
+
if (roleResponse.data?.result?.[0]) {
|
|
980
|
+
const role = roleResponse.data.result[0];
|
|
981
|
+
// Check if user already has this role
|
|
982
|
+
const existingRoleResponse = await this.client.makeRequest({
|
|
983
|
+
method: 'GET',
|
|
984
|
+
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
985
|
+
params: {
|
|
986
|
+
sysparm_query: `user=${userSysId}^role=${role.sys_id}`,
|
|
987
|
+
sysparm_limit: 1
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
if (existingRoleResponse.data?.result?.length > 0) {
|
|
991
|
+
this.logger.info(`User already has role: ${roleName}`);
|
|
992
|
+
assignedRoles.push(roleName);
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
// Attempt to assign role
|
|
996
|
+
const assignmentResponse = await this.client.makeRequest({
|
|
997
|
+
method: 'POST',
|
|
998
|
+
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
999
|
+
data: {
|
|
1000
|
+
user: userSysId,
|
|
1001
|
+
role: role.sys_id,
|
|
1002
|
+
state: 'active',
|
|
1003
|
+
granted_by: 'snow_flow_automation'
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
if (assignmentResponse.data?.result) {
|
|
1007
|
+
assignedRoles.push(roleName);
|
|
1008
|
+
assignmentDetails.push({
|
|
1009
|
+
role: roleName,
|
|
1010
|
+
sys_id: assignmentResponse.data.result.sys_id,
|
|
1011
|
+
status: 'assigned'
|
|
1012
|
+
});
|
|
1013
|
+
this.logger.info(`Successfully assigned role: ${roleName}`);
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
this.logger.warn(`Role not found: ${roleName}`);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
catch (roleError) {
|
|
1021
|
+
this.logger.warn(`Failed to assign role ${roleName}`, { error: roleError });
|
|
1022
|
+
assignmentDetails.push({
|
|
1023
|
+
role: roleName,
|
|
1024
|
+
status: 'failed',
|
|
1025
|
+
error: roleError instanceof Error ? roleError.message : String(roleError)
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
success: assignedRoles.length > 0,
|
|
1031
|
+
assigned_roles: assignedRoles,
|
|
1032
|
+
details: assignmentDetails
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
catch (error) {
|
|
1036
|
+
return {
|
|
1037
|
+
success: false,
|
|
1038
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
/**
|
|
1043
|
+
* Attempt OAuth scope elevation
|
|
1044
|
+
*/
|
|
1045
|
+
async attemptOAuthScopeElevation(missingScopes, operation, context) {
|
|
1046
|
+
try {
|
|
1047
|
+
this.logger.info('Requesting OAuth scope elevation...', { missing_scopes: missingScopes });
|
|
1048
|
+
// In most ServiceNow OAuth implementations, scope elevation requires
|
|
1049
|
+
// re-authorization through the OAuth flow. We can't dynamically elevate
|
|
1050
|
+
// scopes without user interaction.
|
|
1051
|
+
// Check if we can refresh token with additional scopes
|
|
1052
|
+
let currentToken = null;
|
|
1053
|
+
try {
|
|
1054
|
+
if (typeof this.oauth.getTokenInfo === 'function') {
|
|
1055
|
+
currentToken = await this.oauth.getTokenInfo();
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
catch (error) {
|
|
1059
|
+
this.logger.debug('Could not get current token info', { error });
|
|
1060
|
+
}
|
|
1061
|
+
if (!currentToken) {
|
|
1062
|
+
return {
|
|
1063
|
+
success: false,
|
|
1064
|
+
error: 'No active OAuth token found or method not available'
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
// Attempt token refresh - this typically won't add new scopes
|
|
1068
|
+
// but might refresh existing elevated permissions
|
|
1069
|
+
const refreshResult = await this.oauth.refreshAccessToken();
|
|
1070
|
+
if (!refreshResult.success) {
|
|
1071
|
+
return {
|
|
1072
|
+
success: false,
|
|
1073
|
+
error: 'Failed to refresh OAuth token'
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
// Check if the refresh gave us the scopes we need
|
|
1077
|
+
let newTokenInfo = null;
|
|
1078
|
+
try {
|
|
1079
|
+
if (typeof this.oauth.getTokenInfo === 'function') {
|
|
1080
|
+
newTokenInfo = await this.oauth.getTokenInfo();
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
catch (error) {
|
|
1084
|
+
this.logger.debug('Could not get new token info after refresh', { error });
|
|
1085
|
+
}
|
|
1086
|
+
const newScopes = newTokenInfo?.scope?.split(' ') || [];
|
|
1087
|
+
const actuallyMissing = missingScopes.filter(scope => !newScopes.includes(scope));
|
|
1088
|
+
if (actuallyMissing.length === 0) {
|
|
1089
|
+
return {
|
|
1090
|
+
success: true,
|
|
1091
|
+
elevated_scopes: missingScopes
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
// For missing scopes, we need manual re-authorization
|
|
1095
|
+
return {
|
|
1096
|
+
success: false,
|
|
1097
|
+
error: `OAuth scopes require manual re-authorization: ${actuallyMissing.join(', ')}`
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
catch (error) {
|
|
1101
|
+
return {
|
|
1102
|
+
success: false,
|
|
1103
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Enhanced deployment context extraction
|
|
1109
|
+
*/
|
|
1110
|
+
async extractEnhancedDeploymentContext(operation, context) {
|
|
1111
|
+
try {
|
|
1112
|
+
// Extract from agent context
|
|
1113
|
+
const contextData = context;
|
|
1114
|
+
// Enhanced artifact type detection
|
|
1115
|
+
const artifactType = this.extractArtifactType(operation);
|
|
1116
|
+
const artifactName = contextData.artifact_name ||
|
|
1117
|
+
this.extractArtifactName(operation) ||
|
|
1118
|
+
`fallback_${artifactType}_${Date.now()}`;
|
|
1119
|
+
// Determine current scope from various sources
|
|
1120
|
+
let currentScope = contextData.current_scope || 'scoped_application';
|
|
1121
|
+
if (contextData.deployment_config?.sys_scope) {
|
|
1122
|
+
currentScope = contextData.deployment_config.sys_scope;
|
|
1123
|
+
}
|
|
1124
|
+
// Analyze original error for better global scope strategy
|
|
1125
|
+
const originalError = contextData.original_error || 'Scoped deployment failed';
|
|
1126
|
+
const globalScopeStrategy = this.analyzeErrorForGlobalScopeStrategy(originalError);
|
|
1127
|
+
return {
|
|
1128
|
+
artifact_type: artifactType,
|
|
1129
|
+
artifact_name: artifactName,
|
|
1130
|
+
current_scope: currentScope,
|
|
1131
|
+
original_error: originalError,
|
|
1132
|
+
global_scope_strategy: globalScopeStrategy,
|
|
1133
|
+
deployment_config: contextData.deployment_config || {},
|
|
1134
|
+
operation_context: {
|
|
1135
|
+
session_id: context.session_id,
|
|
1136
|
+
agent_id: context.agent_id,
|
|
1137
|
+
timestamp: new Date().toISOString()
|
|
1138
|
+
}
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
catch (error) {
|
|
1142
|
+
this.logger.warn('Failed to extract enhanced deployment context', { error });
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
/**
|
|
1147
|
+
* Analyze error for global scope strategy
|
|
1148
|
+
*/
|
|
1149
|
+
analyzeErrorForGlobalScopeStrategy(error) {
|
|
1150
|
+
const errorLower = error.toLowerCase();
|
|
1151
|
+
if (errorLower.includes('scope') || errorLower.includes('application')) {
|
|
1152
|
+
return 'direct_global_deployment';
|
|
1153
|
+
}
|
|
1154
|
+
if (errorLower.includes('permission') || errorLower.includes('access')) {
|
|
1155
|
+
return 'elevated_permissions_deployment';
|
|
1156
|
+
}
|
|
1157
|
+
if (errorLower.includes('table') || errorLower.includes('field')) {
|
|
1158
|
+
return 'schema_aware_deployment';
|
|
1159
|
+
}
|
|
1160
|
+
return 'standard_global_deployment';
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Validate global scope deployment permissions
|
|
1164
|
+
*/
|
|
1165
|
+
async validateGlobalScopePermissions(artifactType, context) {
|
|
1166
|
+
try {
|
|
1167
|
+
// Check basic global scope permissions
|
|
1168
|
+
const requiredRoles = ['admin'];
|
|
1169
|
+
const requiredScopes = ['write', 'admin'];
|
|
1170
|
+
// Get current user roles (simplified check)
|
|
1171
|
+
let hasAdminRole = false;
|
|
1172
|
+
try {
|
|
1173
|
+
const rolesResponse = await this.client.makeRequest({
|
|
1174
|
+
method: 'GET',
|
|
1175
|
+
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
1176
|
+
params: {
|
|
1177
|
+
sysparm_query: 'user.user_name=javascript:gs.getUserName()^role.name=admin',
|
|
1178
|
+
sysparm_limit: 1
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
hasAdminRole = rolesResponse.data?.result?.length > 0;
|
|
1182
|
+
}
|
|
1183
|
+
catch (roleCheckError) {
|
|
1184
|
+
this.logger.warn('Could not verify admin role', { error: roleCheckError });
|
|
1185
|
+
}
|
|
1186
|
+
if (!hasAdminRole) {
|
|
1187
|
+
return {
|
|
1188
|
+
canProceed: false,
|
|
1189
|
+
missing_permissions: ['admin'],
|
|
1190
|
+
recommended_actions: [
|
|
1191
|
+
'Request admin role from ServiceNow administrator',
|
|
1192
|
+
'Use snow_escalate_permissions() for automatic elevation',
|
|
1193
|
+
'Consider scoped deployment alternative'
|
|
1194
|
+
]
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
return { canProceed: true };
|
|
1198
|
+
}
|
|
1199
|
+
catch (error) {
|
|
1200
|
+
this.logger.warn('Permission validation failed', { error });
|
|
1201
|
+
return {
|
|
1202
|
+
canProceed: false,
|
|
1203
|
+
missing_permissions: ['unknown'],
|
|
1204
|
+
recommended_actions: ['Check authentication and try again']
|
|
1205
|
+
};
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Determine global deployment strategy
|
|
1210
|
+
*/
|
|
1211
|
+
determineGlobalDeploymentStrategy(deploymentContext) {
|
|
1212
|
+
const strategies = {
|
|
1213
|
+
direct_global_deployment: {
|
|
1214
|
+
name: 'Direct Global Deployment',
|
|
1215
|
+
reasons: ['Scope-related error detected', 'Standard global scope approach'],
|
|
1216
|
+
estimated_success_rate: 0.75
|
|
1217
|
+
},
|
|
1218
|
+
elevated_permissions_deployment: {
|
|
1219
|
+
name: 'Elevated Permissions Deployment',
|
|
1220
|
+
reasons: ['Permission error detected', 'Admin-level deployment required'],
|
|
1221
|
+
estimated_success_rate: 0.60
|
|
1222
|
+
},
|
|
1223
|
+
schema_aware_deployment: {
|
|
1224
|
+
name: 'Schema-Aware Deployment',
|
|
1225
|
+
reasons: ['Table/field error detected', 'Enhanced validation required'],
|
|
1226
|
+
estimated_success_rate: 0.80
|
|
1227
|
+
},
|
|
1228
|
+
standard_global_deployment: {
|
|
1229
|
+
name: 'Standard Global Deployment',
|
|
1230
|
+
reasons: ['Generic error', 'Default global scope approach'],
|
|
1231
|
+
estimated_success_rate: 0.65
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
return strategies[deploymentContext.global_scope_strategy] || strategies.standard_global_deployment;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Attempt automatic role assignment (usually requires admin privileges)
|
|
1238
|
+
*/
|
|
1239
|
+
async attemptRoleAssignment(userId, roles) {
|
|
1240
|
+
try {
|
|
1241
|
+
// This typically fails in most ServiceNow instances for security reasons
|
|
1242
|
+
// But we attempt it for completeness
|
|
1243
|
+
for (const roleName of roles) {
|
|
1244
|
+
// First, get the role sys_id
|
|
1245
|
+
const roleResponse = await this.client.makeRequest({
|
|
1246
|
+
method: 'GET',
|
|
1247
|
+
endpoint: '/api/now/v2/table/sys_user_role',
|
|
1248
|
+
params: {
|
|
1249
|
+
sysparm_query: `name=${roleName}`,
|
|
1250
|
+
sysparm_fields: 'sys_id,name',
|
|
1251
|
+
sysparm_limit: 1
|
|
1252
|
+
}
|
|
1253
|
+
});
|
|
1254
|
+
if (roleResponse.data?.result?.[0]) {
|
|
1255
|
+
const roleSysId = roleResponse.data.result[0].sys_id;
|
|
1256
|
+
// Attempt to assign role to user
|
|
1257
|
+
await this.client.makeRequest({
|
|
1258
|
+
method: 'POST',
|
|
1259
|
+
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
1260
|
+
data: {
|
|
1261
|
+
user: userId,
|
|
1262
|
+
role: roleSysId
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
return { success: true };
|
|
1268
|
+
}
|
|
1269
|
+
catch (error) {
|
|
1270
|
+
return {
|
|
1271
|
+
success: false,
|
|
1272
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1273
|
+
};
|
|
1274
|
+
}
|
|
696
1275
|
}
|
|
697
1276
|
async fallbackToGlobalScope(operation, context) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
1277
|
+
this.logger.info(`🌐 Attempting intelligent global scope fallback for ${operation}`);
|
|
1278
|
+
try {
|
|
1279
|
+
// Enhanced pre-flight validation
|
|
1280
|
+
const authValidation = await this.validateServiceNowConnection();
|
|
1281
|
+
if (!authValidation.success) {
|
|
1282
|
+
return {
|
|
1283
|
+
success: false,
|
|
1284
|
+
reason: 'Authentication validation failed before global scope fallback',
|
|
1285
|
+
auth_error: authValidation.error,
|
|
1286
|
+
recovery_action: 'Fix authentication first, then retry global scope deployment'
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
// Comprehensive deployment context extraction with validation
|
|
1290
|
+
const deploymentContext = await this.extractEnhancedDeploymentContext(operation, context);
|
|
1291
|
+
if (!deploymentContext) {
|
|
1292
|
+
this.logger.warn('Unable to extract deployment context - attempting generic global scope deployment');
|
|
1293
|
+
return await this.attemptGenericGlobalScopeDeployment(operation, context);
|
|
1294
|
+
}
|
|
1295
|
+
// Validate global scope deployment permissions
|
|
1296
|
+
const globalScopeValidation = await this.validateGlobalScopePermissions(deploymentContext.artifact_type, context);
|
|
1297
|
+
if (!globalScopeValidation.canProceed) {
|
|
1298
|
+
return {
|
|
1299
|
+
success: false,
|
|
1300
|
+
reason: 'Insufficient permissions for global scope deployment',
|
|
1301
|
+
missing_permissions: globalScopeValidation.missing_permissions,
|
|
1302
|
+
recommended_actions: globalScopeValidation.recommended_actions,
|
|
1303
|
+
escalation_required: true
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
this.logger.info('🔧 Configuring global scope deployment', {
|
|
1307
|
+
original_scope: deploymentContext.current_scope,
|
|
1308
|
+
artifact_type: deploymentContext.artifact_type,
|
|
1309
|
+
artifact_name: deploymentContext.artifact_name,
|
|
1310
|
+
global_scope_strategy: deploymentContext.global_scope_strategy
|
|
1311
|
+
});
|
|
1312
|
+
// Enhanced deployment strategy based on artifact type and complexity
|
|
1313
|
+
let globalDeploymentResult;
|
|
1314
|
+
const deploymentStrategy = this.determineGlobalDeploymentStrategy(deploymentContext);
|
|
1315
|
+
this.logger.info(`📋 Using deployment strategy: ${deploymentStrategy.name}`, {
|
|
1316
|
+
reasons: deploymentStrategy.reasons,
|
|
1317
|
+
estimated_success_rate: deploymentStrategy.estimated_success_rate
|
|
1318
|
+
});
|
|
1319
|
+
switch (deploymentContext.artifact_type.toLowerCase()) {
|
|
1320
|
+
case 'widget':
|
|
1321
|
+
globalDeploymentResult = await this.deployWidgetInGlobalScopeEnhanced(deploymentContext, deploymentStrategy);
|
|
1322
|
+
break;
|
|
1323
|
+
case 'flow':
|
|
1324
|
+
case 'workflow':
|
|
1325
|
+
globalDeploymentResult = await this.deployFlowInGlobalScopeEnhanced(deploymentContext, deploymentStrategy);
|
|
1326
|
+
break;
|
|
1327
|
+
case 'script':
|
|
1328
|
+
case 'script_include':
|
|
1329
|
+
globalDeploymentResult = await this.deployScriptInGlobalScopeEnhanced(deploymentContext, deploymentStrategy);
|
|
1330
|
+
break;
|
|
1331
|
+
case 'business_rule':
|
|
1332
|
+
globalDeploymentResult = await this.deployBusinessRuleInGlobalScopeEnhanced(deploymentContext, deploymentStrategy);
|
|
1333
|
+
break;
|
|
1334
|
+
case 'table':
|
|
1335
|
+
globalDeploymentResult = await this.deployTableInGlobalScopeEnhanced(deploymentContext, deploymentStrategy);
|
|
1336
|
+
break;
|
|
1337
|
+
case 'application':
|
|
1338
|
+
// Enhanced application scope handling
|
|
1339
|
+
return await this.handleApplicationScopeFallback(deploymentContext, context);
|
|
1340
|
+
default:
|
|
1341
|
+
globalDeploymentResult = await this.deployGenericArtifactInGlobalScopeEnhanced(deploymentContext, deploymentStrategy);
|
|
1342
|
+
}
|
|
1343
|
+
// Enhanced result processing with comprehensive validation
|
|
1344
|
+
if (globalDeploymentResult.success) {
|
|
1345
|
+
this.logger.info('✅ Global scope deployment successful', {
|
|
1346
|
+
sys_id: globalDeploymentResult.sys_id,
|
|
1347
|
+
artifact_type: deploymentContext.artifact_type,
|
|
1348
|
+
deployment_time_ms: globalDeploymentResult.deployment_time_ms,
|
|
1349
|
+
strategy_used: deploymentStrategy.name
|
|
1350
|
+
});
|
|
1351
|
+
// Enhanced context update with deployment tracking
|
|
1352
|
+
await this.updateContextWithEnhancedGlobalDeployment(context, {
|
|
1353
|
+
...globalDeploymentResult,
|
|
1354
|
+
deployment_context: deploymentContext,
|
|
1355
|
+
strategy_used: deploymentStrategy
|
|
1356
|
+
});
|
|
1357
|
+
// Post-deployment validation
|
|
1358
|
+
const validationResult = await this.validateGlobalDeployment(globalDeploymentResult.sys_id, deploymentContext.artifact_type);
|
|
1359
|
+
return {
|
|
1360
|
+
success: true,
|
|
1361
|
+
reason: 'Successfully deployed in global scope with validation',
|
|
1362
|
+
deployment_details: {
|
|
1363
|
+
sys_id: globalDeploymentResult.sys_id,
|
|
1364
|
+
scope: 'global',
|
|
1365
|
+
artifact_type: deploymentContext.artifact_type,
|
|
1366
|
+
artifact_name: deploymentContext.artifact_name,
|
|
1367
|
+
fallback_reason: deploymentContext.original_error,
|
|
1368
|
+
strategy_used: deploymentStrategy.name,
|
|
1369
|
+
deployment_time_ms: globalDeploymentResult.deployment_time_ms,
|
|
1370
|
+
validation_status: validationResult.status,
|
|
1371
|
+
functional_verification: validationResult.functional_tests
|
|
1372
|
+
},
|
|
1373
|
+
next_steps: [
|
|
1374
|
+
'1. Verify artifact functionality in global scope',
|
|
1375
|
+
'2. Review and update ACL permissions if needed',
|
|
1376
|
+
'3. Document scope change for future reference',
|
|
1377
|
+
'4. Test integration with other components',
|
|
1378
|
+
'5. Consider long-term scope strategy'
|
|
1379
|
+
],
|
|
1380
|
+
warnings: validationResult.warnings || [],
|
|
1381
|
+
recommendations: this.generateGlobalScopeRecommendations(deploymentContext, globalDeploymentResult)
|
|
1382
|
+
};
|
|
1383
|
+
}
|
|
1384
|
+
else {
|
|
1385
|
+
// Enhanced failure handling with intelligent retry suggestions
|
|
1386
|
+
const failureAnalysis = await this.analyzeGlobalScopeFailure(globalDeploymentResult, deploymentContext, deploymentStrategy);
|
|
1387
|
+
this.logger.error('❌ Global scope deployment failed', {
|
|
1388
|
+
original_error: deploymentContext.original_error,
|
|
1389
|
+
global_deployment_error: globalDeploymentResult.error,
|
|
1390
|
+
failure_analysis: failureAnalysis
|
|
1391
|
+
});
|
|
1392
|
+
return {
|
|
1393
|
+
success: false,
|
|
1394
|
+
reason: 'Global scope deployment failed after comprehensive attempts',
|
|
1395
|
+
original_error: deploymentContext.original_error,
|
|
1396
|
+
global_deployment_error: globalDeploymentResult.error,
|
|
1397
|
+
failure_analysis: failureAnalysis,
|
|
1398
|
+
alternative_strategies: [
|
|
1399
|
+
'1. Manual Update Set creation and deployment',
|
|
1400
|
+
'2. Phased deployment (create components separately)',
|
|
1401
|
+
'3. XML-based deployment approach',
|
|
1402
|
+
'4. ServiceNow Studio development environment',
|
|
1403
|
+
'5. Direct database approach (requires elevated access)'
|
|
1404
|
+
],
|
|
1405
|
+
diagnostic_commands: [
|
|
1406
|
+
'snow_deployment_debug() - Get detailed error analysis',
|
|
1407
|
+
'snow_validate_deployment() - Pre-deployment validation',
|
|
1408
|
+
'snow_auth_diagnostics() - Check authentication and permissions',
|
|
1409
|
+
'snow_comprehensive_search() - Find similar existing artifacts'
|
|
1410
|
+
],
|
|
1411
|
+
manual_options: failureAnalysis.manual_steps,
|
|
1412
|
+
escalation_required: failureAnalysis.requires_admin_intervention,
|
|
1413
|
+
estimated_manual_effort: failureAnalysis.estimated_manual_time
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
catch (error) {
|
|
1418
|
+
this.logger.error('🚨 Global scope fallback process failed critically', {
|
|
1419
|
+
error,
|
|
1420
|
+
operation,
|
|
1421
|
+
context,
|
|
1422
|
+
stack: error instanceof Error ? error.stack : 'No stack trace available'
|
|
1423
|
+
});
|
|
1424
|
+
// Request immediate Queen intervention for critical failures
|
|
1425
|
+
await this.requestQueenIntervention(context, {
|
|
1426
|
+
type: 'global_scope_fallback_failure',
|
|
1427
|
+
priority: 'critical',
|
|
1428
|
+
description: `Critical failure during global scope fallback for ${operation}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1429
|
+
attempted_solutions: ['global_scope_deployment', 'enhanced_validation']
|
|
1430
|
+
});
|
|
1431
|
+
return {
|
|
1432
|
+
success: false,
|
|
1433
|
+
reason: 'Critical error during global scope fallback process',
|
|
1434
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1435
|
+
error_type: this.categorizeError(error instanceof Error ? error.message : String(error)),
|
|
1436
|
+
stack_trace: error instanceof Error ? error.stack : null,
|
|
1437
|
+
recovery_actions: [
|
|
1438
|
+
'1. Emergency: Verify ServiceNow instance connectivity',
|
|
1439
|
+
'2. Critical: Check authentication and API access',
|
|
1440
|
+
'3. High: Review deployment configuration for errors',
|
|
1441
|
+
'4. Medium: Contact ServiceNow administrator immediately',
|
|
1442
|
+
'5. Low: Review system logs for additional context'
|
|
1443
|
+
],
|
|
1444
|
+
emergency_contact: true,
|
|
1445
|
+
requires_immediate_attention: true,
|
|
1446
|
+
fallback_options: [
|
|
1447
|
+
'Switch to manual deployment process',
|
|
1448
|
+
'Use ServiceNow Studio for development',
|
|
1449
|
+
'Create support ticket with ServiceNow',
|
|
1450
|
+
'Engage emergency deployment procedures'
|
|
1451
|
+
]
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Extract deployment context from operation and context
|
|
1457
|
+
*/
|
|
1458
|
+
extractDeploymentContext(operation, context) {
|
|
1459
|
+
try {
|
|
1460
|
+
// Try to extract context from agent context or operation string
|
|
1461
|
+
const contextData = context; // Allow access to additional properties
|
|
1462
|
+
return {
|
|
1463
|
+
artifact_type: this.extractArtifactType(operation),
|
|
1464
|
+
artifact_name: contextData.artifact_name || this.extractArtifactName(operation),
|
|
1465
|
+
current_scope: contextData.current_scope || 'scoped_application',
|
|
1466
|
+
original_error: contextData.original_error || 'Scoped deployment failed',
|
|
1467
|
+
deployment_config: contextData.deployment_config || {}
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
catch (error) {
|
|
1471
|
+
this.logger.warn('Failed to extract deployment context', { error });
|
|
1472
|
+
return null;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
/**
|
|
1476
|
+
* Extract artifact type from operation string
|
|
1477
|
+
*/
|
|
1478
|
+
extractArtifactType(operation) {
|
|
1479
|
+
const patterns = [
|
|
1480
|
+
{ pattern: /widget/i, type: 'widget' },
|
|
1481
|
+
{ pattern: /flow|workflow/i, type: 'flow' },
|
|
1482
|
+
{ pattern: /script.*include/i, type: 'script_include' },
|
|
1483
|
+
{ pattern: /business.*rule/i, type: 'business_rule' },
|
|
1484
|
+
{ pattern: /application|app/i, type: 'application' },
|
|
1485
|
+
{ pattern: /script/i, type: 'script' }
|
|
1486
|
+
];
|
|
1487
|
+
for (const { pattern, type } of patterns) {
|
|
1488
|
+
if (pattern.test(operation)) {
|
|
1489
|
+
return type;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return 'unknown';
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Extract artifact name from operation string
|
|
1496
|
+
*/
|
|
1497
|
+
extractArtifactName(operation) {
|
|
1498
|
+
// Try to extract name from common patterns
|
|
1499
|
+
const nameMatch = operation.match(/deploy(?:ing)?\s+([a-zA-Z0-9_\-\s]+)/i);
|
|
1500
|
+
return nameMatch ? nameMatch[1].trim() : 'fallback_artifact';
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Deploy widget in global scope
|
|
1504
|
+
*/
|
|
1505
|
+
async deployWidgetInGlobalScope(context) {
|
|
1506
|
+
try {
|
|
1507
|
+
const widgetData = {
|
|
1508
|
+
...context.deployment_config,
|
|
1509
|
+
name: context.artifact_name,
|
|
1510
|
+
sys_scope: 'global' // Force global scope
|
|
1511
|
+
};
|
|
1512
|
+
const response = await this.client.makeRequest({
|
|
1513
|
+
method: 'POST',
|
|
1514
|
+
endpoint: '/api/now/v2/table/sp_widget',
|
|
1515
|
+
data: widgetData
|
|
1516
|
+
});
|
|
1517
|
+
if (response.data?.result) {
|
|
1518
|
+
return {
|
|
1519
|
+
success: true,
|
|
1520
|
+
sys_id: response.data.result.sys_id,
|
|
1521
|
+
name: response.data.result.name
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
return { success: false, error: 'No result returned from widget creation' };
|
|
1525
|
+
}
|
|
1526
|
+
catch (error) {
|
|
1527
|
+
return {
|
|
1528
|
+
success: false,
|
|
1529
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Deploy flow in global scope
|
|
1535
|
+
*/
|
|
1536
|
+
async deployFlowInGlobalScope(context) {
|
|
1537
|
+
try {
|
|
1538
|
+
const flowData = {
|
|
1539
|
+
...context.deployment_config,
|
|
1540
|
+
name: context.artifact_name,
|
|
1541
|
+
sys_scope: 'global' // Force global scope
|
|
1542
|
+
};
|
|
1543
|
+
const response = await this.client.makeRequest({
|
|
1544
|
+
method: 'POST',
|
|
1545
|
+
endpoint: '/api/now/v2/table/wf_workflow',
|
|
1546
|
+
data: flowData
|
|
1547
|
+
});
|
|
1548
|
+
if (response.data?.result) {
|
|
1549
|
+
return {
|
|
1550
|
+
success: true,
|
|
1551
|
+
sys_id: response.data.result.sys_id,
|
|
1552
|
+
name: response.data.result.name
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
return { success: false, error: 'No result returned from flow creation' };
|
|
1556
|
+
}
|
|
1557
|
+
catch (error) {
|
|
1558
|
+
return {
|
|
1559
|
+
success: false,
|
|
1560
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Deploy script include in global scope
|
|
1566
|
+
*/
|
|
1567
|
+
async deployScriptInGlobalScope(context) {
|
|
1568
|
+
try {
|
|
1569
|
+
const scriptData = {
|
|
1570
|
+
...context.deployment_config,
|
|
1571
|
+
name: context.artifact_name,
|
|
1572
|
+
sys_scope: 'global' // Force global scope
|
|
1573
|
+
};
|
|
1574
|
+
const response = await this.client.makeRequest({
|
|
1575
|
+
method: 'POST',
|
|
1576
|
+
endpoint: '/api/now/v2/table/sys_script_include',
|
|
1577
|
+
data: scriptData
|
|
1578
|
+
});
|
|
1579
|
+
if (response.data?.result) {
|
|
1580
|
+
return {
|
|
1581
|
+
success: true,
|
|
1582
|
+
sys_id: response.data.result.sys_id,
|
|
1583
|
+
name: response.data.result.name
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
return { success: false, error: 'No result returned from script creation' };
|
|
1587
|
+
}
|
|
1588
|
+
catch (error) {
|
|
1589
|
+
return {
|
|
1590
|
+
success: false,
|
|
1591
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
/**
|
|
1596
|
+
* Deploy business rule in global scope
|
|
1597
|
+
*/
|
|
1598
|
+
async deployBusinessRuleInGlobalScope(context) {
|
|
1599
|
+
try {
|
|
1600
|
+
const ruleData = {
|
|
1601
|
+
...context.deployment_config,
|
|
1602
|
+
name: context.artifact_name,
|
|
1603
|
+
sys_scope: 'global' // Force global scope
|
|
1604
|
+
};
|
|
1605
|
+
const response = await this.client.makeRequest({
|
|
1606
|
+
method: 'POST',
|
|
1607
|
+
endpoint: '/api/now/v2/table/sys_script',
|
|
1608
|
+
data: ruleData
|
|
1609
|
+
});
|
|
1610
|
+
if (response.data?.result) {
|
|
1611
|
+
return {
|
|
1612
|
+
success: true,
|
|
1613
|
+
sys_id: response.data.result.sys_id,
|
|
1614
|
+
name: response.data.result.name
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1617
|
+
return { success: false, error: 'No result returned from business rule creation' };
|
|
1618
|
+
}
|
|
1619
|
+
catch (error) {
|
|
1620
|
+
return {
|
|
1621
|
+
success: false,
|
|
1622
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
/**
|
|
1627
|
+
* Generic global scope deployment for unknown artifact types
|
|
1628
|
+
*/
|
|
1629
|
+
async deployGenericArtifactInGlobalScope(context) {
|
|
1630
|
+
try {
|
|
1631
|
+
// Attempt deployment based on common table patterns
|
|
1632
|
+
const possibleTables = [
|
|
1633
|
+
'sys_script_include',
|
|
1634
|
+
'sys_script',
|
|
1635
|
+
'sys_ui_script',
|
|
1636
|
+
'sys_ui_page'
|
|
1637
|
+
];
|
|
1638
|
+
for (const table of possibleTables) {
|
|
1639
|
+
try {
|
|
1640
|
+
const data = {
|
|
1641
|
+
...context.deployment_config,
|
|
1642
|
+
name: context.artifact_name,
|
|
1643
|
+
sys_scope: 'global'
|
|
1644
|
+
};
|
|
1645
|
+
const response = await this.client.makeRequest({
|
|
1646
|
+
method: 'POST',
|
|
1647
|
+
endpoint: `/api/now/v2/table/${table}`,
|
|
1648
|
+
data
|
|
1649
|
+
});
|
|
1650
|
+
if (response.data?.result) {
|
|
1651
|
+
return {
|
|
1652
|
+
success: true,
|
|
1653
|
+
sys_id: response.data.result.sys_id,
|
|
1654
|
+
name: response.data.result.name,
|
|
1655
|
+
table_used: table
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
catch (tableError) {
|
|
1660
|
+
// Continue to next table
|
|
1661
|
+
this.logger.debug(`Failed to deploy to ${table}`, { error: tableError });
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return { success: false, error: 'No suitable table found for deployment' };
|
|
1665
|
+
}
|
|
1666
|
+
catch (error) {
|
|
1667
|
+
return {
|
|
1668
|
+
success: false,
|
|
1669
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
/**
|
|
1674
|
+
* Update context with global deployment information
|
|
1675
|
+
*/
|
|
1676
|
+
async updateContextWithGlobalDeployment(context, deploymentResult) {
|
|
1677
|
+
try {
|
|
1678
|
+
// Store deployment information in memory for coordination
|
|
1679
|
+
await this.memory.updateSharedContext({
|
|
1680
|
+
session_id: context.session_id,
|
|
1681
|
+
context_key: 'global_deployment_fallback',
|
|
1682
|
+
context_value: JSON.stringify({
|
|
1683
|
+
original_context: context,
|
|
1684
|
+
global_deployment: deploymentResult,
|
|
1685
|
+
timestamp: new Date().toISOString()
|
|
1686
|
+
}),
|
|
1687
|
+
created_by_agent: context.agent_id
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
catch (error) {
|
|
1691
|
+
this.logger.warn('Failed to update context with global deployment', { error });
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
/**
|
|
1695
|
+
* Enhanced context update with deployment tracking
|
|
1696
|
+
*/
|
|
1697
|
+
async updateContextWithEnhancedGlobalDeployment(context, deploymentResult) {
|
|
1698
|
+
try {
|
|
1699
|
+
await this.memory.updateSharedContext({
|
|
1700
|
+
session_id: context.session_id,
|
|
1701
|
+
context_key: 'enhanced_global_deployment',
|
|
1702
|
+
context_value: JSON.stringify({
|
|
1703
|
+
deployment_result: deploymentResult,
|
|
1704
|
+
context: context,
|
|
1705
|
+
timestamp: new Date().toISOString()
|
|
1706
|
+
}),
|
|
1707
|
+
created_by_agent: context.agent_id
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
catch (error) {
|
|
1711
|
+
this.logger.warn('Failed to update enhanced global deployment context', { error });
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
/**
|
|
1715
|
+
* Enhanced widget deployment in global scope
|
|
1716
|
+
*/
|
|
1717
|
+
async deployWidgetInGlobalScopeEnhanced(context, strategy) {
|
|
1718
|
+
try {
|
|
1719
|
+
const startTime = Date.now();
|
|
1720
|
+
const result = await this.deployWidgetInGlobalScope(context);
|
|
1721
|
+
return {
|
|
1722
|
+
...result,
|
|
1723
|
+
deployment_time_ms: Date.now() - startTime,
|
|
1724
|
+
strategy_applied: strategy.name
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
catch (error) {
|
|
1728
|
+
return {
|
|
1729
|
+
success: false,
|
|
1730
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1731
|
+
deployment_time_ms: 0
|
|
1732
|
+
};
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
/**
|
|
1736
|
+
* Enhanced flow deployment in global scope
|
|
1737
|
+
*/
|
|
1738
|
+
async deployFlowInGlobalScopeEnhanced(context, strategy) {
|
|
1739
|
+
try {
|
|
1740
|
+
const startTime = Date.now();
|
|
1741
|
+
const result = await this.deployFlowInGlobalScope(context);
|
|
1742
|
+
return {
|
|
1743
|
+
...result,
|
|
1744
|
+
deployment_time_ms: Date.now() - startTime,
|
|
1745
|
+
strategy_applied: strategy.name
|
|
1746
|
+
};
|
|
1747
|
+
}
|
|
1748
|
+
catch (error) {
|
|
1749
|
+
return {
|
|
1750
|
+
success: false,
|
|
1751
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1752
|
+
deployment_time_ms: 0
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
/**
|
|
1757
|
+
* Enhanced script deployment in global scope
|
|
1758
|
+
*/
|
|
1759
|
+
async deployScriptInGlobalScopeEnhanced(context, strategy) {
|
|
1760
|
+
try {
|
|
1761
|
+
const startTime = Date.now();
|
|
1762
|
+
const result = await this.deployScriptInGlobalScope(context);
|
|
1763
|
+
return {
|
|
1764
|
+
...result,
|
|
1765
|
+
deployment_time_ms: Date.now() - startTime,
|
|
1766
|
+
strategy_applied: strategy.name
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
catch (error) {
|
|
1770
|
+
return {
|
|
1771
|
+
success: false,
|
|
1772
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1773
|
+
deployment_time_ms: 0
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Enhanced business rule deployment in global scope
|
|
1779
|
+
*/
|
|
1780
|
+
async deployBusinessRuleInGlobalScopeEnhanced(context, strategy) {
|
|
1781
|
+
try {
|
|
1782
|
+
const startTime = Date.now();
|
|
1783
|
+
const result = await this.deployBusinessRuleInGlobalScope(context);
|
|
1784
|
+
return {
|
|
1785
|
+
...result,
|
|
1786
|
+
deployment_time_ms: Date.now() - startTime,
|
|
1787
|
+
strategy_applied: strategy.name
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
catch (error) {
|
|
1791
|
+
return {
|
|
1792
|
+
success: false,
|
|
1793
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1794
|
+
deployment_time_ms: 0
|
|
1795
|
+
};
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Enhanced table deployment in global scope
|
|
1800
|
+
*/
|
|
1801
|
+
async deployTableInGlobalScopeEnhanced(context, strategy) {
|
|
1802
|
+
try {
|
|
1803
|
+
const startTime = Date.now();
|
|
1804
|
+
const tableData = {
|
|
1805
|
+
...context.deployment_config,
|
|
1806
|
+
name: context.artifact_name,
|
|
1807
|
+
sys_scope: 'global'
|
|
1808
|
+
};
|
|
1809
|
+
const response = await this.client.makeRequest({
|
|
1810
|
+
method: 'POST',
|
|
1811
|
+
endpoint: '/api/now/v2/table/sys_db_object',
|
|
1812
|
+
data: tableData
|
|
1813
|
+
});
|
|
1814
|
+
if (response.data?.result) {
|
|
1815
|
+
return {
|
|
1816
|
+
success: true,
|
|
1817
|
+
sys_id: response.data.result.sys_id,
|
|
1818
|
+
name: response.data.result.name,
|
|
1819
|
+
deployment_time_ms: Date.now() - startTime,
|
|
1820
|
+
strategy_applied: strategy.name
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
return {
|
|
1824
|
+
success: false,
|
|
1825
|
+
error: 'No result returned from table creation',
|
|
1826
|
+
deployment_time_ms: Date.now() - startTime
|
|
1827
|
+
};
|
|
1828
|
+
}
|
|
1829
|
+
catch (error) {
|
|
1830
|
+
return {
|
|
1831
|
+
success: false,
|
|
1832
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1833
|
+
deployment_time_ms: 0
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
/**
|
|
1838
|
+
* Enhanced generic artifact deployment in global scope
|
|
1839
|
+
*/
|
|
1840
|
+
async deployGenericArtifactInGlobalScopeEnhanced(context, strategy) {
|
|
1841
|
+
try {
|
|
1842
|
+
const startTime = Date.now();
|
|
1843
|
+
const result = await this.deployGenericArtifactInGlobalScope(context);
|
|
1844
|
+
return {
|
|
1845
|
+
...result,
|
|
1846
|
+
deployment_time_ms: Date.now() - startTime,
|
|
1847
|
+
strategy_applied: strategy.name
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
catch (error) {
|
|
1851
|
+
return {
|
|
1852
|
+
success: false,
|
|
1853
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1854
|
+
deployment_time_ms: 0
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Handle application scope fallback
|
|
1860
|
+
*/
|
|
1861
|
+
async handleApplicationScopeFallback(deploymentContext, context) {
|
|
1862
|
+
try {
|
|
1863
|
+
this.logger.info('🏢 Handling application scope fallback...');
|
|
1864
|
+
return {
|
|
1865
|
+
success: false,
|
|
1866
|
+
reason: 'Applications require special scope handling - cannot deploy in global scope',
|
|
1867
|
+
alternative_strategies: [
|
|
1868
|
+
'1. Create scoped application with broader permissions',
|
|
1869
|
+
'2. Deploy individual components separately in global scope',
|
|
1870
|
+
'3. Request administrator to configure application scope',
|
|
1871
|
+
'4. Use ServiceNow Studio for application development'
|
|
1872
|
+
],
|
|
1873
|
+
manual_steps: [
|
|
1874
|
+
'Navigate to System Applications > Studio',
|
|
1875
|
+
'Create new scoped application',
|
|
1876
|
+
'Configure appropriate scope permissions',
|
|
1877
|
+
'Deploy components within application scope'
|
|
1878
|
+
],
|
|
1879
|
+
estimated_time: '15-30 minutes with proper permissions'
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
catch (error) {
|
|
1883
|
+
return {
|
|
1884
|
+
success: false,
|
|
1885
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1886
|
+
reason: 'Failed to analyze application scope requirements'
|
|
1887
|
+
};
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Attempt generic global scope deployment
|
|
1892
|
+
*/
|
|
1893
|
+
async attemptGenericGlobalScopeDeployment(operation, context) {
|
|
1894
|
+
try {
|
|
1895
|
+
this.logger.info('🌐 Attempting generic global scope deployment...');
|
|
1896
|
+
return {
|
|
1897
|
+
success: false,
|
|
1898
|
+
reason: 'Generic global scope deployment requires more specific context',
|
|
1899
|
+
recommended_actions: [
|
|
1900
|
+
'Provide more specific deployment context',
|
|
1901
|
+
'Use artifact-specific deployment methods',
|
|
1902
|
+
'Check deployment configuration',
|
|
1903
|
+
'Contact administrator for manual deployment'
|
|
1904
|
+
]
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
catch (error) {
|
|
1908
|
+
return {
|
|
1909
|
+
success: false,
|
|
1910
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1911
|
+
};
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Validate global deployment
|
|
1916
|
+
*/
|
|
1917
|
+
async validateGlobalDeployment(sysId, artifactType) {
|
|
1918
|
+
try {
|
|
1919
|
+
const table = this.getTableForType(artifactType);
|
|
1920
|
+
const validationResponse = await this.client.makeRequest({
|
|
1921
|
+
method: 'GET',
|
|
1922
|
+
endpoint: `/api/now/v2/table/${table}/${sysId}`,
|
|
1923
|
+
params: {
|
|
1924
|
+
sysparm_fields: 'sys_id,name,active,sys_scope'
|
|
1925
|
+
}
|
|
1926
|
+
});
|
|
1927
|
+
if (validationResponse.data?.result) {
|
|
1928
|
+
const artifact = validationResponse.data.result;
|
|
1929
|
+
return {
|
|
1930
|
+
status: 'validated',
|
|
1931
|
+
functional_tests: [
|
|
1932
|
+
`Artifact exists: ${artifact.name}`,
|
|
1933
|
+
`Scope: ${artifact.sys_scope || 'global'}`,
|
|
1934
|
+
`Active: ${artifact.active || 'unknown'}`
|
|
1935
|
+
],
|
|
1936
|
+
warnings: artifact.sys_scope !== 'global' ? ['Artifact may not be in global scope'] : []
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
return {
|
|
1940
|
+
status: 'validation_failed',
|
|
1941
|
+
functional_tests: [],
|
|
1942
|
+
warnings: ['Could not validate artifact deployment']
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
catch (error) {
|
|
1946
|
+
return {
|
|
1947
|
+
status: 'validation_error',
|
|
1948
|
+
functional_tests: [],
|
|
1949
|
+
warnings: [`Validation error: ${error instanceof Error ? error.message : String(error)}`]
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
/**
|
|
1954
|
+
* Generate global scope recommendations
|
|
1955
|
+
*/
|
|
1956
|
+
generateGlobalScopeRecommendations(deploymentContext, deploymentResult) {
|
|
1957
|
+
const recommendations = [
|
|
1958
|
+
'✅ Document this global scope deployment for future reference',
|
|
1959
|
+
'🔍 Review ACL permissions to ensure proper access control',
|
|
1960
|
+
'📋 Consider creating Update Set for deployment tracking'
|
|
1961
|
+
];
|
|
1962
|
+
if (deploymentContext.artifact_type === 'widget') {
|
|
1963
|
+
recommendations.push('🎨 Test widget rendering in Service Portal');
|
|
1964
|
+
}
|
|
1965
|
+
if (deploymentContext.artifact_type === 'flow') {
|
|
1966
|
+
recommendations.push('⚡ Activate flow and test trigger conditions');
|
|
1967
|
+
}
|
|
1968
|
+
return recommendations;
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Analyze global scope failure
|
|
1972
|
+
*/
|
|
1973
|
+
async analyzeGlobalScopeFailure(deploymentResult, deploymentContext, strategy) {
|
|
1974
|
+
const errorMessage = deploymentResult.error || 'Unknown error';
|
|
1975
|
+
const errorType = this.categorizeError(errorMessage);
|
|
1976
|
+
return {
|
|
1977
|
+
error_category: errorType,
|
|
1978
|
+
strategy_used: strategy.name,
|
|
1979
|
+
requires_admin_intervention: errorType === 'permissions' || errorType === 'authentication',
|
|
1980
|
+
estimated_manual_time: '10-30 minutes',
|
|
1981
|
+
manual_steps: [
|
|
1982
|
+
'1. Review ServiceNow system logs for detailed error information',
|
|
1983
|
+
'2. Check user permissions and roles',
|
|
1984
|
+
'3. Verify artifact configuration meets ServiceNow requirements',
|
|
1985
|
+
'4. Consider alternative deployment approaches',
|
|
1986
|
+
'5. Contact ServiceNow administrator if permission issues persist'
|
|
1987
|
+
],
|
|
1988
|
+
technical_details: {
|
|
1989
|
+
original_error: deploymentContext.original_error,
|
|
1990
|
+
global_deployment_error: errorMessage,
|
|
1991
|
+
artifact_type: deploymentContext.artifact_type,
|
|
1992
|
+
deployment_strategy: strategy.name
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
701
1995
|
}
|
|
702
1996
|
async delay(ms) {
|
|
703
1997
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -710,6 +2004,60 @@ ${errorText}
|
|
|
710
2004
|
throw new Error(`Mock mode is not supported for ${operation}. All operations must use real ServiceNow instance.`);
|
|
711
2005
|
}
|
|
712
2006
|
}
|
|
2007
|
+
/**
|
|
2008
|
+
* List available MCP resources
|
|
2009
|
+
*/
|
|
2010
|
+
async listMCPResources() {
|
|
2011
|
+
try {
|
|
2012
|
+
const resources = await this.resourceManager.listResources();
|
|
2013
|
+
this.logger.debug(`Listed ${resources.length} MCP resources`);
|
|
2014
|
+
return {
|
|
2015
|
+
resources: resources.map(resource => ({
|
|
2016
|
+
uri: resource.uri,
|
|
2017
|
+
name: resource.name,
|
|
2018
|
+
description: resource.description,
|
|
2019
|
+
mimeType: resource.mimeType
|
|
2020
|
+
}))
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
catch (error) {
|
|
2024
|
+
this.logger.error('Failed to list MCP resources:', error);
|
|
2025
|
+
return { resources: [] };
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
/**
|
|
2029
|
+
* Read a specific MCP resource by URI
|
|
2030
|
+
*/
|
|
2031
|
+
async readMCPResource(uri) {
|
|
2032
|
+
try {
|
|
2033
|
+
const resource = await this.resourceManager.readResource(uri);
|
|
2034
|
+
this.logger.debug(`Read MCP resource: ${uri}`);
|
|
2035
|
+
return {
|
|
2036
|
+
contents: [{
|
|
2037
|
+
uri: resource.uri,
|
|
2038
|
+
mimeType: resource.mimeType,
|
|
2039
|
+
text: resource.text
|
|
2040
|
+
}]
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
catch (error) {
|
|
2044
|
+
this.logger.error(`Failed to read MCP resource ${uri}:`, error);
|
|
2045
|
+
throw new Error(`Resource not found or inaccessible: ${uri}`);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
/**
|
|
2049
|
+
* Get resource manager statistics
|
|
2050
|
+
*/
|
|
2051
|
+
getMCPResourceStats() {
|
|
2052
|
+
return this.resourceManager.getResourceStats();
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Clear resource cache (useful for development)
|
|
2056
|
+
*/
|
|
2057
|
+
clearMCPResourceCache() {
|
|
2058
|
+
this.resourceManager.clearCache();
|
|
2059
|
+
this.logger.debug('MCP resource cache cleared');
|
|
2060
|
+
}
|
|
713
2061
|
/**
|
|
714
2062
|
* Start the MCP server
|
|
715
2063
|
*/
|