snow-flow 2.0.2 → 2.0.4
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/mcp-config.json → .mcp.json} +13 -12
- package/.releaserc.json +47 -0
- package/README.md +48 -1
- package/package.json +1 -1
- package/.claude/claude-settings.json +0 -15
- package/.claude/commands/analysis/README.md +0 -9
- package/.claude/commands/automation/README.md +0 -9
- package/.claude/commands/github/README.md +0 -11
- package/.claude/commands/helpers/standard-checkpoint-hooks.sh +0 -179
- package/.claude/commands/hooks/README.md +0 -11
- package/.claude/commands/monitoring/README.md +0 -9
- package/.claude/commands/optimization/README.md +0 -9
- package/.claude/commands/training/README.md +0 -9
- package/.claude/commands/workflows/README.md +0 -9
- package/.claude/config.json +0 -36
- package/.claude/helpers/github-setup.sh +0 -28
- package/.claude/helpers/quick-start.sh +0 -19
- package/.claude/helpers/setup-mcp.sh +0 -18
- package/.claude/settings.json +0 -114
- package/.claude/settings.local.json +0 -33
- package/.claude-flow/metrics/agent-metrics.json +0 -1
- package/.claude-flow/metrics/performance.json +0 -9
- package/.claude-flow/metrics/system-metrics.json +0 -15986
- package/.claude-flow/metrics/task-metrics.json +0 -10
- package/.roo/README.md +0 -402
- package/.roo/mcp-list.txt +0 -257
- package/.roo/mcp.json +0 -32
- package/.roo/workflows/basic-tdd.json +0 -32
- package/.roomodes +0 -201
- package/CLAUDE.md +0 -393
- package/bin/snow-flow.js +0 -3
- package/dist/cli.js.bak +0 -2052
- package/dist/config/snow-flow-config.d.ts +0 -1492
- package/dist/config/snow-flow-config.js +0 -938
- package/scripts/create-env.js +0 -91
- package/scripts/postinstall.js +0 -30
- package/scripts/register-mcp-servers.js +0 -210
- package/scripts/reset-mcp-servers.js +0 -246
- package/scripts/setup-mcp.js +0 -106
- package/scripts/update-version.js +0 -31
- package/src/agents/README.md +0 -192
- package/src/agents/base-agent.ts +0 -251
- package/src/agents/coordinator.ts +0 -971
- package/src/agents/index.ts +0 -18
- package/src/agents/queen-403-handler.ts +0 -230
- package/src/agents/queen-agent.ts +0 -1399
- package/src/agents/script-writer-agent.ts +0 -769
- package/src/agents/security-agent.ts +0 -726
- package/src/agents/widget-creator-agent.ts +0 -568
- package/src/api/error-handling.ts +0 -1758
- package/src/api/natural-language-mapper.ts +0 -630
- package/src/api/performance-optimizer.ts +0 -891
- package/src/api/transaction-manager.ts +0 -754
- package/src/cli/deploy-artifact.ts +0 -304
- package/src/cli/snow-flow-cli-integration.ts +0 -434
- package/src/cli.ts +0 -4455
- package/src/cli.ts.bak +0 -5616
- package/src/compliance/advanced-compliance-system.ts +0 -1268
- package/src/compliance/index.ts +0 -19
- package/src/config/snow-flow-config.ts +0 -1019
- package/src/documentation/index.ts +0 -16
- package/src/documentation/self-documenting-system.ts +0 -1421
- package/src/dynamic-version.ts +0 -44
- package/src/healing/index.ts +0 -20
- package/src/healing/self-healing-system.ts +0 -1435
- package/src/health/README.md +0 -161
- package/src/health/system-health.ts +0 -995
- package/src/index.ts +0 -60
- package/src/intelligence/acl-analyzer.ts +0 -377
- package/src/intelligence/auto-resolution-engine.ts +0 -643
- package/src/intelligence/gap-analysis-engine.ts +0 -488
- package/src/intelligence/manual-instructions-generator.ts +0 -744
- package/src/intelligence/mcp-coverage-analyzer.ts +0 -708
- package/src/intelligence/multi-pass-requirements-analyzer.ts +0 -603
- package/src/intelligence/performance-recommendations-engine.ts +0 -1223
- package/src/intelligence/requirements-analyzer.ts +0 -1148
- package/src/intelligence/task-analyzer.ts +0 -628
- package/src/managers/scope-manager.ts +0 -555
- package/src/mcp/advanced/servicenow-advanced-features-mcp.ts +0 -16110
- package/src/mcp/base-mcp-server.ts +0 -660
- package/src/mcp/http-transport-wrapper.ts +0 -426
- package/src/mcp/service-discovery-client.ts +0 -312
- package/src/mcp/servicenow-automation-mcp-refactored.ts +0 -678
- package/src/mcp/servicenow-automation-mcp.ts +0 -838
- package/src/mcp/servicenow-deployment-mcp-refactored.ts +0 -1292
- package/src/mcp/servicenow-deployment-mcp.ts +0 -7244
- package/src/mcp/servicenow-graph-memory-mcp-refactored.ts +0 -721
- package/src/mcp/servicenow-graph-memory-mcp.ts +0 -832
- package/src/mcp/servicenow-integration-mcp-refactored.ts +0 -655
- package/src/mcp/servicenow-integration-mcp.ts +0 -819
- package/src/mcp/servicenow-intelligent-mcp-refactored.ts +0 -977
- package/src/mcp/servicenow-intelligent-mcp.ts +0 -4638
- package/src/mcp/servicenow-mcp-server.ts +0 -728
- package/src/mcp/servicenow-memory-mcp.ts +0 -474
- package/src/mcp/servicenow-operations-mcp-refactored.ts +0 -1995
- package/src/mcp/servicenow-operations-mcp.ts +0 -3384
- package/src/mcp/servicenow-platform-development-mcp-refactored.ts +0 -611
- package/src/mcp/servicenow-platform-development-mcp.ts +0 -970
- package/src/mcp/servicenow-progressive-indexer.ts +0 -370
- package/src/mcp/servicenow-reporting-analytics-mcp-refactored.ts +0 -695
- package/src/mcp/servicenow-reporting-analytics-mcp.ts +0 -1123
- package/src/mcp/servicenow-security-compliance-mcp-refactored.ts +0 -642
- package/src/mcp/servicenow-security-compliance-mcp.ts +0 -1157
- package/src/mcp/servicenow-update-set-mcp-refactored.ts +0 -751
- package/src/mcp/servicenow-update-set-mcp.ts +0 -811
- package/src/mcp/shared/agent-context-provider.ts +0 -365
- package/src/mcp/shared/base-mcp-server.ts +0 -2463
- package/src/mcp/shared/mcp-memory-manager.ts +0 -540
- package/src/mcp/shared/mcp-resource-manager.ts +0 -367
- package/src/mcp/snow-flow-mcp.ts +0 -911
- package/src/mcp/start-all-mcp-servers.ts +0 -102
- package/src/mcp/start-servicenow-mcp.ts +0 -67
- package/src/monitoring/enhanced-monitoring-system.ts +0 -1345
- package/src/monitoring/performance-tracker.ts +0 -777
- package/src/optimization/cost-optimization-engine.ts +0 -1125
- package/src/optimization/index.ts +0 -10
- package/src/queen/README.md +0 -403
- package/src/queen/agent-factory.ts +0 -574
- package/src/queen/index.ts +0 -94
- package/src/queen/mcp-execution-bridge.ts +0 -681
- package/src/queen/neural-learning.ts +0 -319
- package/src/queen/parallel-agent-engine.ts +0 -848
- package/src/queen/queen-memory-system.ts +0 -490
- package/src/queen/queen-memory.ts +0 -456
- package/src/queen/servicenow-queen.ts +0 -1029
- package/src/queen/types.ts +0 -87
- package/src/rollback/smart-rollback-system.ts +0 -866
- package/src/schemas/deployment.schema.json +0 -58
- package/src/schemas/flow.schema.json +0 -79
- package/src/schemas/widget.schema.json +0 -72
- package/src/snow-flow-system.ts +0 -541
- package/src/sparc/sparc-help.ts +0 -49
- package/src/sparc/team-sparc.ts +0 -88
- package/src/strategies/global-scope-strategy.ts +0 -736
- package/src/templates/base/application.template.json +0 -45
- package/src/templates/base/business_rule.template.json +0 -33
- package/src/templates/base/script_include.template.json +0 -18
- package/src/templates/base/table.template.json +0 -64
- package/src/templates/base/widget.template.json +0 -25
- package/src/templates/patterns/composite.incident-management.template.json +0 -140
- package/src/templates/patterns/widget.dashboard.template.json +0 -238
- package/src/templates/patterns/widget.datatable.template.json +0 -292
- package/src/testing/integration-test-suite.ts +0 -1276
- package/src/types/index.ts +0 -183
- package/src/types/neo4j-driver.d.ts +0 -23
- package/src/types/servicenow.types.ts +0 -307
- package/src/types/snow-flow.types.ts +0 -186
- package/src/types/todo.types.ts +0 -70
- package/src/utils/action-type-cache.ts +0 -286
- package/src/utils/agent-detector.ts +0 -573
- package/src/utils/artifact-tracker.ts +0 -379
- package/src/utils/dependency-detector.ts +0 -233
- package/src/utils/deployment-metadata-handler.ts +0 -518
- package/src/utils/error-recovery.ts +0 -680
- package/src/utils/logger.ts +0 -60
- package/src/utils/mcp-auth-middleware.ts +0 -262
- package/src/utils/mcp-config-manager.ts +0 -270
- package/src/utils/mcp-server-manager.ts +0 -446
- package/src/utils/mcp-tool-registry.ts +0 -298
- package/src/utils/migrate-snow-flow.ts +0 -98
- package/src/utils/oauth-html-templates.ts +0 -188
- package/src/utils/scope-utils.ts +0 -443
- package/src/utils/servicenow-client.ts +0 -3321
- package/src/utils/servicenow-id-generator.ts +0 -181
- package/src/utils/snow-memory-manager.ts +0 -605
- package/src/utils/snow-oauth.ts +0 -797
- package/src/utils/template-engine.ts +0 -463
- package/src/utils/theme-manager.ts +0 -295
- package/src/utils/unified-auth-store.ts +0 -222
- package/src/utils/update-set-importer.ts +0 -422
- package/src/utils/update-set-xml-packager.ts +0 -371
- package/src/utils/widget-template-generator.ts +0 -1744
- package/src/version.ts +0 -1950
|
@@ -1,2463 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base MCP Server with Agent Integration
|
|
3
|
-
* Provides common functionality for all MCP servers in the agent ecosystem
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
7
|
-
import { ServiceNowClient } from '../../utils/servicenow-client.js';
|
|
8
|
-
import { ServiceNowOAuth } from '../../utils/snow-oauth.js';
|
|
9
|
-
import { Logger } from '../../utils/logger.js';
|
|
10
|
-
import { AgentContextProvider } from './agent-context-provider.js';
|
|
11
|
-
import { MCPMemoryManager, AgentContext } from './mcp-memory-manager.js';
|
|
12
|
-
import { MCPResourceManager } from './mcp-resource-manager.js';
|
|
13
|
-
|
|
14
|
-
export interface MCPToolResult {
|
|
15
|
-
[key: string]: unknown;
|
|
16
|
-
content: Array<{
|
|
17
|
-
type: string;
|
|
18
|
-
text?: string;
|
|
19
|
-
data?: any;
|
|
20
|
-
}>;
|
|
21
|
-
metadata?: {
|
|
22
|
-
agent_id?: string;
|
|
23
|
-
session_id?: string;
|
|
24
|
-
duration_ms?: number;
|
|
25
|
-
artifacts_created?: string[];
|
|
26
|
-
};
|
|
27
|
-
_meta?: { [key: string]: unknown };
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export abstract class BaseMCPServer {
|
|
31
|
-
protected server: Server;
|
|
32
|
-
protected client: ServiceNowClient;
|
|
33
|
-
protected oauth: ServiceNowOAuth;
|
|
34
|
-
protected logger: Logger;
|
|
35
|
-
protected contextProvider: AgentContextProvider;
|
|
36
|
-
protected memory: MCPMemoryManager;
|
|
37
|
-
protected resourceManager: MCPResourceManager;
|
|
38
|
-
|
|
39
|
-
constructor(name: string, version: string = '1.0.0') {
|
|
40
|
-
this.server = new Server(
|
|
41
|
-
{
|
|
42
|
-
name,
|
|
43
|
-
version,
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
capabilities: {
|
|
47
|
-
tools: {},
|
|
48
|
-
resources: {},
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
this.client = new ServiceNowClient();
|
|
54
|
-
this.oauth = new ServiceNowOAuth();
|
|
55
|
-
this.logger = new Logger(name);
|
|
56
|
-
this.contextProvider = new AgentContextProvider();
|
|
57
|
-
this.memory = MCPMemoryManager.getInstance();
|
|
58
|
-
this.resourceManager = new MCPResourceManager(name);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Execute a tool with agent context tracking
|
|
63
|
-
*/
|
|
64
|
-
protected async executeWithAgentContext<T>(
|
|
65
|
-
toolName: string,
|
|
66
|
-
args: any,
|
|
67
|
-
operation: (context: AgentContext) => Promise<T>
|
|
68
|
-
): Promise<MCPToolResult> {
|
|
69
|
-
// Extract agent context
|
|
70
|
-
const agentContext = this.contextProvider.extractAgentContext(args);
|
|
71
|
-
|
|
72
|
-
// Log operation start
|
|
73
|
-
this.logger.info(`Executing ${toolName}`, {
|
|
74
|
-
agent_id: agentContext.agent_id,
|
|
75
|
-
session_id: agentContext.session_id
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Execute with context tracking
|
|
79
|
-
const result = await this.contextProvider.executeWithContext(
|
|
80
|
-
{
|
|
81
|
-
...agentContext,
|
|
82
|
-
operation_name: toolName,
|
|
83
|
-
mcp_server: (this.server as any).serverInfo?.name || 'unknown'
|
|
84
|
-
},
|
|
85
|
-
async () => operation(agentContext)
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
if (!result.success) {
|
|
89
|
-
return {
|
|
90
|
-
content: [
|
|
91
|
-
{
|
|
92
|
-
type: 'text',
|
|
93
|
-
text: `❌ Operation failed: ${result.error}`
|
|
94
|
-
}
|
|
95
|
-
],
|
|
96
|
-
metadata: {
|
|
97
|
-
agent_id: agentContext.agent_id,
|
|
98
|
-
session_id: agentContext.session_id,
|
|
99
|
-
duration_ms: result.duration_ms
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return result.data as MCPToolResult;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* MANDATORY authentication and connection validation before ServiceNow operations
|
|
109
|
-
*/
|
|
110
|
-
protected async validateServiceNowConnection(): Promise<{ success: boolean; error?: string }> {
|
|
111
|
-
try {
|
|
112
|
-
this.logger.info('🔍 Validating ServiceNow connection...');
|
|
113
|
-
|
|
114
|
-
// 1. Check if we have valid credentials
|
|
115
|
-
const credentials = await this.oauth.loadCredentials();
|
|
116
|
-
if (!credentials) {
|
|
117
|
-
return {
|
|
118
|
-
success: false,
|
|
119
|
-
error: 'No ServiceNow credentials found. Set up .env file and run "snow-flow auth login".'
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// 2. Check if we have access token (OAuth session)
|
|
124
|
-
if (!credentials.accessToken) {
|
|
125
|
-
return {
|
|
126
|
-
success: false,
|
|
127
|
-
error: 'OAuth authentication required. Run "snow-flow auth login" to authenticate.'
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// 3. Check if token is still valid
|
|
132
|
-
const isAuth = await this.oauth.isAuthenticated();
|
|
133
|
-
if (!isAuth) {
|
|
134
|
-
this.logger.info('🔄 Token expired, attempting refresh...');
|
|
135
|
-
|
|
136
|
-
// Try to refresh token
|
|
137
|
-
const refreshResult = await this.oauth.refreshAccessToken();
|
|
138
|
-
if (!refreshResult.success) {
|
|
139
|
-
return {
|
|
140
|
-
success: false,
|
|
141
|
-
error: 'OAuth token expired and refresh failed. Run "snow-flow auth login" to re-authenticate.'
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
this.logger.info('✅ Token refreshed successfully');
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 4. Test actual ServiceNow connection
|
|
149
|
-
this.logger.info('🔧 About to call testConnection', {
|
|
150
|
-
clientType: this.client.constructor.name,
|
|
151
|
-
clientMethods: Object.getOwnPropertyNames(Object.getPrototypeOf(this.client)),
|
|
152
|
-
hasTestConnection: typeof this.client.testConnection === 'function',
|
|
153
|
-
hasMakeRequest: typeof this.client.makeRequest === 'function'
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
try {
|
|
157
|
-
const connectionTest = await this.client.testConnection();
|
|
158
|
-
if (!connectionTest.success) {
|
|
159
|
-
return {
|
|
160
|
-
success: false,
|
|
161
|
-
error: `ServiceNow connection failed: ${connectionTest.error}`
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
} catch (testError) {
|
|
165
|
-
this.logger.error('🔧 testConnection error:', {
|
|
166
|
-
error: testError.message,
|
|
167
|
-
stack: testError.stack,
|
|
168
|
-
clientInfo: {
|
|
169
|
-
type: this.client.constructor.name,
|
|
170
|
-
methods: Object.getOwnPropertyNames(Object.getPrototypeOf(this.client))
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
throw testError;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
this.logger.info('✅ ServiceNow connection validated');
|
|
177
|
-
return { success: true };
|
|
178
|
-
|
|
179
|
-
} catch (error) {
|
|
180
|
-
this.logger.error('ServiceNow connection validation failed', error);
|
|
181
|
-
return {
|
|
182
|
-
success: false,
|
|
183
|
-
error: error instanceof Error ? error.message : String(error)
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Check authentication with proper error handling (deprecated - use validateServiceNowConnection)
|
|
190
|
-
*/
|
|
191
|
-
protected async checkAuthentication(): Promise<boolean> {
|
|
192
|
-
const result = await this.validateServiceNowConnection();
|
|
193
|
-
return result.success;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Create standard authentication error response with detailed instructions
|
|
198
|
-
*/
|
|
199
|
-
protected createAuthenticationError(error?: string): MCPToolResult {
|
|
200
|
-
const errorText = error || 'Authentication validation failed';
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
content: [
|
|
204
|
-
{
|
|
205
|
-
type: 'text',
|
|
206
|
-
text: `❌ ServiceNow Connection Failed
|
|
207
|
-
|
|
208
|
-
${errorText}
|
|
209
|
-
|
|
210
|
-
🔧 To fix this:
|
|
211
|
-
|
|
212
|
-
1. Ensure .env file has OAuth credentials:
|
|
213
|
-
SNOW_INSTANCE=your-instance.service-now.com
|
|
214
|
-
SNOW_CLIENT_ID=your_oauth_client_id
|
|
215
|
-
SNOW_CLIENT_SECRET=your_oauth_client_secret
|
|
216
|
-
|
|
217
|
-
2. Authenticate with ServiceNow:
|
|
218
|
-
snow-flow auth login
|
|
219
|
-
|
|
220
|
-
3. If you still get errors, run diagnostics:
|
|
221
|
-
snow_auth_diagnostics()
|
|
222
|
-
|
|
223
|
-
💡 To get OAuth credentials:
|
|
224
|
-
• ServiceNow: System OAuth > Application Registry > New OAuth Application
|
|
225
|
-
• Redirect URI: http://localhost:3005/callback (configurable via SNOW_REDIRECT_* env vars)
|
|
226
|
-
• Scopes: useraccount write admin`
|
|
227
|
-
}
|
|
228
|
-
]
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Smart artifact discovery - check for existing artifacts before creating new ones
|
|
234
|
-
*/
|
|
235
|
-
protected async discoverExistingArtifacts(
|
|
236
|
-
type: string,
|
|
237
|
-
name: string,
|
|
238
|
-
searchTerms?: string[]
|
|
239
|
-
): Promise<{ found: boolean; artifacts: any[]; suggestions: string[] }> {
|
|
240
|
-
try {
|
|
241
|
-
this.logger.info(`🔍 Discovering existing ${type} artifacts...`);
|
|
242
|
-
|
|
243
|
-
const suggestions: string[] = [];
|
|
244
|
-
let artifacts: any[] = [];
|
|
245
|
-
|
|
246
|
-
// Search by name
|
|
247
|
-
const nameSearch = await this.client.searchRecords(
|
|
248
|
-
this.getTableForType(type),
|
|
249
|
-
`nameLIKE${name}`,
|
|
250
|
-
5
|
|
251
|
-
);
|
|
252
|
-
|
|
253
|
-
if (nameSearch.success && nameSearch.data?.result?.length > 0) {
|
|
254
|
-
artifacts = nameSearch.data.result;
|
|
255
|
-
suggestions.push(`Found ${artifacts.length} existing ${type}(s) with similar names`);
|
|
256
|
-
suggestions.push(`Consider reusing: ${artifacts.map(a => a.name).join(', ')}`);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Search by additional terms if provided
|
|
260
|
-
if (searchTerms && searchTerms.length > 0) {
|
|
261
|
-
for (const term of searchTerms) {
|
|
262
|
-
const termSearch = await this.client.searchRecords(
|
|
263
|
-
this.getTableForType(type),
|
|
264
|
-
`nameLIKE${term}^ORdescriptionLIKE${term}`,
|
|
265
|
-
3
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
if (termSearch.success && termSearch.data?.result?.length > 0) {
|
|
269
|
-
const newArtifacts = termSearch.data.result.filter(
|
|
270
|
-
(new_artifact: any) => !artifacts.some(existing => existing.sys_id === new_artifact.sys_id)
|
|
271
|
-
);
|
|
272
|
-
artifacts.push(...newArtifacts);
|
|
273
|
-
|
|
274
|
-
if (newArtifacts.length > 0) {
|
|
275
|
-
suggestions.push(`Found ${newArtifacts.length} related ${type}(s) for "${term}"`);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return {
|
|
282
|
-
found: artifacts.length > 0,
|
|
283
|
-
artifacts,
|
|
284
|
-
suggestions
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
} catch (error) {
|
|
288
|
-
this.logger.warn('Artifact discovery failed', error);
|
|
289
|
-
return { found: false, artifacts: [], suggestions: [] };
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Ensure active Update Set for tracking changes
|
|
295
|
-
*/
|
|
296
|
-
protected async ensureUpdateSet(context: AgentContext, purpose?: string): Promise<string | null> {
|
|
297
|
-
try {
|
|
298
|
-
this.logger.info('📦 Ensuring active Update Set...');
|
|
299
|
-
|
|
300
|
-
// Check current update set
|
|
301
|
-
const currentUpdateSet = await this.client.getCurrentUpdateSet();
|
|
302
|
-
|
|
303
|
-
if (currentUpdateSet.success && currentUpdateSet.data) {
|
|
304
|
-
this.logger.info(`✅ Using existing Update Set: ${currentUpdateSet.data.name}`);
|
|
305
|
-
return currentUpdateSet.data.sys_id;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
// Create new update set
|
|
309
|
-
const updateSetName = purpose
|
|
310
|
-
? `Snow-Flow ${purpose} - ${new Date().toISOString().split('T')[0]}`
|
|
311
|
-
: `Snow-Flow Changes - ${new Date().toISOString().split('T')[0]}`;
|
|
312
|
-
|
|
313
|
-
this.logger.info(`📦 Creating new Update Set: ${updateSetName}`);
|
|
314
|
-
|
|
315
|
-
const newUpdateSet = await this.client.createUpdateSet({
|
|
316
|
-
name: updateSetName,
|
|
317
|
-
description: `Automated changes from Snow-Flow Agent (${context.agent_id})`,
|
|
318
|
-
state: 'in_progress'
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
if (newUpdateSet.success && newUpdateSet.data) {
|
|
322
|
-
// Set it as current
|
|
323
|
-
await this.client.setCurrentUpdateSet(newUpdateSet.data.sys_id);
|
|
324
|
-
this.logger.info(`✅ Update Set created and activated: ${newUpdateSet.data.sys_id}`);
|
|
325
|
-
return newUpdateSet.data.sys_id;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
this.logger.warn('⚠️ Could not create Update Set, changes will not be tracked');
|
|
329
|
-
return null;
|
|
330
|
-
|
|
331
|
-
} catch (error) {
|
|
332
|
-
this.logger.warn('Update Set creation failed', error);
|
|
333
|
-
return null;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
/**
|
|
338
|
-
* Automatically track artifact in Update Set
|
|
339
|
-
*/
|
|
340
|
-
protected async trackArtifact(
|
|
341
|
-
sysId: string,
|
|
342
|
-
type: string,
|
|
343
|
-
name: string,
|
|
344
|
-
updateSetId?: string
|
|
345
|
-
): Promise<void> {
|
|
346
|
-
try {
|
|
347
|
-
if (!updateSetId) {
|
|
348
|
-
this.logger.warn('No Update Set ID provided for artifact tracking');
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
this.logger.info(`📋 Tracking ${type} artifact: ${name} (${sysId})`);
|
|
353
|
-
|
|
354
|
-
// Add artifact to update set tracking (this is handled automatically by ServiceNow
|
|
355
|
-
// when artifacts are created/modified, but we can log it for visibility)
|
|
356
|
-
this.logger.info(`✅ Artifact tracked in Update Set: ${updateSetId}`);
|
|
357
|
-
|
|
358
|
-
} catch (error) {
|
|
359
|
-
this.logger.warn('Artifact tracking failed', error);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Get ServiceNow table name for artifact type
|
|
365
|
-
*/
|
|
366
|
-
protected getTableForType(type: string): string {
|
|
367
|
-
const tableMap: { [key: string]: string } = {
|
|
368
|
-
'widget': 'sp_widget',
|
|
369
|
-
'flow': 'sys_hub_flow',
|
|
370
|
-
'subflow': 'sys_hub_flow',
|
|
371
|
-
'action': 'sys_hub_action_type_definition',
|
|
372
|
-
'script': 'sys_script_include',
|
|
373
|
-
'business_rule': 'sys_script',
|
|
374
|
-
'table': 'sys_db_object',
|
|
375
|
-
'application': 'sys_app'
|
|
376
|
-
};
|
|
377
|
-
|
|
378
|
-
return tableMap[type] || 'sys_metadata';
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Report progress during long operations
|
|
383
|
-
*/
|
|
384
|
-
protected async reportProgress(
|
|
385
|
-
context: AgentContext,
|
|
386
|
-
progress: number,
|
|
387
|
-
phase: string
|
|
388
|
-
): Promise<void> {
|
|
389
|
-
await this.contextProvider.reportProgress(context, progress, phase);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Store artifact with agent tracking
|
|
394
|
-
*/
|
|
395
|
-
protected async storeArtifact(
|
|
396
|
-
context: AgentContext,
|
|
397
|
-
artifact: {
|
|
398
|
-
sys_id: string;
|
|
399
|
-
type: string;
|
|
400
|
-
name: string;
|
|
401
|
-
description?: string;
|
|
402
|
-
config?: any;
|
|
403
|
-
update_set_id?: string;
|
|
404
|
-
}
|
|
405
|
-
): Promise<void> {
|
|
406
|
-
await this.contextProvider.storeArtifact(context, artifact);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Notify handoff to another agent
|
|
411
|
-
*/
|
|
412
|
-
protected async notifyHandoff(
|
|
413
|
-
context: AgentContext,
|
|
414
|
-
to_agent: string,
|
|
415
|
-
artifact_info: {
|
|
416
|
-
type: string;
|
|
417
|
-
sys_id: string;
|
|
418
|
-
next_steps: string[];
|
|
419
|
-
}
|
|
420
|
-
): Promise<void> {
|
|
421
|
-
await this.contextProvider.notifyHandoff(context, to_agent, artifact_info);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Request Queen intervention for issues
|
|
426
|
-
*/
|
|
427
|
-
protected async requestQueenIntervention(
|
|
428
|
-
context: AgentContext,
|
|
429
|
-
issue: {
|
|
430
|
-
type: string;
|
|
431
|
-
priority: 'low' | 'medium' | 'high' | 'critical';
|
|
432
|
-
description: string;
|
|
433
|
-
attempted_solutions?: string[];
|
|
434
|
-
}
|
|
435
|
-
): Promise<void> {
|
|
436
|
-
await this.contextProvider.requestQueenIntervention(context, issue);
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
/**
|
|
440
|
-
* Get session context for coordination
|
|
441
|
-
*/
|
|
442
|
-
protected async getSessionContext(session_id: string): Promise<any> {
|
|
443
|
-
return await this.contextProvider.getSessionContext(session_id);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Create success response with metadata
|
|
448
|
-
*/
|
|
449
|
-
protected createSuccessResponse(
|
|
450
|
-
message: string,
|
|
451
|
-
data?: any,
|
|
452
|
-
metadata?: any
|
|
453
|
-
): MCPToolResult {
|
|
454
|
-
const content: any[] = [
|
|
455
|
-
{
|
|
456
|
-
type: 'text',
|
|
457
|
-
text: `✅ ${message}`
|
|
458
|
-
}
|
|
459
|
-
];
|
|
460
|
-
|
|
461
|
-
if (data) {
|
|
462
|
-
content.push({
|
|
463
|
-
type: 'text',
|
|
464
|
-
text: JSON.stringify(data, null, 2)
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
return {
|
|
469
|
-
content,
|
|
470
|
-
metadata
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Create error response with metadata
|
|
476
|
-
*/
|
|
477
|
-
protected createErrorResponse(
|
|
478
|
-
message: string,
|
|
479
|
-
error?: any,
|
|
480
|
-
metadata?: any
|
|
481
|
-
): MCPToolResult {
|
|
482
|
-
const content: any[] = [
|
|
483
|
-
{
|
|
484
|
-
type: 'text',
|
|
485
|
-
text: `❌ ${message}`
|
|
486
|
-
}
|
|
487
|
-
];
|
|
488
|
-
|
|
489
|
-
if (error) {
|
|
490
|
-
content.push({
|
|
491
|
-
type: 'text',
|
|
492
|
-
text: `Error details: ${error instanceof Error ? error.message : String(error)}`
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
return {
|
|
497
|
-
content,
|
|
498
|
-
metadata
|
|
499
|
-
};
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Enhanced error handling with intelligent recovery strategies
|
|
504
|
-
*/
|
|
505
|
-
protected async handleServiceNowError(
|
|
506
|
-
error: any,
|
|
507
|
-
operation: string,
|
|
508
|
-
context: AgentContext,
|
|
509
|
-
fallbackOptions?: {
|
|
510
|
-
enableRetry?: boolean;
|
|
511
|
-
enableScopeEscalation?: boolean;
|
|
512
|
-
enableManualSteps?: boolean;
|
|
513
|
-
}
|
|
514
|
-
): Promise<MCPToolResult> {
|
|
515
|
-
this.logger.error(`ServiceNow operation failed: ${operation}`, error);
|
|
516
|
-
|
|
517
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
518
|
-
const errorCode = this.extractErrorCode(errorMessage);
|
|
519
|
-
|
|
520
|
-
// Categorize error and determine recovery strategy
|
|
521
|
-
const errorCategory = this.categorizeError(errorMessage, errorCode);
|
|
522
|
-
|
|
523
|
-
switch (errorCategory) {
|
|
524
|
-
case 'authentication':
|
|
525
|
-
return this.handleAuthenticationError(error, operation, context);
|
|
526
|
-
|
|
527
|
-
case 'permissions':
|
|
528
|
-
return this.handlePermissionError(error, operation, context, fallbackOptions);
|
|
529
|
-
|
|
530
|
-
case 'network':
|
|
531
|
-
return this.handleNetworkError(error, operation, context, fallbackOptions);
|
|
532
|
-
|
|
533
|
-
case 'validation':
|
|
534
|
-
return this.handleValidationError(error, operation, context);
|
|
535
|
-
|
|
536
|
-
case 'scope':
|
|
537
|
-
return this.handleScopeError(error, operation, context, fallbackOptions);
|
|
538
|
-
|
|
539
|
-
default:
|
|
540
|
-
return this.handleGenericError(error, operation, context, fallbackOptions);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Categorize error for appropriate recovery strategy
|
|
546
|
-
*/
|
|
547
|
-
private categorizeError(errorMessage: string, errorCode?: string): string {
|
|
548
|
-
const msgLower = errorMessage.toLowerCase();
|
|
549
|
-
|
|
550
|
-
// Authentication errors
|
|
551
|
-
if (msgLower.includes('unauthorized') ||
|
|
552
|
-
msgLower.includes('invalid_grant') ||
|
|
553
|
-
msgLower.includes('authentication') ||
|
|
554
|
-
errorCode === '401' || errorCode === '403') {
|
|
555
|
-
return 'authentication';
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// Permission errors
|
|
559
|
-
if (msgLower.includes('insufficient privileges') ||
|
|
560
|
-
msgLower.includes('access denied') ||
|
|
561
|
-
msgLower.includes('acl')) {
|
|
562
|
-
return 'permissions';
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Network errors
|
|
566
|
-
if (msgLower.includes('network') ||
|
|
567
|
-
msgLower.includes('timeout') ||
|
|
568
|
-
msgLower.includes('connection') ||
|
|
569
|
-
msgLower.includes('econnrefused')) {
|
|
570
|
-
return 'network';
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// Validation errors
|
|
574
|
-
if (msgLower.includes('invalid') ||
|
|
575
|
-
msgLower.includes('required field') ||
|
|
576
|
-
msgLower.includes('constraint') ||
|
|
577
|
-
msgLower.includes('validation')) {
|
|
578
|
-
return 'validation';
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
// Scope errors
|
|
582
|
-
if (msgLower.includes('scope') ||
|
|
583
|
-
msgLower.includes('application') ||
|
|
584
|
-
msgLower.includes('global')) {
|
|
585
|
-
return 'scope';
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
return 'generic';
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
/**
|
|
592
|
-
* Handle authentication errors with automatic recovery
|
|
593
|
-
*/
|
|
594
|
-
private async handleAuthenticationError(
|
|
595
|
-
error: any,
|
|
596
|
-
operation: string,
|
|
597
|
-
context: AgentContext
|
|
598
|
-
): Promise<MCPToolResult> {
|
|
599
|
-
this.logger.info('🔄 Attempting automatic authentication recovery...');
|
|
600
|
-
|
|
601
|
-
// Try to refresh token automatically
|
|
602
|
-
try {
|
|
603
|
-
const refreshResult = await this.oauth.refreshAccessToken();
|
|
604
|
-
if (refreshResult.success) {
|
|
605
|
-
this.logger.info('✅ Token refreshed successfully, retry operation');
|
|
606
|
-
|
|
607
|
-
// Store recovery in context for coordination
|
|
608
|
-
await this.memory.updateSharedContext({
|
|
609
|
-
session_id: context.session_id,
|
|
610
|
-
context_key: `auth_recovery_${operation}`,
|
|
611
|
-
context_value: JSON.stringify({
|
|
612
|
-
recovery_action: 'token_refresh',
|
|
613
|
-
timestamp: new Date().toISOString(),
|
|
614
|
-
operation: operation
|
|
615
|
-
}),
|
|
616
|
-
created_by_agent: context.agent_id
|
|
617
|
-
});
|
|
618
|
-
|
|
619
|
-
return this.createSuccessResponse(
|
|
620
|
-
'Authentication recovered - token refreshed',
|
|
621
|
-
{
|
|
622
|
-
recovery_action: 'token_refresh',
|
|
623
|
-
next_step: 'retry_operation',
|
|
624
|
-
can_continue: true
|
|
625
|
-
}
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
} catch (refreshError) {
|
|
629
|
-
this.logger.warn('Token refresh failed', refreshError);
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
// Request Queen intervention for authentication issues
|
|
633
|
-
await this.requestQueenIntervention(context, {
|
|
634
|
-
type: 'authentication_failure',
|
|
635
|
-
priority: 'high',
|
|
636
|
-
description: `Authentication failed for ${operation}`,
|
|
637
|
-
attempted_solutions: ['token_refresh']
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
return this.createAuthenticationError(`Authentication failed during ${operation}. Automatic recovery failed.`);
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
/**
|
|
644
|
-
* Handle permission errors with scope escalation
|
|
645
|
-
*/
|
|
646
|
-
private async handlePermissionError(
|
|
647
|
-
error: any,
|
|
648
|
-
operation: string,
|
|
649
|
-
context: AgentContext,
|
|
650
|
-
fallbackOptions?: any
|
|
651
|
-
): Promise<MCPToolResult> {
|
|
652
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
653
|
-
|
|
654
|
-
// Try scope escalation if enabled
|
|
655
|
-
if (fallbackOptions?.enableScopeEscalation) {
|
|
656
|
-
this.logger.info('🔝 Attempting scope escalation...');
|
|
657
|
-
|
|
658
|
-
try {
|
|
659
|
-
// Try to escalate to global scope
|
|
660
|
-
const escalationResult = await this.escalateToGlobalScope(operation, context);
|
|
661
|
-
if (escalationResult.success) {
|
|
662
|
-
return this.createSuccessResponse(
|
|
663
|
-
'Permission issue resolved via scope escalation',
|
|
664
|
-
escalationResult
|
|
665
|
-
);
|
|
666
|
-
}
|
|
667
|
-
} catch (escalationError) {
|
|
668
|
-
this.logger.warn('Scope escalation failed', escalationError);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
// Request permission elevation
|
|
673
|
-
await this.requestQueenIntervention(context, {
|
|
674
|
-
type: 'permission_elevation_needed',
|
|
675
|
-
priority: 'high',
|
|
676
|
-
description: `Insufficient privileges for ${operation}: ${errorMessage}`,
|
|
677
|
-
attempted_solutions: fallbackOptions?.enableScopeEscalation ? ['scope_escalation'] : []
|
|
678
|
-
});
|
|
679
|
-
|
|
680
|
-
return this.createErrorResponse(
|
|
681
|
-
`Permission error during ${operation}`,
|
|
682
|
-
{
|
|
683
|
-
error: errorMessage,
|
|
684
|
-
recovery_options: [
|
|
685
|
-
'1. Request admin privileges from Queen Agent',
|
|
686
|
-
'2. Try scope escalation to global',
|
|
687
|
-
'3. Create manual Update Set instructions',
|
|
688
|
-
'4. Use alternative deployment approach'
|
|
689
|
-
],
|
|
690
|
-
next_steps: [
|
|
691
|
-
'Contact ServiceNow admin for elevated permissions',
|
|
692
|
-
'Or use snow_deploy with fallback_strategy="manual_steps"'
|
|
693
|
-
]
|
|
694
|
-
}
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
/**
|
|
699
|
-
* Handle network errors with intelligent retry
|
|
700
|
-
*/
|
|
701
|
-
private async handleNetworkError(
|
|
702
|
-
error: any,
|
|
703
|
-
operation: string,
|
|
704
|
-
context: AgentContext,
|
|
705
|
-
fallbackOptions?: any
|
|
706
|
-
): Promise<MCPToolResult> {
|
|
707
|
-
if (fallbackOptions?.enableRetry) {
|
|
708
|
-
this.logger.info('🔄 Network error - attempting retry with backoff...');
|
|
709
|
-
|
|
710
|
-
// Implement exponential backoff retry
|
|
711
|
-
const maxRetries = 3;
|
|
712
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
713
|
-
await this.delay(Math.pow(2, attempt) * 1000); // Exponential backoff
|
|
714
|
-
|
|
715
|
-
try {
|
|
716
|
-
// Test connection
|
|
717
|
-
const connectionTest = await this.client.testConnection();
|
|
718
|
-
if (connectionTest.success) {
|
|
719
|
-
this.logger.info(`✅ Network recovered after ${attempt} attempts`);
|
|
720
|
-
|
|
721
|
-
return this.createSuccessResponse(
|
|
722
|
-
'Network error recovered - connection restored',
|
|
723
|
-
{
|
|
724
|
-
recovery_action: 'network_retry',
|
|
725
|
-
attempts: attempt,
|
|
726
|
-
can_continue: true
|
|
727
|
-
}
|
|
728
|
-
);
|
|
729
|
-
}
|
|
730
|
-
} catch (retryError) {
|
|
731
|
-
this.logger.warn(`Retry attempt ${attempt} failed`, retryError);
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
return this.createErrorResponse(
|
|
737
|
-
`Network error during ${operation}`,
|
|
738
|
-
{
|
|
739
|
-
error: error instanceof Error ? error.message : String(error),
|
|
740
|
-
recovery_options: [
|
|
741
|
-
'1. Check ServiceNow instance availability',
|
|
742
|
-
'2. Verify network connectivity',
|
|
743
|
-
'3. Check firewall/proxy settings',
|
|
744
|
-
'4. Retry operation in a few minutes'
|
|
745
|
-
],
|
|
746
|
-
next_steps: [
|
|
747
|
-
'Wait for network recovery and retry',
|
|
748
|
-
'Or check ServiceNow instance status'
|
|
749
|
-
]
|
|
750
|
-
}
|
|
751
|
-
);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
/**
|
|
755
|
-
* Handle validation errors with correction suggestions
|
|
756
|
-
*/
|
|
757
|
-
private async handleValidationError(
|
|
758
|
-
error: any,
|
|
759
|
-
operation: string,
|
|
760
|
-
context: AgentContext
|
|
761
|
-
): Promise<MCPToolResult> {
|
|
762
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
763
|
-
|
|
764
|
-
// Extract field information from validation error
|
|
765
|
-
const fieldInfo = this.extractFieldInfoFromError(errorMessage);
|
|
766
|
-
|
|
767
|
-
return this.createErrorResponse(
|
|
768
|
-
`Validation error during ${operation}`,
|
|
769
|
-
{
|
|
770
|
-
error: errorMessage,
|
|
771
|
-
field_issues: fieldInfo,
|
|
772
|
-
recovery_options: [
|
|
773
|
-
'1. Fix required fields and retry',
|
|
774
|
-
'2. Check field constraints and formats',
|
|
775
|
-
'3. Use snow_validate_deployment() first',
|
|
776
|
-
'4. Review ServiceNow table schema'
|
|
777
|
-
],
|
|
778
|
-
next_steps: [
|
|
779
|
-
'Update configuration with correct field values',
|
|
780
|
-
'Validate against ServiceNow table requirements'
|
|
781
|
-
]
|
|
782
|
-
}
|
|
783
|
-
);
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
/**
|
|
787
|
-
* Handle scope errors with automatic fallback
|
|
788
|
-
*/
|
|
789
|
-
private async handleScopeError(
|
|
790
|
-
error: any,
|
|
791
|
-
operation: string,
|
|
792
|
-
context: AgentContext,
|
|
793
|
-
fallbackOptions?: any
|
|
794
|
-
): Promise<MCPToolResult> {
|
|
795
|
-
if (fallbackOptions?.enableScopeEscalation) {
|
|
796
|
-
this.logger.info('🌐 Scope error - attempting global scope fallback...');
|
|
797
|
-
|
|
798
|
-
try {
|
|
799
|
-
const scopeFallback = await this.fallbackToGlobalScope(operation, context);
|
|
800
|
-
if (scopeFallback.success) {
|
|
801
|
-
return this.createSuccessResponse(
|
|
802
|
-
'Scope error resolved - using global scope',
|
|
803
|
-
scopeFallback
|
|
804
|
-
);
|
|
805
|
-
}
|
|
806
|
-
} catch (scopeError) {
|
|
807
|
-
this.logger.warn('Global scope fallback failed', scopeError);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
return this.createErrorResponse(
|
|
812
|
-
`Scope error during ${operation}`,
|
|
813
|
-
{
|
|
814
|
-
error: error instanceof Error ? error.message : String(error),
|
|
815
|
-
recovery_options: [
|
|
816
|
-
'1. Deploy to global scope instead',
|
|
817
|
-
'2. Create scoped application first',
|
|
818
|
-
'3. Use manual deployment approach',
|
|
819
|
-
'4. Check application scope permissions'
|
|
820
|
-
],
|
|
821
|
-
next_steps: [
|
|
822
|
-
'Try snow_deploy with scope_strategy="global"',
|
|
823
|
-
'Or create application scope manually first'
|
|
824
|
-
]
|
|
825
|
-
}
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
/**
|
|
830
|
-
* Handle generic errors with comprehensive guidance
|
|
831
|
-
*/
|
|
832
|
-
private async handleGenericError(
|
|
833
|
-
error: any,
|
|
834
|
-
operation: string,
|
|
835
|
-
context: AgentContext,
|
|
836
|
-
fallbackOptions?: any
|
|
837
|
-
): Promise<MCPToolResult> {
|
|
838
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
839
|
-
|
|
840
|
-
// Generate contextual recovery options based on operation type
|
|
841
|
-
const recoveryOptions = this.generateRecoveryOptions(operation, errorMessage);
|
|
842
|
-
|
|
843
|
-
// Store error for pattern analysis
|
|
844
|
-
await this.memory.updateSharedContext({
|
|
845
|
-
session_id: context.session_id,
|
|
846
|
-
context_key: `error_${operation}_${Date.now()}`,
|
|
847
|
-
context_value: JSON.stringify({
|
|
848
|
-
error: errorMessage,
|
|
849
|
-
operation: operation,
|
|
850
|
-
timestamp: new Date().toISOString(),
|
|
851
|
-
recovery_attempted: Object.keys(fallbackOptions || {})
|
|
852
|
-
}),
|
|
853
|
-
created_by_agent: context.agent_id
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
return this.createErrorResponse(
|
|
857
|
-
`Unexpected error during ${operation}`,
|
|
858
|
-
{
|
|
859
|
-
error: errorMessage,
|
|
860
|
-
recovery_options: recoveryOptions,
|
|
861
|
-
next_steps: [
|
|
862
|
-
'Review error details and try alternative approach',
|
|
863
|
-
'Use snow_deployment_debug() for diagnostics',
|
|
864
|
-
'Contact Queen Agent for intervention'
|
|
865
|
-
],
|
|
866
|
-
fallback_available: fallbackOptions?.enableManualSteps || false
|
|
867
|
-
}
|
|
868
|
-
);
|
|
869
|
-
}
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Helper methods for error handling
|
|
873
|
-
*/
|
|
874
|
-
private extractErrorCode(errorMessage: string): string | undefined {
|
|
875
|
-
const codeMatch = errorMessage.match(/(\d{3})/);
|
|
876
|
-
return codeMatch ? codeMatch[1] : undefined;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
private extractFieldInfoFromError(errorMessage: string): any {
|
|
880
|
-
const fieldMatch = errorMessage.match(/field[:\s]+([a-z_]+)/i);
|
|
881
|
-
const requiredMatch = errorMessage.match(/required/i);
|
|
882
|
-
|
|
883
|
-
return {
|
|
884
|
-
field: fieldMatch ? fieldMatch[1] : null,
|
|
885
|
-
is_required: !!requiredMatch,
|
|
886
|
-
raw_message: errorMessage
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
private generateRecoveryOptions(operation: string, errorMessage: string): string[] {
|
|
891
|
-
const options = [
|
|
892
|
-
'1. Review error message and fix configuration',
|
|
893
|
-
'2. Check ServiceNow instance status and permissions'
|
|
894
|
-
];
|
|
895
|
-
|
|
896
|
-
if (operation.includes('deploy')) {
|
|
897
|
-
options.push('3. Use snow_validate_deployment() before retrying');
|
|
898
|
-
options.push('4. Try manual deployment with Update Set');
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
if (operation.includes('search') || operation.includes('find')) {
|
|
902
|
-
options.push('3. Try alternative search terms');
|
|
903
|
-
options.push('4. Use snow_comprehensive_search() for broader search');
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
options.push('5. Request Queen Agent intervention for complex issues');
|
|
907
|
-
|
|
908
|
-
return options;
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
private async escalateToGlobalScope(operation: string, context: AgentContext): Promise<any> {
|
|
912
|
-
this.logger.info(`🔝 Attempting permission escalation for ${operation}`);
|
|
913
|
-
|
|
914
|
-
try {
|
|
915
|
-
// Enhanced authentication validation
|
|
916
|
-
const authValidation = await this.validateServiceNowConnection();
|
|
917
|
-
if (!authValidation.success) {
|
|
918
|
-
return {
|
|
919
|
-
success: false,
|
|
920
|
-
reason: 'Authentication validation failed before escalation',
|
|
921
|
-
recovery_action: 'Run: snow-flow auth login',
|
|
922
|
-
auth_error: authValidation.error
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
|
|
926
|
-
// Get current user information with enhanced OAuth scope handling
|
|
927
|
-
this.logger.info('🔍 Analyzing current user permissions and OAuth scopes...');
|
|
928
|
-
|
|
929
|
-
let currentUser: any = null;
|
|
930
|
-
let currentRoles: string[] = [];
|
|
931
|
-
let oAuthScopes: string[] = [];
|
|
932
|
-
|
|
933
|
-
try {
|
|
934
|
-
// Get current user details
|
|
935
|
-
const userResponse = await this.client.makeRequest({
|
|
936
|
-
method: 'GET',
|
|
937
|
-
endpoint: '/api/now/v2/table/sys_user',
|
|
938
|
-
params: {
|
|
939
|
-
sysparm_query: 'active=true^user_name=javascript:gs.getUserName()',
|
|
940
|
-
sysparm_fields: 'sys_id,user_name,name,email,active,locked_out,roles',
|
|
941
|
-
sysparm_limit: 1
|
|
942
|
-
}
|
|
943
|
-
});
|
|
944
|
-
|
|
945
|
-
if (userResponse.data?.result?.[0]) {
|
|
946
|
-
currentUser = userResponse.data.result[0];
|
|
947
|
-
this.logger.info(`👤 Current user: ${currentUser.name} (${currentUser.user_name})`);
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
// Get detailed role information
|
|
951
|
-
const rolesResponse = await this.client.makeRequest({
|
|
952
|
-
method: 'GET',
|
|
953
|
-
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
954
|
-
params: {
|
|
955
|
-
sysparm_query: `user.user_name=${currentUser?.user_name || 'javascript:gs.getUserName()'}`,
|
|
956
|
-
sysparm_fields: 'role.name,role.description,state,inherited',
|
|
957
|
-
sysparm_limit: 100
|
|
958
|
-
}
|
|
959
|
-
});
|
|
960
|
-
|
|
961
|
-
if (rolesResponse.data?.result) {
|
|
962
|
-
currentRoles = rolesResponse.data.result
|
|
963
|
-
.filter((r: any) => r.state === 'active')
|
|
964
|
-
.map((r: any) => r['role.name'])
|
|
965
|
-
.filter(Boolean);
|
|
966
|
-
|
|
967
|
-
this.logger.info(`🛡️ Active roles: ${currentRoles.join(', ')}`);
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
// Check OAuth token scopes if available
|
|
971
|
-
try {
|
|
972
|
-
// Attempt to get token info if method exists
|
|
973
|
-
if (typeof (this.oauth as any).getTokenInfo === 'function') {
|
|
974
|
-
const tokenInfo = await (this.oauth as any).getTokenInfo();
|
|
975
|
-
if (tokenInfo?.scope) {
|
|
976
|
-
oAuthScopes = tokenInfo.scope.split(' ');
|
|
977
|
-
this.logger.info(`🔑 OAuth scopes: ${oAuthScopes.join(', ')}`);
|
|
978
|
-
}
|
|
979
|
-
} else {
|
|
980
|
-
this.logger.debug('OAuth getTokenInfo method not available');
|
|
981
|
-
}
|
|
982
|
-
} catch (scopeError) {
|
|
983
|
-
this.logger.debug('Could not retrieve OAuth scope information', { error: scopeError });
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
} catch (roleError) {
|
|
987
|
-
this.logger.warn('Could not fetch complete user information', { error: roleError });
|
|
988
|
-
// Continue with limited information
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
// Enhanced role _analysis based on operation type
|
|
992
|
-
const requiredRoles = this.determineRequiredRoles(operation);
|
|
993
|
-
const requiredScopes = this.determineRequiredOAuthScopes(operation);
|
|
994
|
-
const missingRoles = requiredRoles.filter(role => !currentRoles.includes(role));
|
|
995
|
-
const missingScopes = requiredScopes.filter(scope => !oAuthScopes.includes(scope));
|
|
996
|
-
|
|
997
|
-
this.logger.info(`📊 Permission Analysis:`, {
|
|
998
|
-
required_roles: requiredRoles,
|
|
999
|
-
current_roles: currentRoles,
|
|
1000
|
-
missing_roles: missingRoles,
|
|
1001
|
-
required_scopes: requiredScopes,
|
|
1002
|
-
current_scopes: oAuthScopes,
|
|
1003
|
-
missing_scopes: missingScopes
|
|
1004
|
-
});
|
|
1005
|
-
|
|
1006
|
-
// Check if user has sufficient permissions
|
|
1007
|
-
if (missingRoles.length === 0 && missingScopes.length === 0) {
|
|
1008
|
-
// User has all required permissions, issue might be elsewhere
|
|
1009
|
-
return {
|
|
1010
|
-
success: true,
|
|
1011
|
-
reason: 'User has all required roles and scopes - permission issue may be configuration related',
|
|
1012
|
-
current_roles: currentRoles,
|
|
1013
|
-
current_scopes: oAuthScopes,
|
|
1014
|
-
diagnostic_actions: [
|
|
1015
|
-
'Check ACL rules for the specific table/operation',
|
|
1016
|
-
'Verify application scope permissions',
|
|
1017
|
-
'Check domain separation settings',
|
|
1018
|
-
'Review field-level security',
|
|
1019
|
-
'Validate write permissions for target table'
|
|
1020
|
-
]
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
// Enhanced automatic role assignment with better error handling
|
|
1025
|
-
let autoAssignmentAttempted = false;
|
|
1026
|
-
const canAutoAssign = currentRoles.includes('admin') ||
|
|
1027
|
-
currentRoles.includes('user_admin') ||
|
|
1028
|
-
currentRoles.includes('system_administrator');
|
|
1029
|
-
|
|
1030
|
-
if (canAutoAssign && missingRoles.length > 0) {
|
|
1031
|
-
this.logger.info('🤖 Attempting automatic role assignment...');
|
|
1032
|
-
|
|
1033
|
-
try {
|
|
1034
|
-
const assignmentResult = await this.attemptEnhancedRoleAssignment(
|
|
1035
|
-
currentUser?.sys_id,
|
|
1036
|
-
missingRoles,
|
|
1037
|
-
context
|
|
1038
|
-
);
|
|
1039
|
-
|
|
1040
|
-
if (assignmentResult.success) {
|
|
1041
|
-
autoAssignmentAttempted = true;
|
|
1042
|
-
|
|
1043
|
-
// Update context with successful assignment
|
|
1044
|
-
await this.memory.updateSharedContext({
|
|
1045
|
-
session_id: context.session_id,
|
|
1046
|
-
context_key: `permission_escalation_${operation}`,
|
|
1047
|
-
context_value: JSON.stringify({
|
|
1048
|
-
escalation_type: 'automatic_role_assignment',
|
|
1049
|
-
assigned_roles: assignmentResult.assigned_roles,
|
|
1050
|
-
timestamp: new Date().toISOString(),
|
|
1051
|
-
operation: operation
|
|
1052
|
-
}),
|
|
1053
|
-
created_by_agent: context.agent_id
|
|
1054
|
-
});
|
|
1055
|
-
|
|
1056
|
-
return {
|
|
1057
|
-
success: true,
|
|
1058
|
-
reason: 'Roles automatically assigned successfully',
|
|
1059
|
-
assigned_roles: assignmentResult.assigned_roles,
|
|
1060
|
-
assignment_details: assignmentResult.details,
|
|
1061
|
-
recommended_action: 'Retry the original operation - new permissions are active'
|
|
1062
|
-
};
|
|
1063
|
-
}
|
|
1064
|
-
} catch (assignError) {
|
|
1065
|
-
this.logger.warn('Automatic role assignment failed', { error: assignError });
|
|
1066
|
-
autoAssignmentAttempted = true; // We tried
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
// Enhanced OAuth scope elevation attempt
|
|
1071
|
-
if (missingScopes.length > 0) {
|
|
1072
|
-
this.logger.info('🔑 Attempting OAuth scope elevation...');
|
|
1073
|
-
|
|
1074
|
-
try {
|
|
1075
|
-
const scopeElevationResult = await this.attemptOAuthScopeElevation(
|
|
1076
|
-
missingScopes,
|
|
1077
|
-
operation,
|
|
1078
|
-
context
|
|
1079
|
-
);
|
|
1080
|
-
|
|
1081
|
-
if (scopeElevationResult.success) {
|
|
1082
|
-
return {
|
|
1083
|
-
success: true,
|
|
1084
|
-
reason: 'OAuth scopes elevated successfully',
|
|
1085
|
-
elevated_scopes: scopeElevationResult.elevated_scopes,
|
|
1086
|
-
recommended_action: 'Retry the original operation with elevated OAuth permissions'
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
} catch (scopeError) {
|
|
1090
|
-
this.logger.warn('OAuth scope elevation failed', { error: scopeError });
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
// Request Queen intervention for complex permission issues
|
|
1095
|
-
await this.requestQueenIntervention(context, {
|
|
1096
|
-
type: 'permission_escalation_required',
|
|
1097
|
-
priority: 'high',
|
|
1098
|
-
description: `Permission escalation needed for ${operation}. Missing roles: ${missingRoles.join(', ')}. Missing OAuth scopes: ${missingScopes.join(', ')}.`,
|
|
1099
|
-
attempted_solutions: [
|
|
1100
|
-
...(autoAssignmentAttempted ? ['automatic_role_assignment'] : []),
|
|
1101
|
-
...(missingScopes.length > 0 ? ['oauth_scope_elevation'] : [])
|
|
1102
|
-
]
|
|
1103
|
-
});
|
|
1104
|
-
|
|
1105
|
-
// Comprehensive manual escalation instructions
|
|
1106
|
-
const manualSteps = [
|
|
1107
|
-
`1. ServiceNow Administrator Actions Required:`,
|
|
1108
|
-
` • Navigate to User Administration > Users`,
|
|
1109
|
-
` • Find user: ${currentUser?.name || 'Current User'} (${currentUser?.user_name || 'unknown'})`,
|
|
1110
|
-
` • Add missing roles: ${missingRoles.join(', ')}`,
|
|
1111
|
-
...missingScopes.length > 0 ? [
|
|
1112
|
-
`2. OAuth Application Configuration:`,
|
|
1113
|
-
` • Update OAuth application scopes to include: ${missingScopes.join(', ')}`,
|
|
1114
|
-
` • Re-authorize application with elevated permissions`
|
|
1115
|
-
] : [],
|
|
1116
|
-
`${missingScopes.length > 0 ? '3' : '2'}. Verification Steps:`,
|
|
1117
|
-
` • Test permissions: snow_validate_live_connection({ test_level: "permissions" })`,
|
|
1118
|
-
` • Retry operation: ${operation}`
|
|
1119
|
-
];
|
|
1120
|
-
|
|
1121
|
-
return {
|
|
1122
|
-
success: false,
|
|
1123
|
-
reason: 'Manual permission escalation required',
|
|
1124
|
-
current_user: currentUser,
|
|
1125
|
-
current_roles: currentRoles,
|
|
1126
|
-
missing_roles: missingRoles,
|
|
1127
|
-
current_oauth_scopes: oAuthScopes,
|
|
1128
|
-
missing_oauth_scopes: missingScopes,
|
|
1129
|
-
escalation_required: true,
|
|
1130
|
-
auto_assignment_attempted: autoAssignmentAttempted,
|
|
1131
|
-
manual_steps: manualSteps,
|
|
1132
|
-
estimated_time: `${5 + (missingRoles.length * 2)}–${10 + (missingRoles.length * 3)} minutes with admin access`,
|
|
1133
|
-
priority: missingRoles.includes('admin') || missingRoles.includes('system_administrator') ? 'critical' : 'high'
|
|
1134
|
-
};
|
|
1135
|
-
|
|
1136
|
-
} catch (error) {
|
|
1137
|
-
this.logger.error('🚨 Permission escalation process failed', { error, operation, context });
|
|
1138
|
-
|
|
1139
|
-
return {
|
|
1140
|
-
success: false,
|
|
1141
|
-
reason: 'Critical error during permission escalation',
|
|
1142
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1143
|
-
error_type: this.categorizeError(error instanceof Error ? error.message : String(error)),
|
|
1144
|
-
recovery_actions: [
|
|
1145
|
-
'1. Verify ServiceNow connection: snow_validate_live_connection()',
|
|
1146
|
-
'2. Check authentication status: snow_auth_diagnostics()',
|
|
1147
|
-
'3. Confirm API access is enabled in ServiceNow',
|
|
1148
|
-
'4. Contact ServiceNow administrator for manual role assignment',
|
|
1149
|
-
'5. Review system logs for additional error details'
|
|
1150
|
-
],
|
|
1151
|
-
escalation_required: true,
|
|
1152
|
-
contact_admin: true
|
|
1153
|
-
};
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
|
|
1157
|
-
/**
|
|
1158
|
-
* Determine required roles based on operation type
|
|
1159
|
-
*/
|
|
1160
|
-
private determineRequiredRoles(operation: string): string[] {
|
|
1161
|
-
const roles: string[] = [];
|
|
1162
|
-
|
|
1163
|
-
// Base roles for most operations
|
|
1164
|
-
if (operation.includes('deploy') || operation.includes('create') || operation.includes('update')) {
|
|
1165
|
-
roles.push('rest_service', 'web_service_admin');
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
// Widget operations
|
|
1169
|
-
if (operation.includes('widget')) {
|
|
1170
|
-
roles.push('sp_admin', 'sp_portal_admin');
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
// Flow operations
|
|
1174
|
-
if (operation.includes('flow') || operation.includes('workflow')) {
|
|
1175
|
-
roles.push('flow_designer', 'admin');
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
// Application operations
|
|
1179
|
-
if (operation.includes('application') || operation.includes('app')) {
|
|
1180
|
-
roles.push('app_creator', 'admin');
|
|
1181
|
-
}
|
|
1182
|
-
|
|
1183
|
-
// Update set operations
|
|
1184
|
-
if (operation.includes('update_set')) {
|
|
1185
|
-
roles.push('admin');
|
|
1186
|
-
}
|
|
1187
|
-
|
|
1188
|
-
// Global scope operations
|
|
1189
|
-
if (operation.includes('global')) {
|
|
1190
|
-
roles.push('admin', 'system_administrator');
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
return Array.from(new Set(roles)); // Remove duplicates
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
/**
|
|
1197
|
-
* Determine required OAuth scopes based on operation type
|
|
1198
|
-
*/
|
|
1199
|
-
private determineRequiredOAuthScopes(operation: string): string[] {
|
|
1200
|
-
const scopes: string[] = ['useraccount']; // Base scope for most operations
|
|
1201
|
-
|
|
1202
|
-
// Operations that require write access
|
|
1203
|
-
if (operation.includes('deploy') || operation.includes('create') || operation.includes('update')) {
|
|
1204
|
-
scopes.push('write');
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
// Administrative operations
|
|
1208
|
-
if (operation.includes('admin') || operation.includes('global') || operation.includes('system')) {
|
|
1209
|
-
scopes.push('admin');
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
// Flow Designer operations may need specific scopes
|
|
1213
|
-
if (operation.includes('flow') || operation.includes('workflow')) {
|
|
1214
|
-
scopes.push('write', 'admin');
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
return Array.from(new Set(scopes)); // Remove duplicates
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
/**
|
|
1221
|
-
* Enhanced role assignment with better validation
|
|
1222
|
-
*/
|
|
1223
|
-
private async attemptEnhancedRoleAssignment(
|
|
1224
|
-
userSysId: string,
|
|
1225
|
-
roles: string[],
|
|
1226
|
-
context: AgentContext
|
|
1227
|
-
): Promise<{success: boolean, assigned_roles?: string[], details?: any, error?: string}> {
|
|
1228
|
-
try {
|
|
1229
|
-
const assignedRoles: string[] = [];
|
|
1230
|
-
const assignmentDetails: any[] = [];
|
|
1231
|
-
|
|
1232
|
-
for (const roleName of roles) {
|
|
1233
|
-
try {
|
|
1234
|
-
// Get the role sys_id with enhanced validation
|
|
1235
|
-
const roleResponse = await this.client.makeRequest({
|
|
1236
|
-
method: 'GET',
|
|
1237
|
-
endpoint: '/api/now/v2/table/sys_user_role',
|
|
1238
|
-
params: {
|
|
1239
|
-
sysparm_query: `name=${roleName}^active=true`,
|
|
1240
|
-
sysparm_fields: 'sys_id,name,description,can_delegate',
|
|
1241
|
-
sysparm_limit: 1
|
|
1242
|
-
}
|
|
1243
|
-
});
|
|
1244
|
-
|
|
1245
|
-
if (roleResponse.data?.result?.[0]) {
|
|
1246
|
-
const role = roleResponse.data.result[0];
|
|
1247
|
-
|
|
1248
|
-
// Check if user already has this role
|
|
1249
|
-
const existingRoleResponse = await this.client.makeRequest({
|
|
1250
|
-
method: 'GET',
|
|
1251
|
-
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
1252
|
-
params: {
|
|
1253
|
-
sysparm_query: `user=${userSysId}^role=${role.sys_id}`,
|
|
1254
|
-
sysparm_limit: 1
|
|
1255
|
-
}
|
|
1256
|
-
});
|
|
1257
|
-
|
|
1258
|
-
if (existingRoleResponse.data?.result?.length > 0) {
|
|
1259
|
-
this.logger.info(`User already has role: ${roleName}`);
|
|
1260
|
-
assignedRoles.push(roleName);
|
|
1261
|
-
continue;
|
|
1262
|
-
}
|
|
1263
|
-
|
|
1264
|
-
// Attempt to assign role
|
|
1265
|
-
const assignmentResponse = await this.client.makeRequest({
|
|
1266
|
-
method: 'POST',
|
|
1267
|
-
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
1268
|
-
data: {
|
|
1269
|
-
user: userSysId,
|
|
1270
|
-
role: role.sys_id,
|
|
1271
|
-
state: 'active',
|
|
1272
|
-
granted_by: 'snow_flow_automation'
|
|
1273
|
-
}
|
|
1274
|
-
});
|
|
1275
|
-
|
|
1276
|
-
if (assignmentResponse.data?.result) {
|
|
1277
|
-
assignedRoles.push(roleName);
|
|
1278
|
-
assignmentDetails.push({
|
|
1279
|
-
role: roleName,
|
|
1280
|
-
sys_id: assignmentResponse.data.result.sys_id,
|
|
1281
|
-
status: 'assigned'
|
|
1282
|
-
});
|
|
1283
|
-
this.logger.info(`Successfully assigned role: ${roleName}`);
|
|
1284
|
-
}
|
|
1285
|
-
} else {
|
|
1286
|
-
this.logger.warn(`Role not found: ${roleName}`);
|
|
1287
|
-
}
|
|
1288
|
-
} catch (roleError) {
|
|
1289
|
-
this.logger.warn(`Failed to assign role ${roleName}`, { error: roleError });
|
|
1290
|
-
assignmentDetails.push({
|
|
1291
|
-
role: roleName,
|
|
1292
|
-
status: 'failed',
|
|
1293
|
-
error: roleError instanceof Error ? roleError.message : String(roleError)
|
|
1294
|
-
});
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
|
|
1298
|
-
return {
|
|
1299
|
-
success: assignedRoles.length > 0,
|
|
1300
|
-
assigned_roles: assignedRoles,
|
|
1301
|
-
details: assignmentDetails
|
|
1302
|
-
};
|
|
1303
|
-
} catch (error) {
|
|
1304
|
-
return {
|
|
1305
|
-
success: false,
|
|
1306
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1307
|
-
};
|
|
1308
|
-
}
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
/**
|
|
1312
|
-
* Attempt OAuth scope elevation
|
|
1313
|
-
*/
|
|
1314
|
-
private async attemptOAuthScopeElevation(
|
|
1315
|
-
missingScopes: string[],
|
|
1316
|
-
operation: string,
|
|
1317
|
-
context: AgentContext
|
|
1318
|
-
): Promise<{success: boolean, elevated_scopes?: string[], error?: string}> {
|
|
1319
|
-
try {
|
|
1320
|
-
this.logger.info('Requesting OAuth scope elevation...', { missing_scopes: missingScopes });
|
|
1321
|
-
|
|
1322
|
-
// In most ServiceNow OAuth implementations, scope elevation requires
|
|
1323
|
-
// re-authorization through the OAuth flow. We can't dynamically elevate
|
|
1324
|
-
// scopes without user interaction.
|
|
1325
|
-
|
|
1326
|
-
// Check if we can refresh token with additional scopes
|
|
1327
|
-
let currentToken = null;
|
|
1328
|
-
try {
|
|
1329
|
-
if (typeof (this.oauth as any).getTokenInfo === 'function') {
|
|
1330
|
-
currentToken = await (this.oauth as any).getTokenInfo();
|
|
1331
|
-
}
|
|
1332
|
-
} catch (error) {
|
|
1333
|
-
this.logger.debug('Could not get current token info', { error });
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
if (!currentToken) {
|
|
1337
|
-
return {
|
|
1338
|
-
success: false,
|
|
1339
|
-
error: 'No active OAuth token found or method not available'
|
|
1340
|
-
};
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
// Attempt token refresh - this typically won't add new scopes
|
|
1344
|
-
// but might refresh existing elevated permissions
|
|
1345
|
-
const refreshResult = await this.oauth.refreshAccessToken();
|
|
1346
|
-
if (!refreshResult.success) {
|
|
1347
|
-
return {
|
|
1348
|
-
success: false,
|
|
1349
|
-
error: 'Failed to refresh OAuth token'
|
|
1350
|
-
};
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
// Check if the refresh gave us the scopes we need
|
|
1354
|
-
let newTokenInfo = null;
|
|
1355
|
-
try {
|
|
1356
|
-
if (typeof (this.oauth as any).getTokenInfo === 'function') {
|
|
1357
|
-
newTokenInfo = await (this.oauth as any).getTokenInfo();
|
|
1358
|
-
}
|
|
1359
|
-
} catch (error) {
|
|
1360
|
-
this.logger.debug('Could not get new token info after refresh', { error });
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
const newScopes = newTokenInfo?.scope?.split(' ') || [];
|
|
1364
|
-
const actuallyMissing = missingScopes.filter(scope => !newScopes.includes(scope));
|
|
1365
|
-
|
|
1366
|
-
if (actuallyMissing.length === 0) {
|
|
1367
|
-
return {
|
|
1368
|
-
success: true,
|
|
1369
|
-
elevated_scopes: missingScopes
|
|
1370
|
-
};
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
// For missing scopes, we need manual re-authorization
|
|
1374
|
-
return {
|
|
1375
|
-
success: false,
|
|
1376
|
-
error: `OAuth scopes require manual re-authorization: ${actuallyMissing.join(', ')}`
|
|
1377
|
-
};
|
|
1378
|
-
|
|
1379
|
-
} catch (error) {
|
|
1380
|
-
return {
|
|
1381
|
-
success: false,
|
|
1382
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1383
|
-
};
|
|
1384
|
-
}
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
/**
|
|
1388
|
-
* Enhanced deployment context extraction
|
|
1389
|
-
*/
|
|
1390
|
-
private async extractEnhancedDeploymentContext(operation: string, context: AgentContext): Promise<any> {
|
|
1391
|
-
try {
|
|
1392
|
-
// Extract from agent context
|
|
1393
|
-
const contextData = context as any;
|
|
1394
|
-
|
|
1395
|
-
// Enhanced artifact type detection
|
|
1396
|
-
const artifactType = this.extractArtifactType(operation);
|
|
1397
|
-
const artifactName = contextData.artifact_name ||
|
|
1398
|
-
this.extractArtifactName(operation) ||
|
|
1399
|
-
`fallback_${artifactType}_${Date.now()}`;
|
|
1400
|
-
|
|
1401
|
-
// Determine current scope from various sources
|
|
1402
|
-
let currentScope = contextData.current_scope || 'scoped_application';
|
|
1403
|
-
if (contextData.deployment_config?.sys_scope) {
|
|
1404
|
-
currentScope = contextData.deployment_config.sys_scope;
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
// Analyze original error for better global scope strategy
|
|
1408
|
-
const originalError = contextData.original_error || 'Scoped deployment failed';
|
|
1409
|
-
const globalScopeStrategy = this.analyzeErrorForGlobalScopeStrategy(originalError);
|
|
1410
|
-
|
|
1411
|
-
return {
|
|
1412
|
-
artifact_type: artifactType,
|
|
1413
|
-
artifact_name: artifactName,
|
|
1414
|
-
current_scope: currentScope,
|
|
1415
|
-
original_error: originalError,
|
|
1416
|
-
global_scope_strategy: globalScopeStrategy,
|
|
1417
|
-
deployment_config: contextData.deployment_config || {},
|
|
1418
|
-
operation_context: {
|
|
1419
|
-
session_id: context.session_id,
|
|
1420
|
-
agent_id: context.agent_id,
|
|
1421
|
-
timestamp: new Date().toISOString()
|
|
1422
|
-
}
|
|
1423
|
-
};
|
|
1424
|
-
} catch (error) {
|
|
1425
|
-
this.logger.warn('Failed to extract enhanced deployment context', { error });
|
|
1426
|
-
return null;
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
|
|
1430
|
-
/**
|
|
1431
|
-
* Analyze error for global scope strategy
|
|
1432
|
-
*/
|
|
1433
|
-
private analyzeErrorForGlobalScopeStrategy(error: string): string {
|
|
1434
|
-
const errorLower = error.toLowerCase();
|
|
1435
|
-
|
|
1436
|
-
if (errorLower.includes('scope') || errorLower.includes('application')) {
|
|
1437
|
-
return 'direct_global_deployment';
|
|
1438
|
-
}
|
|
1439
|
-
if (errorLower.includes('permission') || errorLower.includes('access')) {
|
|
1440
|
-
return 'elevated_permissions_deployment';
|
|
1441
|
-
}
|
|
1442
|
-
if (errorLower.includes('table') || errorLower.includes('field')) {
|
|
1443
|
-
return 'schema_aware_deployment';
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
return 'standard_global_deployment';
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
/**
|
|
1450
|
-
* Validate global scope deployment permissions
|
|
1451
|
-
*/
|
|
1452
|
-
private async validateGlobalScopePermissions(
|
|
1453
|
-
artifactType: string,
|
|
1454
|
-
context: AgentContext
|
|
1455
|
-
): Promise<{canProceed: boolean, missing_permissions?: string[], recommended_actions?: string[]}> {
|
|
1456
|
-
try {
|
|
1457
|
-
// Check basic global scope permissions
|
|
1458
|
-
const requiredRoles = ['admin'];
|
|
1459
|
-
const requiredScopes = ['write', 'admin'];
|
|
1460
|
-
|
|
1461
|
-
// Get current user roles (simplified check)
|
|
1462
|
-
let hasAdminRole = false;
|
|
1463
|
-
try {
|
|
1464
|
-
const rolesResponse = await this.client.makeRequest({
|
|
1465
|
-
method: 'GET',
|
|
1466
|
-
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
1467
|
-
params: {
|
|
1468
|
-
sysparm_query: 'user.user_name=javascript:gs.getUserName()^role.name=admin',
|
|
1469
|
-
sysparm_limit: 1
|
|
1470
|
-
}
|
|
1471
|
-
});
|
|
1472
|
-
hasAdminRole = rolesResponse.data?.result?.length > 0;
|
|
1473
|
-
} catch (roleCheckError) {
|
|
1474
|
-
this.logger.warn('Could not verify admin role', { error: roleCheckError });
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
if (!hasAdminRole) {
|
|
1478
|
-
return {
|
|
1479
|
-
canProceed: false,
|
|
1480
|
-
missing_permissions: ['admin'],
|
|
1481
|
-
recommended_actions: [
|
|
1482
|
-
'Request admin role from ServiceNow administrator',
|
|
1483
|
-
'Use snow_escalate_permissions() for automatic elevation',
|
|
1484
|
-
'Consider scoped deployment alternative'
|
|
1485
|
-
]
|
|
1486
|
-
};
|
|
1487
|
-
}
|
|
1488
|
-
|
|
1489
|
-
return { canProceed: true };
|
|
1490
|
-
} catch (error) {
|
|
1491
|
-
this.logger.warn('Permission validation failed', { error });
|
|
1492
|
-
return {
|
|
1493
|
-
canProceed: false,
|
|
1494
|
-
missing_permissions: ['unknown'],
|
|
1495
|
-
recommended_actions: ['Check authentication and try again']
|
|
1496
|
-
};
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
/**
|
|
1501
|
-
* Determine global deployment strategy
|
|
1502
|
-
*/
|
|
1503
|
-
private determineGlobalDeploymentStrategy(deploymentContext: any): any {
|
|
1504
|
-
const strategies = {
|
|
1505
|
-
direct_global_deployment: {
|
|
1506
|
-
name: 'Direct Global Deployment',
|
|
1507
|
-
reasons: ['Scope-related error detected', 'Standard global scope approach'],
|
|
1508
|
-
estimated_success_rate: 0.75
|
|
1509
|
-
},
|
|
1510
|
-
elevated_permissions_deployment: {
|
|
1511
|
-
name: 'Elevated Permissions Deployment',
|
|
1512
|
-
reasons: ['Permission error detected', 'Admin-level deployment required'],
|
|
1513
|
-
estimated_success_rate: 0.60
|
|
1514
|
-
},
|
|
1515
|
-
schema_aware_deployment: {
|
|
1516
|
-
name: 'Schema-Aware Deployment',
|
|
1517
|
-
reasons: ['Table/field error detected', 'Enhanced validation required'],
|
|
1518
|
-
estimated_success_rate: 0.80
|
|
1519
|
-
},
|
|
1520
|
-
standard_global_deployment: {
|
|
1521
|
-
name: 'Standard Global Deployment',
|
|
1522
|
-
reasons: ['Generic error', 'Default global scope approach'],
|
|
1523
|
-
estimated_success_rate: 0.65
|
|
1524
|
-
}
|
|
1525
|
-
};
|
|
1526
|
-
|
|
1527
|
-
return strategies[deploymentContext.global_scope_strategy] || strategies.standard_global_deployment;
|
|
1528
|
-
}
|
|
1529
|
-
|
|
1530
|
-
/**
|
|
1531
|
-
* Attempt automatic role assignment (usually requires admin privileges)
|
|
1532
|
-
*/
|
|
1533
|
-
private async attemptRoleAssignment(userId: string, roles: string[]): Promise<{success: boolean, error?: string}> {
|
|
1534
|
-
try {
|
|
1535
|
-
// This typically fails in most ServiceNow instances for security reasons
|
|
1536
|
-
// But we attempt it for completeness
|
|
1537
|
-
for (const roleName of roles) {
|
|
1538
|
-
// First, get the role sys_id
|
|
1539
|
-
const roleResponse = await this.client.makeRequest({
|
|
1540
|
-
method: 'GET',
|
|
1541
|
-
endpoint: '/api/now/v2/table/sys_user_role',
|
|
1542
|
-
params: {
|
|
1543
|
-
sysparm_query: `name=${roleName}`,
|
|
1544
|
-
sysparm_fields: 'sys_id,name',
|
|
1545
|
-
sysparm_limit: 1
|
|
1546
|
-
}
|
|
1547
|
-
});
|
|
1548
|
-
|
|
1549
|
-
if (roleResponse.data?.result?.[0]) {
|
|
1550
|
-
const roleSysId = roleResponse.data.result[0].sys_id;
|
|
1551
|
-
|
|
1552
|
-
// Attempt to assign role to user
|
|
1553
|
-
await this.client.makeRequest({
|
|
1554
|
-
method: 'POST',
|
|
1555
|
-
endpoint: '/api/now/v2/table/sys_user_has_role',
|
|
1556
|
-
data: {
|
|
1557
|
-
user: userId,
|
|
1558
|
-
role: roleSysId
|
|
1559
|
-
}
|
|
1560
|
-
});
|
|
1561
|
-
}
|
|
1562
|
-
}
|
|
1563
|
-
|
|
1564
|
-
return { success: true };
|
|
1565
|
-
} catch (error) {
|
|
1566
|
-
return {
|
|
1567
|
-
success: false,
|
|
1568
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1569
|
-
};
|
|
1570
|
-
}
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
private async fallbackToGlobalScope(operation: string, context: AgentContext): Promise<any> {
|
|
1574
|
-
this.logger.info(`🌐 Attempting intelligent global scope fallback for ${operation}`);
|
|
1575
|
-
|
|
1576
|
-
try {
|
|
1577
|
-
// Enhanced pre-flight validation
|
|
1578
|
-
const authValidation = await this.validateServiceNowConnection();
|
|
1579
|
-
if (!authValidation.success) {
|
|
1580
|
-
return {
|
|
1581
|
-
success: false,
|
|
1582
|
-
reason: 'Authentication validation failed before global scope fallback',
|
|
1583
|
-
auth_error: authValidation.error,
|
|
1584
|
-
recovery_action: 'Fix authentication first, then retry global scope deployment'
|
|
1585
|
-
};
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
// Comprehensive deployment context extraction with validation
|
|
1589
|
-
const deploymentContext = await this.extractEnhancedDeploymentContext(operation, context);
|
|
1590
|
-
|
|
1591
|
-
if (!deploymentContext) {
|
|
1592
|
-
this.logger.warn('Unable to extract deployment context - attempting generic global scope deployment');
|
|
1593
|
-
return await this.attemptGenericGlobalScopeDeployment(operation, context);
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
// Validate global scope deployment permissions
|
|
1597
|
-
const globalScopeValidation = await this.validateGlobalScopePermissions(
|
|
1598
|
-
deploymentContext.artifact_type,
|
|
1599
|
-
context
|
|
1600
|
-
);
|
|
1601
|
-
|
|
1602
|
-
if (!globalScopeValidation.canProceed) {
|
|
1603
|
-
return {
|
|
1604
|
-
success: false,
|
|
1605
|
-
reason: 'Insufficient permissions for global scope deployment',
|
|
1606
|
-
missing_permissions: globalScopeValidation.missing_permissions,
|
|
1607
|
-
recommended_actions: globalScopeValidation.recommended_actions,
|
|
1608
|
-
escalation_required: true
|
|
1609
|
-
};
|
|
1610
|
-
}
|
|
1611
|
-
|
|
1612
|
-
this.logger.info('🔧 Configuring global scope deployment', {
|
|
1613
|
-
original_scope: deploymentContext.current_scope,
|
|
1614
|
-
artifact_type: deploymentContext.artifact_type,
|
|
1615
|
-
artifact_name: deploymentContext.artifact_name,
|
|
1616
|
-
global_scope_strategy: deploymentContext.global_scope_strategy
|
|
1617
|
-
});
|
|
1618
|
-
|
|
1619
|
-
// Enhanced deployment strategy based on artifact type and complexity
|
|
1620
|
-
let globalDeploymentResult;
|
|
1621
|
-
const deploymentStrategy = this.determineGlobalDeploymentStrategy(deploymentContext);
|
|
1622
|
-
|
|
1623
|
-
this.logger.info(`📋 Using deployment strategy: ${deploymentStrategy.name}`, {
|
|
1624
|
-
reasons: deploymentStrategy.reasons,
|
|
1625
|
-
estimated_success_rate: deploymentStrategy.estimated_success_rate
|
|
1626
|
-
});
|
|
1627
|
-
|
|
1628
|
-
switch (deploymentContext.artifact_type.toLowerCase()) {
|
|
1629
|
-
case 'widget':
|
|
1630
|
-
globalDeploymentResult = await this.deployWidgetInGlobalScopeEnhanced(
|
|
1631
|
-
deploymentContext,
|
|
1632
|
-
deploymentStrategy
|
|
1633
|
-
);
|
|
1634
|
-
break;
|
|
1635
|
-
|
|
1636
|
-
case 'flow':
|
|
1637
|
-
case 'workflow':
|
|
1638
|
-
globalDeploymentResult = await this.deployFlowInGlobalScopeEnhanced(
|
|
1639
|
-
deploymentContext,
|
|
1640
|
-
deploymentStrategy
|
|
1641
|
-
);
|
|
1642
|
-
break;
|
|
1643
|
-
|
|
1644
|
-
case 'script':
|
|
1645
|
-
case 'script_include':
|
|
1646
|
-
globalDeploymentResult = await this.deployScriptInGlobalScopeEnhanced(
|
|
1647
|
-
deploymentContext,
|
|
1648
|
-
deploymentStrategy
|
|
1649
|
-
);
|
|
1650
|
-
break;
|
|
1651
|
-
|
|
1652
|
-
case 'business_rule':
|
|
1653
|
-
globalDeploymentResult = await this.deployBusinessRuleInGlobalScopeEnhanced(
|
|
1654
|
-
deploymentContext,
|
|
1655
|
-
deploymentStrategy
|
|
1656
|
-
);
|
|
1657
|
-
break;
|
|
1658
|
-
|
|
1659
|
-
case 'table':
|
|
1660
|
-
globalDeploymentResult = await this.deployTableInGlobalScopeEnhanced(
|
|
1661
|
-
deploymentContext,
|
|
1662
|
-
deploymentStrategy
|
|
1663
|
-
);
|
|
1664
|
-
break;
|
|
1665
|
-
|
|
1666
|
-
case 'application':
|
|
1667
|
-
// Enhanced application scope handling
|
|
1668
|
-
return await this.handleApplicationScopeFallback(deploymentContext, context);
|
|
1669
|
-
|
|
1670
|
-
default:
|
|
1671
|
-
globalDeploymentResult = await this.deployGenericArtifactInGlobalScopeEnhanced(
|
|
1672
|
-
deploymentContext,
|
|
1673
|
-
deploymentStrategy
|
|
1674
|
-
);
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
// Enhanced result processing with comprehensive validation
|
|
1678
|
-
if (globalDeploymentResult.success) {
|
|
1679
|
-
this.logger.info('✅ Global scope deployment successful', {
|
|
1680
|
-
sys_id: globalDeploymentResult.sys_id,
|
|
1681
|
-
artifact_type: deploymentContext.artifact_type,
|
|
1682
|
-
deployment_time_ms: globalDeploymentResult.deployment_time_ms,
|
|
1683
|
-
strategy_used: deploymentStrategy.name
|
|
1684
|
-
});
|
|
1685
|
-
|
|
1686
|
-
// Enhanced context update with deployment tracking
|
|
1687
|
-
await this.updateContextWithEnhancedGlobalDeployment(context, {
|
|
1688
|
-
...globalDeploymentResult,
|
|
1689
|
-
deployment_context: deploymentContext,
|
|
1690
|
-
strategy_used: deploymentStrategy
|
|
1691
|
-
});
|
|
1692
|
-
|
|
1693
|
-
// Post-deployment validation
|
|
1694
|
-
const validationResult = await this.validateGlobalDeployment(
|
|
1695
|
-
globalDeploymentResult.sys_id,
|
|
1696
|
-
deploymentContext.artifact_type
|
|
1697
|
-
);
|
|
1698
|
-
|
|
1699
|
-
return {
|
|
1700
|
-
success: true,
|
|
1701
|
-
reason: 'Successfully deployed in global scope with validation',
|
|
1702
|
-
deployment_details: {
|
|
1703
|
-
sys_id: globalDeploymentResult.sys_id,
|
|
1704
|
-
scope: 'global',
|
|
1705
|
-
artifact_type: deploymentContext.artifact_type,
|
|
1706
|
-
artifact_name: deploymentContext.artifact_name,
|
|
1707
|
-
fallback_reason: deploymentContext.original_error,
|
|
1708
|
-
strategy_used: deploymentStrategy.name,
|
|
1709
|
-
deployment_time_ms: globalDeploymentResult.deployment_time_ms,
|
|
1710
|
-
validation_status: validationResult.status,
|
|
1711
|
-
functional_verification: validationResult.functional_tests
|
|
1712
|
-
},
|
|
1713
|
-
next_steps: [
|
|
1714
|
-
'1. Verify artifact functionality in global scope',
|
|
1715
|
-
'2. Review and update ACL permissions if needed',
|
|
1716
|
-
'3. Document scope change for future reference',
|
|
1717
|
-
'4. Test integration with other components',
|
|
1718
|
-
'5. Consider long-term scope strategy'
|
|
1719
|
-
],
|
|
1720
|
-
warnings: validationResult.warnings || [],
|
|
1721
|
-
recommendations: this.generateGlobalScopeRecommendations(deploymentContext, globalDeploymentResult)
|
|
1722
|
-
};
|
|
1723
|
-
} else {
|
|
1724
|
-
// Enhanced failure handling with intelligent retry suggestions
|
|
1725
|
-
const failureAnalysis = await this.analyzeGlobalScopeFailure(
|
|
1726
|
-
globalDeploymentResult,
|
|
1727
|
-
deploymentContext,
|
|
1728
|
-
deploymentStrategy
|
|
1729
|
-
);
|
|
1730
|
-
|
|
1731
|
-
this.logger.error('❌ Global scope deployment failed', {
|
|
1732
|
-
original_error: deploymentContext.original_error,
|
|
1733
|
-
global_deployment_error: globalDeploymentResult.error,
|
|
1734
|
-
failure__analysis: failureAnalysis
|
|
1735
|
-
});
|
|
1736
|
-
|
|
1737
|
-
return {
|
|
1738
|
-
success: false,
|
|
1739
|
-
reason: 'Global scope deployment failed after comprehensive attempts',
|
|
1740
|
-
original_error: deploymentContext.original_error,
|
|
1741
|
-
global_deployment_error: globalDeploymentResult.error,
|
|
1742
|
-
failure__analysis: failureAnalysis,
|
|
1743
|
-
alternative_strategies: [
|
|
1744
|
-
'1. Manual Update Set creation and deployment',
|
|
1745
|
-
'2. Phased deployment (create components separately)',
|
|
1746
|
-
'3. XML-based deployment approach',
|
|
1747
|
-
'4. ServiceNow Studio development environment',
|
|
1748
|
-
'5. Direct database approach (requires elevated access)'
|
|
1749
|
-
],
|
|
1750
|
-
diagnostic_commands: [
|
|
1751
|
-
'snow_deployment_debug() - Get detailed error _analysis',
|
|
1752
|
-
'snow_validate_deployment() - Pre-deployment validation',
|
|
1753
|
-
'snow_auth_diagnostics() - Check authentication and permissions',
|
|
1754
|
-
'snow_comprehensive_search() - Find similar existing artifacts'
|
|
1755
|
-
],
|
|
1756
|
-
manual_options: failureAnalysis.manual_steps,
|
|
1757
|
-
escalation_required: failureAnalysis.requires_admin_intervention,
|
|
1758
|
-
estimated_manual_effort: failureAnalysis.estimated_manual_time
|
|
1759
|
-
};
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
} catch (error) {
|
|
1763
|
-
this.logger.error('🚨 Global scope fallback process failed critically', {
|
|
1764
|
-
error,
|
|
1765
|
-
operation,
|
|
1766
|
-
context,
|
|
1767
|
-
stack: error instanceof Error ? error.stack : 'No stack trace available'
|
|
1768
|
-
});
|
|
1769
|
-
|
|
1770
|
-
// Request immediate Queen intervention for critical failures
|
|
1771
|
-
await this.requestQueenIntervention(context, {
|
|
1772
|
-
type: 'global_scope_fallback_failure',
|
|
1773
|
-
priority: 'critical',
|
|
1774
|
-
description: `Critical failure during global scope fallback for ${operation}: ${error instanceof Error ? error.message : String(error)}`,
|
|
1775
|
-
attempted_solutions: ['global_scope_deployment', 'enhanced_validation']
|
|
1776
|
-
});
|
|
1777
|
-
|
|
1778
|
-
return {
|
|
1779
|
-
success: false,
|
|
1780
|
-
reason: 'Critical error during global scope fallback process',
|
|
1781
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1782
|
-
error_type: this.categorizeError(error instanceof Error ? error.message : String(error)),
|
|
1783
|
-
stack_trace: error instanceof Error ? error.stack : null,
|
|
1784
|
-
recovery_actions: [
|
|
1785
|
-
'1. Emergency: Verify ServiceNow instance connectivity',
|
|
1786
|
-
'2. Critical: Check authentication and API access',
|
|
1787
|
-
'3. High: Review deployment configuration for errors',
|
|
1788
|
-
'4. Medium: Contact ServiceNow administrator immediately',
|
|
1789
|
-
'5. Low: Review system logs for additional context'
|
|
1790
|
-
],
|
|
1791
|
-
emergency_contact: true,
|
|
1792
|
-
requires_immediate_attention: true,
|
|
1793
|
-
fallback_options: [
|
|
1794
|
-
'Switch to manual deployment process',
|
|
1795
|
-
'Use ServiceNow Studio for development',
|
|
1796
|
-
'Create support ticket with ServiceNow',
|
|
1797
|
-
'Engage emergency deployment procedures'
|
|
1798
|
-
]
|
|
1799
|
-
};
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
|
-
|
|
1803
|
-
/**
|
|
1804
|
-
* Extract deployment context from operation and context
|
|
1805
|
-
*/
|
|
1806
|
-
private extractDeploymentContext(operation: string, context: AgentContext): any {
|
|
1807
|
-
try {
|
|
1808
|
-
// Try to extract context from agent context or operation string
|
|
1809
|
-
const contextData = context as any; // Allow access to additional properties
|
|
1810
|
-
|
|
1811
|
-
return {
|
|
1812
|
-
artifact_type: this.extractArtifactType(operation),
|
|
1813
|
-
artifact_name: contextData.artifact_name || this.extractArtifactName(operation),
|
|
1814
|
-
current_scope: contextData.current_scope || 'scoped_application',
|
|
1815
|
-
original_error: contextData.original_error || 'Scoped deployment failed',
|
|
1816
|
-
deployment_config: contextData.deployment_config || {}
|
|
1817
|
-
};
|
|
1818
|
-
} catch (error) {
|
|
1819
|
-
this.logger.warn('Failed to extract deployment context', { error });
|
|
1820
|
-
return null;
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
/**
|
|
1825
|
-
* Extract artifact type from operation string
|
|
1826
|
-
*/
|
|
1827
|
-
private extractArtifactType(operation: string): string {
|
|
1828
|
-
const patterns = [
|
|
1829
|
-
{ pattern: /widget/i, type: 'widget' },
|
|
1830
|
-
{ pattern: /flow|workflow/i, type: 'flow' },
|
|
1831
|
-
{ pattern: /script.*include/i, type: 'script_include' },
|
|
1832
|
-
{ pattern: /business.*rule/i, type: 'business_rule' },
|
|
1833
|
-
{ pattern: /application|app/i, type: 'application' },
|
|
1834
|
-
{ pattern: /script/i, type: 'script' }
|
|
1835
|
-
];
|
|
1836
|
-
|
|
1837
|
-
for (const { pattern, type } of patterns) {
|
|
1838
|
-
if (pattern.test(operation)) {
|
|
1839
|
-
return type;
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
return 'unknown';
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
/**
|
|
1847
|
-
* Extract artifact name from operation string
|
|
1848
|
-
*/
|
|
1849
|
-
private extractArtifactName(operation: string): string {
|
|
1850
|
-
// Try to extract name from common patterns
|
|
1851
|
-
const nameMatch = operation.match(/deploy(?:ing)?\s+([a-zA-Z0-9_\-\s]+)/i);
|
|
1852
|
-
return nameMatch ? nameMatch[1].trim() : 'fallback_artifact';
|
|
1853
|
-
}
|
|
1854
|
-
|
|
1855
|
-
/**
|
|
1856
|
-
* Deploy widget in global scope
|
|
1857
|
-
*/
|
|
1858
|
-
private async deployWidgetInGlobalScope(context: any): Promise<any> {
|
|
1859
|
-
try {
|
|
1860
|
-
const widgetData = {
|
|
1861
|
-
...context.deployment_config,
|
|
1862
|
-
name: context.artifact_name,
|
|
1863
|
-
sys_scope: 'global' // Force global scope
|
|
1864
|
-
};
|
|
1865
|
-
|
|
1866
|
-
const response = await this.client.makeRequest({
|
|
1867
|
-
method: 'POST',
|
|
1868
|
-
endpoint: '/api/now/v2/table/sp_widget',
|
|
1869
|
-
data: widgetData
|
|
1870
|
-
});
|
|
1871
|
-
|
|
1872
|
-
if (response.data?.result) {
|
|
1873
|
-
return {
|
|
1874
|
-
success: true,
|
|
1875
|
-
sys_id: response.data.result.sys_id,
|
|
1876
|
-
name: response.data.result.name
|
|
1877
|
-
};
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
return { success: false, error: 'No result returned from widget creation' };
|
|
1881
|
-
} catch (error) {
|
|
1882
|
-
return {
|
|
1883
|
-
success: false,
|
|
1884
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1885
|
-
};
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
|
|
1889
|
-
/**
|
|
1890
|
-
* Deploy flow in global scope
|
|
1891
|
-
*/
|
|
1892
|
-
private async deployFlowInGlobalScope(context: any): Promise<any> {
|
|
1893
|
-
try {
|
|
1894
|
-
const flowData = {
|
|
1895
|
-
...context.deployment_config,
|
|
1896
|
-
name: context.artifact_name,
|
|
1897
|
-
sys_scope: 'global' // Force global scope
|
|
1898
|
-
};
|
|
1899
|
-
|
|
1900
|
-
const response = await this.client.makeRequest({
|
|
1901
|
-
method: 'POST',
|
|
1902
|
-
endpoint: '/api/now/v2/table/wf_workflow',
|
|
1903
|
-
data: flowData
|
|
1904
|
-
});
|
|
1905
|
-
|
|
1906
|
-
if (response.data?.result) {
|
|
1907
|
-
return {
|
|
1908
|
-
success: true,
|
|
1909
|
-
sys_id: response.data.result.sys_id,
|
|
1910
|
-
name: response.data.result.name
|
|
1911
|
-
};
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
return { success: false, error: 'No result returned from flow creation' };
|
|
1915
|
-
} catch (error) {
|
|
1916
|
-
return {
|
|
1917
|
-
success: false,
|
|
1918
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
/**
|
|
1924
|
-
* Deploy script include in global scope
|
|
1925
|
-
*/
|
|
1926
|
-
private async deployScriptInGlobalScope(context: any): Promise<any> {
|
|
1927
|
-
try {
|
|
1928
|
-
const scriptData = {
|
|
1929
|
-
...context.deployment_config,
|
|
1930
|
-
name: context.artifact_name,
|
|
1931
|
-
sys_scope: 'global' // Force global scope
|
|
1932
|
-
};
|
|
1933
|
-
|
|
1934
|
-
const response = await this.client.makeRequest({
|
|
1935
|
-
method: 'POST',
|
|
1936
|
-
endpoint: '/api/now/v2/table/sys_script_include',
|
|
1937
|
-
data: scriptData
|
|
1938
|
-
});
|
|
1939
|
-
|
|
1940
|
-
if (response.data?.result) {
|
|
1941
|
-
return {
|
|
1942
|
-
success: true,
|
|
1943
|
-
sys_id: response.data.result.sys_id,
|
|
1944
|
-
name: response.data.result.name
|
|
1945
|
-
};
|
|
1946
|
-
}
|
|
1947
|
-
|
|
1948
|
-
return { success: false, error: 'No result returned from script creation' };
|
|
1949
|
-
} catch (error) {
|
|
1950
|
-
return {
|
|
1951
|
-
success: false,
|
|
1952
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1953
|
-
};
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
/**
|
|
1958
|
-
* Deploy business rule in global scope
|
|
1959
|
-
*/
|
|
1960
|
-
private async deployBusinessRuleInGlobalScope(context: any): Promise<any> {
|
|
1961
|
-
try {
|
|
1962
|
-
const ruleData = {
|
|
1963
|
-
...context.deployment_config,
|
|
1964
|
-
name: context.artifact_name,
|
|
1965
|
-
sys_scope: 'global' // Force global scope
|
|
1966
|
-
};
|
|
1967
|
-
|
|
1968
|
-
const response = await this.client.makeRequest({
|
|
1969
|
-
method: 'POST',
|
|
1970
|
-
endpoint: '/api/now/v2/table/sys_script',
|
|
1971
|
-
data: ruleData
|
|
1972
|
-
});
|
|
1973
|
-
|
|
1974
|
-
if (response.data?.result) {
|
|
1975
|
-
return {
|
|
1976
|
-
success: true,
|
|
1977
|
-
sys_id: response.data.result.sys_id,
|
|
1978
|
-
name: response.data.result.name
|
|
1979
|
-
};
|
|
1980
|
-
}
|
|
1981
|
-
|
|
1982
|
-
return { success: false, error: 'No result returned from business rule creation' };
|
|
1983
|
-
} catch (error) {
|
|
1984
|
-
return {
|
|
1985
|
-
success: false,
|
|
1986
|
-
error: error instanceof Error ? error.message : String(error)
|
|
1987
|
-
};
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
|
|
1991
|
-
/**
|
|
1992
|
-
* Generic global scope deployment for unknown artifact types
|
|
1993
|
-
*/
|
|
1994
|
-
private async deployGenericArtifactInGlobalScope(context: any): Promise<any> {
|
|
1995
|
-
try {
|
|
1996
|
-
// Attempt deployment based on common table patterns
|
|
1997
|
-
const possibleTables = [
|
|
1998
|
-
'sys_script_include',
|
|
1999
|
-
'sys_script',
|
|
2000
|
-
'sys_ui_script',
|
|
2001
|
-
'sys_ui_page'
|
|
2002
|
-
];
|
|
2003
|
-
|
|
2004
|
-
for (const table of possibleTables) {
|
|
2005
|
-
try {
|
|
2006
|
-
const data = {
|
|
2007
|
-
...context.deployment_config,
|
|
2008
|
-
name: context.artifact_name,
|
|
2009
|
-
sys_scope: 'global'
|
|
2010
|
-
};
|
|
2011
|
-
|
|
2012
|
-
const response = await this.client.makeRequest({
|
|
2013
|
-
method: 'POST',
|
|
2014
|
-
endpoint: `/api/now/v2/table/${table}`,
|
|
2015
|
-
data
|
|
2016
|
-
});
|
|
2017
|
-
|
|
2018
|
-
if (response.data?.result) {
|
|
2019
|
-
return {
|
|
2020
|
-
success: true,
|
|
2021
|
-
sys_id: response.data.result.sys_id,
|
|
2022
|
-
name: response.data.result.name,
|
|
2023
|
-
table_used: table
|
|
2024
|
-
};
|
|
2025
|
-
}
|
|
2026
|
-
} catch (tableError) {
|
|
2027
|
-
// Continue to next table
|
|
2028
|
-
this.logger.debug(`Failed to deploy to ${table}`, { error: tableError });
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
return { success: false, error: 'No suitable table found for deployment' };
|
|
2033
|
-
} catch (error) {
|
|
2034
|
-
return {
|
|
2035
|
-
success: false,
|
|
2036
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2037
|
-
};
|
|
2038
|
-
}
|
|
2039
|
-
}
|
|
2040
|
-
|
|
2041
|
-
/**
|
|
2042
|
-
* Update context with global deployment information
|
|
2043
|
-
*/
|
|
2044
|
-
private async updateContextWithGlobalDeployment(context: AgentContext, deploymentResult: any): Promise<void> {
|
|
2045
|
-
try {
|
|
2046
|
-
// Store deployment information in memory for coordination
|
|
2047
|
-
await this.memory.updateSharedContext({
|
|
2048
|
-
session_id: context.session_id,
|
|
2049
|
-
context_key: 'global_deployment_fallback',
|
|
2050
|
-
context_value: JSON.stringify({
|
|
2051
|
-
original_context: context,
|
|
2052
|
-
global_deployment: deploymentResult,
|
|
2053
|
-
timestamp: new Date().toISOString()
|
|
2054
|
-
}),
|
|
2055
|
-
created_by_agent: context.agent_id
|
|
2056
|
-
});
|
|
2057
|
-
} catch (error) {
|
|
2058
|
-
this.logger.warn('Failed to update context with global deployment', { error });
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
/**
|
|
2063
|
-
* Enhanced context update with deployment tracking
|
|
2064
|
-
*/
|
|
2065
|
-
private async updateContextWithEnhancedGlobalDeployment(context: AgentContext, deploymentResult: any): Promise<void> {
|
|
2066
|
-
try {
|
|
2067
|
-
await this.memory.updateSharedContext({
|
|
2068
|
-
session_id: context.session_id,
|
|
2069
|
-
context_key: 'enhanced_global_deployment',
|
|
2070
|
-
context_value: JSON.stringify({
|
|
2071
|
-
deployment_result: deploymentResult,
|
|
2072
|
-
context: context,
|
|
2073
|
-
timestamp: new Date().toISOString()
|
|
2074
|
-
}),
|
|
2075
|
-
created_by_agent: context.agent_id
|
|
2076
|
-
});
|
|
2077
|
-
} catch (error) {
|
|
2078
|
-
this.logger.warn('Failed to update enhanced global deployment context', { error });
|
|
2079
|
-
}
|
|
2080
|
-
}
|
|
2081
|
-
|
|
2082
|
-
/**
|
|
2083
|
-
* Enhanced widget deployment in global scope
|
|
2084
|
-
*/
|
|
2085
|
-
private async deployWidgetInGlobalScopeEnhanced(context: any, strategy: any): Promise<any> {
|
|
2086
|
-
try {
|
|
2087
|
-
const startTime = Date.now();
|
|
2088
|
-
const result = await this.deployWidgetInGlobalScope(context);
|
|
2089
|
-
|
|
2090
|
-
return {
|
|
2091
|
-
...result,
|
|
2092
|
-
deployment_time_ms: Date.now() - startTime,
|
|
2093
|
-
strategy_applied: strategy.name
|
|
2094
|
-
};
|
|
2095
|
-
} catch (error) {
|
|
2096
|
-
return {
|
|
2097
|
-
success: false,
|
|
2098
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2099
|
-
deployment_time_ms: 0
|
|
2100
|
-
};
|
|
2101
|
-
}
|
|
2102
|
-
}
|
|
2103
|
-
|
|
2104
|
-
/**
|
|
2105
|
-
* Enhanced flow deployment in global scope
|
|
2106
|
-
*/
|
|
2107
|
-
private async deployFlowInGlobalScopeEnhanced(context: any, strategy: any): Promise<any> {
|
|
2108
|
-
try {
|
|
2109
|
-
const startTime = Date.now();
|
|
2110
|
-
const result = await this.deployFlowInGlobalScope(context);
|
|
2111
|
-
|
|
2112
|
-
return {
|
|
2113
|
-
...result,
|
|
2114
|
-
deployment_time_ms: Date.now() - startTime,
|
|
2115
|
-
strategy_applied: strategy.name
|
|
2116
|
-
};
|
|
2117
|
-
} catch (error) {
|
|
2118
|
-
return {
|
|
2119
|
-
success: false,
|
|
2120
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2121
|
-
deployment_time_ms: 0
|
|
2122
|
-
};
|
|
2123
|
-
}
|
|
2124
|
-
}
|
|
2125
|
-
|
|
2126
|
-
/**
|
|
2127
|
-
* Enhanced script deployment in global scope
|
|
2128
|
-
*/
|
|
2129
|
-
private async deployScriptInGlobalScopeEnhanced(context: any, strategy: any): Promise<any> {
|
|
2130
|
-
try {
|
|
2131
|
-
const startTime = Date.now();
|
|
2132
|
-
const result = await this.deployScriptInGlobalScope(context);
|
|
2133
|
-
|
|
2134
|
-
return {
|
|
2135
|
-
...result,
|
|
2136
|
-
deployment_time_ms: Date.now() - startTime,
|
|
2137
|
-
strategy_applied: strategy.name
|
|
2138
|
-
};
|
|
2139
|
-
} catch (error) {
|
|
2140
|
-
return {
|
|
2141
|
-
success: false,
|
|
2142
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2143
|
-
deployment_time_ms: 0
|
|
2144
|
-
};
|
|
2145
|
-
}
|
|
2146
|
-
}
|
|
2147
|
-
|
|
2148
|
-
/**
|
|
2149
|
-
* Enhanced business rule deployment in global scope
|
|
2150
|
-
*/
|
|
2151
|
-
private async deployBusinessRuleInGlobalScopeEnhanced(context: any, strategy: any): Promise<any> {
|
|
2152
|
-
try {
|
|
2153
|
-
const startTime = Date.now();
|
|
2154
|
-
const result = await this.deployBusinessRuleInGlobalScope(context);
|
|
2155
|
-
|
|
2156
|
-
return {
|
|
2157
|
-
...result,
|
|
2158
|
-
deployment_time_ms: Date.now() - startTime,
|
|
2159
|
-
strategy_applied: strategy.name
|
|
2160
|
-
};
|
|
2161
|
-
} catch (error) {
|
|
2162
|
-
return {
|
|
2163
|
-
success: false,
|
|
2164
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2165
|
-
deployment_time_ms: 0
|
|
2166
|
-
};
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
/**
|
|
2171
|
-
* Enhanced table deployment in global scope
|
|
2172
|
-
*/
|
|
2173
|
-
private async deployTableInGlobalScopeEnhanced(context: any, strategy: any): Promise<any> {
|
|
2174
|
-
try {
|
|
2175
|
-
const startTime = Date.now();
|
|
2176
|
-
|
|
2177
|
-
const tableData = {
|
|
2178
|
-
...context.deployment_config,
|
|
2179
|
-
name: context.artifact_name,
|
|
2180
|
-
sys_scope: 'global'
|
|
2181
|
-
};
|
|
2182
|
-
|
|
2183
|
-
const response = await this.client.makeRequest({
|
|
2184
|
-
method: 'POST',
|
|
2185
|
-
endpoint: '/api/now/v2/table/sys_db_object',
|
|
2186
|
-
data: tableData
|
|
2187
|
-
});
|
|
2188
|
-
|
|
2189
|
-
if (response.data?.result) {
|
|
2190
|
-
return {
|
|
2191
|
-
success: true,
|
|
2192
|
-
sys_id: response.data.result.sys_id,
|
|
2193
|
-
name: response.data.result.name,
|
|
2194
|
-
deployment_time_ms: Date.now() - startTime,
|
|
2195
|
-
strategy_applied: strategy.name
|
|
2196
|
-
};
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
|
-
return {
|
|
2200
|
-
success: false,
|
|
2201
|
-
error: 'No result returned from table creation',
|
|
2202
|
-
deployment_time_ms: Date.now() - startTime
|
|
2203
|
-
};
|
|
2204
|
-
} catch (error) {
|
|
2205
|
-
return {
|
|
2206
|
-
success: false,
|
|
2207
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2208
|
-
deployment_time_ms: 0
|
|
2209
|
-
};
|
|
2210
|
-
}
|
|
2211
|
-
}
|
|
2212
|
-
|
|
2213
|
-
/**
|
|
2214
|
-
* Enhanced generic artifact deployment in global scope
|
|
2215
|
-
*/
|
|
2216
|
-
private async deployGenericArtifactInGlobalScopeEnhanced(context: any, strategy: any): Promise<any> {
|
|
2217
|
-
try {
|
|
2218
|
-
const startTime = Date.now();
|
|
2219
|
-
const result = await this.deployGenericArtifactInGlobalScope(context);
|
|
2220
|
-
|
|
2221
|
-
return {
|
|
2222
|
-
...result,
|
|
2223
|
-
deployment_time_ms: Date.now() - startTime,
|
|
2224
|
-
strategy_applied: strategy.name
|
|
2225
|
-
};
|
|
2226
|
-
} catch (error) {
|
|
2227
|
-
return {
|
|
2228
|
-
success: false,
|
|
2229
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2230
|
-
deployment_time_ms: 0
|
|
2231
|
-
};
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
|
|
2235
|
-
/**
|
|
2236
|
-
* Handle application scope fallback
|
|
2237
|
-
*/
|
|
2238
|
-
private async handleApplicationScopeFallback(deploymentContext: any, context: AgentContext): Promise<any> {
|
|
2239
|
-
try {
|
|
2240
|
-
this.logger.info('🏢 Handling application scope fallback...');
|
|
2241
|
-
|
|
2242
|
-
return {
|
|
2243
|
-
success: false,
|
|
2244
|
-
reason: 'Applications require special scope handling - cannot deploy in global scope',
|
|
2245
|
-
alternative_strategies: [
|
|
2246
|
-
'1. Create scoped application with broader permissions',
|
|
2247
|
-
'2. Deploy individual components separately in global scope',
|
|
2248
|
-
'3. Request administrator to configure application scope',
|
|
2249
|
-
'4. Use ServiceNow Studio for application development'
|
|
2250
|
-
],
|
|
2251
|
-
manual_steps: [
|
|
2252
|
-
'Navigate to System Applications > Studio',
|
|
2253
|
-
'Create new scoped application',
|
|
2254
|
-
'Configure appropriate scope permissions',
|
|
2255
|
-
'Deploy components within application scope'
|
|
2256
|
-
],
|
|
2257
|
-
estimated_time: '15-30 minutes with proper permissions'
|
|
2258
|
-
};
|
|
2259
|
-
} catch (error) {
|
|
2260
|
-
return {
|
|
2261
|
-
success: false,
|
|
2262
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2263
|
-
reason: 'Failed to analyze application scope requirements'
|
|
2264
|
-
};
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
|
|
2268
|
-
/**
|
|
2269
|
-
* Attempt generic global scope deployment
|
|
2270
|
-
*/
|
|
2271
|
-
private async attemptGenericGlobalScopeDeployment(operation: string, context: AgentContext): Promise<any> {
|
|
2272
|
-
try {
|
|
2273
|
-
this.logger.info('🌐 Attempting generic global scope deployment...');
|
|
2274
|
-
|
|
2275
|
-
return {
|
|
2276
|
-
success: false,
|
|
2277
|
-
reason: 'Generic global scope deployment requires more specific context',
|
|
2278
|
-
recommended_actions: [
|
|
2279
|
-
'Provide more specific deployment context',
|
|
2280
|
-
'Use artifact-specific deployment methods',
|
|
2281
|
-
'Check deployment configuration',
|
|
2282
|
-
'Contact administrator for manual deployment'
|
|
2283
|
-
]
|
|
2284
|
-
};
|
|
2285
|
-
} catch (error) {
|
|
2286
|
-
return {
|
|
2287
|
-
success: false,
|
|
2288
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2289
|
-
};
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
/**
|
|
2294
|
-
* Validate global deployment
|
|
2295
|
-
*/
|
|
2296
|
-
private async validateGlobalDeployment(sysId: string, artifactType: string): Promise<any> {
|
|
2297
|
-
try {
|
|
2298
|
-
const table = this.getTableForType(artifactType);
|
|
2299
|
-
|
|
2300
|
-
const validationResponse = await this.client.makeRequest({
|
|
2301
|
-
method: 'GET',
|
|
2302
|
-
endpoint: `/api/now/v2/table/${table}/${sysId}`,
|
|
2303
|
-
params: {
|
|
2304
|
-
sysparm_fields: 'sys_id,name,active,sys_scope'
|
|
2305
|
-
}
|
|
2306
|
-
});
|
|
2307
|
-
|
|
2308
|
-
if (validationResponse.data?.result) {
|
|
2309
|
-
const artifact = validationResponse.data.result;
|
|
2310
|
-
return {
|
|
2311
|
-
status: 'validated',
|
|
2312
|
-
functional_tests: [
|
|
2313
|
-
`Artifact exists: ${artifact.name}`,
|
|
2314
|
-
`Scope: ${artifact.sys_scope || 'global'}`,
|
|
2315
|
-
`Active: ${artifact.active || 'unknown'}`
|
|
2316
|
-
],
|
|
2317
|
-
warnings: artifact.sys_scope !== 'global' ? ['Artifact may not be in global scope'] : []
|
|
2318
|
-
};
|
|
2319
|
-
}
|
|
2320
|
-
|
|
2321
|
-
return {
|
|
2322
|
-
status: 'validation_failed',
|
|
2323
|
-
functional_tests: [],
|
|
2324
|
-
warnings: ['Could not validate artifact deployment']
|
|
2325
|
-
};
|
|
2326
|
-
} catch (error) {
|
|
2327
|
-
return {
|
|
2328
|
-
status: 'validation_error',
|
|
2329
|
-
functional_tests: [],
|
|
2330
|
-
warnings: [`Validation error: ${error instanceof Error ? error.message : String(error)}`]
|
|
2331
|
-
};
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
/**
|
|
2336
|
-
* Generate global scope recommendations
|
|
2337
|
-
*/
|
|
2338
|
-
private generateGlobalScopeRecommendations(deploymentContext: any, deploymentResult: any): string[] {
|
|
2339
|
-
const recommendations = [
|
|
2340
|
-
'✅ Document this global scope deployment for future reference',
|
|
2341
|
-
'🔍 Review ACL permissions to ensure proper access control',
|
|
2342
|
-
'📋 Consider creating Update Set for deployment tracking'
|
|
2343
|
-
];
|
|
2344
|
-
|
|
2345
|
-
if (deploymentContext.artifact_type === 'widget') {
|
|
2346
|
-
recommendations.push('🎨 Test widget rendering in Service Portal');
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
|
-
if (deploymentContext.artifact_type === 'flow') {
|
|
2350
|
-
recommendations.push('⚡ Activate flow and test trigger conditions');
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
return recommendations;
|
|
2354
|
-
}
|
|
2355
|
-
|
|
2356
|
-
/**
|
|
2357
|
-
* Analyze global scope failure
|
|
2358
|
-
*/
|
|
2359
|
-
private async analyzeGlobalScopeFailure(deploymentResult: any, deploymentContext: any, strategy: any): Promise<any> {
|
|
2360
|
-
const errorMessage = deploymentResult.error || 'Unknown error';
|
|
2361
|
-
const errorType = this.categorizeError(errorMessage);
|
|
2362
|
-
|
|
2363
|
-
return {
|
|
2364
|
-
error_category: errorType,
|
|
2365
|
-
strategy_used: strategy.name,
|
|
2366
|
-
requires_admin_intervention: errorType === 'permissions' || errorType === 'authentication',
|
|
2367
|
-
estimated_manual_time: '10-30 minutes',
|
|
2368
|
-
manual_steps: [
|
|
2369
|
-
'1. Review ServiceNow system logs for detailed error information',
|
|
2370
|
-
'2. Check user permissions and roles',
|
|
2371
|
-
'3. Verify artifact configuration meets ServiceNow requirements',
|
|
2372
|
-
'4. Consider alternative deployment approaches',
|
|
2373
|
-
'5. Contact ServiceNow administrator if permission issues persist'
|
|
2374
|
-
],
|
|
2375
|
-
technical_details: {
|
|
2376
|
-
original_error: deploymentContext.original_error,
|
|
2377
|
-
global_deployment_error: errorMessage,
|
|
2378
|
-
artifact_type: deploymentContext.artifact_type,
|
|
2379
|
-
deployment_strategy: strategy.name
|
|
2380
|
-
}
|
|
2381
|
-
};
|
|
2382
|
-
}
|
|
2383
|
-
|
|
2384
|
-
private async delay(ms: number): Promise<void> {
|
|
2385
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
2386
|
-
}
|
|
2387
|
-
|
|
2388
|
-
/**
|
|
2389
|
-
* Handle mock data removal - ensure real operations only
|
|
2390
|
-
*/
|
|
2391
|
-
protected assertNoMockData(operation: string): void {
|
|
2392
|
-
if (process.env.SNOW_FLOW_MOCK_MODE === 'true') {
|
|
2393
|
-
throw new Error(`Mock mode is not supported for ${operation}. All operations must use real ServiceNow instance.`);
|
|
2394
|
-
}
|
|
2395
|
-
}
|
|
2396
|
-
|
|
2397
|
-
/**
|
|
2398
|
-
* List available MCP resources
|
|
2399
|
-
*/
|
|
2400
|
-
protected async listMCPResources(): Promise<any> {
|
|
2401
|
-
try {
|
|
2402
|
-
const resources = await this.resourceManager.listResources();
|
|
2403
|
-
this.logger.debug(`Listed ${resources.length} MCP resources`);
|
|
2404
|
-
|
|
2405
|
-
return {
|
|
2406
|
-
resources: resources.map(resource => ({
|
|
2407
|
-
uri: resource.uri,
|
|
2408
|
-
name: resource.name,
|
|
2409
|
-
description: resource.description,
|
|
2410
|
-
mimeType: resource.mimeType
|
|
2411
|
-
}))
|
|
2412
|
-
};
|
|
2413
|
-
} catch (error) {
|
|
2414
|
-
this.logger.error('Failed to list MCP resources:', error);
|
|
2415
|
-
return { resources: [] };
|
|
2416
|
-
}
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2419
|
-
/**
|
|
2420
|
-
* Read a specific MCP resource by URI
|
|
2421
|
-
*/
|
|
2422
|
-
protected async readMCPResource(uri: string): Promise<any> {
|
|
2423
|
-
try {
|
|
2424
|
-
const resource = await this.resourceManager.readResource(uri);
|
|
2425
|
-
this.logger.debug(`Read MCP resource: ${uri}`);
|
|
2426
|
-
|
|
2427
|
-
return {
|
|
2428
|
-
contents: [{
|
|
2429
|
-
uri: resource.uri,
|
|
2430
|
-
mimeType: resource.mimeType,
|
|
2431
|
-
text: resource.text
|
|
2432
|
-
}]
|
|
2433
|
-
};
|
|
2434
|
-
} catch (error) {
|
|
2435
|
-
this.logger.error(`Failed to read MCP resource ${uri}:`, error);
|
|
2436
|
-
throw new Error(`Resource not found or inaccessible: ${uri}`);
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
|
|
2440
|
-
/**
|
|
2441
|
-
* Get resource manager statistics
|
|
2442
|
-
*/
|
|
2443
|
-
protected getMCPResourceStats(): any {
|
|
2444
|
-
return this.resourceManager.getResourceStats();
|
|
2445
|
-
}
|
|
2446
|
-
|
|
2447
|
-
/**
|
|
2448
|
-
* Clear resource cache (useful for development)
|
|
2449
|
-
*/
|
|
2450
|
-
protected clearMCPResourceCache(): void {
|
|
2451
|
-
this.resourceManager.clearCache();
|
|
2452
|
-
this.logger.debug('MCP resource cache cleared');
|
|
2453
|
-
}
|
|
2454
|
-
|
|
2455
|
-
/**
|
|
2456
|
-
* Start the MCP server
|
|
2457
|
-
*/
|
|
2458
|
-
async start(): Promise<void> {
|
|
2459
|
-
const transport = new (await import('@modelcontextprotocol/sdk/server/stdio.js')).StdioServerTransport();
|
|
2460
|
-
await this.server.connect(transport);
|
|
2461
|
-
this.logger.info(`${(this.server as any).serverInfo?.name || 'Unknown'} MCP server started`);
|
|
2462
|
-
}
|
|
2463
|
-
}
|