snow-flow 2.0.2 → 2.0.3
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 +6 -0
- 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,4638 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* ServiceNow Intelligent MCP Server
|
|
4
|
-
* Natural language processing for ServiceNow artifacts with intelligent indexing
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
|
-
import {
|
|
10
|
-
CallToolRequestSchema,
|
|
11
|
-
ErrorCode,
|
|
12
|
-
ListToolsRequestSchema,
|
|
13
|
-
McpError,
|
|
14
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
15
|
-
import { ServiceNowClient } from '../utils/servicenow-client.js';
|
|
16
|
-
import { mcpAuth } from '../utils/mcp-auth-middleware.js';
|
|
17
|
-
import { mcpConfig } from '../utils/mcp-config-manager.js';
|
|
18
|
-
import { Logger } from '../utils/logger.js';
|
|
19
|
-
import { widgetTemplateGenerator } from '../utils/widget-template-generator.js';
|
|
20
|
-
import { promises as fs } from 'fs';
|
|
21
|
-
import { join } from 'path';
|
|
22
|
-
import { SelfDocumentingSystem } from '../documentation/self-documenting-system.js';
|
|
23
|
-
import { CostOptimizationEngine } from '../optimization/cost-optimization-engine.js';
|
|
24
|
-
import { AdvancedComplianceSystem } from '../compliance/advanced-compliance-system.js';
|
|
25
|
-
import { SelfHealingSystem } from '../healing/self-healing-system.js';
|
|
26
|
-
import { MemorySystem } from '../memory/memory-system.js';
|
|
27
|
-
|
|
28
|
-
interface ParsedIntent {
|
|
29
|
-
action: 'find' | 'edit' | 'create' | 'clone';
|
|
30
|
-
artifactType: string; // Allow any artifact type from the comprehensive table mapping
|
|
31
|
-
identifier: string;
|
|
32
|
-
modification?: string;
|
|
33
|
-
confidence: number;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
interface IndexedArtifact {
|
|
37
|
-
meta: {
|
|
38
|
-
sys_id: string;
|
|
39
|
-
name: string;
|
|
40
|
-
title?: string;
|
|
41
|
-
type: string;
|
|
42
|
-
last_updated: string;
|
|
43
|
-
};
|
|
44
|
-
structure: any;
|
|
45
|
-
context: any;
|
|
46
|
-
relationships: any;
|
|
47
|
-
claudeSummary: string;
|
|
48
|
-
modificationPoints: any[];
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export class ServiceNowIntelligentMCP {
|
|
52
|
-
private server: Server;
|
|
53
|
-
private client: ServiceNowClient;
|
|
54
|
-
private logger: Logger;
|
|
55
|
-
private memoryPath: string;
|
|
56
|
-
private config: ReturnType<typeof mcpConfig.getMemoryConfig>;
|
|
57
|
-
private documentationSystem: SelfDocumentingSystem;
|
|
58
|
-
private costOptimizationEngine: CostOptimizationEngine;
|
|
59
|
-
private complianceSystem: AdvancedComplianceSystem;
|
|
60
|
-
private selfHealingSystem: SelfHealingSystem;
|
|
61
|
-
private memorySystem: MemorySystem;
|
|
62
|
-
private memoryIndex: Map<string, any> = new Map();
|
|
63
|
-
|
|
64
|
-
constructor() {
|
|
65
|
-
this.server = new Server(
|
|
66
|
-
{
|
|
67
|
-
name: 'servicenow-intelligent',
|
|
68
|
-
version: '1.0.0',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
capabilities: {
|
|
72
|
-
tools: {},
|
|
73
|
-
},
|
|
74
|
-
}
|
|
75
|
-
);
|
|
76
|
-
|
|
77
|
-
this.client = new ServiceNowClient();
|
|
78
|
-
this.logger = new Logger('ServiceNowIntelligentMCP');
|
|
79
|
-
this.config = mcpConfig.getMemoryConfig();
|
|
80
|
-
this.memoryPath = this.config.path || join(process.cwd(), 'memory', 'servicenow_artifacts');
|
|
81
|
-
|
|
82
|
-
this.setupHandlers();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private async initializeSystems(): Promise<void> {
|
|
86
|
-
try {
|
|
87
|
-
// Initialize systems synchronously during server startup
|
|
88
|
-
this.memorySystem = new MemorySystem({
|
|
89
|
-
dbPath: './intelligent-mcp.db',
|
|
90
|
-
cache: { enabled: true, maxSize: 100, ttl: 3600 },
|
|
91
|
-
ttl: { default: 3600, session: 7200, artifact: 86400, metric: 604800 }
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
await this.memorySystem.initialize();
|
|
95
|
-
this.logger.info('Memory system initialized');
|
|
96
|
-
|
|
97
|
-
this.documentationSystem = new SelfDocumentingSystem(this.client, this.memorySystem);
|
|
98
|
-
this.costOptimizationEngine = new CostOptimizationEngine(this.client, this.memorySystem);
|
|
99
|
-
this.complianceSystem = new AdvancedComplianceSystem(this.client, this.memorySystem);
|
|
100
|
-
this.selfHealingSystem = new SelfHealingSystem(this.client, this.memorySystem);
|
|
101
|
-
|
|
102
|
-
this.logger.info('All systems initialized successfully');
|
|
103
|
-
} catch (error) {
|
|
104
|
-
this.logger.error('Failed to initialize systems:', error);
|
|
105
|
-
// Initialize minimal fallback systems
|
|
106
|
-
this.memorySystem = new MemorySystem({ dbPath: './intelligent-mcp.db' });
|
|
107
|
-
await this.memorySystem.initialize();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private setupHandlers() {
|
|
112
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
113
|
-
tools: [
|
|
114
|
-
{
|
|
115
|
-
name: 'snow_find_artifact',
|
|
116
|
-
description: 'AUTONOMOUS artifact discovery - finds ServiceNow artifacts using natural language, searches memory first, then ServiceNow. NO MANUAL SEARCH NEEDED.',
|
|
117
|
-
inputSchema: {
|
|
118
|
-
type: 'object',
|
|
119
|
-
properties: {
|
|
120
|
-
query: { type: 'string', description: 'Natural language query (e.g., "the widget that shows incidents on homepage")' },
|
|
121
|
-
type: { type: 'string', enum: ['widget', 'flow', 'script', 'application', 'any'], description: 'Artifact type filter' },
|
|
122
|
-
},
|
|
123
|
-
required: ['query'],
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
name: 'snow_edit_artifact',
|
|
128
|
-
description: 'AUTONOMOUS artifact modification - edits ServiceNow artifacts using natural language, handles errors automatically, retries on failure. DIRECT MODIFICATION.',
|
|
129
|
-
inputSchema: {
|
|
130
|
-
type: 'object',
|
|
131
|
-
properties: {
|
|
132
|
-
query: { type: 'string', description: 'Natural language edit instruction (e.g., "pas de flow aan met de naam approval request flow en zorg dat er na de approval stap een mailtje naar test@admin.nl wordt gestuurd")' },
|
|
133
|
-
},
|
|
134
|
-
required: ['query'],
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
name: 'snow_get_by_sysid',
|
|
139
|
-
description: 'DIRECT sys_id lookup - get artifact by exact sys_id, much faster and more reliable than text search',
|
|
140
|
-
inputSchema: {
|
|
141
|
-
type: 'object',
|
|
142
|
-
properties: {
|
|
143
|
-
sys_id: { type: 'string', description: 'System ID of the artifact' },
|
|
144
|
-
table: { type: 'string', description: 'ServiceNow table name (e.g., sp_widget, wf_workflow, sys_script_include)' },
|
|
145
|
-
},
|
|
146
|
-
required: ['sys_id', 'table'],
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
name: 'snow_edit_by_sysid',
|
|
151
|
-
description: 'DIRECT sys_id edit - update specific fields of artifact by sys_id, much more reliable than text-based search',
|
|
152
|
-
inputSchema: {
|
|
153
|
-
type: 'object',
|
|
154
|
-
properties: {
|
|
155
|
-
sys_id: { type: 'string', description: 'System ID of the artifact to edit' },
|
|
156
|
-
table: { type: 'string', description: 'ServiceNow table name' },
|
|
157
|
-
field: { type: 'string', description: 'Field name to update (e.g., script, server_script, template)' },
|
|
158
|
-
value: { type: 'string', description: 'New value for the field' },
|
|
159
|
-
},
|
|
160
|
-
required: ['sys_id', 'table', 'field', 'value'],
|
|
161
|
-
},
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
name: 'snow_analyze_artifact',
|
|
165
|
-
description: 'AUTONOMOUS deep _analysis - intelligently indexes artifacts for optimal Claude understanding, stores in memory for future use. SELF-LEARNING SYSTEM.',
|
|
166
|
-
inputSchema: {
|
|
167
|
-
type: 'object',
|
|
168
|
-
properties: {
|
|
169
|
-
sys_id: { type: 'string', description: 'System ID of the artifact' },
|
|
170
|
-
table: { type: 'string', description: 'ServiceNow table name' },
|
|
171
|
-
},
|
|
172
|
-
required: ['sys_id', 'table'],
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
name: 'snow_memory_search',
|
|
177
|
-
description: 'Search indexed ServiceNow artifacts in memory',
|
|
178
|
-
inputSchema: {
|
|
179
|
-
type: 'object',
|
|
180
|
-
properties: {
|
|
181
|
-
query: { type: 'string', description: 'Search query' },
|
|
182
|
-
type: { type: 'string', enum: ['widget', 'flow', 'script', 'application'], description: 'Artifact type' },
|
|
183
|
-
},
|
|
184
|
-
required: ['query'],
|
|
185
|
-
},
|
|
186
|
-
},
|
|
187
|
-
{
|
|
188
|
-
name: 'snow_comprehensive_search',
|
|
189
|
-
description: 'COMPREHENSIVE multi-table search - searches across all relevant ServiceNow tables for artifacts. Perfect for finding hard-to-locate items.',
|
|
190
|
-
inputSchema: {
|
|
191
|
-
type: 'object',
|
|
192
|
-
properties: {
|
|
193
|
-
query: { type: 'string', description: 'Natural language search query' },
|
|
194
|
-
include_inactive: { type: 'boolean', description: 'Include inactive records', default: false },
|
|
195
|
-
},
|
|
196
|
-
required: ['query'],
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
name: 'snow_sync_data_consistency',
|
|
201
|
-
description: 'AUTONOMOUS data synchronization - fixes data consistency issues by refreshing cache, re-indexing artifacts, and validating sys_id mappings. AUTO-HEALING.',
|
|
202
|
-
inputSchema: {
|
|
203
|
-
type: 'object',
|
|
204
|
-
properties: {
|
|
205
|
-
operation: { type: 'string', enum: ['refresh_cache', 'validate_sysids', 'reindex_artifacts', 'full_sync'], description: 'Type of sync operation' },
|
|
206
|
-
sys_id: { type: 'string', description: 'Specific sys_id to validate (optional)' },
|
|
207
|
-
table: { type: 'string', description: 'Specific table to sync (optional)' },
|
|
208
|
-
},
|
|
209
|
-
required: ['operation'],
|
|
210
|
-
},
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
name: 'snow_validate_live_connection',
|
|
214
|
-
description: 'REAL-TIME connection validation - validates live ServiceNow connection, authentication, and permissions. Returns actual instance status and capabilities.',
|
|
215
|
-
inputSchema: {
|
|
216
|
-
type: 'object',
|
|
217
|
-
properties: {
|
|
218
|
-
test_level: { type: 'string', enum: ['basic', 'full', 'permissions'], description: 'Level of validation (basic=ping, full=read test, permissions=write test)', default: 'basic' },
|
|
219
|
-
include_performance: { type: 'boolean', description: 'Include response time metrics', default: false },
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
name: 'batch_deployment_validator',
|
|
225
|
-
description: 'COMPREHENSIVE batch validation - validates multiple deployments simultaneously, checks dependencies, conflicts, and provides rollback recommendations.',
|
|
226
|
-
inputSchema: {
|
|
227
|
-
type: 'object',
|
|
228
|
-
properties: {
|
|
229
|
-
artifacts: { type: 'array', items: { type: 'object', properties: { type: { type: 'string' }, sys_id: { type: 'string' }, table: { type: 'string' } } }, description: 'List of artifacts to validate' },
|
|
230
|
-
validation_level: { type: 'string', enum: ['syntax', 'dependencies', 'full'], description: 'Level of validation', default: 'full' },
|
|
231
|
-
check_conflicts: { type: 'boolean', description: 'Check for conflicts between artifacts', default: true },
|
|
232
|
-
},
|
|
233
|
-
required: ['artifacts'],
|
|
234
|
-
},
|
|
235
|
-
},
|
|
236
|
-
{
|
|
237
|
-
name: 'deployment_rollback_manager',
|
|
238
|
-
description: 'AUTOMATIC rollback management - monitors deployments, detects failures, and provides automatic rollback capabilities with detailed recovery steps.',
|
|
239
|
-
inputSchema: {
|
|
240
|
-
type: 'object',
|
|
241
|
-
properties: {
|
|
242
|
-
update_set_id: { type: 'string', description: 'Update Set sys_id to monitor/rollback' },
|
|
243
|
-
action: { type: 'string', enum: ['monitor', 'rollback', 'validate_rollback'], description: 'Action to perform' },
|
|
244
|
-
rollback_reason: { type: 'string', description: 'Reason for rollback (required for rollback action)' },
|
|
245
|
-
create_backup: { type: 'boolean', description: 'Create backup before rollback', default: true },
|
|
246
|
-
},
|
|
247
|
-
required: ['update_set_id', 'action'],
|
|
248
|
-
},
|
|
249
|
-
},
|
|
250
|
-
{
|
|
251
|
-
name: 'snow_escalate_permissions',
|
|
252
|
-
description: 'PERMISSION ESCALATION - Request temporary elevated permissions for complex development workflows. Handles admin role requirements automatically.',
|
|
253
|
-
inputSchema: {
|
|
254
|
-
type: 'object',
|
|
255
|
-
properties: {
|
|
256
|
-
required_roles: { type: 'array', items: { type: 'string' }, description: 'Required roles (admin, app_creator, system_administrator)' },
|
|
257
|
-
duration: { type: 'string', enum: ['session', 'temporary', 'workflow'], description: 'Duration of elevation', default: 'session' },
|
|
258
|
-
reason: { type: 'string', description: 'Reason for permission escalation' },
|
|
259
|
-
workflow_context: { type: 'string', description: 'Context of the development workflow requiring elevation' },
|
|
260
|
-
},
|
|
261
|
-
required: ['required_roles', 'reason'],
|
|
262
|
-
},
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
name: 'snow_analyze_requirements',
|
|
266
|
-
description: 'INTELLIGENT REQUIREMENT ANALYSIS - Auto-discovers dependencies, suggests existing components, creates dependency maps for complex objectives.',
|
|
267
|
-
inputSchema: {
|
|
268
|
-
type: 'object',
|
|
269
|
-
properties: {
|
|
270
|
-
objective: { type: 'string', description: 'Development objective (e.g., "iPhone provisioning for new users")' },
|
|
271
|
-
auto_discover_dependencies: { type: 'boolean', description: 'Automatically discover required dependencies', default: true },
|
|
272
|
-
suggest_existing_components: { type: 'boolean', description: 'Suggest reuse of existing components', default: true },
|
|
273
|
-
create_dependency_map: { type: 'boolean', description: 'Create visual dependency map', default: true },
|
|
274
|
-
scope_preference: { type: 'string', enum: ['global', 'scoped', 'auto'], description: 'Deployment scope preference', default: 'auto' },
|
|
275
|
-
},
|
|
276
|
-
required: ['objective'],
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
{
|
|
280
|
-
name: 'snow_smart_update_set',
|
|
281
|
-
description: 'SMART UPDATE SET MANAGEMENT - Automatic artifact tracking, conflict detection, dependency validation, and rollback points.',
|
|
282
|
-
inputSchema: {
|
|
283
|
-
type: 'object',
|
|
284
|
-
properties: {
|
|
285
|
-
action: { type: 'string', enum: ['create', 'track', 'validate', 'conflict_check'], description: 'Update Set management action' },
|
|
286
|
-
auto_track_related_artifacts: { type: 'boolean', description: 'Automatically track related artifacts', default: true },
|
|
287
|
-
conflict_detection: { type: 'boolean', description: 'Enable conflict detection', default: true },
|
|
288
|
-
dependency_validation: { type: 'boolean', description: 'Validate dependencies', default: true },
|
|
289
|
-
rollback_points: { type: 'boolean', description: 'Create rollback points', default: true },
|
|
290
|
-
update_set_name: { type: 'string', description: 'Name for new Update Set (required for create action)' },
|
|
291
|
-
},
|
|
292
|
-
required: ['action'],
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
name: 'snow_orchestrate_development',
|
|
297
|
-
description: 'UNIFIED DEVELOPMENT ORCHESTRATION - Single command for complex workflows with auto-spawning agents, shared memory, and progress monitoring.',
|
|
298
|
-
inputSchema: {
|
|
299
|
-
type: 'object',
|
|
300
|
-
properties: {
|
|
301
|
-
objective: { type: 'string', description: 'Development objective (e.g., "iPhone provisioning workflow")' },
|
|
302
|
-
auto_spawn_agents: { type: 'boolean', description: 'Automatically spawn required agents', default: true },
|
|
303
|
-
shared_memory: { type: 'boolean', description: 'Enable shared memory between agents', default: true },
|
|
304
|
-
parallel_execution: { type: 'boolean', description: 'Enable parallel execution', default: true },
|
|
305
|
-
progress_monitoring: { type: 'boolean', description: 'Real-time progress monitoring', default: true },
|
|
306
|
-
auto_permissions: { type: 'boolean', description: 'Automatic permission escalation', default: false },
|
|
307
|
-
smart_discovery: { type: 'boolean', description: 'Smart artifact discovery and reuse', default: true },
|
|
308
|
-
live_testing: { type: 'boolean', description: 'Enable live testing during development', default: true },
|
|
309
|
-
auto_deploy: { type: 'boolean', description: 'Automatic deployment when ready', default: false },
|
|
310
|
-
},
|
|
311
|
-
required: ['objective'],
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
{
|
|
315
|
-
name: 'snow_resilient_deployment',
|
|
316
|
-
description: 'RESILIENT DEPLOYMENT - Advanced error recovery with retry mechanisms, fallback strategies, checkpoints, and graceful degradation.',
|
|
317
|
-
inputSchema: {
|
|
318
|
-
type: 'object',
|
|
319
|
-
properties: {
|
|
320
|
-
artifacts: { type: 'array', items: { type: 'object' }, description: 'Artifacts to deploy' },
|
|
321
|
-
retry_on_failure: { type: 'boolean', description: 'Enable automatic retry on failure', default: true },
|
|
322
|
-
fallback_strategies: { type: 'array', items: { type: 'string', enum: ['global_scope', 'manual_approval', 'staged_deployment'] }, description: 'Fallback strategies' },
|
|
323
|
-
checkpoint_restoration: { type: 'boolean', description: 'Enable checkpoint restoration', default: true },
|
|
324
|
-
graceful_degradation: { type: 'boolean', description: 'Enable graceful degradation', default: true },
|
|
325
|
-
max_retries: { type: 'number', description: 'Maximum retry attempts', default: 3 },
|
|
326
|
-
},
|
|
327
|
-
required: ['artifacts'],
|
|
328
|
-
},
|
|
329
|
-
},
|
|
330
|
-
],
|
|
331
|
-
}));
|
|
332
|
-
|
|
333
|
-
// Register tool handlers
|
|
334
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
335
|
-
const { name, arguments: args } = request.params;
|
|
336
|
-
|
|
337
|
-
try {
|
|
338
|
-
// Authenticate if needed
|
|
339
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
340
|
-
if (!authResult.success) {
|
|
341
|
-
throw new McpError(ErrorCode.InternalError, authResult.error || 'Authentication required');
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
switch (name) {
|
|
345
|
-
case 'snow_find_artifact':
|
|
346
|
-
return await this.findArtifact(args);
|
|
347
|
-
case 'snow_edit_artifact':
|
|
348
|
-
return await this.editArtifact(args);
|
|
349
|
-
case 'snow_get_by_sysid':
|
|
350
|
-
return await this.getBySysId(args);
|
|
351
|
-
case 'snow_edit_by_sysid':
|
|
352
|
-
return await this.editBySysId(args);
|
|
353
|
-
case 'snow_analyze_artifact':
|
|
354
|
-
return await this.analyzeArtifact(args);
|
|
355
|
-
case 'snow_memory_search':
|
|
356
|
-
return await this.searchMemory(args);
|
|
357
|
-
case 'snow_comprehensive_search':
|
|
358
|
-
return await this.comprehensiveSearch(args);
|
|
359
|
-
case 'snow_sync_data_consistency':
|
|
360
|
-
return await this.syncDataConsistency(args);
|
|
361
|
-
case 'snow_validate_live_connection':
|
|
362
|
-
return await this.validateLiveConnection(args);
|
|
363
|
-
case 'batch_deployment_validator':
|
|
364
|
-
return await this.batchDeploymentValidator(args);
|
|
365
|
-
case 'deployment_rollback_manager':
|
|
366
|
-
return await this.deploymentRollbackManager(args);
|
|
367
|
-
case 'snow_escalate_permissions':
|
|
368
|
-
return await this.escalatePermissions(args);
|
|
369
|
-
case 'snow_analyze_requirements':
|
|
370
|
-
return await this.analyzeRequirements(args);
|
|
371
|
-
case 'snow_smart_update_set':
|
|
372
|
-
return await this.smartUpdateSet(args);
|
|
373
|
-
case 'snow_orchestrate_development':
|
|
374
|
-
return await this.orchestrateDevelopment(args);
|
|
375
|
-
case 'snow_resilient_deployment':
|
|
376
|
-
return await this.resilientDeployment(args);
|
|
377
|
-
case 'snow_verify_artifact_searchable':
|
|
378
|
-
return await this.verifyArtifactSearchable(args);
|
|
379
|
-
case 'snow_generate_documentation':
|
|
380
|
-
return await this.generateDocumentation(args);
|
|
381
|
-
case 'snow_documentation_suggestions':
|
|
382
|
-
return await this.getDocumentationSuggestions(args);
|
|
383
|
-
case 'snow_start_continuous_documentation':
|
|
384
|
-
return await this.startContinuousDocumentation(args);
|
|
385
|
-
case 'snow_analyze_costs':
|
|
386
|
-
const costRequest: any = {
|
|
387
|
-
scope: args.scope || 'all',
|
|
388
|
-
auto_implement: args.auto_implement || false,
|
|
389
|
-
target_reduction: args.target_reduction,
|
|
390
|
-
testing_enabled: args.testing_enabled !== false
|
|
391
|
-
};
|
|
392
|
-
const costResult = await this.costOptimizationEngine.analyzeCosts(costRequest);
|
|
393
|
-
return {
|
|
394
|
-
content: [
|
|
395
|
-
{
|
|
396
|
-
type: 'text',
|
|
397
|
-
text: JSON.stringify(costResult, null, 2)
|
|
398
|
-
}
|
|
399
|
-
]
|
|
400
|
-
};
|
|
401
|
-
case 'snow_cost_dashboard':
|
|
402
|
-
const dashboardResult = await this.costOptimizationEngine.getCostDashboard();
|
|
403
|
-
return {
|
|
404
|
-
content: [
|
|
405
|
-
{
|
|
406
|
-
type: 'text',
|
|
407
|
-
text: JSON.stringify(dashboardResult, null, 2)
|
|
408
|
-
}
|
|
409
|
-
]
|
|
410
|
-
};
|
|
411
|
-
case 'snow_start_autonomous_cost_optimization':
|
|
412
|
-
const startResult = await this.costOptimizationEngine.startAutonomousOptimization(args);
|
|
413
|
-
return {
|
|
414
|
-
content: [
|
|
415
|
-
{
|
|
416
|
-
type: 'text',
|
|
417
|
-
text: JSON.stringify(startResult, null, 2)
|
|
418
|
-
}
|
|
419
|
-
]
|
|
420
|
-
};
|
|
421
|
-
case 'snow_implement_cost_optimization':
|
|
422
|
-
const implementResult = await this.costOptimizationEngine.implementOptimization(String(args.optimization_id));
|
|
423
|
-
return {
|
|
424
|
-
content: [
|
|
425
|
-
{
|
|
426
|
-
type: 'text',
|
|
427
|
-
text: JSON.stringify(implementResult, null, 2)
|
|
428
|
-
}
|
|
429
|
-
]
|
|
430
|
-
};
|
|
431
|
-
case 'snow_assess_compliance':
|
|
432
|
-
const complianceResult = await this.complianceSystem.assessCompliance(args);
|
|
433
|
-
return {
|
|
434
|
-
content: [
|
|
435
|
-
{
|
|
436
|
-
type: 'text',
|
|
437
|
-
text: JSON.stringify(complianceResult, null, 2)
|
|
438
|
-
}
|
|
439
|
-
]
|
|
440
|
-
};
|
|
441
|
-
case 'snow_compliance_dashboard':
|
|
442
|
-
const complianceDashboard = await this.complianceSystem.getComplianceDashboard();
|
|
443
|
-
return {
|
|
444
|
-
content: [
|
|
445
|
-
{
|
|
446
|
-
type: 'text',
|
|
447
|
-
text: JSON.stringify(complianceDashboard, null, 2)
|
|
448
|
-
}
|
|
449
|
-
]
|
|
450
|
-
};
|
|
451
|
-
case 'snow_start_compliance_monitoring':
|
|
452
|
-
const monitoringResult = await this.complianceSystem.startContinuousMonitoring(args);
|
|
453
|
-
return {
|
|
454
|
-
content: [
|
|
455
|
-
{
|
|
456
|
-
type: 'text',
|
|
457
|
-
text: '✅ Compliance monitoring started successfully'
|
|
458
|
-
}
|
|
459
|
-
]
|
|
460
|
-
};
|
|
461
|
-
case 'snow_execute_corrective_action':
|
|
462
|
-
const actionResult = await this.complianceSystem.executeCorrectiveAction(String(args.action_id), args);
|
|
463
|
-
return {
|
|
464
|
-
content: [
|
|
465
|
-
{
|
|
466
|
-
type: 'text',
|
|
467
|
-
text: JSON.stringify(actionResult, null, 2)
|
|
468
|
-
}
|
|
469
|
-
]
|
|
470
|
-
};
|
|
471
|
-
case 'snow_health_check':
|
|
472
|
-
const healthResult = await this.selfHealingSystem.performHealthCheck(args);
|
|
473
|
-
return {
|
|
474
|
-
content: [
|
|
475
|
-
{
|
|
476
|
-
type: 'text',
|
|
477
|
-
text: JSON.stringify(healthResult, null, 2)
|
|
478
|
-
}
|
|
479
|
-
]
|
|
480
|
-
};
|
|
481
|
-
case 'snow_health_dashboard':
|
|
482
|
-
const healthDashboard = await this.selfHealingSystem.getHealthDashboard();
|
|
483
|
-
return {
|
|
484
|
-
content: [
|
|
485
|
-
{
|
|
486
|
-
type: 'text',
|
|
487
|
-
text: JSON.stringify(healthDashboard, null, 2)
|
|
488
|
-
}
|
|
489
|
-
]
|
|
490
|
-
};
|
|
491
|
-
case 'snow_start_autonomous_healing':
|
|
492
|
-
const healingStartResult = await this.selfHealingSystem.startAutonomousHealing(args);
|
|
493
|
-
return {
|
|
494
|
-
content: [
|
|
495
|
-
{
|
|
496
|
-
type: 'text',
|
|
497
|
-
text: '✅ Autonomous healing started successfully'
|
|
498
|
-
}
|
|
499
|
-
]
|
|
500
|
-
};
|
|
501
|
-
case 'snow_execute_healing_action':
|
|
502
|
-
const healingResult = await this.selfHealingSystem.executeHealingAction(String(args.action_id), args);
|
|
503
|
-
return {
|
|
504
|
-
content: [
|
|
505
|
-
{
|
|
506
|
-
type: 'text',
|
|
507
|
-
text: JSON.stringify(healingResult, null, 2)
|
|
508
|
-
}
|
|
509
|
-
]
|
|
510
|
-
};
|
|
511
|
-
default:
|
|
512
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
513
|
-
}
|
|
514
|
-
} catch (error) {
|
|
515
|
-
this.logger.error(`Tool execution failed: ${name}`, error);
|
|
516
|
-
throw new McpError(
|
|
517
|
-
ErrorCode.InternalError,
|
|
518
|
-
error instanceof Error ? error.message : String(error)
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
private async findArtifact(args: any) {
|
|
525
|
-
// Check authentication first
|
|
526
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
527
|
-
if (!authResult.success) {
|
|
528
|
-
return {
|
|
529
|
-
content: [
|
|
530
|
-
{
|
|
531
|
-
type: 'text',
|
|
532
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
533
|
-
},
|
|
534
|
-
],
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
try {
|
|
539
|
-
this.logger.info('🔴 SNOW-002 FIX: Finding ServiceNow artifact with retry logic', { query: args.query });
|
|
540
|
-
|
|
541
|
-
// 1. Parse natural language intent
|
|
542
|
-
const intent = await this.parseIntent(args.query);
|
|
543
|
-
|
|
544
|
-
// 2. Search in memory first
|
|
545
|
-
const memoryResults = await this.searchInMemory(intent);
|
|
546
|
-
|
|
547
|
-
if (memoryResults.length > 0) {
|
|
548
|
-
return {
|
|
549
|
-
content: [
|
|
550
|
-
{
|
|
551
|
-
type: 'text',
|
|
552
|
-
text: `🧠 Found in memory:\n\n${this.formatResults(memoryResults)}\n\n💡 Using cached intelligent index for optimal performance.`,
|
|
553
|
-
},
|
|
554
|
-
],
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
// 🔴 CRITICAL FIX: Search ServiceNow with retry logic for newly created artifacts
|
|
559
|
-
this.logger.info(`🔍 Searching ServiceNow with retry logic for: ${intent.identifier} (type: ${intent.artifactType})`);
|
|
560
|
-
const liveResults = await this.searchServiceNowWithRetry(intent);
|
|
561
|
-
this.logger.info(`✅ ServiceNow search with retry returned ${liveResults?.length || 0} results`);
|
|
562
|
-
|
|
563
|
-
// Debug log
|
|
564
|
-
if (liveResults && liveResults.length > 0) {
|
|
565
|
-
this.logger.info(`First result: ${JSON.stringify(liveResults[0])}`);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// 4. Index results for future use (only if we have results)
|
|
569
|
-
if (liveResults && liveResults.length > 0) {
|
|
570
|
-
this.logger.info('Indexing found artifacts for future use...');
|
|
571
|
-
for (const result of liveResults) {
|
|
572
|
-
await this.intelligentlyIndex(result);
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const instanceInfo = await mcpAuth.getInstanceInfo();
|
|
577
|
-
const resultText = this.formatResults(liveResults);
|
|
578
|
-
this.logger.info(`Formatted result text: ${resultText.substring(0, 200)}...`);
|
|
579
|
-
const editSuggestion = this.generateEditSuggestion(liveResults?.[0]);
|
|
580
|
-
|
|
581
|
-
return {
|
|
582
|
-
content: [
|
|
583
|
-
{
|
|
584
|
-
type: 'text',
|
|
585
|
-
text: `🔍 ServiceNow Search Results:\n\n${resultText}\n\n🔗 ServiceNow Instance: ${instanceInfo.instance}\n\n${editSuggestion}`,
|
|
586
|
-
},
|
|
587
|
-
],
|
|
588
|
-
};
|
|
589
|
-
} catch (error) {
|
|
590
|
-
throw new Error(`Artifact search failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
private async editArtifact(args: any) {
|
|
595
|
-
// Check authentication first
|
|
596
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
597
|
-
if (!authResult.success) {
|
|
598
|
-
return {
|
|
599
|
-
content: [
|
|
600
|
-
{
|
|
601
|
-
type: 'text',
|
|
602
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
603
|
-
},
|
|
604
|
-
],
|
|
605
|
-
};
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
try {
|
|
609
|
-
this.logger.info('Editing ServiceNow artifact', { query: args.query });
|
|
610
|
-
|
|
611
|
-
// 1. Parse edit instruction
|
|
612
|
-
const editIntent = await this.parseEditIntent(args.query);
|
|
613
|
-
|
|
614
|
-
// 2. Find target artifact
|
|
615
|
-
const artifact = await this.findTargetArtifact(editIntent);
|
|
616
|
-
|
|
617
|
-
// 3. Analyze modification requirements
|
|
618
|
-
const modification = await this.analyzeModification(editIntent, artifact);
|
|
619
|
-
|
|
620
|
-
// 4. Apply intelligent modification
|
|
621
|
-
const editedArtifact = await this.applyModification(artifact, modification);
|
|
622
|
-
|
|
623
|
-
// 5. Deploy back to ServiceNow
|
|
624
|
-
const deployResult = await this.deployArtifact(editedArtifact);
|
|
625
|
-
|
|
626
|
-
// 6. Update memory with changes
|
|
627
|
-
await this.updateMemoryIndex(editedArtifact, modification);
|
|
628
|
-
|
|
629
|
-
const instanceInfo = await mcpAuth.getInstanceInfo();
|
|
630
|
-
|
|
631
|
-
return {
|
|
632
|
-
content: [
|
|
633
|
-
{
|
|
634
|
-
type: 'text',
|
|
635
|
-
text: `✅ ServiceNow artifact successfully modified!\n\n🎯 Modification Details:\n- Artifact: ${artifact.name}\n- Type: ${artifact.type}\n- Changes: ${modification.description}\n\n🔗 View in ServiceNow: ${instanceInfo.instance}/nav_to.do?uri=${this.getArtifactUrl(artifact)}\n\n📝 The artifact has been intelligently indexed and is now available for future natural language queries.`,
|
|
636
|
-
},
|
|
637
|
-
],
|
|
638
|
-
};
|
|
639
|
-
} catch (error) {
|
|
640
|
-
throw new Error(`Artifact editing failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
private async analyzeArtifact(args: any) {
|
|
645
|
-
// Check authentication first
|
|
646
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
647
|
-
if (!authResult.success) {
|
|
648
|
-
return {
|
|
649
|
-
content: [
|
|
650
|
-
{
|
|
651
|
-
type: 'text',
|
|
652
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
653
|
-
},
|
|
654
|
-
],
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
try {
|
|
659
|
-
this.logger.info('Analyzing ServiceNow artifact', { sys_id: args.sys_id });
|
|
660
|
-
|
|
661
|
-
// Fetch complete artifact from ServiceNow
|
|
662
|
-
const artifact = await this.client.getRecord(args.table, args.sys_id);
|
|
663
|
-
|
|
664
|
-
// Perform intelligent indexing
|
|
665
|
-
const indexedArtifact = await this.intelligentlyIndex(artifact);
|
|
666
|
-
|
|
667
|
-
// Store in memory
|
|
668
|
-
await this.storeInMemory(indexedArtifact);
|
|
669
|
-
|
|
670
|
-
return {
|
|
671
|
-
content: [
|
|
672
|
-
{
|
|
673
|
-
type: 'text',
|
|
674
|
-
text: `🧠 Artifact Analysis Complete!\n\n📋 Summary:\n${indexedArtifact.claudeSummary}\n\n🏗️ Structure:\n${JSON.stringify(indexedArtifact.structure, null, 2)}\n\n🎯 Modification Points:\n${indexedArtifact.modificationPoints.map(p => `- ${p.description}`).join('\n')}\n\n💾 Artifact has been intelligently indexed and stored in memory for future natural language interactions.`,
|
|
675
|
-
},
|
|
676
|
-
],
|
|
677
|
-
};
|
|
678
|
-
} catch (error) {
|
|
679
|
-
throw new Error(`Artifact _analysis failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
private async searchMemory(args: any) {
|
|
684
|
-
try {
|
|
685
|
-
const results = await this.searchInMemory({
|
|
686
|
-
identifier: args.query,
|
|
687
|
-
artifactType: args.type || 'any',
|
|
688
|
-
action: 'find',
|
|
689
|
-
confidence: 0.8
|
|
690
|
-
});
|
|
691
|
-
|
|
692
|
-
return {
|
|
693
|
-
content: [
|
|
694
|
-
{
|
|
695
|
-
type: 'text',
|
|
696
|
-
text: `🧠 Memory Search Results:\n\n${this.formatMemoryResults(results)}\n\n💡 All results are from the intelligent index for instant access.`,
|
|
697
|
-
},
|
|
698
|
-
],
|
|
699
|
-
};
|
|
700
|
-
} catch (error) {
|
|
701
|
-
throw new Error(`Memory search failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
private async comprehensiveSearch(args: any) {
|
|
706
|
-
// Check authentication first
|
|
707
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
708
|
-
if (!authResult.success) {
|
|
709
|
-
return {
|
|
710
|
-
content: [
|
|
711
|
-
{
|
|
712
|
-
type: 'text',
|
|
713
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
714
|
-
},
|
|
715
|
-
],
|
|
716
|
-
};
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
try {
|
|
720
|
-
this.logger.info('Starting comprehensive search', { query: args.query });
|
|
721
|
-
|
|
722
|
-
// Define tables to search with their descriptions
|
|
723
|
-
const searchTables = [
|
|
724
|
-
{ name: 'sys_script', desc: 'Business Rules', type: 'business_rule' },
|
|
725
|
-
{ name: 'sys_script_include', desc: 'Script Includes', type: 'script_include' },
|
|
726
|
-
{ name: 'sys_script_client', desc: 'Client Scripts', type: 'client_script' },
|
|
727
|
-
{ name: 'sys_ui_script', desc: 'UI Scripts', type: 'ui_script' },
|
|
728
|
-
{ name: 'sp_widget', desc: 'Service Portal Widgets', type: 'widget' },
|
|
729
|
-
{ name: 'sys_hub_flow', desc: 'Flow Designer Flows', type: 'flow' },
|
|
730
|
-
{ name: 'wf_workflow', desc: 'Workflows', type: 'workflow' },
|
|
731
|
-
{ name: 'sys_ui_action', desc: 'UI Actions', type: 'ui_action' },
|
|
732
|
-
{ name: 'sys_ui_policy', desc: 'UI Policies', type: 'ui_policy' },
|
|
733
|
-
{ name: 'sys_data_policy', desc: 'Data Policies', type: 'data_policy' },
|
|
734
|
-
{ name: 'sys_app_application', desc: 'Applications', type: 'application' },
|
|
735
|
-
{ name: 'sys_db_object', desc: 'Tables', type: 'table' },
|
|
736
|
-
{ name: 'sys_dictionary', desc: 'Dictionary/Fields', type: 'field' },
|
|
737
|
-
{ name: 'sysevent_email_action', desc: 'Notifications', type: 'notification' },
|
|
738
|
-
{ name: 'sys_transform_map', desc: 'Transform Maps', type: 'transform_map' },
|
|
739
|
-
{ name: 'sys_ws_definition', desc: 'REST APIs', type: 'rest_api' },
|
|
740
|
-
{ name: 'sc_cat_item', desc: 'Catalog Items', type: 'catalog_item' },
|
|
741
|
-
{ name: 'sc_catalog', desc: 'Service Catalogs', type: 'catalog' },
|
|
742
|
-
{ name: 'sc_category', desc: 'Catalog Categories', type: 'catalog_category' },
|
|
743
|
-
{ name: 'item_option_new', desc: 'Catalog Variables', type: 'catalog_variable' },
|
|
744
|
-
];
|
|
745
|
-
|
|
746
|
-
const searchString = args.query.trim();
|
|
747
|
-
const allResults = [];
|
|
748
|
-
|
|
749
|
-
// Generate multiple search strategies like Claude Code did
|
|
750
|
-
const searchStrategies = [
|
|
751
|
-
{ query: `name=${searchString}`, desc: 'Exact name match' },
|
|
752
|
-
{ query: `nameLIKE${searchString}`, desc: 'Name contains' },
|
|
753
|
-
{ query: `short_descriptionLIKE${searchString}`, desc: 'Description contains' },
|
|
754
|
-
{ query: `nameLIKE${searchString}^ORshort_descriptionLIKE${searchString}`, desc: 'Name or description' },
|
|
755
|
-
];
|
|
756
|
-
|
|
757
|
-
// Add wildcard searches if multiple words
|
|
758
|
-
const words = searchString.split(' ').filter((w: string) => w.length > 2);
|
|
759
|
-
if (words.length > 1) {
|
|
760
|
-
const firstWord = words[0];
|
|
761
|
-
const lastWord = words[words.length - 1];
|
|
762
|
-
searchStrategies.push({
|
|
763
|
-
query: `nameLIKE*${firstWord}*${lastWord}*`,
|
|
764
|
-
desc: 'First and last word match'
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
// 🔴 SNOW-002 FIX: Apply retry logic to comprehensive search as well
|
|
769
|
-
for (const table of searchTables) {
|
|
770
|
-
this.logger.info(`🔴 SNOW-002 FIX: Searching ${table.desc} (${table.name}) with retry logic...`);
|
|
771
|
-
|
|
772
|
-
// Try search with retry logic for each table
|
|
773
|
-
const tableResults = [];
|
|
774
|
-
const maxTableRetries = 3; // Shorter retry for comprehensive search
|
|
775
|
-
|
|
776
|
-
for (let attempt = 1; attempt <= maxTableRetries; attempt++) {
|
|
777
|
-
let foundResults = false;
|
|
778
|
-
|
|
779
|
-
for (const strategy of searchStrategies) {
|
|
780
|
-
try {
|
|
781
|
-
const activeFilter = args.include_inactive ? '' : '^active=true';
|
|
782
|
-
const fullQuery = `${strategy.query}${activeFilter}^LIMIT5`;
|
|
783
|
-
|
|
784
|
-
const results = await this.client.searchRecords(table.name, fullQuery);
|
|
785
|
-
|
|
786
|
-
if (results && results.success && results.data.result.length > 0) {
|
|
787
|
-
// Add metadata to results
|
|
788
|
-
const enhancedResults = results.data.result.map((result: any) => ({
|
|
789
|
-
...result,
|
|
790
|
-
artifact_type: table.type,
|
|
791
|
-
table_name: table.name,
|
|
792
|
-
table_description: table.desc,
|
|
793
|
-
search_strategy: strategy.desc,
|
|
794
|
-
retry_attempt: attempt
|
|
795
|
-
}));
|
|
796
|
-
|
|
797
|
-
tableResults.push(...enhancedResults);
|
|
798
|
-
foundResults = true;
|
|
799
|
-
|
|
800
|
-
// Stop searching this table if we found results
|
|
801
|
-
break;
|
|
802
|
-
}
|
|
803
|
-
} catch (error) {
|
|
804
|
-
this.logger.warn(`Error searching ${table.name} (attempt ${attempt}):`, error);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
// If we found results, stop retrying this table
|
|
809
|
-
if (foundResults) {
|
|
810
|
-
break;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// If no results and not the last attempt, wait before retry
|
|
814
|
-
if (attempt < maxTableRetries) {
|
|
815
|
-
const delay = 800 * attempt; // Shorter delays: 800ms, 1600ms
|
|
816
|
-
this.logger.info(`🔄 No results for ${table.name}, waiting ${delay}ms before retry...`);
|
|
817
|
-
await this.sleep(delay);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
// Add any results found for this table
|
|
822
|
-
allResults.push(...tableResults);
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
// Remove duplicates by sys_id
|
|
826
|
-
const uniqueResults = allResults.filter((result, index, self) =>
|
|
827
|
-
index === self.findIndex(r => r.sys_id === result.sys_id)
|
|
828
|
-
);
|
|
829
|
-
|
|
830
|
-
const instanceInfo = await mcpAuth.getInstanceInfo();
|
|
831
|
-
const resultText = this.formatComprehensiveResults(uniqueResults);
|
|
832
|
-
|
|
833
|
-
return {
|
|
834
|
-
content: [
|
|
835
|
-
{
|
|
836
|
-
type: 'text',
|
|
837
|
-
text: `🔍 Comprehensive ServiceNow Search Results:\n\n${resultText}\n\n🔗 ServiceNow Instance: ${instanceInfo.instance}\n\n💡 Searched across ${searchTables.length} table types with multiple strategies.`,
|
|
838
|
-
},
|
|
839
|
-
],
|
|
840
|
-
};
|
|
841
|
-
} catch (error) {
|
|
842
|
-
throw new Error(`Comprehensive search failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
private async parseIntent(query: string): Promise<ParsedIntent> {
|
|
847
|
-
// Enhanced intent parsing with comprehensive type detection
|
|
848
|
-
const lowercaseQuery = query.toLowerCase();
|
|
849
|
-
|
|
850
|
-
let artifactType = 'any'; // Default to searching all types
|
|
851
|
-
|
|
852
|
-
// Check for specific artifact types (order matters - most specific first)
|
|
853
|
-
|
|
854
|
-
// Service Portal
|
|
855
|
-
if (lowercaseQuery.includes('widget')) artifactType = 'widget';
|
|
856
|
-
else if (lowercaseQuery.includes('portal')) artifactType = 'portal';
|
|
857
|
-
else if (lowercaseQuery.includes('page') && lowercaseQuery.includes('service')) artifactType = 'page';
|
|
858
|
-
else if (lowercaseQuery.includes('theme')) artifactType = 'theme';
|
|
859
|
-
|
|
860
|
-
// Flow Designer & Workflow
|
|
861
|
-
else if (lowercaseQuery.includes('flow designer') || lowercaseQuery.includes('sys_hub_flow')) artifactType = 'flow';
|
|
862
|
-
else if (lowercaseQuery.includes('workflow') && !lowercaseQuery.includes('orchestration')) artifactType = 'workflow';
|
|
863
|
-
else if (lowercaseQuery.includes('workflow activity')) artifactType = 'workflow_activity';
|
|
864
|
-
else if (lowercaseQuery.includes('flow')) artifactType = 'flow';
|
|
865
|
-
|
|
866
|
-
// Scripts & Automation (most specific first)
|
|
867
|
-
else if (lowercaseQuery.includes('script include')) artifactType = 'script_include';
|
|
868
|
-
else if (lowercaseQuery.includes('business rule')) artifactType = 'business_rule';
|
|
869
|
-
else if (lowercaseQuery.includes('client script')) artifactType = 'client_script';
|
|
870
|
-
else if (lowercaseQuery.includes('ui script')) artifactType = 'ui_script';
|
|
871
|
-
else if (lowercaseQuery.includes('ui action')) artifactType = 'ui_action';
|
|
872
|
-
else if (lowercaseQuery.includes('ui policy action')) artifactType = 'ui_policy_action';
|
|
873
|
-
else if (lowercaseQuery.includes('ui policy')) artifactType = 'ui_policy';
|
|
874
|
-
else if (lowercaseQuery.includes('data policy rule')) artifactType = 'data_policy_rule';
|
|
875
|
-
else if (lowercaseQuery.includes('data policy')) artifactType = 'data_policy';
|
|
876
|
-
|
|
877
|
-
// Applications
|
|
878
|
-
else if (lowercaseQuery.includes('scoped app')) artifactType = 'scoped_app';
|
|
879
|
-
else if (lowercaseQuery.includes('application') || lowercaseQuery.includes('app')) artifactType = 'application';
|
|
880
|
-
|
|
881
|
-
// Tables & Fields
|
|
882
|
-
else if (lowercaseQuery.includes('field') || lowercaseQuery.includes('dictionary')) artifactType = 'field';
|
|
883
|
-
else if (lowercaseQuery.includes('table')) artifactType = 'table';
|
|
884
|
-
|
|
885
|
-
// Forms & UI
|
|
886
|
-
else if (lowercaseQuery.includes('form section')) artifactType = 'form_section';
|
|
887
|
-
else if (lowercaseQuery.includes('list control')) artifactType = 'list_control';
|
|
888
|
-
else if (lowercaseQuery.includes('related list')) artifactType = 'related_list';
|
|
889
|
-
else if (lowercaseQuery.includes('form')) artifactType = 'form';
|
|
890
|
-
else if (lowercaseQuery.includes('list')) artifactType = 'list';
|
|
891
|
-
else if (lowercaseQuery.includes('view')) artifactType = 'view';
|
|
892
|
-
else if (lowercaseQuery.includes('formatter')) artifactType = 'formatter';
|
|
893
|
-
|
|
894
|
-
// Reports & Dashboards
|
|
895
|
-
else if (lowercaseQuery.includes('pa dashboard')) artifactType = 'pa_dashboard';
|
|
896
|
-
else if (lowercaseQuery.includes('pa widget')) artifactType = 'pa_widget';
|
|
897
|
-
else if (lowercaseQuery.includes('dashboard')) artifactType = 'dashboard';
|
|
898
|
-
else if (lowercaseQuery.includes('report')) artifactType = 'report';
|
|
899
|
-
else if (lowercaseQuery.includes('gauge')) artifactType = 'gauge';
|
|
900
|
-
else if (lowercaseQuery.includes('chart')) artifactType = 'chart';
|
|
901
|
-
else if (lowercaseQuery.includes('indicator')) artifactType = 'indicator';
|
|
902
|
-
|
|
903
|
-
// Security & Access Control
|
|
904
|
-
else if (lowercaseQuery.includes('acl') || lowercaseQuery.includes('access control')) artifactType = 'acl';
|
|
905
|
-
else if (lowercaseQuery.includes('role')) artifactType = 'role';
|
|
906
|
-
else if (lowercaseQuery.includes('group')) artifactType = 'group';
|
|
907
|
-
|
|
908
|
-
// Notifications & Communication
|
|
909
|
-
else if (lowercaseQuery.includes('email template')) artifactType = 'email_template';
|
|
910
|
-
else if (lowercaseQuery.includes('notification')) artifactType = 'notification';
|
|
911
|
-
|
|
912
|
-
// Import & Export
|
|
913
|
-
else if (lowercaseQuery.includes('import set')) artifactType = 'import_set';
|
|
914
|
-
else if (lowercaseQuery.includes('transform map')) artifactType = 'transform_map';
|
|
915
|
-
|
|
916
|
-
// Web Services
|
|
917
|
-
else if (lowercaseQuery.includes('rest api') || lowercaseQuery.includes('rest')) artifactType = 'rest_api';
|
|
918
|
-
else if (lowercaseQuery.includes('soap api') || lowercaseQuery.includes('soap')) artifactType = 'soap_api';
|
|
919
|
-
|
|
920
|
-
// Scheduled Jobs
|
|
921
|
-
else if (lowercaseQuery.includes('scheduled job')) artifactType = 'scheduled_job';
|
|
922
|
-
else if (lowercaseQuery.includes('scheduled import')) artifactType = 'scheduled_import';
|
|
923
|
-
|
|
924
|
-
// Knowledge Management
|
|
925
|
-
else if (lowercaseQuery.includes('knowledge base')) artifactType = 'knowledge_base';
|
|
926
|
-
else if (lowercaseQuery.includes('knowledge article') || lowercaseQuery.includes('knowledge')) artifactType = 'knowledge_article';
|
|
927
|
-
|
|
928
|
-
// System Administration
|
|
929
|
-
else if (lowercaseQuery.includes('system property') || lowercaseQuery.includes('property')) artifactType = 'property';
|
|
930
|
-
|
|
931
|
-
// Mobile
|
|
932
|
-
else if (lowercaseQuery.includes('mobile app') || lowercaseQuery.includes('mobile')) artifactType = 'mobile_app';
|
|
933
|
-
|
|
934
|
-
// Catalog
|
|
935
|
-
else if (lowercaseQuery.includes('catalog item')) artifactType = 'catalog_item';
|
|
936
|
-
else if (lowercaseQuery.includes('catalog variable')) artifactType = 'catalog_variable';
|
|
937
|
-
else if (lowercaseQuery.includes('catalog')) artifactType = 'catalog';
|
|
938
|
-
|
|
939
|
-
// SLA & Metrics
|
|
940
|
-
else if (lowercaseQuery.includes('sla')) artifactType = 'sla';
|
|
941
|
-
else if (lowercaseQuery.includes('metric')) artifactType = 'metric';
|
|
942
|
-
|
|
943
|
-
// Other common types
|
|
944
|
-
else if (lowercaseQuery.includes('attachment')) artifactType = 'attachment';
|
|
945
|
-
else if (lowercaseQuery.includes('language')) artifactType = 'language';
|
|
946
|
-
else if (lowercaseQuery.includes('translation')) artifactType = 'translated_text';
|
|
947
|
-
else if (lowercaseQuery.includes('processor')) artifactType = 'processor';
|
|
948
|
-
else if (lowercaseQuery.includes('update set')) artifactType = 'update_set';
|
|
949
|
-
else if (lowercaseQuery.includes('ml model') || lowercaseQuery.includes('machine learning')) artifactType = 'ml_model';
|
|
950
|
-
else if (lowercaseQuery.includes('spoke')) artifactType = 'spoke';
|
|
951
|
-
else if (lowercaseQuery.includes('connection')) artifactType = 'connection';
|
|
952
|
-
else if (lowercaseQuery.includes('virtual agent') || lowercaseQuery.includes('chatbot')) artifactType = 'virtual_agent';
|
|
953
|
-
else if (lowercaseQuery.includes('event rule')) artifactType = 'event_rule';
|
|
954
|
-
else if (lowercaseQuery.includes('alert')) artifactType = 'alert';
|
|
955
|
-
else if (lowercaseQuery.includes('discovery')) artifactType = 'discovery_schedule';
|
|
956
|
-
else if (lowercaseQuery.includes('ci class')) artifactType = 'ci_class';
|
|
957
|
-
else if (lowercaseQuery.includes('relationship')) artifactType = 'relationship_type';
|
|
958
|
-
else if (lowercaseQuery.includes('service map')) artifactType = 'service_map';
|
|
959
|
-
else if (lowercaseQuery.includes('orchestration workflow')) artifactType = 'orchestration_workflow';
|
|
960
|
-
else if (lowercaseQuery.includes('pipeline')) artifactType = 'pipeline';
|
|
961
|
-
else if (lowercaseQuery.includes('deployment')) artifactType = 'deployment';
|
|
962
|
-
|
|
963
|
-
// Generic fallbacks
|
|
964
|
-
else if (lowercaseQuery.includes('script')) artifactType = 'script_include';
|
|
965
|
-
|
|
966
|
-
// Smart identifier extraction - remove artifact type keywords and get the actual name
|
|
967
|
-
const identifier = this.extractIdentifier(query, artifactType);
|
|
968
|
-
|
|
969
|
-
return {
|
|
970
|
-
action: 'find',
|
|
971
|
-
artifactType,
|
|
972
|
-
identifier,
|
|
973
|
-
confidence: 0.9,
|
|
974
|
-
};
|
|
975
|
-
}
|
|
976
|
-
|
|
977
|
-
private extractIdentifier(query: string, artifactType: string): string {
|
|
978
|
-
// Smart extraction of actual artifact name by removing type keywords
|
|
979
|
-
let identifier = query.toLowerCase().trim();
|
|
980
|
-
|
|
981
|
-
// Remove common artifact type keywords
|
|
982
|
-
const typeKeywords = [
|
|
983
|
-
'widget', 'portal', 'page', 'theme',
|
|
984
|
-
'flow designer', 'sys_hub_flow', 'workflow', 'workflow activity', 'flow',
|
|
985
|
-
'script include', 'business rule', 'client script', 'ui script', 'ui action',
|
|
986
|
-
'ui policy action', 'ui policy', 'data policy rule', 'data policy',
|
|
987
|
-
'scoped app', 'application', 'app',
|
|
988
|
-
'field', 'dictionary', 'table',
|
|
989
|
-
'form section', 'list control', 'related list', 'form', 'list', 'view', 'formatter',
|
|
990
|
-
'pa dashboard', 'report', 'dashboard', 'gauge', 'indicator',
|
|
991
|
-
'sla', 'metric', 'attachment', 'language', 'translation', 'processor',
|
|
992
|
-
'update set', 'ml model', 'machine learning', 'spoke', 'connection',
|
|
993
|
-
'virtual agent', 'chatbot', 'event rule', 'alert', 'discovery',
|
|
994
|
-
'ci class', 'relationship', 'service map', 'orchestration workflow',
|
|
995
|
-
'pipeline', 'deployment', 'script'
|
|
996
|
-
];
|
|
997
|
-
|
|
998
|
-
// Remove type keywords and common words
|
|
999
|
-
const wordsToRemove = [
|
|
1000
|
-
...typeKeywords,
|
|
1001
|
-
'the', 'a', 'an', 'with', 'that', 'shows', 'displays', 'for', 'on', 'in',
|
|
1002
|
-
'servicenow', 'snow', 'sys_id:', 'system', 'id'
|
|
1003
|
-
];
|
|
1004
|
-
|
|
1005
|
-
// Remove sys_id if present (extract it separately)
|
|
1006
|
-
const sysIdMatch = identifier.match(/sys_id:\s*([a-f0-9]{32})/);
|
|
1007
|
-
if (sysIdMatch) {
|
|
1008
|
-
// If we have a sys_id, prioritize that
|
|
1009
|
-
return sysIdMatch[1];
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
// Remove words to clean up the identifier
|
|
1013
|
-
for (const word of wordsToRemove) {
|
|
1014
|
-
const regex = new RegExp(`\\b${word}\\b`, 'gi');
|
|
1015
|
-
identifier = identifier.replace(regex, ' ');
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
// Clean up whitespace and return
|
|
1019
|
-
identifier = identifier.replace(/\s+/g, ' ').trim();
|
|
1020
|
-
|
|
1021
|
-
// If identifier is empty or too short, return original query
|
|
1022
|
-
if (!identifier || identifier.length < 2) {
|
|
1023
|
-
return query.trim();
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
return identifier;
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
private async parseEditIntent(query: string): Promise<ParsedIntent & { modification: string }> {
|
|
1030
|
-
// Parse edit instructions - would be more sophisticated in real implementation
|
|
1031
|
-
const intent = await this.parseIntent(query);
|
|
1032
|
-
|
|
1033
|
-
return {
|
|
1034
|
-
...intent,
|
|
1035
|
-
action: 'edit',
|
|
1036
|
-
modification: query,
|
|
1037
|
-
};
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
/**
|
|
1041
|
-
* 🔴 CRITICAL FIX SNOW-002: Search ServiceNow with retry logic for newly created artifacts
|
|
1042
|
-
* Addresses: "I created a flow but search says it doesn't exist"
|
|
1043
|
-
* Root Cause: ServiceNow search indexes take time to update after artifact creation
|
|
1044
|
-
*/
|
|
1045
|
-
private async searchServiceNowWithRetry(intent: ParsedIntent): Promise<any[]> {
|
|
1046
|
-
const maxRetries = 5;
|
|
1047
|
-
const baseDelay = 1500; // Start with 1.5 seconds
|
|
1048
|
-
|
|
1049
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1050
|
-
try {
|
|
1051
|
-
this.logger.info(`🔍 Search attempt ${attempt}/${maxRetries} for: ${intent.identifier}`);
|
|
1052
|
-
|
|
1053
|
-
// Try the regular search
|
|
1054
|
-
const results = await this.searchServiceNow(intent);
|
|
1055
|
-
|
|
1056
|
-
if (results && results.length > 0) {
|
|
1057
|
-
this.logger.info(`✅ Found ${results.length} results on attempt ${attempt}`);
|
|
1058
|
-
return results;
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// If no results and not the last attempt, wait and retry
|
|
1062
|
-
if (attempt < maxRetries) {
|
|
1063
|
-
const delay = baseDelay * attempt; // 1.5s, 3s, 4.5s, 6s, 7.5s
|
|
1064
|
-
this.logger.info(`🔄 No results found, waiting ${delay}ms before retry (ServiceNow indexes may be updating...)`);
|
|
1065
|
-
await this.sleep(delay);
|
|
1066
|
-
|
|
1067
|
-
// 🔴 CRITICAL: Try cache invalidation on ServiceNow side
|
|
1068
|
-
if (attempt === 2) {
|
|
1069
|
-
this.logger.info('🔄 Attempting ServiceNow cache refresh...');
|
|
1070
|
-
await this.attemptCacheRefresh(intent);
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
} catch (error) {
|
|
1075
|
-
this.logger.warn(`Search attempt ${attempt} failed:`, error);
|
|
1076
|
-
|
|
1077
|
-
// If this is the last attempt, throw the error
|
|
1078
|
-
if (attempt === maxRetries) {
|
|
1079
|
-
throw error;
|
|
1080
|
-
}
|
|
1081
|
-
|
|
1082
|
-
// Otherwise wait and retry
|
|
1083
|
-
const delay = baseDelay * attempt;
|
|
1084
|
-
this.logger.info(`⏳ Waiting ${delay}ms before retry due to error`);
|
|
1085
|
-
await this.sleep(delay);
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
// 🔴 CRITICAL: If all retries failed, try broad fallback search
|
|
1090
|
-
this.logger.warn('🚨 All retry attempts failed, trying broad fallback search...');
|
|
1091
|
-
return await this.broadFallbackSearch(intent);
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
/**
|
|
1095
|
-
* 🔴 SNOW-002 FIX: Attempt to refresh ServiceNow caches
|
|
1096
|
-
*/
|
|
1097
|
-
private async attemptCacheRefresh(intent: ParsedIntent): Promise<void> {
|
|
1098
|
-
try {
|
|
1099
|
-
const tableMapping: Record<string, string> = {
|
|
1100
|
-
widget: 'sp_widget',
|
|
1101
|
-
flow: 'sys_hub_flow',
|
|
1102
|
-
script: 'sys_script_include',
|
|
1103
|
-
application: 'sys_app_application'
|
|
1104
|
-
};
|
|
1105
|
-
|
|
1106
|
-
const table = tableMapping[intent.artifactType] || 'sys_hub_flow';
|
|
1107
|
-
|
|
1108
|
-
// Try a simple count query to potentially refresh indexes
|
|
1109
|
-
await this.client.searchRecords(table, 'sys_id!=null^LIMIT1');
|
|
1110
|
-
this.logger.info('✨ Cache refresh attempt completed');
|
|
1111
|
-
|
|
1112
|
-
} catch (error) {
|
|
1113
|
-
this.logger.warn('Cache refresh attempt failed:', error);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
/**
|
|
1118
|
-
* 🔴 SNOW-002 FIX: Broad fallback search when all retries fail
|
|
1119
|
-
*/
|
|
1120
|
-
private async broadFallbackSearch(intent: ParsedIntent): Promise<any[]> {
|
|
1121
|
-
this.logger.info('🔍 Attempting broad fallback search across multiple tables...');
|
|
1122
|
-
|
|
1123
|
-
try {
|
|
1124
|
-
// Search across multiple related tables
|
|
1125
|
-
const broadResults = [];
|
|
1126
|
-
const searchTerm = intent.identifier.trim();
|
|
1127
|
-
|
|
1128
|
-
// Define broader table search for common artifacts
|
|
1129
|
-
const fallbackTables = [
|
|
1130
|
-
'sys_hub_flow', 'sp_widget', 'sys_script_include',
|
|
1131
|
-
'sys_script', 'sys_app_application', 'wf_workflow'
|
|
1132
|
-
];
|
|
1133
|
-
|
|
1134
|
-
for (const table of fallbackTables) {
|
|
1135
|
-
try {
|
|
1136
|
-
// Try multiple search strategies
|
|
1137
|
-
const strategies = [
|
|
1138
|
-
`nameLIKE*${searchTerm}*^LIMIT3`,
|
|
1139
|
-
`titleLIKE*${searchTerm}*^LIMIT3`,
|
|
1140
|
-
`short_descriptionLIKE*${searchTerm}*^LIMIT3`
|
|
1141
|
-
];
|
|
1142
|
-
|
|
1143
|
-
for (const query of strategies) {
|
|
1144
|
-
const results = await this.client.searchRecords(table, query);
|
|
1145
|
-
if (results && results.success && results.data.result.length > 0) {
|
|
1146
|
-
const typedResults = results.data.result.map((result: any) => ({
|
|
1147
|
-
...result,
|
|
1148
|
-
table_name: table,
|
|
1149
|
-
search_fallback: true
|
|
1150
|
-
}));
|
|
1151
|
-
broadResults.push(...typedResults);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
1154
|
-
} catch (error) {
|
|
1155
|
-
this.logger.warn(`Fallback search failed for ${table}:`, error);
|
|
1156
|
-
}
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
// Remove duplicates and return
|
|
1160
|
-
const uniqueResults = broadResults.filter((result, index, self) =>
|
|
1161
|
-
index === self.findIndex(r => r.sys_id === result.sys_id)
|
|
1162
|
-
);
|
|
1163
|
-
|
|
1164
|
-
this.logger.info(`🔍 Fallback search found ${uniqueResults.length} results`);
|
|
1165
|
-
return uniqueResults;
|
|
1166
|
-
|
|
1167
|
-
} catch (error) {
|
|
1168
|
-
this.logger.error('Broad fallback search failed:', error);
|
|
1169
|
-
return [];
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
/**
|
|
1174
|
-
* 🔴 SNOW-002 FIX: Special search method for newly created artifacts
|
|
1175
|
-
* Use this immediately after creating an artifact to verify it's searchable
|
|
1176
|
-
*/
|
|
1177
|
-
async searchForRecentlyCreatedArtifact(artifactName: string, artifactType: string, expectedSysId?: string): Promise<any[]> {
|
|
1178
|
-
this.logger.info(`🔍 SNOW-002: Searching for recently created artifact: ${artifactName} (${artifactType})`);
|
|
1179
|
-
|
|
1180
|
-
const intent: ParsedIntent = {
|
|
1181
|
-
identifier: artifactName,
|
|
1182
|
-
artifactType: artifactType,
|
|
1183
|
-
action: 'find',
|
|
1184
|
-
confidence: 0.9
|
|
1185
|
-
};
|
|
1186
|
-
|
|
1187
|
-
// First try the sys_id lookup if we have it (most reliable)
|
|
1188
|
-
if (expectedSysId) {
|
|
1189
|
-
try {
|
|
1190
|
-
this.logger.info(`🎯 Trying direct sys_id lookup: ${expectedSysId}`);
|
|
1191
|
-
const tableMapping: Record<string, string> = {
|
|
1192
|
-
widget: 'sp_widget',
|
|
1193
|
-
flow: 'sys_hub_flow',
|
|
1194
|
-
script: 'sys_script_include',
|
|
1195
|
-
application: 'sys_app_application'
|
|
1196
|
-
};
|
|
1197
|
-
|
|
1198
|
-
const table = tableMapping[artifactType] || 'sys_hub_flow';
|
|
1199
|
-
const directResult = await this.client.searchRecords(table, `sys_id=${expectedSysId}`);
|
|
1200
|
-
|
|
1201
|
-
if (directResult && directResult.success && directResult.data.result.length > 0) {
|
|
1202
|
-
this.logger.info(`✅ Found via direct sys_id lookup`);
|
|
1203
|
-
return directResult.data.result;
|
|
1204
|
-
}
|
|
1205
|
-
} catch (error) {
|
|
1206
|
-
this.logger.warn('Direct sys_id lookup failed:', error);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
// Fall back to name-based search with extended retry logic
|
|
1211
|
-
const maxRetries = 7; // More retries for newly created artifacts
|
|
1212
|
-
const baseDelay = 2000; // Longer initial delay (2 seconds)
|
|
1213
|
-
|
|
1214
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
1215
|
-
try {
|
|
1216
|
-
this.logger.info(`🔍 Post-creation search attempt ${attempt}/${maxRetries}`);
|
|
1217
|
-
|
|
1218
|
-
const results = await this.searchServiceNow(intent);
|
|
1219
|
-
|
|
1220
|
-
if (results && results.length > 0) {
|
|
1221
|
-
this.logger.info(`✅ SNOW-002 RESOLVED: Found ${results.length} results for newly created artifact on attempt ${attempt}`);
|
|
1222
|
-
return results;
|
|
1223
|
-
}
|
|
1224
|
-
|
|
1225
|
-
if (attempt < maxRetries) {
|
|
1226
|
-
// Progressive delay with jitter: 2s, 4s, 6s, 8s, 10s, 12s, 14s
|
|
1227
|
-
const delay = baseDelay * attempt;
|
|
1228
|
-
this.logger.info(`🔄 Artifact not yet searchable, waiting ${delay}ms (ServiceNow indexes updating...)`);
|
|
1229
|
-
await this.sleep(delay);
|
|
1230
|
-
|
|
1231
|
-
// Try cache refresh on every other attempt
|
|
1232
|
-
if (attempt % 2 === 0) {
|
|
1233
|
-
await this.attemptCacheRefresh(intent);
|
|
1234
|
-
}
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
} catch (error) {
|
|
1238
|
-
this.logger.warn(`Post-creation search attempt ${attempt} failed:`, error);
|
|
1239
|
-
|
|
1240
|
-
if (attempt < maxRetries) {
|
|
1241
|
-
const delay = baseDelay * attempt;
|
|
1242
|
-
await this.sleep(delay);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
this.logger.warn('🚨 SNOW-002: Recently created artifact still not searchable after all retries');
|
|
1248
|
-
return [];
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/**
|
|
1252
|
-
* Sleep utility for retry delays
|
|
1253
|
-
*/
|
|
1254
|
-
private sleep(ms: number): Promise<void> {
|
|
1255
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
private async searchServiceNow(intent: ParsedIntent) {
|
|
1259
|
-
try {
|
|
1260
|
-
const tableMapping: Record<string, string> = {
|
|
1261
|
-
// Service Portal
|
|
1262
|
-
widget: 'sp_widget',
|
|
1263
|
-
portal: 'sp_portal',
|
|
1264
|
-
page: 'sp_page',
|
|
1265
|
-
theme: 'sp_theme',
|
|
1266
|
-
|
|
1267
|
-
// Flow Designer & Workflow
|
|
1268
|
-
flow: 'sys_hub_flow',
|
|
1269
|
-
workflow: 'wf_workflow',
|
|
1270
|
-
workflow_activity: 'wf_activity',
|
|
1271
|
-
|
|
1272
|
-
// Scripts & Automation
|
|
1273
|
-
script_include: 'sys_script_include',
|
|
1274
|
-
script: 'sys_script_include',
|
|
1275
|
-
business_rule: 'sys_script',
|
|
1276
|
-
client_script: 'sys_script_client',
|
|
1277
|
-
ui_script: 'sys_ui_script',
|
|
1278
|
-
ui_action: 'sys_ui_action',
|
|
1279
|
-
ui_policy: 'sys_ui_policy',
|
|
1280
|
-
ui_policy_action: 'sys_ui_policy_action',
|
|
1281
|
-
data_policy: 'sys_data_policy',
|
|
1282
|
-
data_policy_rule: 'sys_data_policy_rule',
|
|
1283
|
-
|
|
1284
|
-
// Applications & Scoped Apps
|
|
1285
|
-
application: 'sys_app_application',
|
|
1286
|
-
app: 'sys_app_application',
|
|
1287
|
-
scoped_app: 'sys_app',
|
|
1288
|
-
|
|
1289
|
-
// Tables & Fields
|
|
1290
|
-
table: 'sys_db_object',
|
|
1291
|
-
field: 'sys_dictionary',
|
|
1292
|
-
dictionary: 'sys_dictionary',
|
|
1293
|
-
|
|
1294
|
-
// Forms & UI
|
|
1295
|
-
form: 'sys_form',
|
|
1296
|
-
form_section: 'sys_form_section',
|
|
1297
|
-
list: 'sys_list',
|
|
1298
|
-
list_control: 'sys_list_control',
|
|
1299
|
-
view: 'sys_ui_view',
|
|
1300
|
-
related_list: 'sys_ui_related_list',
|
|
1301
|
-
formatter: 'sys_ui_formatter',
|
|
1302
|
-
|
|
1303
|
-
// Reports & Dashboards
|
|
1304
|
-
report: 'sys_report',
|
|
1305
|
-
dashboard: 'sys_dashboard',
|
|
1306
|
-
gauge: 'pa_dashboards',
|
|
1307
|
-
chart: 'sys_chart',
|
|
1308
|
-
|
|
1309
|
-
// Security & Access Control
|
|
1310
|
-
acl: 'sys_security_acl',
|
|
1311
|
-
role: 'sys_user_role',
|
|
1312
|
-
group: 'sys_user_group',
|
|
1313
|
-
|
|
1314
|
-
// Notifications & Communication
|
|
1315
|
-
notification: 'sysevent_email_action',
|
|
1316
|
-
email_template: 'sysevent_email_template',
|
|
1317
|
-
|
|
1318
|
-
// Import & Export
|
|
1319
|
-
import_set: 'sys_import_set',
|
|
1320
|
-
transform_map: 'sys_transform_map',
|
|
1321
|
-
|
|
1322
|
-
// Web Services
|
|
1323
|
-
rest_api: 'sys_ws_definition',
|
|
1324
|
-
soap_api: 'sys_web_service',
|
|
1325
|
-
|
|
1326
|
-
// Scheduled Jobs
|
|
1327
|
-
scheduled_job: 'sysauto_script',
|
|
1328
|
-
scheduled_import: 'scheduled_import_set',
|
|
1329
|
-
|
|
1330
|
-
// Knowledge Management
|
|
1331
|
-
knowledge_base: 'kb_knowledge_base',
|
|
1332
|
-
knowledge_article: 'kb_knowledge',
|
|
1333
|
-
|
|
1334
|
-
// System Administration
|
|
1335
|
-
property: 'sys_properties',
|
|
1336
|
-
system_property: 'sys_properties',
|
|
1337
|
-
|
|
1338
|
-
// Mobile
|
|
1339
|
-
mobile_app: 'sys_mobile_application',
|
|
1340
|
-
|
|
1341
|
-
// Catalog
|
|
1342
|
-
catalog: 'sc_catalog',
|
|
1343
|
-
catalog_item: 'sc_cat_item',
|
|
1344
|
-
catalog_variable: 'item_option_new',
|
|
1345
|
-
|
|
1346
|
-
// SLA & Metrics
|
|
1347
|
-
sla: 'contract_sla',
|
|
1348
|
-
metric: 'sys_report_color',
|
|
1349
|
-
|
|
1350
|
-
// Attachments & Files
|
|
1351
|
-
attachment: 'sys_attachment',
|
|
1352
|
-
|
|
1353
|
-
// Languages & Translations
|
|
1354
|
-
language: 'sys_language',
|
|
1355
|
-
translated_text: 'sys_translated',
|
|
1356
|
-
|
|
1357
|
-
// Processors & Servlets
|
|
1358
|
-
processor: 'sys_processor',
|
|
1359
|
-
|
|
1360
|
-
// Update Sets & Deployment
|
|
1361
|
-
update_set: 'sys_update_set',
|
|
1362
|
-
|
|
1363
|
-
// AI & ML
|
|
1364
|
-
ml_model: 'ml_model',
|
|
1365
|
-
|
|
1366
|
-
// Integration Hub
|
|
1367
|
-
spoke: 'sys_hub_action_type',
|
|
1368
|
-
connection: 'sys_connection',
|
|
1369
|
-
|
|
1370
|
-
// Virtual Agent
|
|
1371
|
-
virtual_agent: 'sys_cs_topic',
|
|
1372
|
-
chatbot: 'sys_cs_topic',
|
|
1373
|
-
|
|
1374
|
-
// Performance Analytics
|
|
1375
|
-
pa_dashboard: 'pa_dashboards',
|
|
1376
|
-
pa_widget: 'pa_widgets',
|
|
1377
|
-
indicator: 'pa_indicators',
|
|
1378
|
-
|
|
1379
|
-
// Event Management
|
|
1380
|
-
event_rule: 'em_event_rule',
|
|
1381
|
-
alert: 'em_alert',
|
|
1382
|
-
|
|
1383
|
-
// Discovery
|
|
1384
|
-
discovery_schedule: 'discovery_schedule',
|
|
1385
|
-
|
|
1386
|
-
// CMDB
|
|
1387
|
-
ci_class: 'sys_db_object',
|
|
1388
|
-
relationship_type: 'cmdb_rel_type',
|
|
1389
|
-
|
|
1390
|
-
// Service Mapping
|
|
1391
|
-
service_map: 'sa_pattern',
|
|
1392
|
-
|
|
1393
|
-
// Orchestration
|
|
1394
|
-
orchestration_workflow: 'sc_ic_workflow',
|
|
1395
|
-
|
|
1396
|
-
// DevOps
|
|
1397
|
-
pipeline: 'cicd_pipeline',
|
|
1398
|
-
deployment: 'cicd_deployment',
|
|
1399
|
-
};
|
|
1400
|
-
|
|
1401
|
-
// If searching for 'any' type, search only the most common tables
|
|
1402
|
-
if (intent.artifactType === 'any') {
|
|
1403
|
-
this.logger.info('Searching common artifact types...');
|
|
1404
|
-
const allResults = [];
|
|
1405
|
-
|
|
1406
|
-
// Handle "list all" queries for any type
|
|
1407
|
-
const searchString = intent.identifier.trim();
|
|
1408
|
-
if (searchString.toLowerCase().includes('list all') ||
|
|
1409
|
-
searchString.toLowerCase().includes('all artifacts') ||
|
|
1410
|
-
searchString.toLowerCase().includes('show all') ||
|
|
1411
|
-
searchString === '') {
|
|
1412
|
-
this.logger.info('Fetching all common artifact types');
|
|
1413
|
-
|
|
1414
|
-
// Define most common tables to list when showing all
|
|
1415
|
-
const commonTables = {
|
|
1416
|
-
widget: 'sp_widget',
|
|
1417
|
-
business_rule: 'sys_script',
|
|
1418
|
-
client_script: 'sys_script_client',
|
|
1419
|
-
script_include: 'sys_script_include',
|
|
1420
|
-
flow: 'sys_hub_flow',
|
|
1421
|
-
workflow: 'wf_workflow',
|
|
1422
|
-
ui_action: 'sys_ui_action',
|
|
1423
|
-
table: 'sys_db_object',
|
|
1424
|
-
application: 'sys_app_application',
|
|
1425
|
-
};
|
|
1426
|
-
|
|
1427
|
-
for (const [type, table] of Object.entries(commonTables)) {
|
|
1428
|
-
try {
|
|
1429
|
-
const results = await this.client.searchRecords(table, `active=true^ORDERBYname^LIMIT10`);
|
|
1430
|
-
if (results && results.success && results.data.result.length > 0) {
|
|
1431
|
-
const typedResults = results.data.result.map((result: any) => ({
|
|
1432
|
-
...result,
|
|
1433
|
-
artifact_type: type,
|
|
1434
|
-
table_name: table
|
|
1435
|
-
}));
|
|
1436
|
-
allResults.push(...typedResults);
|
|
1437
|
-
}
|
|
1438
|
-
} catch (error) {
|
|
1439
|
-
this.logger.warn(`Error fetching ${table}:`, error);
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
|
|
1443
|
-
return allResults;
|
|
1444
|
-
}
|
|
1445
|
-
|
|
1446
|
-
// Define most common tables to search when type is 'any'
|
|
1447
|
-
const commonTables = {
|
|
1448
|
-
widget: 'sp_widget',
|
|
1449
|
-
business_rule: 'sys_script',
|
|
1450
|
-
client_script: 'sys_script_client',
|
|
1451
|
-
script_include: 'sys_script_include',
|
|
1452
|
-
flow: 'sys_hub_flow',
|
|
1453
|
-
workflow: 'wf_workflow',
|
|
1454
|
-
ui_action: 'sys_ui_action',
|
|
1455
|
-
table: 'sys_db_object',
|
|
1456
|
-
application: 'sys_app_application',
|
|
1457
|
-
};
|
|
1458
|
-
|
|
1459
|
-
for (const [type, table] of Object.entries(commonTables)) {
|
|
1460
|
-
try {
|
|
1461
|
-
// Try exact match first for each table
|
|
1462
|
-
const searchString = intent.identifier.trim();
|
|
1463
|
-
let results = await this.client.searchRecords(table, `name=${searchString}^LIMIT2`);
|
|
1464
|
-
|
|
1465
|
-
// If no exact match, try contains
|
|
1466
|
-
if (!results || !results.success || results.data.result.length === 0) {
|
|
1467
|
-
results = await this.client.searchRecords(table, `nameLIKE${searchString}^LIMIT3`);
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
// If still no results, try wildcards on first term only
|
|
1471
|
-
if (!results || !results.success || results.data.result.length === 0) {
|
|
1472
|
-
const firstTerm = searchString.split(' ')[0];
|
|
1473
|
-
if (firstTerm && firstTerm.length > 2) {
|
|
1474
|
-
results = await this.client.searchRecords(table, `nameLIKE*${firstTerm}*^LIMIT3`);
|
|
1475
|
-
}
|
|
1476
|
-
}
|
|
1477
|
-
|
|
1478
|
-
if (results && results.success && results.data.result.length > 0) {
|
|
1479
|
-
// Add artifact type to results for identification
|
|
1480
|
-
const typedResults = results.data.result.map((result: any) => ({
|
|
1481
|
-
...result,
|
|
1482
|
-
artifact_type: type,
|
|
1483
|
-
table_name: table
|
|
1484
|
-
}));
|
|
1485
|
-
allResults.push(...typedResults);
|
|
1486
|
-
}
|
|
1487
|
-
} catch (error) {
|
|
1488
|
-
this.logger.warn(`Error searching ${table}:`, error);
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
// If still no results, try broader search in common tables only
|
|
1493
|
-
if (allResults.length === 0) {
|
|
1494
|
-
this.logger.info('No results found, trying broader search in common tables...');
|
|
1495
|
-
const firstTerm = intent.identifier.split(' ')[0];
|
|
1496
|
-
if (firstTerm && firstTerm.length > 2) {
|
|
1497
|
-
for (const [type, table] of Object.entries(commonTables)) {
|
|
1498
|
-
try {
|
|
1499
|
-
const broadQuery = `(nameLIKE*${firstTerm}*^ORtitleLIKE*${firstTerm}*^ORshort_descriptionLIKE*${firstTerm}*)^LIMIT5`;
|
|
1500
|
-
const results = await this.client.searchRecords(table, broadQuery, 5);
|
|
1501
|
-
if (results && results.success && results.data.result.length > 0) {
|
|
1502
|
-
const typedResults = results.data.result.map((result: any) => ({
|
|
1503
|
-
...result,
|
|
1504
|
-
artifact_type: type,
|
|
1505
|
-
table_name: table
|
|
1506
|
-
}));
|
|
1507
|
-
allResults.push(...typedResults);
|
|
1508
|
-
}
|
|
1509
|
-
} catch (error) {
|
|
1510
|
-
this.logger.warn(`Error in broad search ${table}:`, error);
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
return allResults;
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
// Search specific table
|
|
1520
|
-
const table = tableMapping[intent.artifactType];
|
|
1521
|
-
if (!table) {
|
|
1522
|
-
this.logger.warn(`Unknown artifact type: ${intent.artifactType}`);
|
|
1523
|
-
return [];
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
// First try exact match
|
|
1527
|
-
const searchString = intent.identifier.trim();
|
|
1528
|
-
let results: any;
|
|
1529
|
-
|
|
1530
|
-
// Handle "list all" queries
|
|
1531
|
-
if (searchString.toLowerCase().includes('list all') ||
|
|
1532
|
-
searchString.toLowerCase().includes('all flows') ||
|
|
1533
|
-
searchString.toLowerCase().includes('show all') ||
|
|
1534
|
-
searchString === '') {
|
|
1535
|
-
this.logger.info(`Fetching all ${intent.artifactType} records`);
|
|
1536
|
-
// Get all active records of this type, sorted by name
|
|
1537
|
-
results = await this.client.searchRecords(table, `active=true^ORDERBYname^LIMIT50`);
|
|
1538
|
-
|
|
1539
|
-
// Add artifact type to results
|
|
1540
|
-
if (results && results.success && results.data.result.length > 0) {
|
|
1541
|
-
const enhancedResults = results.data.result.map((result: any) => ({
|
|
1542
|
-
...result,
|
|
1543
|
-
artifact_type: intent.artifactType,
|
|
1544
|
-
table_name: table
|
|
1545
|
-
}));
|
|
1546
|
-
return enhancedResults;
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
return [];
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
// Try exact name match first
|
|
1553
|
-
this.logger.info(`Trying exact match: name=${searchString}`);
|
|
1554
|
-
results = await this.client.searchRecords(table, `name=${searchString}^LIMIT5`);
|
|
1555
|
-
|
|
1556
|
-
// If no exact match, try contains without wildcards (ServiceNow specific)
|
|
1557
|
-
if (!results || !results.success || results.data.result.length === 0) {
|
|
1558
|
-
this.logger.info(`No exact match, trying contains: nameLIKE${searchString}`);
|
|
1559
|
-
results = await this.client.searchRecords(table, `nameLIKE${searchString}^LIMIT10`);
|
|
1560
|
-
}
|
|
1561
|
-
|
|
1562
|
-
// Also try description fields
|
|
1563
|
-
if (!results || !results.success || results.data.result.length === 0) {
|
|
1564
|
-
this.logger.info(`No name match, trying description: short_descriptionLIKE${searchString}`);
|
|
1565
|
-
results = await this.client.searchRecords(table, `short_descriptionLIKE${searchString}^ORdescriptionLIKE${searchString}^LIMIT10`);
|
|
1566
|
-
}
|
|
1567
|
-
|
|
1568
|
-
// If still no results, use the complex query
|
|
1569
|
-
if (!results || !results.success || results.data.result.length === 0) {
|
|
1570
|
-
const query = this.buildServiceNowQuery(intent);
|
|
1571
|
-
this.logger.info(`No contains match, trying complex query: ${query}`);
|
|
1572
|
-
results = await this.client.searchRecords(table, query);
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
// If still no results, try a broader search
|
|
1576
|
-
if (!results || !results.success || results.data.result.length === 0) {
|
|
1577
|
-
this.logger.info(`No results found, trying broader search...`);
|
|
1578
|
-
|
|
1579
|
-
// Try searching with just the first search term with wildcards
|
|
1580
|
-
const firstTerm = intent.identifier.split(' ')[0];
|
|
1581
|
-
if (firstTerm && firstTerm.length > 2) {
|
|
1582
|
-
const broadQuery = `(nameLIKE*${firstTerm}*^ORtitleLIKE*${firstTerm}*^ORshort_descriptionLIKE*${firstTerm}*)^LIMIT10`;
|
|
1583
|
-
results = await this.client.searchRecords(table, broadQuery);
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
// If still no results, return empty array instead of sample records
|
|
1587
|
-
if (!results || !results.success || results.data.result.length === 0) {
|
|
1588
|
-
this.logger.info(`No results found for search term: ${searchString}`);
|
|
1589
|
-
return [];
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
|
|
1593
|
-
// Ensure we always return an array
|
|
1594
|
-
return (results && results.success && results.data.result) ? results.data.result : [];
|
|
1595
|
-
} catch (error) {
|
|
1596
|
-
this.logger.error('Error searching ServiceNow', error);
|
|
1597
|
-
return [];
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
private buildServiceNowQuery(intent: ParsedIntent): string {
|
|
1602
|
-
// Build proper ServiceNow encoded query
|
|
1603
|
-
const searchString = intent.identifier.trim();
|
|
1604
|
-
|
|
1605
|
-
if (searchString.length === 0) {
|
|
1606
|
-
// Return first 10 active records if no search terms
|
|
1607
|
-
return 'active=true^LIMIT10';
|
|
1608
|
-
}
|
|
1609
|
-
|
|
1610
|
-
// First try exact name match
|
|
1611
|
-
const exactQuery = `name=${searchString}`;
|
|
1612
|
-
|
|
1613
|
-
// Then try name contains (without wildcards for multi-word)
|
|
1614
|
-
const containsQuery = `nameLIKE${searchString}`;
|
|
1615
|
-
|
|
1616
|
-
// For single words, use wildcards (keep original case for better matching)
|
|
1617
|
-
const searchTerms = searchString.split(' ').filter(term => term.length > 1);
|
|
1618
|
-
const wildcardQueries = [];
|
|
1619
|
-
|
|
1620
|
-
if (searchTerms.length === 1) {
|
|
1621
|
-
// Single word - use wildcards with both cases
|
|
1622
|
-
const term = searchTerms[0];
|
|
1623
|
-
const termLower = term.toLowerCase();
|
|
1624
|
-
wildcardQueries.push(`nameLIKE*${term}*`);
|
|
1625
|
-
wildcardQueries.push(`nameLIKE*${termLower}*`);
|
|
1626
|
-
wildcardQueries.push(`titleLIKE*${term}*`);
|
|
1627
|
-
wildcardQueries.push(`titleLIKE*${termLower}*`);
|
|
1628
|
-
wildcardQueries.push(`short_descriptionLIKE*${termLower}*`);
|
|
1629
|
-
} else {
|
|
1630
|
-
// Multiple words - search for first and last word with wildcards
|
|
1631
|
-
const firstTerm = searchTerms[0];
|
|
1632
|
-
const lastTerm = searchTerms[searchTerms.length - 1];
|
|
1633
|
-
const firstLower = firstTerm.toLowerCase();
|
|
1634
|
-
const lastLower = lastTerm.toLowerCase();
|
|
1635
|
-
|
|
1636
|
-
wildcardQueries.push(`nameLIKE*${firstTerm}*`);
|
|
1637
|
-
wildcardQueries.push(`nameLIKE*${firstLower}*`);
|
|
1638
|
-
wildcardQueries.push(`nameLIKE*${lastTerm}*`);
|
|
1639
|
-
wildcardQueries.push(`nameLIKE*${lastLower}*`);
|
|
1640
|
-
wildcardQueries.push(`titleLIKE*${firstTerm}*`);
|
|
1641
|
-
wildcardQueries.push(`titleLIKE*${lastTerm}*`);
|
|
1642
|
-
}
|
|
1643
|
-
|
|
1644
|
-
// Combine queries: try exact match OR contains OR wildcards
|
|
1645
|
-
const allQueries = [exactQuery, containsQuery, ...wildcardQueries];
|
|
1646
|
-
const query = `(${allQueries.join('^OR')})^LIMIT20`;
|
|
1647
|
-
return query;
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
|
-
private async intelligentlyIndex(artifact: any): Promise<IndexedArtifact> {
|
|
1651
|
-
// Intelligent indexing based on artifact type
|
|
1652
|
-
const structure = await this.decomposeArtifact(artifact);
|
|
1653
|
-
const context = await this.extractContext(artifact);
|
|
1654
|
-
const relationships = await this.mapRelationships(artifact);
|
|
1655
|
-
const claudeSummary = await this.createClaudeSummary(artifact);
|
|
1656
|
-
const modificationPoints = await this.identifyModificationPoints(artifact);
|
|
1657
|
-
|
|
1658
|
-
return {
|
|
1659
|
-
meta: {
|
|
1660
|
-
sys_id: artifact.sys_id,
|
|
1661
|
-
name: artifact.name || artifact.title,
|
|
1662
|
-
title: artifact.title,
|
|
1663
|
-
type: artifact.sys_class_name || 'unknown',
|
|
1664
|
-
last_updated: artifact.sys_updated_on,
|
|
1665
|
-
},
|
|
1666
|
-
structure,
|
|
1667
|
-
context,
|
|
1668
|
-
relationships,
|
|
1669
|
-
claudeSummary,
|
|
1670
|
-
modificationPoints,
|
|
1671
|
-
};
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
private async decomposeArtifact(artifact: any) {
|
|
1675
|
-
// Decompose artifact based on type
|
|
1676
|
-
if (artifact.sys_class_name === 'sp_widget') {
|
|
1677
|
-
return this.decomposeWidget(artifact);
|
|
1678
|
-
} else if (artifact.sys_class_name === 'sys_hub_flow') {
|
|
1679
|
-
return this.decomposeFlow(artifact);
|
|
1680
|
-
}
|
|
1681
|
-
return { type: 'unknown', components: [] };
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
private async decomposeWidget(widget: any) {
|
|
1685
|
-
return {
|
|
1686
|
-
type: 'widget',
|
|
1687
|
-
components: {
|
|
1688
|
-
template: widget.template ? 'HTML template present' : 'No template',
|
|
1689
|
-
css: widget.css ? 'Custom CSS present' : 'No custom CSS',
|
|
1690
|
-
client_script: widget.client_script ? 'Client script present' : 'No client script',
|
|
1691
|
-
server_script: widget.server_script ? 'Server script present' : 'No server script',
|
|
1692
|
-
options: widget.option_schema ? JSON.parse(widget.option_schema) : [],
|
|
1693
|
-
},
|
|
1694
|
-
};
|
|
1695
|
-
}
|
|
1696
|
-
|
|
1697
|
-
private async decomposeFlow(flow: any) {
|
|
1698
|
-
return {
|
|
1699
|
-
type: 'flow',
|
|
1700
|
-
components: {
|
|
1701
|
-
name: flow.name,
|
|
1702
|
-
description: flow.description,
|
|
1703
|
-
active: flow.active,
|
|
1704
|
-
trigger: flow.trigger_conditions || 'Unknown trigger',
|
|
1705
|
-
steps: 'Flow definition _analysis would go here',
|
|
1706
|
-
},
|
|
1707
|
-
};
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
private async extractContext(artifact: any) {
|
|
1711
|
-
return {
|
|
1712
|
-
usage: 'Context _analysis would determine usage patterns',
|
|
1713
|
-
dependencies: 'Related artifacts would be identified here',
|
|
1714
|
-
impact: 'Impact _analysis would be performed here',
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
private async mapRelationships(artifact: any) {
|
|
1719
|
-
return {
|
|
1720
|
-
relatedArtifacts: [],
|
|
1721
|
-
dependencies: [],
|
|
1722
|
-
usage: [],
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
private async createClaudeSummary(artifact: any): Promise<string> {
|
|
1727
|
-
const name = artifact.name || artifact.title || 'Unknown';
|
|
1728
|
-
const type = artifact.sys_class_name || 'artifact';
|
|
1729
|
-
|
|
1730
|
-
return `${name} is a ${type} in ServiceNow. It can be modified using natural language instructions through Snow-Flow.`;
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
private async identifyModificationPoints(artifact: any) {
|
|
1734
|
-
// Identify common modification points
|
|
1735
|
-
return [
|
|
1736
|
-
{
|
|
1737
|
-
location: 'main_configuration',
|
|
1738
|
-
type: 'modify_settings',
|
|
1739
|
-
description: 'Main configuration can be modified',
|
|
1740
|
-
},
|
|
1741
|
-
];
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
private async storeInMemory(artifact: IndexedArtifact) {
|
|
1745
|
-
// Store in memory for future access
|
|
1746
|
-
await fs.mkdir(this.memoryPath, { recursive: true });
|
|
1747
|
-
const filePath = join(this.memoryPath, `${artifact.meta.sys_id}.json`);
|
|
1748
|
-
await fs.writeFile(filePath, JSON.stringify(artifact, null, 2));
|
|
1749
|
-
}
|
|
1750
|
-
|
|
1751
|
-
private async searchInMemory(intent: ParsedIntent) {
|
|
1752
|
-
// Search in memory index
|
|
1753
|
-
try {
|
|
1754
|
-
const files = await fs.readdir(this.memoryPath);
|
|
1755
|
-
const results = [];
|
|
1756
|
-
|
|
1757
|
-
for (const file of files) {
|
|
1758
|
-
if (file.endsWith('.json')) {
|
|
1759
|
-
try {
|
|
1760
|
-
const content = await fs.readFile(join(this.memoryPath, file), 'utf8');
|
|
1761
|
-
const artifact = JSON.parse(content);
|
|
1762
|
-
|
|
1763
|
-
// Ensure artifact has basic structure
|
|
1764
|
-
if (artifact && (artifact.meta || artifact.claudeSummary)) {
|
|
1765
|
-
if (this.matchesIntent(artifact, intent)) {
|
|
1766
|
-
results.push(artifact);
|
|
1767
|
-
}
|
|
1768
|
-
}
|
|
1769
|
-
} catch (parseError) {
|
|
1770
|
-
this.logger.warn(`Failed to parse memory file: ${file}`, parseError);
|
|
1771
|
-
// Continue with next file
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
|
|
1776
|
-
return results;
|
|
1777
|
-
} catch (error) {
|
|
1778
|
-
return [];
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
private matchesIntent(artifact: IndexedArtifact, intent: ParsedIntent): boolean {
|
|
1783
|
-
const searchTerm = intent.identifier.toLowerCase();
|
|
1784
|
-
|
|
1785
|
-
// Safe access to artifact properties with fallbacks
|
|
1786
|
-
const artifactName = (artifact.meta?.name || artifact.meta?.title || '').toLowerCase();
|
|
1787
|
-
const artifactSummary = (artifact.claudeSummary || '').toLowerCase();
|
|
1788
|
-
|
|
1789
|
-
return artifactName.includes(searchTerm) || artifactSummary.includes(searchTerm);
|
|
1790
|
-
}
|
|
1791
|
-
|
|
1792
|
-
private formatResults(results: any[]): string {
|
|
1793
|
-
if (!results || results.length === 0) {
|
|
1794
|
-
return '❌ No artifacts found matching your search criteria.\n\n🔍 **Debugging Info:**\n- ServiceNow connection appears to be working\n- Try broader search terms\n- Check if widgets exist in your ServiceNow instance\n- Use snow_deploy_widget to create new widgets first';
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
const formattedResults = results.map((result, index) => {
|
|
1798
|
-
const name = result.name || result.title || result.display_name || result.sys_id || 'Unknown';
|
|
1799
|
-
const type = result.sys_class_name || result.type || 'Unknown';
|
|
1800
|
-
const artifactType = result.artifact_type || 'Unknown';
|
|
1801
|
-
const tableName = result.table_name || 'Unknown';
|
|
1802
|
-
const id = result.sys_id || 'Unknown';
|
|
1803
|
-
const updated = result.sys_updated_on || result.last_updated || 'Unknown';
|
|
1804
|
-
const active = result.active !== undefined ? (result.active ? 'Active' : 'Inactive') : 'Unknown';
|
|
1805
|
-
const description = result.short_description || result.description || 'No description';
|
|
1806
|
-
|
|
1807
|
-
return `${index + 1}. **${name}**\n - Artifact Type: ${artifactType}\n - Table: ${tableName}\n - Class: ${type}\n - ID: ${id}\n - Status: ${active}\n - Description: ${description}\n - Updated: ${updated}`;
|
|
1808
|
-
}).join('\n\n');
|
|
1809
|
-
|
|
1810
|
-
return `✅ Found ${results.length} artifact(s):\n\n${formattedResults}`;
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
private formatMemoryResults(results: IndexedArtifact[]): string {
|
|
1814
|
-
return results.map((result, index) =>
|
|
1815
|
-
`${index + 1}. **${result.meta.name}**\n - Type: ${result.meta.type}\n - Summary: ${result.claudeSummary}\n - Modification Points: ${result.modificationPoints.length}`
|
|
1816
|
-
).join('\n\n');
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
private formatComprehensiveResults(results: any[]): string {
|
|
1820
|
-
if (!results || results.length === 0) {
|
|
1821
|
-
return '❌ No artifacts found across all ServiceNow tables.\n\n🔍 **Suggestions:**\n- Check spelling and try different terms\n- Include inactive records with include_inactive=true\n- Try broader search terms\n- The artifact might be in a scoped application';
|
|
1822
|
-
}
|
|
1823
|
-
|
|
1824
|
-
// Group results by table type
|
|
1825
|
-
const groupedResults = results.reduce((groups, result) => {
|
|
1826
|
-
const tableDesc = result.table_description || result.table_name;
|
|
1827
|
-
if (!groups[tableDesc]) {
|
|
1828
|
-
groups[tableDesc] = [];
|
|
1829
|
-
}
|
|
1830
|
-
groups[tableDesc].push(result);
|
|
1831
|
-
return groups;
|
|
1832
|
-
}, {} as Record<string, any[]>);
|
|
1833
|
-
|
|
1834
|
-
let output = `✅ Found ${results.length} artifact(s) across ${Object.keys(groupedResults).length} table type(s):\n\n`;
|
|
1835
|
-
|
|
1836
|
-
for (const [tableDesc, tableResults] of Object.entries(groupedResults)) {
|
|
1837
|
-
output += `## ${tableDesc}\n`;
|
|
1838
|
-
|
|
1839
|
-
(tableResults as any[]).forEach((result: any, index: number) => {
|
|
1840
|
-
const name = result.name || result.title || result.display_name || result.sys_id || 'Unknown';
|
|
1841
|
-
const active = result.active !== undefined ? (result.active ? 'Active' : 'Inactive') : 'Unknown';
|
|
1842
|
-
const description = result.short_description || result.description || 'No description';
|
|
1843
|
-
const strategy = result.search_strategy || 'Unknown';
|
|
1844
|
-
const collection = result.collection || result.table_name || 'Unknown';
|
|
1845
|
-
|
|
1846
|
-
output += `${index + 1}. **${name}**\n`;
|
|
1847
|
-
output += ` - Table: ${collection}\n`;
|
|
1848
|
-
output += ` - Status: ${active}\n`;
|
|
1849
|
-
output += ` - Description: ${description}\n`;
|
|
1850
|
-
output += ` - Found via: ${strategy}\n`;
|
|
1851
|
-
output += ` - Sys ID: ${result.sys_id}\n`;
|
|
1852
|
-
|
|
1853
|
-
if (result.when) output += ` - When: ${result.when}\n`;
|
|
1854
|
-
if (result.order) output += ` - Order: ${result.order}\n`;
|
|
1855
|
-
if (result.condition) output += ` - Condition: ${result.condition}\n`;
|
|
1856
|
-
|
|
1857
|
-
output += '\n';
|
|
1858
|
-
});
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
return output;
|
|
1862
|
-
}
|
|
1863
|
-
|
|
1864
|
-
private generateEditSuggestion(artifact: any): string {
|
|
1865
|
-
if (!artifact) {
|
|
1866
|
-
return '💡 Use snow_edit_artifact to modify ServiceNow artifacts with natural language.';
|
|
1867
|
-
}
|
|
1868
|
-
|
|
1869
|
-
const name = artifact.name || artifact.title || artifact.display_name || 'the artifact';
|
|
1870
|
-
return `💡 To edit this artifact, use:\n\`snow-flow edit "modify ${name} to add [your requirements]"\``;
|
|
1871
|
-
}
|
|
1872
|
-
|
|
1873
|
-
private async findTargetArtifact(intent: ParsedIntent) {
|
|
1874
|
-
// Find the specific artifact to edit
|
|
1875
|
-
const results = await this.searchServiceNow(intent);
|
|
1876
|
-
if (results.length === 0) {
|
|
1877
|
-
throw new Error('No matching artifact found');
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
// Return the best match based on relevance scoring
|
|
1881
|
-
return this.selectBestMatch(results, intent);
|
|
1882
|
-
}
|
|
1883
|
-
|
|
1884
|
-
/**
|
|
1885
|
-
* Select the best matching artifact based on relevance scoring
|
|
1886
|
-
*/
|
|
1887
|
-
private selectBestMatch(results: any[], intent: ParsedIntent): any {
|
|
1888
|
-
if (results.length === 1) {
|
|
1889
|
-
return results[0];
|
|
1890
|
-
}
|
|
1891
|
-
|
|
1892
|
-
// Score each result based on multiple factors
|
|
1893
|
-
const scoredResults = results.map(result => {
|
|
1894
|
-
let score = 0;
|
|
1895
|
-
|
|
1896
|
-
// Name/title similarity
|
|
1897
|
-
const name = (result.name || result.title || '').toLowerCase();
|
|
1898
|
-
const identifier = intent.identifier.toLowerCase();
|
|
1899
|
-
|
|
1900
|
-
if (name === identifier) {
|
|
1901
|
-
score += 100; // Exact match
|
|
1902
|
-
} else if (name.includes(identifier) || identifier.includes(name)) {
|
|
1903
|
-
score += 50; // Partial match
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
// Type match
|
|
1907
|
-
if (result.type === intent.artifactType) {
|
|
1908
|
-
score += 30;
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
// Recency (prefer more recently updated)
|
|
1912
|
-
if (result.sys_updated_on) {
|
|
1913
|
-
const daysSinceUpdate = (Date.now() - new Date(result.sys_updated_on).getTime()) / (1000 * 60 * 60 * 24);
|
|
1914
|
-
score += Math.max(0, 10 - daysSinceUpdate); // More points for recent updates
|
|
1915
|
-
}
|
|
1916
|
-
|
|
1917
|
-
// Active/non-test artifacts preferred
|
|
1918
|
-
if (result.active !== false && !name.includes('test') && !name.includes('mock')) {
|
|
1919
|
-
score += 10;
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
return { result, score };
|
|
1923
|
-
});
|
|
1924
|
-
|
|
1925
|
-
// Sort by score and return the best match
|
|
1926
|
-
scoredResults.sort((a, b) => b.score - a.score);
|
|
1927
|
-
|
|
1928
|
-
this.logger.info('Selected best match', {
|
|
1929
|
-
selected: scoredResults[0].result.name,
|
|
1930
|
-
score: scoredResults[0].score,
|
|
1931
|
-
totalResults: results.length
|
|
1932
|
-
});
|
|
1933
|
-
|
|
1934
|
-
return scoredResults[0].result;
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
/**
|
|
1938
|
-
* Perform comprehensive flow _analysis and testing
|
|
1939
|
-
*/
|
|
1940
|
-
private async performFlowAnalysis(sysId: string, flowType: string, flowData: any): Promise<any> {
|
|
1941
|
-
const _analysis = {
|
|
1942
|
-
structureValid: false,
|
|
1943
|
-
triggerAnalysis: null,
|
|
1944
|
-
recommendedTests: [],
|
|
1945
|
-
performanceScore: 0,
|
|
1946
|
-
securityIssues: [],
|
|
1947
|
-
integrationPoints: []
|
|
1948
|
-
};
|
|
1949
|
-
|
|
1950
|
-
try {
|
|
1951
|
-
// Analyze flow structure
|
|
1952
|
-
if (flowType === 'flow_designer' && flowData.latest_snapshot) {
|
|
1953
|
-
const snapshot = JSON.parse(flowData.latest_snapshot);
|
|
1954
|
-
const activities = snapshot.activities || snapshot.steps || [];
|
|
1955
|
-
|
|
1956
|
-
_analysis.structureValid = activities.length > 0;
|
|
1957
|
-
_analysis.performanceScore = this.calculateFlowPerformanceScore(activities);
|
|
1958
|
-
_analysis.securityIssues = this.identifySecurityIssues(activities);
|
|
1959
|
-
_analysis.integrationPoints = this.findIntegrationPoints(activities);
|
|
1960
|
-
}
|
|
1961
|
-
|
|
1962
|
-
// Analyze trigger conditions
|
|
1963
|
-
if (flowData.table && flowData.condition) {
|
|
1964
|
-
_analysis.triggerAnalysis = {
|
|
1965
|
-
table: flowData.table,
|
|
1966
|
-
condition: flowData.condition,
|
|
1967
|
-
active: flowData.active,
|
|
1968
|
-
validTrigger: true
|
|
1969
|
-
};
|
|
1970
|
-
|
|
1971
|
-
// Recommend specific tests based on trigger
|
|
1972
|
-
_analysis.recommendedTests = this.generateFlowTestRecommendations(flowData);
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
return _analysis;
|
|
1976
|
-
} catch (error) {
|
|
1977
|
-
this.logger.error('Flow _analysis failed', { sysId, error: error.message });
|
|
1978
|
-
return {
|
|
1979
|
-
..._analysis,
|
|
1980
|
-
error: error.message,
|
|
1981
|
-
recommendation: 'Use snow_test_flow_with_mock() for safer testing'
|
|
1982
|
-
};
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
|
|
1986
|
-
private calculateFlowPerformanceScore(activities: any[]): number {
|
|
1987
|
-
let score = 100;
|
|
1988
|
-
|
|
1989
|
-
// Deduct points for potential performance issues
|
|
1990
|
-
activities.forEach(activity => {
|
|
1991
|
-
if (activity.type?.includes('wait') || activity.type?.includes('timer')) {
|
|
1992
|
-
score -= 5; // Wait activities can slow flows
|
|
1993
|
-
}
|
|
1994
|
-
if (activity.script && activity.script.length > 1000) {
|
|
1995
|
-
score -= 10; // Large scripts may impact performance
|
|
1996
|
-
}
|
|
1997
|
-
if (activity.type?.includes('loop')) {
|
|
1998
|
-
score -= 15; // Loops can be performance bottlenecks
|
|
1999
|
-
}
|
|
2000
|
-
});
|
|
2001
|
-
|
|
2002
|
-
return Math.max(0, score);
|
|
2003
|
-
}
|
|
2004
|
-
|
|
2005
|
-
private identifySecurityIssues(activities: any[]): string[] {
|
|
2006
|
-
const issues = [];
|
|
2007
|
-
|
|
2008
|
-
activities.forEach((activity, index) => {
|
|
2009
|
-
if (activity.script) {
|
|
2010
|
-
// Check for potential security issues
|
|
2011
|
-
if (activity.script.includes('eval(')) {
|
|
2012
|
-
issues.push(`Activity ${index + 1}: Uses eval() which is a security risk`);
|
|
2013
|
-
}
|
|
2014
|
-
if (activity.script.includes('gs.getUser().getUserID()') && !activity.script.includes('canRead')) {
|
|
2015
|
-
issues.push(`Activity ${index + 1}: May have access control issues`);
|
|
2016
|
-
}
|
|
2017
|
-
if (activity.script.includes('XMLHttpRequest')) {
|
|
2018
|
-
issues.push(`Activity ${index + 1}: External HTTP calls may pose security risks`);
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
});
|
|
2022
|
-
|
|
2023
|
-
return issues;
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
private findIntegrationPoints(activities: any[]): string[] {
|
|
2027
|
-
const integrations = [];
|
|
2028
|
-
|
|
2029
|
-
activities.forEach((activity, index) => {
|
|
2030
|
-
if (activity.type?.includes('rest') || activity.type?.includes('soap')) {
|
|
2031
|
-
integrations.push(`Activity ${index + 1}: External API integration (${activity.type})`);
|
|
2032
|
-
}
|
|
2033
|
-
if (activity.script && activity.script.includes('RESTMessage')) {
|
|
2034
|
-
integrations.push(`Activity ${index + 1}: REST Message integration`);
|
|
2035
|
-
}
|
|
2036
|
-
if (activity.script && activity.script.includes('SOAPMessage')) {
|
|
2037
|
-
integrations.push(`Activity ${index + 1}: SOAP Message integration`);
|
|
2038
|
-
}
|
|
2039
|
-
});
|
|
2040
|
-
|
|
2041
|
-
return integrations;
|
|
2042
|
-
}
|
|
2043
|
-
|
|
2044
|
-
private generateFlowTestRecommendations(flowData: any): string[] {
|
|
2045
|
-
const recommendations = [];
|
|
2046
|
-
|
|
2047
|
-
if (flowData.table) {
|
|
2048
|
-
recommendations.push(`Create test record in ${flowData.table} table`);
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
if (flowData.condition) {
|
|
2052
|
-
recommendations.push(`Test with records that match condition: ${flowData.condition}`);
|
|
2053
|
-
recommendations.push(`Test with records that don't match condition (negative test)`);
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
if (flowData.trigger_type === 'record_updated') {
|
|
2057
|
-
recommendations.push('Test by updating existing records');
|
|
2058
|
-
} else if (flowData.trigger_type === 'record_created') {
|
|
2059
|
-
recommendations.push('Test by creating new records');
|
|
2060
|
-
}
|
|
2061
|
-
|
|
2062
|
-
recommendations.push('Use snow_test_flow_with_mock() for isolated testing');
|
|
2063
|
-
|
|
2064
|
-
return recommendations;
|
|
2065
|
-
}
|
|
2066
|
-
|
|
2067
|
-
private async analyzeModification(intent: ParsedIntent, artifact: any) {
|
|
2068
|
-
// Analyze what modification is needed
|
|
2069
|
-
return {
|
|
2070
|
-
type: 'configuration_change',
|
|
2071
|
-
description: intent.modification || 'General modification',
|
|
2072
|
-
target: artifact.sys_id,
|
|
2073
|
-
};
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2076
|
-
private async applyModification(artifact: any, modification: any) {
|
|
2077
|
-
// Apply the modification - this would be more sophisticated
|
|
2078
|
-
return {
|
|
2079
|
-
...artifact,
|
|
2080
|
-
modified: true,
|
|
2081
|
-
modification_applied: modification,
|
|
2082
|
-
};
|
|
2083
|
-
}
|
|
2084
|
-
|
|
2085
|
-
private async deployArtifact(artifact: any) {
|
|
2086
|
-
this.logger.info('Deploying modified artifact to ServiceNow', {
|
|
2087
|
-
type: artifact.type,
|
|
2088
|
-
sys_id: artifact.sys_id,
|
|
2089
|
-
name: artifact.name
|
|
2090
|
-
});
|
|
2091
|
-
|
|
2092
|
-
try {
|
|
2093
|
-
// Determine the table name based on artifact type
|
|
2094
|
-
let tableName: string;
|
|
2095
|
-
let updateData: any = {};
|
|
2096
|
-
|
|
2097
|
-
switch (artifact.type) {
|
|
2098
|
-
case 'widget':
|
|
2099
|
-
tableName = 'sp_widget';
|
|
2100
|
-
updateData = {
|
|
2101
|
-
name: artifact.name,
|
|
2102
|
-
title: artifact.title,
|
|
2103
|
-
description: artifact.description,
|
|
2104
|
-
template: artifact.template,
|
|
2105
|
-
css: artifact.css,
|
|
2106
|
-
client_script: artifact.client_script,
|
|
2107
|
-
server_script: artifact.server_script,
|
|
2108
|
-
option_schema: artifact.option_schema
|
|
2109
|
-
};
|
|
2110
|
-
break;
|
|
2111
|
-
|
|
2112
|
-
case 'flow':
|
|
2113
|
-
tableName = 'sys_hub_flow';
|
|
2114
|
-
updateData = {
|
|
2115
|
-
name: artifact.name,
|
|
2116
|
-
description: artifact.description,
|
|
2117
|
-
active: artifact.active,
|
|
2118
|
-
trigger_conditions: artifact.trigger_conditions,
|
|
2119
|
-
flow_definition: typeof artifact.flow_definition === 'string'
|
|
2120
|
-
? artifact.flow_definition
|
|
2121
|
-
: JSON.stringify(artifact.flow_definition)
|
|
2122
|
-
};
|
|
2123
|
-
break;
|
|
2124
|
-
|
|
2125
|
-
case 'subflow':
|
|
2126
|
-
tableName = 'sys_hub_subflow';
|
|
2127
|
-
updateData = {
|
|
2128
|
-
name: artifact.name,
|
|
2129
|
-
description: artifact.description,
|
|
2130
|
-
inputs: typeof artifact.inputs === 'string'
|
|
2131
|
-
? artifact.inputs
|
|
2132
|
-
: JSON.stringify(artifact.inputs || []),
|
|
2133
|
-
outputs: typeof artifact.outputs === 'string'
|
|
2134
|
-
? artifact.outputs
|
|
2135
|
-
: JSON.stringify(artifact.outputs || [])
|
|
2136
|
-
};
|
|
2137
|
-
break;
|
|
2138
|
-
|
|
2139
|
-
case 'business_rule':
|
|
2140
|
-
tableName = 'sys_script';
|
|
2141
|
-
updateData = {
|
|
2142
|
-
name: artifact.name,
|
|
2143
|
-
description: artifact.description,
|
|
2144
|
-
script: artifact.script,
|
|
2145
|
-
condition: artifact.condition,
|
|
2146
|
-
when: artifact.when,
|
|
2147
|
-
active: artifact.active
|
|
2148
|
-
};
|
|
2149
|
-
break;
|
|
2150
|
-
|
|
2151
|
-
case 'script_include':
|
|
2152
|
-
tableName = 'sys_script_include';
|
|
2153
|
-
updateData = {
|
|
2154
|
-
name: artifact.name,
|
|
2155
|
-
description: artifact.description,
|
|
2156
|
-
script: artifact.script,
|
|
2157
|
-
api_name: artifact.api_name,
|
|
2158
|
-
active: artifact.active
|
|
2159
|
-
};
|
|
2160
|
-
break;
|
|
2161
|
-
|
|
2162
|
-
case 'client_script':
|
|
2163
|
-
tableName = 'sys_script_client';
|
|
2164
|
-
updateData = {
|
|
2165
|
-
name: artifact.name,
|
|
2166
|
-
description: artifact.description,
|
|
2167
|
-
script: artifact.script,
|
|
2168
|
-
table: artifact.table,
|
|
2169
|
-
type: artifact.script_type,
|
|
2170
|
-
condition: artifact.condition,
|
|
2171
|
-
active: artifact.active
|
|
2172
|
-
};
|
|
2173
|
-
break;
|
|
2174
|
-
|
|
2175
|
-
case 'ui_policy':
|
|
2176
|
-
tableName = 'sys_ui_policy';
|
|
2177
|
-
updateData = {
|
|
2178
|
-
short_description: artifact.name,
|
|
2179
|
-
description: artifact.description,
|
|
2180
|
-
conditions: artifact.condition,
|
|
2181
|
-
on_load: artifact.on_load,
|
|
2182
|
-
reverse_if_false: artifact.reverse_if_false,
|
|
2183
|
-
active: artifact.active
|
|
2184
|
-
};
|
|
2185
|
-
break;
|
|
2186
|
-
|
|
2187
|
-
case 'application':
|
|
2188
|
-
tableName = 'sys_app';
|
|
2189
|
-
updateData = {
|
|
2190
|
-
name: artifact.name,
|
|
2191
|
-
short_description: artifact.short_description,
|
|
2192
|
-
description: artifact.description,
|
|
2193
|
-
version: artifact.version,
|
|
2194
|
-
active: artifact.active
|
|
2195
|
-
};
|
|
2196
|
-
break;
|
|
2197
|
-
|
|
2198
|
-
default:
|
|
2199
|
-
// Use the artifact's table property if available, or try to infer from type
|
|
2200
|
-
tableName = artifact.table || artifact.type;
|
|
2201
|
-
updateData = { ...artifact };
|
|
2202
|
-
delete updateData.sys_id;
|
|
2203
|
-
delete updateData.type;
|
|
2204
|
-
delete updateData.table;
|
|
2205
|
-
delete updateData.modified;
|
|
2206
|
-
delete updateData.modification_applied;
|
|
2207
|
-
break;
|
|
2208
|
-
}
|
|
2209
|
-
|
|
2210
|
-
// Remove undefined/null values to avoid overwriting with empty data
|
|
2211
|
-
Object.keys(updateData).forEach(key => {
|
|
2212
|
-
if (updateData[key] === undefined || updateData[key] === null) {
|
|
2213
|
-
delete updateData[key];
|
|
2214
|
-
}
|
|
2215
|
-
});
|
|
2216
|
-
|
|
2217
|
-
// Update the artifact in ServiceNow using the sys_id
|
|
2218
|
-
const result = await this.client.put(`/api/now/table/${tableName}/${artifact.sys_id}`, updateData);
|
|
2219
|
-
|
|
2220
|
-
if (result.result) {
|
|
2221
|
-
this.logger.info('Artifact successfully deployed to ServiceNow', {
|
|
2222
|
-
type: artifact.type,
|
|
2223
|
-
sys_id: artifact.sys_id,
|
|
2224
|
-
name: artifact.name,
|
|
2225
|
-
table: tableName
|
|
2226
|
-
});
|
|
2227
|
-
|
|
2228
|
-
// Get instance info for URL generation
|
|
2229
|
-
const instanceInfo = await this.client.getInstanceInfo();
|
|
2230
|
-
const baseUrl = (instanceInfo.result && typeof instanceInfo.result === 'object' && 'instance_url' in instanceInfo.result
|
|
2231
|
-
? instanceInfo.result.instance_url
|
|
2232
|
-
: null) ||
|
|
2233
|
-
`https://${process.env.SNOW_INSTANCE?.replace(/\/$/, '') || 'instance'}.service-now.com`;
|
|
2234
|
-
|
|
2235
|
-
return {
|
|
2236
|
-
success: true,
|
|
2237
|
-
message: 'Artifact deployed successfully',
|
|
2238
|
-
data: {
|
|
2239
|
-
sys_id: artifact.sys_id,
|
|
2240
|
-
name: artifact.name,
|
|
2241
|
-
type: artifact.type,
|
|
2242
|
-
table: tableName,
|
|
2243
|
-
url: `${baseUrl}/nav_to.do?uri=${tableName}.do?sys_id=${artifact.sys_id}`,
|
|
2244
|
-
last_updated: new Date().toISOString()
|
|
2245
|
-
}
|
|
2246
|
-
};
|
|
2247
|
-
} else {
|
|
2248
|
-
throw new Error('No result returned from ServiceNow API');
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
} catch (error) {
|
|
2252
|
-
this.logger.error('Failed to deploy artifact to ServiceNow', {
|
|
2253
|
-
type: artifact.type,
|
|
2254
|
-
sys_id: artifact.sys_id,
|
|
2255
|
-
name: artifact.name,
|
|
2256
|
-
error: error instanceof Error ? error.message : String(error)
|
|
2257
|
-
});
|
|
2258
|
-
|
|
2259
|
-
return {
|
|
2260
|
-
success: false,
|
|
2261
|
-
message: `Failed to deploy artifact: ${error instanceof Error ? error.message : String(error)}`,
|
|
2262
|
-
error: error instanceof Error ? error.message : String(error),
|
|
2263
|
-
data: {
|
|
2264
|
-
sys_id: artifact.sys_id,
|
|
2265
|
-
name: artifact.name,
|
|
2266
|
-
type: artifact.type,
|
|
2267
|
-
deployment_attempt: new Date().toISOString()
|
|
2268
|
-
}
|
|
2269
|
-
};
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
|
|
2273
|
-
private async updateMemoryIndex(artifact: any, modification: any) {
|
|
2274
|
-
// Update the memory index with the changes
|
|
2275
|
-
const indexed = await this.intelligentlyIndex(artifact);
|
|
2276
|
-
await this.storeInMemory(indexed);
|
|
2277
|
-
}
|
|
2278
|
-
|
|
2279
|
-
private getArtifactUrl(artifact: any): string {
|
|
2280
|
-
// Generate ServiceNow URL for the artifact
|
|
2281
|
-
return `sys_id=${artifact.sys_id}`;
|
|
2282
|
-
}
|
|
2283
|
-
|
|
2284
|
-
private async getBySysId(args: any) {
|
|
2285
|
-
// Check authentication first
|
|
2286
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
2287
|
-
if (!authResult.success) {
|
|
2288
|
-
return {
|
|
2289
|
-
content: [
|
|
2290
|
-
{
|
|
2291
|
-
type: 'text',
|
|
2292
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
2293
|
-
},
|
|
2294
|
-
],
|
|
2295
|
-
};
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
|
-
try {
|
|
2299
|
-
this.logger.info('Getting artifact by sys_id', { sys_id: args.sys_id, table: args.table });
|
|
2300
|
-
|
|
2301
|
-
// Direct lookup by sys_id with retry logic for newly created records
|
|
2302
|
-
let artifact = null;
|
|
2303
|
-
let lastError = null;
|
|
2304
|
-
|
|
2305
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
2306
|
-
try {
|
|
2307
|
-
// Note: client.getRecord returns the record directly, not a response object
|
|
2308
|
-
artifact = await this.client.getRecord(args.table, args.sys_id);
|
|
2309
|
-
if (artifact) {
|
|
2310
|
-
break; // Success, exit retry loop
|
|
2311
|
-
}
|
|
2312
|
-
} catch (error) {
|
|
2313
|
-
lastError = error;
|
|
2314
|
-
this.logger.warn(`Attempt ${attempt + 1} failed for sys_id lookup:`, error);
|
|
2315
|
-
|
|
2316
|
-
// If this is not the last attempt, wait before retrying
|
|
2317
|
-
if (attempt < 2) {
|
|
2318
|
-
await new Promise(resolve => setTimeout(resolve, 1000 * (attempt + 1))); // Progressive delay
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
}
|
|
2322
|
-
|
|
2323
|
-
// If direct lookup failed, try fallback search for newly created records
|
|
2324
|
-
if (!artifact) {
|
|
2325
|
-
this.logger.info('Direct lookup failed, trying fallback search...');
|
|
2326
|
-
try {
|
|
2327
|
-
const searchResult = await this.client.searchRecords(args.table, `sys_id=${args.sys_id}`, 1);
|
|
2328
|
-
if (searchResult.success && searchResult.data.result.length > 0) {
|
|
2329
|
-
artifact = searchResult.data.result[0];
|
|
2330
|
-
this.logger.info('Found artifact via fallback search');
|
|
2331
|
-
}
|
|
2332
|
-
} catch (searchError) {
|
|
2333
|
-
this.logger.warn('Fallback search also failed:', searchError);
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
|
-
|
|
2337
|
-
if (!artifact) {
|
|
2338
|
-
return {
|
|
2339
|
-
content: [
|
|
2340
|
-
{
|
|
2341
|
-
type: 'text',
|
|
2342
|
-
text: `❌ Artifact not found with sys_id: ${args.sys_id} in table: ${args.table}\n\n🔍 **Troubleshooting:**\n- Verify the sys_id is correct\n- Check if the record was created in a different Update Set\n- Ensure you have read access to this table\n- For newly created records, try again in a few seconds\n\n**Last Error:** ${lastError?.message || 'Record not found'}`,
|
|
2343
|
-
},
|
|
2344
|
-
],
|
|
2345
|
-
};
|
|
2346
|
-
}
|
|
2347
|
-
|
|
2348
|
-
// Format the response with relevant fields
|
|
2349
|
-
const formattedArtifact = {
|
|
2350
|
-
sys_id: artifact.sys_id,
|
|
2351
|
-
name: artifact.name || artifact.title || 'Unknown',
|
|
2352
|
-
table: args.table,
|
|
2353
|
-
...artifact
|
|
2354
|
-
};
|
|
2355
|
-
|
|
2356
|
-
return {
|
|
2357
|
-
content: [
|
|
2358
|
-
{
|
|
2359
|
-
type: 'text',
|
|
2360
|
-
text: `✅ Found artifact by sys_id!\n\n🎯 **${formattedArtifact.name}**\n🆔 sys_id: ${args.sys_id}\n📊 Table: ${args.table}\n\n**All Fields:**\n${JSON.stringify(formattedArtifact, null, 2)}`,
|
|
2361
|
-
},
|
|
2362
|
-
],
|
|
2363
|
-
};
|
|
2364
|
-
} catch (error) {
|
|
2365
|
-
this.logger.error('Failed to get artifact by sys_id:', error);
|
|
2366
|
-
return {
|
|
2367
|
-
content: [
|
|
2368
|
-
{
|
|
2369
|
-
type: 'text',
|
|
2370
|
-
text: `❌ Error getting artifact by sys_id: ${error}`,
|
|
2371
|
-
},
|
|
2372
|
-
],
|
|
2373
|
-
};
|
|
2374
|
-
}
|
|
2375
|
-
}
|
|
2376
|
-
|
|
2377
|
-
private async editBySysId(args: any) {
|
|
2378
|
-
// Check authentication first
|
|
2379
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
2380
|
-
if (!authResult.success) {
|
|
2381
|
-
return {
|
|
2382
|
-
content: [
|
|
2383
|
-
{
|
|
2384
|
-
type: 'text',
|
|
2385
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
2386
|
-
},
|
|
2387
|
-
],
|
|
2388
|
-
};
|
|
2389
|
-
}
|
|
2390
|
-
|
|
2391
|
-
try {
|
|
2392
|
-
this.logger.info('Editing artifact by sys_id', {
|
|
2393
|
-
sys_id: args.sys_id,
|
|
2394
|
-
table: args.table,
|
|
2395
|
-
field: args.field,
|
|
2396
|
-
value_length: args.value?.length || 0
|
|
2397
|
-
});
|
|
2398
|
-
|
|
2399
|
-
// First, ensure we have an update set
|
|
2400
|
-
const updateSetResult = await this.client.ensureUpdateSet();
|
|
2401
|
-
if (!updateSetResult.success) {
|
|
2402
|
-
this.logger.warn('No update set available, continuing without one');
|
|
2403
|
-
}
|
|
2404
|
-
|
|
2405
|
-
// Build update data
|
|
2406
|
-
const updateData = {
|
|
2407
|
-
[args.field]: args.value
|
|
2408
|
-
};
|
|
2409
|
-
|
|
2410
|
-
// Update the record directly by sys_id with validation
|
|
2411
|
-
const response = await this.client.updateRecord(args.table, args.sys_id, updateData);
|
|
2412
|
-
|
|
2413
|
-
if (!response.success) {
|
|
2414
|
-
// Try to provide more specific error information
|
|
2415
|
-
let errorDetails = response.error || 'Unknown error';
|
|
2416
|
-
if (response.error?.includes('404') || response.error?.includes('not found')) {
|
|
2417
|
-
errorDetails += `\n\n🔍 **Troubleshooting:**
|
|
2418
|
-
- Verify the sys_id exists in the specified table
|
|
2419
|
-
- Check if the record was recently created (may need a moment to be available)
|
|
2420
|
-
- Ensure you have write access to this record
|
|
2421
|
-
|
|
2422
|
-
🔧 **Update Set Considerations:**
|
|
2423
|
-
- Check current Update Set: snow_smart_update_set with action="track"
|
|
2424
|
-
- Verify artifact is tracked: snow_get_by_sysid
|
|
2425
|
-
- Ensure Update Set is active for tracking changes
|
|
2426
|
-
|
|
2427
|
-
💡 **Debug steps:**
|
|
2428
|
-
1. Check current Update Set status
|
|
2429
|
-
2. Verify artifact creation was tracked
|
|
2430
|
-
3. Use mock testing if record access fails`;
|
|
2431
|
-
}
|
|
2432
|
-
|
|
2433
|
-
return {
|
|
2434
|
-
content: [
|
|
2435
|
-
{
|
|
2436
|
-
type: 'text',
|
|
2437
|
-
text: `❌ Failed to update artifact: ${errorDetails}`,
|
|
2438
|
-
},
|
|
2439
|
-
],
|
|
2440
|
-
};
|
|
2441
|
-
}
|
|
2442
|
-
|
|
2443
|
-
// Verify the update was applied successfully by reading back the field
|
|
2444
|
-
let verificationResult = '';
|
|
2445
|
-
try {
|
|
2446
|
-
const updatedRecord = await this.client.getRecord(args.table, args.sys_id);
|
|
2447
|
-
if (updatedRecord && updatedRecord[args.field] === args.value) {
|
|
2448
|
-
verificationResult = '\n\n✅ **Verification:** Update confirmed - field value matches expected value';
|
|
2449
|
-
} else {
|
|
2450
|
-
verificationResult = '\n\n⚠️ **Verification:** Update may not have been applied correctly - consider checking manually';
|
|
2451
|
-
}
|
|
2452
|
-
} catch (verifyError) {
|
|
2453
|
-
verificationResult = '\n\n⚠️ **Verification:** Could not verify update (record may still be processing)';
|
|
2454
|
-
}
|
|
2455
|
-
|
|
2456
|
-
return {
|
|
2457
|
-
content: [
|
|
2458
|
-
{
|
|
2459
|
-
type: 'text',
|
|
2460
|
-
text: `✅ Artifact successfully updated!\n\n🎯 **Updated Field:** ${args.field}\n🆔 sys_id: ${args.sys_id}\n📊 Table: ${args.table}\n📝 Value Length: ${args.value.length} characters\n\n✨ Update applied directly via sys_id - much more reliable than text search!${verificationResult}`,
|
|
2461
|
-
},
|
|
2462
|
-
],
|
|
2463
|
-
};
|
|
2464
|
-
} catch (error) {
|
|
2465
|
-
this.logger.error('Failed to edit artifact by sys_id:', error);
|
|
2466
|
-
return {
|
|
2467
|
-
content: [
|
|
2468
|
-
{
|
|
2469
|
-
type: 'text',
|
|
2470
|
-
text: `❌ Error editing artifact by sys_id: ${error}`,
|
|
2471
|
-
},
|
|
2472
|
-
],
|
|
2473
|
-
};
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
private async syncDataConsistency(args: any) {
|
|
2478
|
-
// Check authentication first
|
|
2479
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
2480
|
-
if (!authResult.success) {
|
|
2481
|
-
return {
|
|
2482
|
-
content: [
|
|
2483
|
-
{
|
|
2484
|
-
type: 'text',
|
|
2485
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
2486
|
-
},
|
|
2487
|
-
],
|
|
2488
|
-
};
|
|
2489
|
-
}
|
|
2490
|
-
|
|
2491
|
-
try {
|
|
2492
|
-
this.logger.info('Starting data consistency sync', { operation: args.operation });
|
|
2493
|
-
let syncResults = [];
|
|
2494
|
-
|
|
2495
|
-
switch (args.operation) {
|
|
2496
|
-
case 'refresh_cache':
|
|
2497
|
-
syncResults = await this.refreshCache(args.table);
|
|
2498
|
-
break;
|
|
2499
|
-
case 'validate_sysids':
|
|
2500
|
-
syncResults = await this.validateSysIds(args.sys_id, args.table);
|
|
2501
|
-
break;
|
|
2502
|
-
case 'reindex_artifacts':
|
|
2503
|
-
syncResults = await this.reindexArtifacts(args.table);
|
|
2504
|
-
break;
|
|
2505
|
-
case 'full_sync':
|
|
2506
|
-
syncResults = await this.performFullSync();
|
|
2507
|
-
break;
|
|
2508
|
-
default:
|
|
2509
|
-
throw new Error(`Unknown sync operation: ${args.operation}`);
|
|
2510
|
-
}
|
|
2511
|
-
|
|
2512
|
-
return {
|
|
2513
|
-
content: [
|
|
2514
|
-
{
|
|
2515
|
-
type: 'text',
|
|
2516
|
-
text: `✅ Data consistency sync completed!\n\n🔄 **Operation:** ${args.operation}\n\n📋 **Results:**\n${syncResults.map(r => `- ${r}`).join('\n')}\n\n✨ Data consistency issues have been resolved. Try your operations again.`,
|
|
2517
|
-
},
|
|
2518
|
-
],
|
|
2519
|
-
};
|
|
2520
|
-
} catch (error) {
|
|
2521
|
-
this.logger.error('Data consistency sync failed:', error);
|
|
2522
|
-
return {
|
|
2523
|
-
content: [
|
|
2524
|
-
{
|
|
2525
|
-
type: 'text',
|
|
2526
|
-
text: `❌ Data consistency sync failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
2527
|
-
},
|
|
2528
|
-
],
|
|
2529
|
-
};
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
private async refreshCache(table?: string): Promise<string[]> {
|
|
2534
|
-
const results = [];
|
|
2535
|
-
|
|
2536
|
-
if (table) {
|
|
2537
|
-
// Clear any cached data for specific table
|
|
2538
|
-
this.memoryIndex.clear(); // Clear in-memory cache
|
|
2539
|
-
results.push(`Cache cleared for table: ${table}`);
|
|
2540
|
-
results.push(`Note: This MCP uses real-time queries, no persistent cache to refresh`);
|
|
2541
|
-
} else {
|
|
2542
|
-
// Clear all in-memory data
|
|
2543
|
-
this.memoryIndex.clear();
|
|
2544
|
-
results.push('All internal caches cleared');
|
|
2545
|
-
results.push(`Note: This MCP uses real-time ServiceNow queries for accuracy`);
|
|
2546
|
-
}
|
|
2547
|
-
|
|
2548
|
-
return results;
|
|
2549
|
-
}
|
|
2550
|
-
|
|
2551
|
-
private async validateSysIds(sys_id?: string, table?: string): Promise<string[]> {
|
|
2552
|
-
const results = [];
|
|
2553
|
-
|
|
2554
|
-
if (sys_id && table) {
|
|
2555
|
-
// Validate specific sys_id
|
|
2556
|
-
try {
|
|
2557
|
-
const record = await this.client.getRecord(table, sys_id);
|
|
2558
|
-
if (record && record.success && record.data) {
|
|
2559
|
-
results.push(`✅ sys_id ${sys_id} validated in table ${table}`);
|
|
2560
|
-
} else {
|
|
2561
|
-
results.push(`❌ sys_id ${sys_id} not found in table ${table}`);
|
|
2562
|
-
}
|
|
2563
|
-
} catch (error) {
|
|
2564
|
-
results.push(`❌ sys_id ${sys_id} validation failed: ${error}`);
|
|
2565
|
-
}
|
|
2566
|
-
} else {
|
|
2567
|
-
// Validate all known sys_ids in memory index
|
|
2568
|
-
const memoryEntries = Array.from(this.memoryIndex.entries());
|
|
2569
|
-
if (memoryEntries.length === 0) {
|
|
2570
|
-
results.push('📝 No sys_ids found in memory index to validate');
|
|
2571
|
-
return results;
|
|
2572
|
-
}
|
|
2573
|
-
|
|
2574
|
-
results.push(`🔍 Validating ${memoryEntries.length} sys_ids from memory index...`);
|
|
2575
|
-
let validCount = 0;
|
|
2576
|
-
let invalidCount = 0;
|
|
2577
|
-
|
|
2578
|
-
for (const [key, data] of memoryEntries) {
|
|
2579
|
-
// Extract sys_id and table from memory data if available
|
|
2580
|
-
try {
|
|
2581
|
-
if (data.sys_id && data.table) {
|
|
2582
|
-
const record = await this.client.getRecord(data.table, data.sys_id);
|
|
2583
|
-
if (record && record.success && record.data) {
|
|
2584
|
-
validCount++;
|
|
2585
|
-
} else {
|
|
2586
|
-
invalidCount++;
|
|
2587
|
-
results.push(`❌ Invalid: ${data.sys_id} in ${data.table}`);
|
|
2588
|
-
}
|
|
2589
|
-
}
|
|
2590
|
-
} catch (error) {
|
|
2591
|
-
invalidCount++;
|
|
2592
|
-
results.push(`❌ Validation error for ${key}: ${error}`);
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
|
|
2596
|
-
results.push(`✅ Validation complete: ${validCount} valid, ${invalidCount} invalid`);
|
|
2597
|
-
}
|
|
2598
|
-
|
|
2599
|
-
return results;
|
|
2600
|
-
}
|
|
2601
|
-
|
|
2602
|
-
private async reindexArtifacts(table?: string): Promise<string[]> {
|
|
2603
|
-
const results = [];
|
|
2604
|
-
|
|
2605
|
-
if (table) {
|
|
2606
|
-
// Re-index artifacts from specific table
|
|
2607
|
-
try {
|
|
2608
|
-
// Query the table to get current artifacts
|
|
2609
|
-
const searchResults = await this.client.searchRecords(table, 'sys_idISNOTEMPTY', 100);
|
|
2610
|
-
if (searchResults.success && searchResults.data) {
|
|
2611
|
-
const artifacts = Array.isArray(searchResults.data) ? searchResults.data : [searchResults.data];
|
|
2612
|
-
|
|
2613
|
-
// Clear existing entries for this table
|
|
2614
|
-
for (const [key, data] of this.memoryIndex.entries()) {
|
|
2615
|
-
if (data.table === table) {
|
|
2616
|
-
this.memoryIndex.delete(key);
|
|
2617
|
-
}
|
|
2618
|
-
}
|
|
2619
|
-
|
|
2620
|
-
// Re-index with fresh data
|
|
2621
|
-
let indexedCount = 0;
|
|
2622
|
-
for (const artifact of artifacts) {
|
|
2623
|
-
if (artifact.sys_id && artifact.name) {
|
|
2624
|
-
const indexKey = `${table}_${artifact.name.replace(/\s+/g, '_')}`;
|
|
2625
|
-
this.memoryIndex.set(indexKey, {
|
|
2626
|
-
sys_id: artifact.sys_id,
|
|
2627
|
-
table: table,
|
|
2628
|
-
name: artifact.name,
|
|
2629
|
-
type: this.getArtifactTypeFromTable(table),
|
|
2630
|
-
indexed_at: new Date().toISOString()
|
|
2631
|
-
});
|
|
2632
|
-
indexedCount++;
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
|
-
results.push(`✅ Re-indexed ${indexedCount} artifacts from table: ${table}`);
|
|
2637
|
-
} else {
|
|
2638
|
-
results.push(`⚠️ Could not fetch artifacts from table: ${table}`);
|
|
2639
|
-
}
|
|
2640
|
-
} catch (error) {
|
|
2641
|
-
results.push(`❌ Re-indexing failed for table ${table}: ${error}`);
|
|
2642
|
-
}
|
|
2643
|
-
} else {
|
|
2644
|
-
// Re-index all known artifact tables
|
|
2645
|
-
const artifactTables = ['sp_widget', 'wf_workflow', 'sys_script_include', 'sys_script'];
|
|
2646
|
-
results.push(`🔍 Re-indexing artifacts from ${artifactTables.length} tables...`);
|
|
2647
|
-
|
|
2648
|
-
let totalIndexed = 0;
|
|
2649
|
-
for (const tableName of artifactTables) {
|
|
2650
|
-
const tableResults = await this.reindexArtifacts(tableName);
|
|
2651
|
-
totalIndexed += tableResults.filter(r => r.includes('✅')).length;
|
|
2652
|
-
}
|
|
2653
|
-
|
|
2654
|
-
results.push(`✅ Global re-indexing complete: ${totalIndexed} artifacts indexed`);
|
|
2655
|
-
results.push(`💾 Memory index size: ${this.memoryIndex.size} entries`);
|
|
2656
|
-
}
|
|
2657
|
-
|
|
2658
|
-
return results;
|
|
2659
|
-
}
|
|
2660
|
-
|
|
2661
|
-
private getArtifactTypeFromTable(table: string): string {
|
|
2662
|
-
const tableTypeMap: { [key: string]: string } = {
|
|
2663
|
-
'sp_widget': 'widget',
|
|
2664
|
-
'wf_workflow': 'flow',
|
|
2665
|
-
'sys_script_include': 'script',
|
|
2666
|
-
'sys_script': 'business_rule',
|
|
2667
|
-
'sys_db_object': 'table'
|
|
2668
|
-
};
|
|
2669
|
-
return tableTypeMap[table] || 'unknown';
|
|
2670
|
-
}
|
|
2671
|
-
|
|
2672
|
-
private async performFullSync(): Promise<string[]> {
|
|
2673
|
-
const results = [];
|
|
2674
|
-
|
|
2675
|
-
// Combine all sync operations
|
|
2676
|
-
const cacheResults = await this.refreshCache();
|
|
2677
|
-
const validateResults = await this.validateSysIds();
|
|
2678
|
-
const reindexResults = await this.reindexArtifacts();
|
|
2679
|
-
|
|
2680
|
-
results.push(...cacheResults, ...validateResults, ...reindexResults);
|
|
2681
|
-
results.push('Full synchronization completed');
|
|
2682
|
-
|
|
2683
|
-
return results;
|
|
2684
|
-
}
|
|
2685
|
-
|
|
2686
|
-
private async validateLiveConnection(args: any) {
|
|
2687
|
-
const { test_level = 'basic', include_performance = false } = args;
|
|
2688
|
-
|
|
2689
|
-
const startTime = Date.now();
|
|
2690
|
-
const results: any = {
|
|
2691
|
-
connection_status: 'unknown',
|
|
2692
|
-
authentication_status: 'unknown',
|
|
2693
|
-
permissions_status: 'unknown',
|
|
2694
|
-
instance_info: {},
|
|
2695
|
-
performance_metrics: {},
|
|
2696
|
-
timestamp: new Date().toISOString()
|
|
2697
|
-
};
|
|
2698
|
-
|
|
2699
|
-
try {
|
|
2700
|
-
// Basic connection test
|
|
2701
|
-
const systemInfo = await this.client.get('/api/now/table/sys_properties', { sysparm_limit: 1 });
|
|
2702
|
-
const basicResponseTime = Date.now() - startTime;
|
|
2703
|
-
|
|
2704
|
-
results.connection_status = 'success';
|
|
2705
|
-
results.authentication_status = 'success';
|
|
2706
|
-
results.instance_info = {
|
|
2707
|
-
instance_url: systemInfo.request?.host || 'unknown',
|
|
2708
|
-
version: systemInfo.result?.[0]?.sys_created_on ? 'accessible' : 'unknown'
|
|
2709
|
-
};
|
|
2710
|
-
|
|
2711
|
-
if (include_performance) {
|
|
2712
|
-
results.performance_metrics.basic_response_time_ms = basicResponseTime;
|
|
2713
|
-
}
|
|
2714
|
-
|
|
2715
|
-
// Full test - read permissions
|
|
2716
|
-
if (test_level === 'full' || test_level === 'permissions') {
|
|
2717
|
-
const testStartTime = Date.now();
|
|
2718
|
-
const tableTest = await this.client.get('/api/now/table/sys_user', { sysparm_limit: 1 });
|
|
2719
|
-
const readResponseTime = Date.now() - testStartTime;
|
|
2720
|
-
|
|
2721
|
-
results.permissions_status = tableTest.result ? 'read_success' : 'read_limited';
|
|
2722
|
-
|
|
2723
|
-
if (include_performance) {
|
|
2724
|
-
results.performance_metrics.read_test_response_time_ms = readResponseTime;
|
|
2725
|
-
}
|
|
2726
|
-
}
|
|
2727
|
-
|
|
2728
|
-
// Permissions test - write permissions
|
|
2729
|
-
if (test_level === 'permissions') {
|
|
2730
|
-
try {
|
|
2731
|
-
const writeStartTime = Date.now();
|
|
2732
|
-
// Test write by creating a test record in a safe table
|
|
2733
|
-
const testRecord = await this.client.post('/api/now/table/sys_update_set', {
|
|
2734
|
-
name: `Test Connection ${Date.now()}`,
|
|
2735
|
-
description: 'Temporary test record for connection validation - safe to delete'
|
|
2736
|
-
});
|
|
2737
|
-
|
|
2738
|
-
if (testRecord.result?.sys_id) {
|
|
2739
|
-
// Immediately delete the test record
|
|
2740
|
-
await this.client.delete(`/api/now/table/sys_update_set/${testRecord.result.sys_id}`);
|
|
2741
|
-
results.permissions_status = 'write_success';
|
|
2742
|
-
}
|
|
2743
|
-
|
|
2744
|
-
const writeResponseTime = Date.now() - writeStartTime;
|
|
2745
|
-
if (include_performance) {
|
|
2746
|
-
results.performance_metrics.write_test_response_time_ms = writeResponseTime;
|
|
2747
|
-
}
|
|
2748
|
-
} catch (writeError) {
|
|
2749
|
-
results.permissions_status = 'write_failed';
|
|
2750
|
-
results.write_error = writeError instanceof Error ? writeError.message : 'Unknown write error';
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
|
|
2754
|
-
return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
|
|
2755
|
-
|
|
2756
|
-
} catch (error) {
|
|
2757
|
-
results.connection_status = 'failed';
|
|
2758
|
-
results.error = error instanceof Error ? error.message : 'Unknown connection error';
|
|
2759
|
-
|
|
2760
|
-
return { content: [{
|
|
2761
|
-
type: 'text',
|
|
2762
|
-
text: `❌ Live Connection Validation Failed:\n${JSON.stringify(results, null, 2)}`
|
|
2763
|
-
}] };
|
|
2764
|
-
}
|
|
2765
|
-
} private async batchDeploymentValidator(args: any) {
|
|
2766
|
-
const { artifacts, validation_level = 'full', check_conflicts = true } = args;
|
|
2767
|
-
|
|
2768
|
-
try {
|
|
2769
|
-
const validationResults: any = {
|
|
2770
|
-
validation_timestamp: new Date().toISOString(),
|
|
2771
|
-
validation_level,
|
|
2772
|
-
total_artifacts: artifacts.length,
|
|
2773
|
-
validation_summary: {
|
|
2774
|
-
passed: 0,
|
|
2775
|
-
failed: 0,
|
|
2776
|
-
warnings: 0
|
|
2777
|
-
},
|
|
2778
|
-
artifact_results: [],
|
|
2779
|
-
conflicts_detected: [],
|
|
2780
|
-
deployment_recommendations: []
|
|
2781
|
-
};
|
|
2782
|
-
|
|
2783
|
-
// Validate each artifact
|
|
2784
|
-
for (const artifact of artifacts) {
|
|
2785
|
-
const artifactResult: any = {
|
|
2786
|
-
sys_id: artifact.sys_id,
|
|
2787
|
-
table: artifact.table,
|
|
2788
|
-
type: artifact.type,
|
|
2789
|
-
validation_status: 'pending',
|
|
2790
|
-
issues: [],
|
|
2791
|
-
recommendations: []
|
|
2792
|
-
};
|
|
2793
|
-
|
|
2794
|
-
try {
|
|
2795
|
-
// Fetch artifact details
|
|
2796
|
-
const artifactData = await this.client.get(`/api/now/table/${artifact.table}/${artifact.sys_id}`);
|
|
2797
|
-
|
|
2798
|
-
if (!artifactData.result) {
|
|
2799
|
-
artifactResult.validation_status = 'failed';
|
|
2800
|
-
artifactResult.issues.push('Artifact not found in ServiceNow');
|
|
2801
|
-
validationResults.validation_summary.failed++;
|
|
2802
|
-
continue;
|
|
2803
|
-
}
|
|
2804
|
-
|
|
2805
|
-
artifactResult.artifact_data = artifactData.result;
|
|
2806
|
-
|
|
2807
|
-
// Syntax validation
|
|
2808
|
-
if (validation_level === 'syntax' || validation_level === 'full') {
|
|
2809
|
-
const syntaxIssues = await this.validateArtifactSyntax(artifactData.result, artifact.type);
|
|
2810
|
-
artifactResult.issues.push(...syntaxIssues);
|
|
2811
|
-
}
|
|
2812
|
-
|
|
2813
|
-
// Dependency validation
|
|
2814
|
-
if (validation_level === 'dependencies' || validation_level === 'full') {
|
|
2815
|
-
const dependencyIssues = await this.validateArtifactDependencies(artifactData.result, artifact.type);
|
|
2816
|
-
artifactResult.issues.push(...dependencyIssues);
|
|
2817
|
-
}
|
|
2818
|
-
|
|
2819
|
-
// Determine validation status
|
|
2820
|
-
if (artifactResult.issues.length === 0) {
|
|
2821
|
-
artifactResult.validation_status = 'passed';
|
|
2822
|
-
validationResults.validation_summary.passed++;
|
|
2823
|
-
} else if (artifactResult.issues.some((issue: any) => issue.severity === 'error')) {
|
|
2824
|
-
artifactResult.validation_status = 'failed';
|
|
2825
|
-
validationResults.validation_summary.failed++;
|
|
2826
|
-
} else {
|
|
2827
|
-
artifactResult.validation_status = 'warning';
|
|
2828
|
-
validationResults.validation_summary.warnings++;
|
|
2829
|
-
}
|
|
2830
|
-
|
|
2831
|
-
} catch (error) {
|
|
2832
|
-
artifactResult.validation_status = 'failed';
|
|
2833
|
-
artifactResult.issues.push({
|
|
2834
|
-
severity: 'error',
|
|
2835
|
-
message: `Validation error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
2836
|
-
});
|
|
2837
|
-
validationResults.validation_summary.failed++;
|
|
2838
|
-
}
|
|
2839
|
-
|
|
2840
|
-
validationResults.artifact_results.push(artifactResult);
|
|
2841
|
-
}
|
|
2842
|
-
|
|
2843
|
-
// Check for conflicts between artifacts
|
|
2844
|
-
if (check_conflicts) {
|
|
2845
|
-
validationResults.conflicts_detected = await this.detectArtifactConflicts(artifacts);
|
|
2846
|
-
}
|
|
2847
|
-
|
|
2848
|
-
// Generate deployment recommendations
|
|
2849
|
-
validationResults.deployment_recommendations = this.generateDeploymentRecommendations(validationResults);
|
|
2850
|
-
|
|
2851
|
-
return { content: [{ type: 'text', text: JSON.stringify(validationResults, null, 2) }] };
|
|
2852
|
-
|
|
2853
|
-
} catch (error) {
|
|
2854
|
-
return { content: [{
|
|
2855
|
-
type: 'text',
|
|
2856
|
-
text: `❌ Batch Deployment Validation Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
2857
|
-
}] };
|
|
2858
|
-
}
|
|
2859
|
-
}
|
|
2860
|
-
|
|
2861
|
-
private async deploymentRollbackManager(args: any) {
|
|
2862
|
-
const { update_set_id, action, rollback_reason, create_backup = true } = args;
|
|
2863
|
-
|
|
2864
|
-
try {
|
|
2865
|
-
const rollbackResults: any = {
|
|
2866
|
-
update_set_id,
|
|
2867
|
-
action,
|
|
2868
|
-
timestamp: new Date().toISOString(),
|
|
2869
|
-
status: 'pending',
|
|
2870
|
-
backup_created: false,
|
|
2871
|
-
rollback_steps: []
|
|
2872
|
-
};
|
|
2873
|
-
|
|
2874
|
-
// Get update set details
|
|
2875
|
-
const updateSet = await this.client.get(`/api/now/table/sys_update_set/${update_set_id}`);
|
|
2876
|
-
|
|
2877
|
-
if (!updateSet.result) {
|
|
2878
|
-
throw new Error(`Update Set not found: ${update_set_id}`);
|
|
2879
|
-
}
|
|
2880
|
-
|
|
2881
|
-
rollbackResults.update_set_info = updateSet.result;
|
|
2882
|
-
|
|
2883
|
-
switch (action) {
|
|
2884
|
-
case 'monitor':
|
|
2885
|
-
rollbackResults.status = 'monitoring';
|
|
2886
|
-
rollbackResults.monitoring_data = await this.monitorUpdateSetDeployment(update_set_id);
|
|
2887
|
-
break;
|
|
2888
|
-
|
|
2889
|
-
case 'validate_rollback':
|
|
2890
|
-
rollbackResults.status = 'validation_complete';
|
|
2891
|
-
rollbackResults.rollback_feasibility = await this.validateRollbackFeasibility(update_set_id);
|
|
2892
|
-
break;
|
|
2893
|
-
|
|
2894
|
-
case 'rollback':
|
|
2895
|
-
if (!rollback_reason) {
|
|
2896
|
-
throw new Error('Rollback reason is required for rollback action');
|
|
2897
|
-
}
|
|
2898
|
-
|
|
2899
|
-
rollbackResults.rollback_reason = rollback_reason;
|
|
2900
|
-
|
|
2901
|
-
// Create backup if requested
|
|
2902
|
-
if (create_backup) {
|
|
2903
|
-
const backupResult = await this.createUpdateSetBackup(update_set_id);
|
|
2904
|
-
rollbackResults.backup_created = true;
|
|
2905
|
-
rollbackResults.backup_info = backupResult;
|
|
2906
|
-
}
|
|
2907
|
-
|
|
2908
|
-
// Perform rollback
|
|
2909
|
-
const rollbackSteps = await this.performUpdateSetRollback(update_set_id, rollback_reason);
|
|
2910
|
-
rollbackResults.rollback_steps = rollbackSteps;
|
|
2911
|
-
rollbackResults.status = 'rollback_complete';
|
|
2912
|
-
break;
|
|
2913
|
-
|
|
2914
|
-
default:
|
|
2915
|
-
throw new Error(`Unknown action: ${action}`);
|
|
2916
|
-
}
|
|
2917
|
-
|
|
2918
|
-
return { content: [{ type: 'text', text: JSON.stringify(rollbackResults, null, 2) }] };
|
|
2919
|
-
|
|
2920
|
-
} catch (error) {
|
|
2921
|
-
return { content: [{
|
|
2922
|
-
type: 'text',
|
|
2923
|
-
text: `❌ Rollback Management Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
2924
|
-
}] };
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
|
|
2928
|
-
// Helper methods for the new functionality
|
|
2929
|
-
private calculateFlowComplexity(steps: any[]): number {
|
|
2930
|
-
// Simple complexity calculation based on step count and script presence
|
|
2931
|
-
let complexity = steps.length;
|
|
2932
|
-
const scriptSteps = steps.filter(step => step.script && step.script.trim().length > 0);
|
|
2933
|
-
complexity += scriptSteps.length * 2; // Script steps are more complex
|
|
2934
|
-
return Math.min(complexity, 100); // Cap at 100
|
|
2935
|
-
}
|
|
2936
|
-
|
|
2937
|
-
private async validateArtifactSyntax(artifact: any, type: string): Promise<any[]> {
|
|
2938
|
-
const issues = [];
|
|
2939
|
-
|
|
2940
|
-
// Basic syntax validation based on artifact type
|
|
2941
|
-
if (type === 'script' && artifact.script) {
|
|
2942
|
-
// Check for basic JavaScript syntax issues
|
|
2943
|
-
if (artifact.script.includes('gs.') && !artifact.script.includes('current')) {
|
|
2944
|
-
issues.push({
|
|
2945
|
-
severity: 'warning',
|
|
2946
|
-
message: 'Script uses gs. methods but may be missing current record context'
|
|
2947
|
-
});
|
|
2948
|
-
}
|
|
2949
|
-
}
|
|
2950
|
-
|
|
2951
|
-
return issues;
|
|
2952
|
-
}
|
|
2953
|
-
|
|
2954
|
-
private async validateArtifactDependencies(artifact: any, type: string): Promise<any[]> {
|
|
2955
|
-
const issues = [];
|
|
2956
|
-
|
|
2957
|
-
// Check for common dependency issues
|
|
2958
|
-
if (artifact.sys_scope && artifact.sys_scope !== 'global') {
|
|
2959
|
-
issues.push({
|
|
2960
|
-
severity: 'info',
|
|
2961
|
-
message: `Artifact is in scope: ${artifact.sys_scope}`
|
|
2962
|
-
});
|
|
2963
|
-
}
|
|
2964
|
-
|
|
2965
|
-
return issues;
|
|
2966
|
-
}
|
|
2967
|
-
|
|
2968
|
-
private async detectArtifactConflicts(artifacts: any[]): Promise<any[]> {
|
|
2969
|
-
const conflicts = [];
|
|
2970
|
-
|
|
2971
|
-
// Check for naming conflicts
|
|
2972
|
-
const names = artifacts.map(a => a.name).filter(Boolean);
|
|
2973
|
-
const duplicateNames = names.filter((name, index) => names.indexOf(name) !== index);
|
|
2974
|
-
|
|
2975
|
-
for (const name of duplicateNames) {
|
|
2976
|
-
conflicts.push({
|
|
2977
|
-
type: 'naming_conflict',
|
|
2978
|
-
message: `Multiple artifacts with name: ${name}`,
|
|
2979
|
-
severity: 'warning'
|
|
2980
|
-
});
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
|
-
return conflicts;
|
|
2984
|
-
}
|
|
2985
|
-
|
|
2986
|
-
private generateDeploymentRecommendations(validationResults: any): string[] {
|
|
2987
|
-
const recommendations = [];
|
|
2988
|
-
|
|
2989
|
-
if (validationResults.validation_summary.failed > 0) {
|
|
2990
|
-
recommendations.push('Fix all failed validations before deployment');
|
|
2991
|
-
}
|
|
2992
|
-
|
|
2993
|
-
if (validationResults.validation_summary.warnings > 0) {
|
|
2994
|
-
recommendations.push('Review and address warnings for optimal deployment');
|
|
2995
|
-
}
|
|
2996
|
-
|
|
2997
|
-
if (validationResults.conflicts_detected.length > 0) {
|
|
2998
|
-
recommendations.push('Resolve conflicts between artifacts');
|
|
2999
|
-
}
|
|
3000
|
-
|
|
3001
|
-
recommendations.push('Create backup before deployment');
|
|
3002
|
-
recommendations.push('Test in non-production environment first');
|
|
3003
|
-
|
|
3004
|
-
return recommendations;
|
|
3005
|
-
}
|
|
3006
|
-
|
|
3007
|
-
private async monitorUpdateSetDeployment(updateSetId: string): Promise<any> {
|
|
3008
|
-
// Get update set status and related updates
|
|
3009
|
-
const updates = await this.client.get('/api/now/table/sys_update_xml', {
|
|
3010
|
-
sysparm_query: `update_set=${updateSetId}`,
|
|
3011
|
-
sysparm_fields: 'name,type,state,sys_created_on'
|
|
3012
|
-
});
|
|
3013
|
-
|
|
3014
|
-
return {
|
|
3015
|
-
total_updates: updates.result?.length || 0,
|
|
3016
|
-
updates_by_state: this.groupUpdatesByState(updates.result || []),
|
|
3017
|
-
last_activity: updates.result?.[0]?.sys_created_on || null
|
|
3018
|
-
};
|
|
3019
|
-
}
|
|
3020
|
-
|
|
3021
|
-
private async validateRollbackFeasibility(updateSetId: string): Promise<any> {
|
|
3022
|
-
return {
|
|
3023
|
-
feasible: true,
|
|
3024
|
-
considerations: [
|
|
3025
|
-
'Rollback will revert all changes in this update set',
|
|
3026
|
-
'Dependent changes may be affected',
|
|
3027
|
-
'Data created by flows/scripts may not be automatically removed'
|
|
3028
|
-
],
|
|
3029
|
-
estimated_impact: 'medium'
|
|
3030
|
-
};
|
|
3031
|
-
}
|
|
3032
|
-
|
|
3033
|
-
private async createUpdateSetBackup(updateSetId: string): Promise<any> {
|
|
3034
|
-
return {
|
|
3035
|
-
backup_id: `backup_${updateSetId}_${Date.now()}`,
|
|
3036
|
-
backup_timestamp: new Date().toISOString(),
|
|
3037
|
-
status: 'created'
|
|
3038
|
-
};
|
|
3039
|
-
}
|
|
3040
|
-
|
|
3041
|
-
private async performUpdateSetRollback(updateSetId: string, reason: string): Promise<any[]> {
|
|
3042
|
-
return [
|
|
3043
|
-
'Identified changes in update set',
|
|
3044
|
-
'Created rollback plan',
|
|
3045
|
-
'Would execute rollback (simulation mode)',
|
|
3046
|
-
`Rollback reason: ${reason}`
|
|
3047
|
-
];
|
|
3048
|
-
}
|
|
3049
|
-
|
|
3050
|
-
private groupUpdatesByState(updates: any[]): any {
|
|
3051
|
-
const grouped: any = {};
|
|
3052
|
-
for (const update of updates) {
|
|
3053
|
-
const state = update.state || 'unknown';
|
|
3054
|
-
grouped[state] = (grouped[state] || 0) + 1;
|
|
3055
|
-
}
|
|
3056
|
-
return grouped;
|
|
3057
|
-
}
|
|
3058
|
-
|
|
3059
|
-
private async escalatePermissions(args: any) {
|
|
3060
|
-
const { required_roles, duration = 'session', reason, workflow_context } = args;
|
|
3061
|
-
|
|
3062
|
-
try {
|
|
3063
|
-
const escalationResults: any = {
|
|
3064
|
-
requested_roles: required_roles,
|
|
3065
|
-
duration,
|
|
3066
|
-
reason,
|
|
3067
|
-
workflow_context,
|
|
3068
|
-
timestamp: new Date().toISOString(),
|
|
3069
|
-
escalation_status: 'pending',
|
|
3070
|
-
current_permissions: {},
|
|
3071
|
-
required_actions: []
|
|
3072
|
-
};
|
|
3073
|
-
|
|
3074
|
-
// Get current user info
|
|
3075
|
-
const whoAmI = await this.client.get('/api/now/table/sys_user', {
|
|
3076
|
-
sysparm_query: 'user_name=admin', // Try admin user first
|
|
3077
|
-
sysparm_fields: 'sys_id,user_name,name,email',
|
|
3078
|
-
sysparm_limit: 1
|
|
3079
|
-
});
|
|
3080
|
-
|
|
3081
|
-
const currentUser = whoAmI.result?.[0];
|
|
3082
|
-
if (!currentUser) {
|
|
3083
|
-
return { content: [{
|
|
3084
|
-
type: 'text',
|
|
3085
|
-
text: '❌ Could not identify current user. Please ensure you are logged in to ServiceNow.'
|
|
3086
|
-
}] };
|
|
3087
|
-
}
|
|
3088
|
-
|
|
3089
|
-
// Get user roles
|
|
3090
|
-
const userRoles = await this.client.get('/api/now/table/sys_user_has_role', {
|
|
3091
|
-
sysparm_query: `user=${currentUser.sys_id}`,
|
|
3092
|
-
sysparm_fields: 'role.name,role.description,inherited'
|
|
3093
|
-
});
|
|
3094
|
-
|
|
3095
|
-
const currentRoles = userRoles.result?.map((r: any) => r.role?.name).filter(Boolean) || [];
|
|
3096
|
-
const missingRoles = required_roles.filter((role: string) => !currentRoles.includes(role));
|
|
3097
|
-
|
|
3098
|
-
// Get instance URL
|
|
3099
|
-
const instanceUrl = process.env.SNOW_INSTANCE ?
|
|
3100
|
-
`https://${process.env.SNOW_INSTANCE.replace(/\/$/, '')}.service-now.com` :
|
|
3101
|
-
'https://your-instance.service-now.com';
|
|
3102
|
-
|
|
3103
|
-
if (missingRoles.length === 0) {
|
|
3104
|
-
return { content: [{
|
|
3105
|
-
type: 'text',
|
|
3106
|
-
text: `✅ **Permission Check Passed**\n\nYou already have all required roles:\n${required_roles.map((r: string) => `- ✓ ${r}`).join('\n')}\n\nNo escalation needed!`
|
|
3107
|
-
}] };
|
|
3108
|
-
}
|
|
3109
|
-
|
|
3110
|
-
// Build actionable response
|
|
3111
|
-
let response = `🔐 **Permission Escalation Required**\n\n`;
|
|
3112
|
-
response += `**Current User:** ${currentUser.name} (${currentUser.user_name})\n`;
|
|
3113
|
-
response += `**Current Roles:** ${currentRoles.length > 0 ? currentRoles.join(', ') : 'None'}\n`;
|
|
3114
|
-
response += `**Missing Roles:** ${missingRoles.join(', ')}\n`;
|
|
3115
|
-
response += `**Reason:** ${reason}\n`;
|
|
3116
|
-
response += `**Duration:** ${duration}\n\n`;
|
|
3117
|
-
|
|
3118
|
-
response += `## 🎯 Required Actions:\n\n`;
|
|
3119
|
-
|
|
3120
|
-
// Provide specific instructions for each missing role
|
|
3121
|
-
for (const role of missingRoles) {
|
|
3122
|
-
response += `### ${role} Role\n`;
|
|
3123
|
-
|
|
3124
|
-
switch (role) {
|
|
3125
|
-
case 'admin':
|
|
3126
|
-
response += `The **admin** role provides:\n`;
|
|
3127
|
-
response += `- Global scope access for creating widgets, flows, and applications\n`;
|
|
3128
|
-
response += `- Ability to modify system tables and configurations\n`;
|
|
3129
|
-
response += `- Access to all ServiceNow modules and features\n\n`;
|
|
3130
|
-
response += `**How to obtain:**\n`;
|
|
3131
|
-
response += `1. Contact your ServiceNow administrator\n`;
|
|
3132
|
-
response += `2. Or if you have admin access: [Click here to manage user roles](${instanceUrl}/sys_user.do?sys_id=${currentUser.sys_id})\n`;
|
|
3133
|
-
response += `3. In the "Roles" related list, click "Edit" and add "admin"\n\n`;
|
|
3134
|
-
break;
|
|
3135
|
-
|
|
3136
|
-
case 'app_creator':
|
|
3137
|
-
response += `The **app_creator** role provides:\n`;
|
|
3138
|
-
response += `- Create custom applications and scoped apps\n`;
|
|
3139
|
-
response += `- Design application modules and menus\n`;
|
|
3140
|
-
response += `- Manage application artifacts\n\n`;
|
|
3141
|
-
response += `**How to obtain:**\n`;
|
|
3142
|
-
response += `1. Request from ServiceNow administrator\n`;
|
|
3143
|
-
response += `2. Or navigate to: [User Administration > Users](${instanceUrl}/sys_user_list.do)\n`;
|
|
3144
|
-
response += `3. Find your user record and add "app_creator" role\n\n`;
|
|
3145
|
-
break;
|
|
3146
|
-
|
|
3147
|
-
case 'system_administrator':
|
|
3148
|
-
response += `The **system_administrator** role provides:\n`;
|
|
3149
|
-
response += `- Full system access and configuration\n`;
|
|
3150
|
-
response += `- Advanced scripting and development capabilities\n`;
|
|
3151
|
-
response += `- Access to all system properties and settings\n\n`;
|
|
3152
|
-
response += `**How to obtain:**\n`;
|
|
3153
|
-
response += `1. This is a highly privileged role - contact system admin\n`;
|
|
3154
|
-
response += `2. Requires approval from ServiceNow instance owner\n\n`;
|
|
3155
|
-
break;
|
|
3156
|
-
|
|
3157
|
-
case 'global_admin':
|
|
3158
|
-
response += `The **global_admin** role provides:\n`;
|
|
3159
|
-
response += `- Cross-scope application access\n`;
|
|
3160
|
-
response += `- Global artifact creation and management\n`;
|
|
3161
|
-
response += `- Override scope restrictions\n\n`;
|
|
3162
|
-
response += `**How to obtain:**\n`;
|
|
3163
|
-
response += `1. Contact ServiceNow administrator\n`;
|
|
3164
|
-
response += `2. May require business justification\n\n`;
|
|
3165
|
-
break;
|
|
3166
|
-
|
|
3167
|
-
default:
|
|
3168
|
-
response += `The **${role}** role is required for this operation.\n\n`;
|
|
3169
|
-
response += `**How to obtain:**\n`;
|
|
3170
|
-
response += `1. Contact your ServiceNow administrator\n`;
|
|
3171
|
-
response += `2. Request temporary access for: "${reason}"\n\n`;
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
|
|
3175
|
-
response += `## 💡 Alternative Solutions:\n\n`;
|
|
3176
|
-
response += `1. **Use a development instance** where you have admin access\n`;
|
|
3177
|
-
response += `2. **Request a personal developer instance** from [developer.servicenow.com](https://developer.servicenow.com)\n`;
|
|
3178
|
-
response += `3. **Work with a team member** who has the required permissions\n`;
|
|
3179
|
-
response += `4. **Use Update Sets** to package changes for deployment by an admin\n\n`;
|
|
3180
|
-
|
|
3181
|
-
response += `## 📋 Template Request for Admin:\n\n`;
|
|
3182
|
-
response += `\`\`\`\n`;
|
|
3183
|
-
response += `Subject: Temporary Permission Request - ${reason}\n\n`;
|
|
3184
|
-
response += `Hi Admin,\n\n`;
|
|
3185
|
-
response += `I need temporary access to the following roles for development:\n`;
|
|
3186
|
-
response += `- Roles needed: ${missingRoles.join(', ')}\n`;
|
|
3187
|
-
response += `- Reason: ${reason}\n`;
|
|
3188
|
-
response += `- Duration: ${duration}\n`;
|
|
3189
|
-
response += `- Context: ${workflow_context || 'ServiceNow multi-agent development'}\n\n`;
|
|
3190
|
-
response += `These permissions can be revoked after the ${duration === 'session' ? 'current session' : duration}.\n\n`;
|
|
3191
|
-
response += `Thank you!\n`;
|
|
3192
|
-
response += `\`\`\``;
|
|
3193
|
-
|
|
3194
|
-
return { content: [{ type: 'text', text: response }] };
|
|
3195
|
-
|
|
3196
|
-
} catch (error) {
|
|
3197
|
-
return { content: [{
|
|
3198
|
-
type: 'text',
|
|
3199
|
-
text: `❌ Permission Escalation Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
3200
|
-
}] };
|
|
3201
|
-
}
|
|
3202
|
-
}
|
|
3203
|
-
|
|
3204
|
-
private async analyzeRequirements(args: any) {
|
|
3205
|
-
const {
|
|
3206
|
-
objective,
|
|
3207
|
-
auto_discover_dependencies = true,
|
|
3208
|
-
suggest_existing_components = true,
|
|
3209
|
-
create_dependency_map = true,
|
|
3210
|
-
scope_preference = 'auto'
|
|
3211
|
-
} = args;
|
|
3212
|
-
|
|
3213
|
-
try {
|
|
3214
|
-
const _analysis: any = {
|
|
3215
|
-
objective,
|
|
3216
|
-
analysis_timestamp: new Date().toISOString(),
|
|
3217
|
-
discovered_requirements: [],
|
|
3218
|
-
existing_components: [],
|
|
3219
|
-
dependency_map: {},
|
|
3220
|
-
deployment_plan: {},
|
|
3221
|
-
recommendations: []
|
|
3222
|
-
};
|
|
3223
|
-
|
|
3224
|
-
// Parse objective to identify required components
|
|
3225
|
-
const lowerObjective = objective.toLowerCase();
|
|
3226
|
-
const requiredComponents = [];
|
|
3227
|
-
|
|
3228
|
-
// Intelligent component detection
|
|
3229
|
-
if (lowerObjective.includes('provision') || lowerObjective.includes('user')) {
|
|
3230
|
-
requiredComponents.push('user_management', 'provisioning_workflow');
|
|
3231
|
-
}
|
|
3232
|
-
if (lowerObjective.includes('iphone') || lowerObjective.includes('mobile') || lowerObjective.includes('device')) {
|
|
3233
|
-
requiredComponents.push('mobile_device_management', 'device_catalog');
|
|
3234
|
-
}
|
|
3235
|
-
if (lowerObjective.includes('approval') || lowerObjective.includes('request')) {
|
|
3236
|
-
requiredComponents.push('approval_workflow', 'request_management');
|
|
3237
|
-
}
|
|
3238
|
-
if (lowerObjective.includes('notification') || lowerObjective.includes('email')) {
|
|
3239
|
-
requiredComponents.push('notification_system', 'email_templates');
|
|
3240
|
-
}
|
|
3241
|
-
|
|
3242
|
-
_analysis.discovered_requirements = requiredComponents;
|
|
3243
|
-
|
|
3244
|
-
// Search for existing components if enabled
|
|
3245
|
-
if (suggest_existing_components && requiredComponents.length > 0) {
|
|
3246
|
-
for (const component of requiredComponents) {
|
|
3247
|
-
try {
|
|
3248
|
-
// Search flows
|
|
3249
|
-
const flows = await this.client.get('/api/now/table/wf_workflow', {
|
|
3250
|
-
sysparm_query: `name CONTAINS ${component} OR description CONTAINS ${component}`,
|
|
3251
|
-
sysparm_limit: 5,
|
|
3252
|
-
sysparm_fields: 'sys_id,name,description,active'
|
|
3253
|
-
});
|
|
3254
|
-
|
|
3255
|
-
// Search widgets
|
|
3256
|
-
const widgets = await this.client.get('/api/now/table/sp_widget', {
|
|
3257
|
-
sysparm_query: `name CONTAINS ${component} OR title CONTAINS ${component}`,
|
|
3258
|
-
sysparm_limit: 5,
|
|
3259
|
-
sysparm_fields: 'sys_id,name,title,description'
|
|
3260
|
-
});
|
|
3261
|
-
|
|
3262
|
-
// Search scripts
|
|
3263
|
-
const scripts = await this.client.get('/api/now/table/sys_script_include', {
|
|
3264
|
-
sysparm_query: `name CONTAINS ${component} OR description CONTAINS ${component}`,
|
|
3265
|
-
sysparm_limit: 5,
|
|
3266
|
-
sysparm_fields: 'sys_id,name,description,active'
|
|
3267
|
-
});
|
|
3268
|
-
|
|
3269
|
-
if (flows.result?.length || widgets.result?.length || scripts.result?.length) {
|
|
3270
|
-
_analysis.existing_components.push({
|
|
3271
|
-
component_type: component,
|
|
3272
|
-
flows: flows.result || [],
|
|
3273
|
-
widgets: widgets.result || [],
|
|
3274
|
-
scripts: scripts.result || [],
|
|
3275
|
-
reuse_recommendation: 'Consider modifying existing components instead of creating new ones'
|
|
3276
|
-
});
|
|
3277
|
-
}
|
|
3278
|
-
} catch (error) {
|
|
3279
|
-
// Continue with other components if one fails
|
|
3280
|
-
}
|
|
3281
|
-
}
|
|
3282
|
-
}
|
|
3283
|
-
|
|
3284
|
-
// Create dependency map if enabled
|
|
3285
|
-
if (create_dependency_map) {
|
|
3286
|
-
_analysis.dependency_map = {
|
|
3287
|
-
primary_objective: objective,
|
|
3288
|
-
required_artifacts: requiredComponents.map(comp => ({
|
|
3289
|
-
name: comp,
|
|
3290
|
-
type: this.inferArtifactType(comp),
|
|
3291
|
-
dependencies: this.inferDependencies(comp),
|
|
3292
|
-
priority: this.inferPriority(comp, objective)
|
|
3293
|
-
})),
|
|
3294
|
-
deployment_order: this.calculateDeploymentOrder(requiredComponents)
|
|
3295
|
-
};
|
|
3296
|
-
}
|
|
3297
|
-
|
|
3298
|
-
// Generate deployment plan
|
|
3299
|
-
_analysis.deployment_plan = {
|
|
3300
|
-
scope_recommendation: this.recommendScope(scope_preference, requiredComponents),
|
|
3301
|
-
estimated_complexity: requiredComponents.length > 3 ? 'high' : requiredComponents.length > 1 ? 'medium' : 'low',
|
|
3302
|
-
estimated_time: `${requiredComponents.length * 2} hours`,
|
|
3303
|
-
required_permissions: this.inferRequiredPermissions(requiredComponents),
|
|
3304
|
-
update_set_strategy: 'Create single Update Set for all related artifacts'
|
|
3305
|
-
};
|
|
3306
|
-
|
|
3307
|
-
// Generate recommendations
|
|
3308
|
-
_analysis.recommendations = [
|
|
3309
|
-
`Development approach: ${_analysis.deployment_plan.estimated_complexity} complexity project`,
|
|
3310
|
-
`Recommended scope: ${_analysis.deployment_plan.scope_recommendation}`,
|
|
3311
|
-
`Consider reusing ${_analysis.existing_components.length} existing components found`,
|
|
3312
|
-
'Create comprehensive test scenarios for all workflows',
|
|
3313
|
-
'Implement proper error handling and notifications'
|
|
3314
|
-
];
|
|
3315
|
-
|
|
3316
|
-
if (_analysis.existing_components.length > 0) {
|
|
3317
|
-
_analysis.recommendations.push('📋 Review existing components before creating new artifacts');
|
|
3318
|
-
}
|
|
3319
|
-
|
|
3320
|
-
return { content: [{ type: 'text', text: JSON.stringify(_analysis, null, 2) }] };
|
|
3321
|
-
|
|
3322
|
-
} catch (error) {
|
|
3323
|
-
return { content: [{
|
|
3324
|
-
type: 'text',
|
|
3325
|
-
text: `❌ Requirements Analysis Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
3326
|
-
}] };
|
|
3327
|
-
}
|
|
3328
|
-
}
|
|
3329
|
-
|
|
3330
|
-
private async smartUpdateSet(args: any) {
|
|
3331
|
-
const {
|
|
3332
|
-
action,
|
|
3333
|
-
auto_track_related_artifacts = true,
|
|
3334
|
-
conflict_detection = true,
|
|
3335
|
-
dependency_validation = true,
|
|
3336
|
-
rollback_points = true,
|
|
3337
|
-
update_set_name
|
|
3338
|
-
} = args;
|
|
3339
|
-
|
|
3340
|
-
try {
|
|
3341
|
-
const results: any = {
|
|
3342
|
-
action,
|
|
3343
|
-
timestamp: new Date().toISOString(),
|
|
3344
|
-
update_set_info: {},
|
|
3345
|
-
tracked_artifacts: [],
|
|
3346
|
-
conflicts_detected: [],
|
|
3347
|
-
dependencies_validated: [],
|
|
3348
|
-
rollback_points_created: []
|
|
3349
|
-
};
|
|
3350
|
-
|
|
3351
|
-
switch (action) {
|
|
3352
|
-
case 'create':
|
|
3353
|
-
if (!update_set_name) {
|
|
3354
|
-
throw new Error('update_set_name is required for create action');
|
|
3355
|
-
}
|
|
3356
|
-
|
|
3357
|
-
const newUpdateSet = await this.client.post('/api/now/table/sys_update_set', {
|
|
3358
|
-
name: update_set_name,
|
|
3359
|
-
description: `Smart Update Set created by Snow-Flow at ${new Date().toISOString()}`,
|
|
3360
|
-
state: 'build'
|
|
3361
|
-
});
|
|
3362
|
-
|
|
3363
|
-
results.update_set_info = newUpdateSet.result;
|
|
3364
|
-
results.status = 'created';
|
|
3365
|
-
break;
|
|
3366
|
-
|
|
3367
|
-
case 'track':
|
|
3368
|
-
// Get current update set
|
|
3369
|
-
const currentUpdateSets = await this.client.get('/api/now/table/sys_update_set', {
|
|
3370
|
-
sysparm_query: 'state=build',
|
|
3371
|
-
sysparm_limit: 1,
|
|
3372
|
-
sysparm_fields: 'sys_id,name,state'
|
|
3373
|
-
});
|
|
3374
|
-
|
|
3375
|
-
if (currentUpdateSets.result?.[0]) {
|
|
3376
|
-
const updateSet = currentUpdateSets.result[0];
|
|
3377
|
-
|
|
3378
|
-
// Get all updates in this set
|
|
3379
|
-
const updates = await this.client.get('/api/now/table/sys_update_xml', {
|
|
3380
|
-
sysparm_query: `update_set=${updateSet.sys_id}`,
|
|
3381
|
-
sysparm_fields: 'name,type,target_name,action'
|
|
3382
|
-
});
|
|
3383
|
-
|
|
3384
|
-
results.update_set_info = updateSet;
|
|
3385
|
-
results.tracked_artifacts = updates.result || [];
|
|
3386
|
-
}
|
|
3387
|
-
break;
|
|
3388
|
-
|
|
3389
|
-
case 'validate':
|
|
3390
|
-
// Validate current update set
|
|
3391
|
-
const validationResults = await this.validateUpdateSetDependencies();
|
|
3392
|
-
results.dependencies_validated = validationResults;
|
|
3393
|
-
break;
|
|
3394
|
-
|
|
3395
|
-
case 'conflict_check':
|
|
3396
|
-
if (conflict_detection) {
|
|
3397
|
-
const conflicts = await this.detectUpdateSetConflicts();
|
|
3398
|
-
results.conflicts_detected = conflicts;
|
|
3399
|
-
}
|
|
3400
|
-
break;
|
|
3401
|
-
}
|
|
3402
|
-
|
|
3403
|
-
// Add rollback points if enabled
|
|
3404
|
-
if (rollback_points && results.update_set_info?.sys_id) {
|
|
3405
|
-
results.rollback_points_created.push({
|
|
3406
|
-
checkpoint_id: `checkpoint_${Date.now()}`,
|
|
3407
|
-
update_set_id: results.update_set_info.sys_id,
|
|
3408
|
-
created_at: new Date().toISOString(),
|
|
3409
|
-
restoration_method: 'Use deployment_rollback_manager tool'
|
|
3410
|
-
});
|
|
3411
|
-
}
|
|
3412
|
-
|
|
3413
|
-
return { content: [{ type: 'text', text: JSON.stringify(results, null, 2) }] };
|
|
3414
|
-
|
|
3415
|
-
} catch (error) {
|
|
3416
|
-
return { content: [{
|
|
3417
|
-
type: 'text',
|
|
3418
|
-
text: `❌ Smart Update Set Management Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
3419
|
-
}] };
|
|
3420
|
-
}
|
|
3421
|
-
}
|
|
3422
|
-
|
|
3423
|
-
private async orchestrateDevelopment(args: any) {
|
|
3424
|
-
const {
|
|
3425
|
-
objective,
|
|
3426
|
-
auto_spawn_agents = true,
|
|
3427
|
-
shared_memory = true,
|
|
3428
|
-
parallel_execution = true,
|
|
3429
|
-
progress_monitoring = true,
|
|
3430
|
-
auto_permissions = false,
|
|
3431
|
-
smart_discovery = true,
|
|
3432
|
-
live_testing = true,
|
|
3433
|
-
auto_deploy = false
|
|
3434
|
-
} = args;
|
|
3435
|
-
|
|
3436
|
-
try {
|
|
3437
|
-
const orchestration: any = {
|
|
3438
|
-
objective,
|
|
3439
|
-
orchestration_id: `orchestration_${Date.now()}`,
|
|
3440
|
-
started_at: new Date().toISOString(),
|
|
3441
|
-
status: 'initializing',
|
|
3442
|
-
mode: auto_deploy ? '🚀 DEPLOYMENT MODE (WILL CREATE REAL ARTIFACTS)' : '📋 PLANNING MODE (ANALYSIS ONLY)',
|
|
3443
|
-
warning: auto_deploy ? '⚠️ WARNING: This will create REAL artifacts in ServiceNow!' : '✅ SAFE: This is planning only, no artifacts will be created',
|
|
3444
|
-
agents_spawned: [],
|
|
3445
|
-
shared_memory_enabled: shared_memory,
|
|
3446
|
-
progress_monitor: {},
|
|
3447
|
-
execution_plan: {},
|
|
3448
|
-
actual_deployments: []
|
|
3449
|
-
};
|
|
3450
|
-
|
|
3451
|
-
// Step 1: Analyze requirements
|
|
3452
|
-
if (smart_discovery) {
|
|
3453
|
-
const requirementAnalysis = await this.analyzeRequirements({
|
|
3454
|
-
objective,
|
|
3455
|
-
auto_discover_dependencies: true,
|
|
3456
|
-
suggest_existing_components: true,
|
|
3457
|
-
create_dependency_map: true
|
|
3458
|
-
});
|
|
3459
|
-
orchestration.requirement__analysis = requirementAnalysis;
|
|
3460
|
-
}
|
|
3461
|
-
|
|
3462
|
-
// Step 2: Check permissions
|
|
3463
|
-
if (auto_permissions) {
|
|
3464
|
-
const permissionCheck = await this.escalatePermissions({
|
|
3465
|
-
required_roles: ['admin', 'app_creator'],
|
|
3466
|
-
duration: 'workflow',
|
|
3467
|
-
reason: `Orchestrated development: ${objective}`
|
|
3468
|
-
});
|
|
3469
|
-
orchestration.permission_check = permissionCheck;
|
|
3470
|
-
}
|
|
3471
|
-
|
|
3472
|
-
// Step 3: Create smart update set
|
|
3473
|
-
const updateSetResult = await this.smartUpdateSet({
|
|
3474
|
-
action: 'create',
|
|
3475
|
-
update_set_name: `Orchestrated Development: ${objective.substring(0, 50)}`,
|
|
3476
|
-
auto_track_related_artifacts: true,
|
|
3477
|
-
conflict_detection: true,
|
|
3478
|
-
rollback_points: true
|
|
3479
|
-
});
|
|
3480
|
-
orchestration.update_set = updateSetResult;
|
|
3481
|
-
|
|
3482
|
-
// Step 4: Generate execution plan
|
|
3483
|
-
orchestration.execution_plan = {
|
|
3484
|
-
phases: [
|
|
3485
|
-
{
|
|
3486
|
-
phase: 1,
|
|
3487
|
-
name: 'Discovery & Analysis',
|
|
3488
|
-
status: 'completed',
|
|
3489
|
-
duration_estimate: '10 minutes'
|
|
3490
|
-
},
|
|
3491
|
-
{
|
|
3492
|
-
phase: 2,
|
|
3493
|
-
name: 'Artifact Development',
|
|
3494
|
-
status: 'pending',
|
|
3495
|
-
duration_estimate: '30-60 minutes',
|
|
3496
|
-
parallel_execution_enabled: parallel_execution
|
|
3497
|
-
},
|
|
3498
|
-
{
|
|
3499
|
-
phase: 3,
|
|
3500
|
-
name: 'Testing & Validation',
|
|
3501
|
-
status: 'pending',
|
|
3502
|
-
duration_estimate: '15 minutes',
|
|
3503
|
-
live_testing_enabled: live_testing
|
|
3504
|
-
},
|
|
3505
|
-
{
|
|
3506
|
-
phase: 4,
|
|
3507
|
-
name: 'Deployment',
|
|
3508
|
-
status: 'pending',
|
|
3509
|
-
duration_estimate: '10 minutes',
|
|
3510
|
-
auto_deploy_enabled: auto_deploy
|
|
3511
|
-
}
|
|
3512
|
-
],
|
|
3513
|
-
total_estimated_time: '65-95 minutes'
|
|
3514
|
-
};
|
|
3515
|
-
|
|
3516
|
-
// Step 5: Set up progress monitoring
|
|
3517
|
-
if (progress_monitoring) {
|
|
3518
|
-
orchestration.progress_monitor = {
|
|
3519
|
-
enabled: true,
|
|
3520
|
-
monitoring_interval: '30 seconds',
|
|
3521
|
-
progress_tracking: 'real-time',
|
|
3522
|
-
completion_notifications: true
|
|
3523
|
-
};
|
|
3524
|
-
}
|
|
3525
|
-
|
|
3526
|
-
// Step 6: Execute actual deployment if auto_deploy is enabled
|
|
3527
|
-
if (auto_deploy) {
|
|
3528
|
-
orchestration.status = 'executing_deployment';
|
|
3529
|
-
|
|
3530
|
-
// Attempt to create real artifacts based on the objective
|
|
3531
|
-
try {
|
|
3532
|
-
const artifactAnalysis = await this.analyzeArtifactRequirements(objective);
|
|
3533
|
-
|
|
3534
|
-
for (const artifact of artifactAnalysis.recommended_artifacts) {
|
|
3535
|
-
try {
|
|
3536
|
-
let deploymentResult: any;
|
|
3537
|
-
|
|
3538
|
-
if (artifact.type === 'flow') {
|
|
3539
|
-
// Use flow composer to create real flow
|
|
3540
|
-
deploymentResult = await this.client.post('/api/now/table/sys_hub_flow', {
|
|
3541
|
-
name: artifact.name,
|
|
3542
|
-
description: artifact.description,
|
|
3543
|
-
active: true,
|
|
3544
|
-
type: 'flow'
|
|
3545
|
-
});
|
|
3546
|
-
} else if (artifact.type === 'widget') {
|
|
3547
|
-
// Create real widget
|
|
3548
|
-
deploymentResult = await this.client.post('/api/now/table/sp_widget', {
|
|
3549
|
-
name: artifact.name,
|
|
3550
|
-
title: artifact.name,
|
|
3551
|
-
description: artifact.description,
|
|
3552
|
-
template: artifact.template || '<div>{{data.message || "Widget created"}}</div>',
|
|
3553
|
-
css: artifact.css || '',
|
|
3554
|
-
client_script: artifact.client_script || '',
|
|
3555
|
-
server_script: artifact.server_script || '(function() { data.message = "Successfully deployed!"; })()',
|
|
3556
|
-
public: true,
|
|
3557
|
-
has_preview: true
|
|
3558
|
-
});
|
|
3559
|
-
}
|
|
3560
|
-
|
|
3561
|
-
if (deploymentResult?.result) {
|
|
3562
|
-
orchestration.actual_deployments.push({
|
|
3563
|
-
type: artifact.type,
|
|
3564
|
-
name: artifact.name,
|
|
3565
|
-
sys_id: deploymentResult.result.sys_id,
|
|
3566
|
-
status: 'deployed',
|
|
3567
|
-
url: `https://${process.env.SNOW_INSTANCE ? process.env.SNOW_INSTANCE.replace(/\/$/, '') : 'instance'}.service-now.com/nav_to.do?uri=${artifact.type === 'flow' ? 'sys_hub_flow' : 'sp_widget'}.do?sys_id=${deploymentResult.result.sys_id}`
|
|
3568
|
-
});
|
|
3569
|
-
}
|
|
3570
|
-
} catch (deployError) {
|
|
3571
|
-
orchestration.actual_deployments.push({
|
|
3572
|
-
type: artifact.type,
|
|
3573
|
-
name: artifact.name,
|
|
3574
|
-
status: 'failed',
|
|
3575
|
-
error: deployError instanceof Error ? deployError.message : String(deployError)
|
|
3576
|
-
});
|
|
3577
|
-
}
|
|
3578
|
-
}
|
|
3579
|
-
|
|
3580
|
-
orchestration.status = 'deployment_complete';
|
|
3581
|
-
orchestration.completed_at = new Date().toISOString();
|
|
3582
|
-
|
|
3583
|
-
} catch (error) {
|
|
3584
|
-
orchestration.status = 'deployment_failed';
|
|
3585
|
-
orchestration.deployment_error = error instanceof Error ? error.message : String(error);
|
|
3586
|
-
}
|
|
3587
|
-
} else {
|
|
3588
|
-
orchestration.status = 'planning_complete';
|
|
3589
|
-
orchestration.completed_at = new Date().toISOString();
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
// Set appropriate next steps based on mode
|
|
3593
|
-
if (auto_deploy) {
|
|
3594
|
-
if (orchestration.actual_deployments.length > 0) {
|
|
3595
|
-
orchestration.next_steps = [
|
|
3596
|
-
'✅ DEPLOYMENT COMPLETE: Real artifacts have been created in ServiceNow',
|
|
3597
|
-
`📊 Created ${orchestration.actual_deployments.filter((d: any) => d.status === 'deployed').length} artifacts successfully`,
|
|
3598
|
-
'Use snow_test_flow_execution or snow_widget_test to validate deployments',
|
|
3599
|
-
'Check ServiceNow instance to verify artifacts are working correctly'
|
|
3600
|
-
];
|
|
3601
|
-
} else {
|
|
3602
|
-
orchestration.next_steps = [
|
|
3603
|
-
'❌ DEPLOYMENT FAILED: No artifacts were created',
|
|
3604
|
-
'Check deployment errors in actual_deployments array',
|
|
3605
|
-
'Verify ServiceNow permissions and authentication',
|
|
3606
|
-
'Consider running in planning mode first (auto_deploy: false)'
|
|
3607
|
-
];
|
|
3608
|
-
}
|
|
3609
|
-
} else {
|
|
3610
|
-
orchestration.next_steps = [
|
|
3611
|
-
'📋 PLANNING COMPLETE: This was _analysis only - no artifacts created',
|
|
3612
|
-
'🚀 To deploy: Re-run with auto_deploy: true',
|
|
3613
|
-
'Execute specific MCP tools manually:',
|
|
3614
|
-
' - snow_create_flow for flow creation',
|
|
3615
|
-
' - snow_deploy_widget for widget deployment',
|
|
3616
|
-
' - snow_resilient_deployment for batch deployment'
|
|
3617
|
-
];
|
|
3618
|
-
}
|
|
3619
|
-
|
|
3620
|
-
return { content: [{ type: 'text', text: JSON.stringify(orchestration, null, 2) }] };
|
|
3621
|
-
|
|
3622
|
-
} catch (error) {
|
|
3623
|
-
return { content: [{
|
|
3624
|
-
type: 'text',
|
|
3625
|
-
text: `❌ Development Orchestration Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
3626
|
-
}] };
|
|
3627
|
-
}
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
private async resilientDeployment(args: any) {
|
|
3631
|
-
const {
|
|
3632
|
-
artifacts,
|
|
3633
|
-
retry_on_failure = true,
|
|
3634
|
-
fallback_strategies = ['global_scope', 'manual_approval'],
|
|
3635
|
-
checkpoint_restoration = true,
|
|
3636
|
-
graceful_degradation = true,
|
|
3637
|
-
max_retries = 3
|
|
3638
|
-
} = args;
|
|
3639
|
-
|
|
3640
|
-
try {
|
|
3641
|
-
const deployment: any = {
|
|
3642
|
-
deployment_id: `deployment_${Date.now()}`,
|
|
3643
|
-
started_at: new Date().toISOString(),
|
|
3644
|
-
artifacts_count: artifacts.length,
|
|
3645
|
-
status: 'initializing',
|
|
3646
|
-
deployment_results: [],
|
|
3647
|
-
checkpoints: [],
|
|
3648
|
-
fallback_actions: [],
|
|
3649
|
-
recovery_plan: {}
|
|
3650
|
-
};
|
|
3651
|
-
|
|
3652
|
-
// Create checkpoint before deployment
|
|
3653
|
-
if (checkpoint_restoration) {
|
|
3654
|
-
const checkpoint = {
|
|
3655
|
-
checkpoint_id: `pre_deployment_${Date.now()}`,
|
|
3656
|
-
created_at: new Date().toISOString(),
|
|
3657
|
-
artifacts_state: 'captured',
|
|
3658
|
-
restoration_available: true
|
|
3659
|
-
};
|
|
3660
|
-
deployment.checkpoints.push(checkpoint);
|
|
3661
|
-
}
|
|
3662
|
-
|
|
3663
|
-
// Process each artifact with resilient deployment
|
|
3664
|
-
for (let i = 0; i < artifacts.length; i++) {
|
|
3665
|
-
const artifact = artifacts[i];
|
|
3666
|
-
const artifactResult: any = {
|
|
3667
|
-
artifact_index: i + 1,
|
|
3668
|
-
artifact_id: artifact.sys_id || artifact.id,
|
|
3669
|
-
status: 'pending',
|
|
3670
|
-
attempts: 0,
|
|
3671
|
-
max_retries,
|
|
3672
|
-
fallback_used: false
|
|
3673
|
-
};
|
|
3674
|
-
|
|
3675
|
-
let deploymentSuccess = false;
|
|
3676
|
-
let lastError = null;
|
|
3677
|
-
|
|
3678
|
-
// Retry logic
|
|
3679
|
-
while (!deploymentSuccess && artifactResult.attempts < max_retries) {
|
|
3680
|
-
artifactResult.attempts++;
|
|
3681
|
-
|
|
3682
|
-
try {
|
|
3683
|
-
// Attempt deployment (this would call actual deployment APIs)
|
|
3684
|
-
const deployResult = await this.attemptArtifactDeployment(artifact);
|
|
3685
|
-
if (deployResult.success) {
|
|
3686
|
-
artifactResult.status = 'deployed';
|
|
3687
|
-
artifactResult.deployment_details = deployResult.details;
|
|
3688
|
-
deploymentSuccess = true;
|
|
3689
|
-
}
|
|
3690
|
-
} catch (error) {
|
|
3691
|
-
lastError = error;
|
|
3692
|
-
|
|
3693
|
-
if (retry_on_failure && artifactResult.attempts < max_retries) {
|
|
3694
|
-
artifactResult.status = `retry_${artifactResult.attempts}`;
|
|
3695
|
-
// Wait before retry
|
|
3696
|
-
await new Promise(resolve => setTimeout(resolve, 2000 * artifactResult.attempts));
|
|
3697
|
-
}
|
|
3698
|
-
}
|
|
3699
|
-
}
|
|
3700
|
-
|
|
3701
|
-
// Apply fallback strategies if deployment failed
|
|
3702
|
-
if (!deploymentSuccess && fallback_strategies.length > 0) {
|
|
3703
|
-
for (const strategy of fallback_strategies) {
|
|
3704
|
-
try {
|
|
3705
|
-
const fallbackResult = await this.applyFallbackStrategy(artifact, strategy);
|
|
3706
|
-
if (fallbackResult.success) {
|
|
3707
|
-
artifactResult.status = 'deployed_with_fallback';
|
|
3708
|
-
artifactResult.fallback_used = strategy;
|
|
3709
|
-
artifactResult.fallback_details = fallbackResult.details;
|
|
3710
|
-
deploymentSuccess = true;
|
|
3711
|
-
break;
|
|
3712
|
-
}
|
|
3713
|
-
} catch (fallbackError) {
|
|
3714
|
-
// Continue to next fallback strategy
|
|
3715
|
-
}
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
|
|
3719
|
-
// Final status
|
|
3720
|
-
if (!deploymentSuccess) {
|
|
3721
|
-
artifactResult.status = 'failed';
|
|
3722
|
-
artifactResult.error = lastError instanceof Error ? lastError.message : 'Deployment failed';
|
|
3723
|
-
|
|
3724
|
-
if (graceful_degradation) {
|
|
3725
|
-
artifactResult.degradation_applied = true;
|
|
3726
|
-
artifactResult.degradation_note = 'Artifact marked for manual deployment';
|
|
3727
|
-
}
|
|
3728
|
-
}
|
|
3729
|
-
|
|
3730
|
-
deployment.deployment_results.push(artifactResult);
|
|
3731
|
-
}
|
|
3732
|
-
|
|
3733
|
-
// Generate recovery plan
|
|
3734
|
-
const failedArtifacts = deployment.deployment_results.filter((r: any) => r.status === 'failed');
|
|
3735
|
-
if (failedArtifacts.length > 0) {
|
|
3736
|
-
deployment.recovery_plan = {
|
|
3737
|
-
failed_artifacts_count: failedArtifacts.length,
|
|
3738
|
-
recovery_options: [
|
|
3739
|
-
'Use checkpoint restoration to revert to pre-deployment state',
|
|
3740
|
-
'Manual deployment of failed artifacts',
|
|
3741
|
-
'Adjust permissions and retry deployment',
|
|
3742
|
-
'Contact ServiceNow administrator for assistance'
|
|
3743
|
-
],
|
|
3744
|
-
checkpoint_available: checkpoint_restoration
|
|
3745
|
-
};
|
|
3746
|
-
}
|
|
3747
|
-
|
|
3748
|
-
// Overall deployment status
|
|
3749
|
-
const successCount = deployment.deployment_results.filter((r: any) =>
|
|
3750
|
-
r.status === 'deployed' || r.status === 'deployed_with_fallback'
|
|
3751
|
-
).length;
|
|
3752
|
-
|
|
3753
|
-
deployment.status = successCount === artifacts.length ? 'completed' :
|
|
3754
|
-
successCount > 0 ? 'partially_completed' : 'failed';
|
|
3755
|
-
deployment.success_rate = `${successCount}/${artifacts.length}`;
|
|
3756
|
-
|
|
3757
|
-
return { content: [{ type: 'text', text: JSON.stringify(deployment, null, 2) }] };
|
|
3758
|
-
|
|
3759
|
-
} catch (error) {
|
|
3760
|
-
return { content: [{
|
|
3761
|
-
type: 'text',
|
|
3762
|
-
text: `❌ Resilient Deployment Failed: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
3763
|
-
}] };
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
3766
|
-
// Helper methods for the new functionality
|
|
3767
|
-
private inferArtifactType(component: string): string {
|
|
3768
|
-
if (component.includes('workflow') || component.includes('approval')) return 'flow';
|
|
3769
|
-
if (component.includes('management') || component.includes('catalog')) return 'widget';
|
|
3770
|
-
if (component.includes('notification') || component.includes('email')) return 'script';
|
|
3771
|
-
return 'mixed';
|
|
3772
|
-
}
|
|
3773
|
-
|
|
3774
|
-
private inferDependencies(component: string): string[] {
|
|
3775
|
-
const deps = [];
|
|
3776
|
-
if (component.includes('user')) deps.push('user_table');
|
|
3777
|
-
if (component.includes('device')) deps.push('device_catalog', 'cmdb');
|
|
3778
|
-
if (component.includes('approval')) deps.push('approval_engine', 'notification_system');
|
|
3779
|
-
return deps;
|
|
3780
|
-
}
|
|
3781
|
-
|
|
3782
|
-
private inferPriority(component: string, objective: string): 'high' | 'medium' | 'low' {
|
|
3783
|
-
if (objective.toLowerCase().includes(component)) return 'high';
|
|
3784
|
-
if (component.includes('workflow') || component.includes('approval')) return 'high';
|
|
3785
|
-
return 'medium';
|
|
3786
|
-
}
|
|
3787
|
-
|
|
3788
|
-
private calculateDeploymentOrder(components: string[]): string[] {
|
|
3789
|
-
// Basic dependency-based ordering
|
|
3790
|
-
const ordered = [...components];
|
|
3791
|
-
return ordered.sort((a, b) => {
|
|
3792
|
-
if (a.includes('table') || a.includes('catalog')) return -1;
|
|
3793
|
-
if (b.includes('table') || b.includes('catalog')) return 1;
|
|
3794
|
-
if (a.includes('script')) return -1;
|
|
3795
|
-
if (b.includes('script')) return 1;
|
|
3796
|
-
return 0;
|
|
3797
|
-
});
|
|
3798
|
-
}
|
|
3799
|
-
|
|
3800
|
-
private recommendScope(preference: string, components: string[]): string {
|
|
3801
|
-
if (preference !== 'auto') return preference;
|
|
3802
|
-
return components.length > 2 ? 'scoped' : 'global';
|
|
3803
|
-
}
|
|
3804
|
-
|
|
3805
|
-
private inferRequiredPermissions(components: string[]): string[] {
|
|
3806
|
-
const permissions = ['basic_user'];
|
|
3807
|
-
if (components.some(c => c.includes('workflow') || c.includes('approval'))) {
|
|
3808
|
-
permissions.push('workflow_designer');
|
|
3809
|
-
}
|
|
3810
|
-
if (components.some(c => c.includes('widget') || c.includes('portal'))) {
|
|
3811
|
-
permissions.push('sp_portal_manager');
|
|
3812
|
-
}
|
|
3813
|
-
if (components.length > 3) {
|
|
3814
|
-
permissions.push('admin');
|
|
3815
|
-
}
|
|
3816
|
-
return permissions;
|
|
3817
|
-
}
|
|
3818
|
-
|
|
3819
|
-
private async validateUpdateSetDependencies(): Promise<any[]> {
|
|
3820
|
-
// Simplified dependency validation
|
|
3821
|
-
return [
|
|
3822
|
-
{ dependency: 'user_table', status: 'validated', details: 'Required table exists' },
|
|
3823
|
-
{ dependency: 'approval_engine', status: 'validated', details: 'Approval framework available' }
|
|
3824
|
-
];
|
|
3825
|
-
}
|
|
3826
|
-
|
|
3827
|
-
private async detectUpdateSetConflicts(): Promise<any[]> {
|
|
3828
|
-
// Simplified conflict detection
|
|
3829
|
-
return [
|
|
3830
|
-
{ conflict_type: 'naming', severity: 'warning', description: 'Similar named artifacts found' }
|
|
3831
|
-
];
|
|
3832
|
-
}
|
|
3833
|
-
|
|
3834
|
-
private async attemptArtifactDeployment(artifact: any): Promise<any> {
|
|
3835
|
-
// 🔧 CRITICAL FIX: Replace mock implementation with real ServiceNow API calls
|
|
3836
|
-
this.logger.info('Attempting real artifact deployment', {
|
|
3837
|
-
type: artifact.type,
|
|
3838
|
-
name: artifact.name || artifact.config?.name
|
|
3839
|
-
});
|
|
3840
|
-
|
|
3841
|
-
try {
|
|
3842
|
-
let result;
|
|
3843
|
-
|
|
3844
|
-
switch (artifact.type) {
|
|
3845
|
-
case 'flow':
|
|
3846
|
-
// Use the existing createFlow method
|
|
3847
|
-
result = await this.client.createFlow({
|
|
3848
|
-
name: artifact.config.name,
|
|
3849
|
-
description: artifact.config.description,
|
|
3850
|
-
type: artifact.config.flow_type || 'flow',
|
|
3851
|
-
table: artifact.config.table,
|
|
3852
|
-
trigger_type: artifact.config.trigger_type,
|
|
3853
|
-
condition: artifact.config.condition,
|
|
3854
|
-
active: artifact.config.active !== false,
|
|
3855
|
-
flow_definition: artifact.config.flow_definition,
|
|
3856
|
-
category: artifact.config.category || 'automation'
|
|
3857
|
-
});
|
|
3858
|
-
break;
|
|
3859
|
-
|
|
3860
|
-
case 'subflow':
|
|
3861
|
-
// Use the existing createSubflow method
|
|
3862
|
-
result = await this.client.createSubflow({
|
|
3863
|
-
name: artifact.config.name,
|
|
3864
|
-
description: artifact.config.description,
|
|
3865
|
-
inputs: artifact.config.inputs || [],
|
|
3866
|
-
outputs: artifact.config.outputs || [],
|
|
3867
|
-
activities: artifact.config.activities || [],
|
|
3868
|
-
category: artifact.config.category || 'custom'
|
|
3869
|
-
});
|
|
3870
|
-
break;
|
|
3871
|
-
|
|
3872
|
-
case 'widget':
|
|
3873
|
-
// Use existing createRecord method for Service Portal widgets
|
|
3874
|
-
result = await this.client.createRecord('sp_widget', {
|
|
3875
|
-
name: artifact.config.name,
|
|
3876
|
-
title: artifact.config.title || artifact.config.name,
|
|
3877
|
-
description: artifact.config.description,
|
|
3878
|
-
template: artifact.config.template || '<div>Widget content</div>',
|
|
3879
|
-
css: artifact.config.css || '',
|
|
3880
|
-
client_script: artifact.config.client_script || '',
|
|
3881
|
-
server_script: artifact.config.server_script || '',
|
|
3882
|
-
option_schema: artifact.config.option_schema || '[]',
|
|
3883
|
-
category: artifact.config.category || 'custom'
|
|
3884
|
-
});
|
|
3885
|
-
break;
|
|
3886
|
-
|
|
3887
|
-
case 'business_rule':
|
|
3888
|
-
// Create business rule
|
|
3889
|
-
result = await this.client.createRecord('sys_script', {
|
|
3890
|
-
name: artifact.config.name,
|
|
3891
|
-
description: artifact.config.description,
|
|
3892
|
-
table: artifact.config.table || 'incident',
|
|
3893
|
-
when: artifact.config.when || 'before',
|
|
3894
|
-
active: artifact.config.active !== false,
|
|
3895
|
-
script: artifact.config.script || '// Business rule script',
|
|
3896
|
-
condition: artifact.config.condition || '',
|
|
3897
|
-
order: artifact.config.order || 100
|
|
3898
|
-
});
|
|
3899
|
-
break;
|
|
3900
|
-
|
|
3901
|
-
case 'script_include':
|
|
3902
|
-
// Create script include
|
|
3903
|
-
result = await this.client.createRecord('sys_script_include', {
|
|
3904
|
-
name: artifact.config.name,
|
|
3905
|
-
description: artifact.config.description,
|
|
3906
|
-
script: artifact.config.script || 'var ' + artifact.config.name + ' = Class.create();',
|
|
3907
|
-
api_name: artifact.config.api_name || artifact.config.name,
|
|
3908
|
-
active: artifact.config.active !== false,
|
|
3909
|
-
accessible_from: artifact.config.accessible_from || 'all'
|
|
3910
|
-
});
|
|
3911
|
-
break;
|
|
3912
|
-
|
|
3913
|
-
case 'table':
|
|
3914
|
-
// Create custom table
|
|
3915
|
-
result = await this.client.createRecord('sys_db_object', {
|
|
3916
|
-
name: artifact.config.name,
|
|
3917
|
-
label: artifact.config.label || artifact.config.name,
|
|
3918
|
-
super_class: artifact.config.super_class || 'task',
|
|
3919
|
-
create_module: artifact.config.create_module !== false,
|
|
3920
|
-
create_menu: artifact.config.create_menu !== false
|
|
3921
|
-
});
|
|
3922
|
-
break;
|
|
3923
|
-
|
|
3924
|
-
case 'application':
|
|
3925
|
-
// Create scoped application
|
|
3926
|
-
result = await this.client.createRecord('sys_app', {
|
|
3927
|
-
name: artifact.config.name,
|
|
3928
|
-
description: artifact.config.description,
|
|
3929
|
-
scope: artifact.config.scope || 'x_custom_' + artifact.config.name.toLowerCase().replace(/\s+/g, '_'),
|
|
3930
|
-
version: artifact.config.version || '1.0.0',
|
|
3931
|
-
vendor: artifact.config.vendor || 'Custom',
|
|
3932
|
-
private: artifact.config.private !== false
|
|
3933
|
-
});
|
|
3934
|
-
break;
|
|
3935
|
-
|
|
3936
|
-
default:
|
|
3937
|
-
throw new Error(`Unsupported artifact type: ${artifact.type}`);
|
|
3938
|
-
}
|
|
3939
|
-
|
|
3940
|
-
if (result.success) {
|
|
3941
|
-
this.logger.info('Artifact deployed successfully', {
|
|
3942
|
-
type: artifact.type,
|
|
3943
|
-
name: artifact.config.name,
|
|
3944
|
-
sys_id: result.data?.sys_id
|
|
3945
|
-
});
|
|
3946
|
-
|
|
3947
|
-
return {
|
|
3948
|
-
success: true,
|
|
3949
|
-
details: {
|
|
3950
|
-
deployed_at: new Date().toISOString(),
|
|
3951
|
-
sys_id: result.data?.sys_id,
|
|
3952
|
-
url: result.data?.url,
|
|
3953
|
-
type: artifact.type,
|
|
3954
|
-
name: artifact.config.name,
|
|
3955
|
-
deployment_method: 'real_api_call'
|
|
3956
|
-
}
|
|
3957
|
-
};
|
|
3958
|
-
} else {
|
|
3959
|
-
throw new Error(result.error || 'Unknown deployment error');
|
|
3960
|
-
}
|
|
3961
|
-
|
|
3962
|
-
} catch (error) {
|
|
3963
|
-
this.logger.error('Artifact deployment failed', {
|
|
3964
|
-
type: artifact.type,
|
|
3965
|
-
name: artifact.config?.name,
|
|
3966
|
-
error: error instanceof Error ? error.message : String(error)
|
|
3967
|
-
});
|
|
3968
|
-
|
|
3969
|
-
return {
|
|
3970
|
-
success: false,
|
|
3971
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3972
|
-
details: {
|
|
3973
|
-
attempted_at: new Date().toISOString(),
|
|
3974
|
-
type: artifact.type,
|
|
3975
|
-
name: artifact.config?.name,
|
|
3976
|
-
deployment_method: 'real_api_call'
|
|
3977
|
-
}
|
|
3978
|
-
};
|
|
3979
|
-
}
|
|
3980
|
-
}
|
|
3981
|
-
|
|
3982
|
-
private async applyFallbackStrategy(artifact: any, strategy: string): Promise<any> {
|
|
3983
|
-
// 🔧 CRITICAL FIX: Replace mock fallback with real implementation strategies
|
|
3984
|
-
this.logger.info('Applying fallback strategy', {
|
|
3985
|
-
strategy,
|
|
3986
|
-
artifactType: artifact.type,
|
|
3987
|
-
artifactName: artifact.config?.name
|
|
3988
|
-
});
|
|
3989
|
-
|
|
3990
|
-
try {
|
|
3991
|
-
let result;
|
|
3992
|
-
|
|
3993
|
-
switch (strategy) {
|
|
3994
|
-
case 'global_scope':
|
|
3995
|
-
// Try deploying to global scope with enhanced permissions
|
|
3996
|
-
artifact.config.scope = 'global';
|
|
3997
|
-
artifact.config.sys_domain = 'global';
|
|
3998
|
-
result = await this.attemptArtifactDeployment(artifact);
|
|
3999
|
-
break;
|
|
4000
|
-
|
|
4001
|
-
case 'simplified_version':
|
|
4002
|
-
// Create a simplified version of the artifact
|
|
4003
|
-
const simplifiedArtifact = { ...artifact };
|
|
4004
|
-
if (artifact.type === 'flow') {
|
|
4005
|
-
// Simplify flow definition - remove complex activities
|
|
4006
|
-
const flowDef = typeof artifact.config.flow_definition === 'string'
|
|
4007
|
-
? JSON.parse(artifact.config.flow_definition)
|
|
4008
|
-
: artifact.config.flow_definition;
|
|
4009
|
-
|
|
4010
|
-
if (flowDef.activities) {
|
|
4011
|
-
flowDef.activities = flowDef.activities.filter((activity: any) =>
|
|
4012
|
-
['approval', 'notification', 'script'].includes(activity.type)
|
|
4013
|
-
);
|
|
4014
|
-
}
|
|
4015
|
-
|
|
4016
|
-
simplifiedArtifact.config.flow_definition = JSON.stringify(flowDef);
|
|
4017
|
-
simplifiedArtifact.config.name += '_simplified';
|
|
4018
|
-
} else if (artifact.type === 'widget') {
|
|
4019
|
-
// Generate simplified but functional widget template
|
|
4020
|
-
const widgetInstruction = artifact.config.description || artifact.config.name || 'simplified widget';
|
|
4021
|
-
const generatedWidget = widgetTemplateGenerator.generateWidget({
|
|
4022
|
-
title: artifact.config.title || artifact.config.name,
|
|
4023
|
-
instruction: widgetInstruction,
|
|
4024
|
-
type: 'info', // Use info type for simplified widgets
|
|
4025
|
-
theme: 'minimal',
|
|
4026
|
-
responsive: true
|
|
4027
|
-
});
|
|
4028
|
-
|
|
4029
|
-
simplifiedArtifact.config.template = generatedWidget.template;
|
|
4030
|
-
simplifiedArtifact.config.css = generatedWidget.css;
|
|
4031
|
-
simplifiedArtifact.config.client_script = generatedWidget.clientScript;
|
|
4032
|
-
simplifiedArtifact.config.server_script = generatedWidget.serverScript;
|
|
4033
|
-
simplifiedArtifact.config.option_schema = generatedWidget.optionSchema;
|
|
4034
|
-
simplifiedArtifact.config.name += '_simplified';
|
|
4035
|
-
}
|
|
4036
|
-
|
|
4037
|
-
result = await this.attemptArtifactDeployment(simplifiedArtifact);
|
|
4038
|
-
break;
|
|
4039
|
-
|
|
4040
|
-
case 'business_rule_fallback':
|
|
4041
|
-
// Convert flow to business rule as fallback
|
|
4042
|
-
if (artifact.type === 'flow') {
|
|
4043
|
-
const businessRuleConfig = {
|
|
4044
|
-
name: `BR_${artifact.config.name}`,
|
|
4045
|
-
description: `Business Rule fallback for flow: ${artifact.config.name}`,
|
|
4046
|
-
table: artifact.config.table || 'incident',
|
|
4047
|
-
when: 'after',
|
|
4048
|
-
script: `
|
|
4049
|
-
// Auto-generated Business Rule fallback for Flow: ${artifact.config.name}
|
|
4050
|
-
// Original flow description: ${artifact.config.description || 'No description'}
|
|
4051
|
-
|
|
4052
|
-
try {
|
|
4053
|
-
// Basic automation logic
|
|
4054
|
-
if (current.isNewRecord()) {
|
|
4055
|
-
gs.info('Record created, executing flow logic: ${artifact.config.name}');
|
|
4056
|
-
|
|
4057
|
-
// Add your custom logic here based on original flow
|
|
4058
|
-
${this.generateBusinessRuleScript(artifact.config)}
|
|
4059
|
-
}
|
|
4060
|
-
} catch (e) {
|
|
4061
|
-
gs.error('Business Rule fallback error: ' + e.message);
|
|
4062
|
-
}
|
|
4063
|
-
`.trim(),
|
|
4064
|
-
active: true,
|
|
4065
|
-
condition: artifact.config.condition || ''
|
|
4066
|
-
};
|
|
4067
|
-
|
|
4068
|
-
result = await this.client.createRecord('sys_script', businessRuleConfig);
|
|
4069
|
-
} else {
|
|
4070
|
-
throw new Error('Business rule fallback only available for flows');
|
|
4071
|
-
}
|
|
4072
|
-
break;
|
|
4073
|
-
|
|
4074
|
-
case 'minimal_deployment':
|
|
4075
|
-
// Deploy with absolute minimum configuration
|
|
4076
|
-
const minimalArtifact = { ...artifact };
|
|
4077
|
-
|
|
4078
|
-
// Strip all non-essential properties
|
|
4079
|
-
const essentialFields = ['name', 'description', 'active'];
|
|
4080
|
-
const cleanedConfig: any = {};
|
|
4081
|
-
|
|
4082
|
-
essentialFields.forEach(field => {
|
|
4083
|
-
if (artifact.config[field] !== undefined) {
|
|
4084
|
-
cleanedConfig[field] = artifact.config[field];
|
|
4085
|
-
}
|
|
4086
|
-
});
|
|
4087
|
-
|
|
4088
|
-
// Add type-specific essentials
|
|
4089
|
-
if (artifact.type === 'flow') {
|
|
4090
|
-
cleanedConfig.type = 'subflow'; // Subflows are simpler
|
|
4091
|
-
cleanedConfig.category = 'custom';
|
|
4092
|
-
} else if (artifact.type === 'widget') {
|
|
4093
|
-
// Generate minimal but functional widget template
|
|
4094
|
-
const widgetInstruction = artifact.config.description || artifact.config.name || 'minimal widget';
|
|
4095
|
-
const generatedWidget = widgetTemplateGenerator.generateWidget({
|
|
4096
|
-
title: artifact.config.title || artifact.config.name,
|
|
4097
|
-
instruction: widgetInstruction,
|
|
4098
|
-
type: 'info', // Use info type for minimal widgets
|
|
4099
|
-
theme: 'minimal',
|
|
4100
|
-
responsive: true
|
|
4101
|
-
});
|
|
4102
|
-
|
|
4103
|
-
cleanedConfig.template = generatedWidget.template;
|
|
4104
|
-
cleanedConfig.css = generatedWidget.css;
|
|
4105
|
-
cleanedConfig.client_script = generatedWidget.clientScript;
|
|
4106
|
-
cleanedConfig.server_script = generatedWidget.serverScript;
|
|
4107
|
-
cleanedConfig.option_schema = generatedWidget.optionSchema;
|
|
4108
|
-
cleanedConfig.title = artifact.config.title || artifact.config.name;
|
|
4109
|
-
}
|
|
4110
|
-
|
|
4111
|
-
minimalArtifact.config = cleanedConfig;
|
|
4112
|
-
minimalArtifact.config.name += '_minimal';
|
|
4113
|
-
|
|
4114
|
-
result = await this.attemptArtifactDeployment(minimalArtifact);
|
|
4115
|
-
break;
|
|
4116
|
-
|
|
4117
|
-
case 'delayed_retry':
|
|
4118
|
-
// Wait and retry original deployment
|
|
4119
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
4120
|
-
result = await this.attemptArtifactDeployment(artifact);
|
|
4121
|
-
break;
|
|
4122
|
-
|
|
4123
|
-
default:
|
|
4124
|
-
throw new Error(`Unknown fallback strategy: ${strategy}`);
|
|
4125
|
-
}
|
|
4126
|
-
|
|
4127
|
-
if (result.success) {
|
|
4128
|
-
this.logger.info('Fallback strategy successful', {
|
|
4129
|
-
strategy,
|
|
4130
|
-
artifactType: artifact.type,
|
|
4131
|
-
artifactName: artifact.config?.name,
|
|
4132
|
-
deployedSysId: result.details?.sys_id
|
|
4133
|
-
});
|
|
4134
|
-
|
|
4135
|
-
return {
|
|
4136
|
-
success: true,
|
|
4137
|
-
details: {
|
|
4138
|
-
fallback_strategy: strategy,
|
|
4139
|
-
applied_at: new Date().toISOString(),
|
|
4140
|
-
original_artifact: artifact.config?.name,
|
|
4141
|
-
deployed_artifact: result.details?.name || artifact.config?.name,
|
|
4142
|
-
sys_id: result.details?.sys_id,
|
|
4143
|
-
deployment_method: 'fallback_strategy'
|
|
4144
|
-
}
|
|
4145
|
-
};
|
|
4146
|
-
} else {
|
|
4147
|
-
throw new Error(result.error || `Fallback strategy ${strategy} failed`);
|
|
4148
|
-
}
|
|
4149
|
-
|
|
4150
|
-
} catch (error) {
|
|
4151
|
-
this.logger.error('Fallback strategy failed', {
|
|
4152
|
-
strategy,
|
|
4153
|
-
artifactType: artifact.type,
|
|
4154
|
-
error: error instanceof Error ? error.message : String(error)
|
|
4155
|
-
});
|
|
4156
|
-
|
|
4157
|
-
return {
|
|
4158
|
-
success: false,
|
|
4159
|
-
error: error instanceof Error ? error.message : String(error),
|
|
4160
|
-
details: {
|
|
4161
|
-
fallback_strategy: strategy,
|
|
4162
|
-
attempted_at: new Date().toISOString(),
|
|
4163
|
-
original_artifact: artifact.config?.name,
|
|
4164
|
-
deployment_method: 'fallback_strategy'
|
|
4165
|
-
}
|
|
4166
|
-
};
|
|
4167
|
-
}
|
|
4168
|
-
}
|
|
4169
|
-
|
|
4170
|
-
private generateBusinessRuleScript(flowConfig: any): string {
|
|
4171
|
-
// Generate basic business rule script based on flow configuration
|
|
4172
|
-
let script = '// Generated business rule logic\n';
|
|
4173
|
-
|
|
4174
|
-
if (flowConfig.trigger_type === 'record_created') {
|
|
4175
|
-
script += 'gs.info("Record created trigger activated");\n';
|
|
4176
|
-
}
|
|
4177
|
-
|
|
4178
|
-
if (flowConfig.condition) {
|
|
4179
|
-
script += `// Original condition: ${flowConfig.condition}\n`;
|
|
4180
|
-
}
|
|
4181
|
-
|
|
4182
|
-
script += 'gs.info("Business rule executed successfully");\n';
|
|
4183
|
-
|
|
4184
|
-
return script;
|
|
4185
|
-
}
|
|
4186
|
-
|
|
4187
|
-
private async generateFlowTestData(flow: any): Promise<any> {
|
|
4188
|
-
return {
|
|
4189
|
-
test_record: { state: 'new', priority: 'medium' },
|
|
4190
|
-
user_context: { role: 'test_user', department: 'IT' },
|
|
4191
|
-
variables: { test_mode: true }
|
|
4192
|
-
};
|
|
4193
|
-
}
|
|
4194
|
-
|
|
4195
|
-
private async runFunctionalTests(flowId: string, testData: any): Promise<any[]> {
|
|
4196
|
-
return [
|
|
4197
|
-
{ test_name: 'Basic Flow Execution', status: 'passed', duration: '2.5s' },
|
|
4198
|
-
{ test_name: 'Variable Passing', status: 'passed', duration: '1.2s' }
|
|
4199
|
-
];
|
|
4200
|
-
}
|
|
4201
|
-
|
|
4202
|
-
private async runEdgeCaseTests(flowId: string): Promise<any[]> {
|
|
4203
|
-
return [
|
|
4204
|
-
{ test_name: 'Null Input Handling', status: 'passed', duration: '1.8s' },
|
|
4205
|
-
{ test_name: 'Invalid State Transition', status: 'failed', error: 'State validation missing' }
|
|
4206
|
-
];
|
|
4207
|
-
}
|
|
4208
|
-
|
|
4209
|
-
private async runPerformanceTests(flowId: string): Promise<any[]> {
|
|
4210
|
-
return [
|
|
4211
|
-
{ test_name: 'Execution Time', status: 'passed', duration: '3.2s', threshold: '5s' },
|
|
4212
|
-
{ test_name: 'Memory Usage', status: 'passed', memory: '12MB', threshold: '50MB' }
|
|
4213
|
-
];
|
|
4214
|
-
}
|
|
4215
|
-
|
|
4216
|
-
private async runIntegrationTests(flowId: string): Promise<any[]> {
|
|
4217
|
-
return [
|
|
4218
|
-
{ test_name: 'External API Calls', status: 'passed', duration: '4.1s' },
|
|
4219
|
-
{ test_name: 'Database Operations', status: 'passed', duration: '2.8s' }
|
|
4220
|
-
];
|
|
4221
|
-
}
|
|
4222
|
-
|
|
4223
|
-
private async runCustomTestScenario(flowId: string, scenario: any): Promise<any> {
|
|
4224
|
-
return {
|
|
4225
|
-
scenario_name: scenario.name || 'Custom Scenario',
|
|
4226
|
-
status: 'passed',
|
|
4227
|
-
duration: '2.1s',
|
|
4228
|
-
details: 'Custom scenario executed successfully'
|
|
4229
|
-
};
|
|
4230
|
-
}
|
|
4231
|
-
|
|
4232
|
-
private async analyzeArtifactRequirements(objective: string): Promise<any> {
|
|
4233
|
-
const artifactAnalysis = {
|
|
4234
|
-
objective,
|
|
4235
|
-
recommended_artifacts: []
|
|
4236
|
-
};
|
|
4237
|
-
|
|
4238
|
-
const lowerObjective = objective.toLowerCase();
|
|
4239
|
-
|
|
4240
|
-
// Simple keyword-based _analysis for artifact type detection
|
|
4241
|
-
if (lowerObjective.includes('flow') || lowerObjective.includes('workflow') || lowerObjective.includes('automation')) {
|
|
4242
|
-
artifactAnalysis.recommended_artifacts.push({
|
|
4243
|
-
type: 'flow',
|
|
4244
|
-
name: `Flow for ${objective.substring(0, 50)}`,
|
|
4245
|
-
description: `Automated flow created for: ${objective}`,
|
|
4246
|
-
priority: 'high'
|
|
4247
|
-
});
|
|
4248
|
-
}
|
|
4249
|
-
|
|
4250
|
-
if (lowerObjective.includes('widget') || lowerObjective.includes('dashboard') || lowerObjective.includes('display')) {
|
|
4251
|
-
artifactAnalysis.recommended_artifacts.push({
|
|
4252
|
-
type: 'widget',
|
|
4253
|
-
name: `Widget for ${objective.substring(0, 50)}`,
|
|
4254
|
-
description: `Service Portal widget for: ${objective}`,
|
|
4255
|
-
template: '<div class="panel panel-default"><div class="panel-body">{{data.message || "Widget deployed successfully"}}<br><small>Created for: ' + objective + '</small></div></div>',
|
|
4256
|
-
css: '.panel { margin: 10px; }',
|
|
4257
|
-
client_script: 'function() { console.log("Widget loaded"); }',
|
|
4258
|
-
server_script: '(function() { data.message = "Successfully deployed for: ' + objective.replace(/"/g, '\\"') + '"; })()',
|
|
4259
|
-
priority: 'medium'
|
|
4260
|
-
});
|
|
4261
|
-
}
|
|
4262
|
-
|
|
4263
|
-
if (lowerObjective.includes('script') || lowerObjective.includes('function') || lowerObjective.includes('utility')) {
|
|
4264
|
-
artifactAnalysis.recommended_artifacts.push({
|
|
4265
|
-
type: 'script_include',
|
|
4266
|
-
name: `Script for ${objective.substring(0, 50)}`,
|
|
4267
|
-
description: `Script include for: ${objective}`,
|
|
4268
|
-
script: `// Script created for: ${objective}\nvar ScriptUtility = Class.create();\nScriptUtility.prototype = {\n initialize: function() {},\n execute: function() {\n gs.info('Script executed for: ${objective}');\n return true;\n },\n type: 'ScriptUtility'\n};\n`,
|
|
4269
|
-
priority: 'low'
|
|
4270
|
-
});
|
|
4271
|
-
}
|
|
4272
|
-
|
|
4273
|
-
// If no specific artifacts detected, create a default flow
|
|
4274
|
-
if (artifactAnalysis.recommended_artifacts.length === 0) {
|
|
4275
|
-
artifactAnalysis.recommended_artifacts.push({
|
|
4276
|
-
type: 'flow',
|
|
4277
|
-
name: `General Flow for ${objective.substring(0, 40)}`,
|
|
4278
|
-
description: `General purpose flow for: ${objective}`,
|
|
4279
|
-
priority: 'medium'
|
|
4280
|
-
});
|
|
4281
|
-
}
|
|
4282
|
-
|
|
4283
|
-
return artifactAnalysis;
|
|
4284
|
-
}
|
|
4285
|
-
|
|
4286
|
-
private generateTestRecommendations(testResults: any): string[] {
|
|
4287
|
-
const recommendations = [];
|
|
4288
|
-
|
|
4289
|
-
if (testResults.test_summary.success_rate < 90) {
|
|
4290
|
-
recommendations.push('⚠️ Consider addressing failed tests before deployment');
|
|
4291
|
-
}
|
|
4292
|
-
|
|
4293
|
-
recommendations.push('✅ Implement monitoring for production flow execution');
|
|
4294
|
-
recommendations.push('📊 Set up performance baselines for future comparisons');
|
|
4295
|
-
|
|
4296
|
-
return recommendations;
|
|
4297
|
-
}
|
|
4298
|
-
|
|
4299
|
-
private async findFlowByNameOrSysId(identifier: string): Promise<any> {
|
|
4300
|
-
try {
|
|
4301
|
-
// First try as sys_id in sys_hub_flow
|
|
4302
|
-
const result = await this.client.get(`/api/now/table/sys_hub_flow/${identifier}`);
|
|
4303
|
-
if (result.result) {
|
|
4304
|
-
result.result.sys_class_name = 'sys_hub_flow';
|
|
4305
|
-
return result.result;
|
|
4306
|
-
}
|
|
4307
|
-
} catch (error) {
|
|
4308
|
-
// Not a sys_id in sys_hub_flow
|
|
4309
|
-
}
|
|
4310
|
-
|
|
4311
|
-
try {
|
|
4312
|
-
// Try as sys_id in wf_workflow
|
|
4313
|
-
const result = await this.client.get(`/api/now/table/wf_workflow/${identifier}`);
|
|
4314
|
-
if (result.result) {
|
|
4315
|
-
result.result.sys_class_name = 'wf_workflow';
|
|
4316
|
-
return result.result;
|
|
4317
|
-
}
|
|
4318
|
-
} catch (error) {
|
|
4319
|
-
// Not a sys_id in wf_workflow
|
|
4320
|
-
}
|
|
4321
|
-
|
|
4322
|
-
// Search by name in both tables
|
|
4323
|
-
try {
|
|
4324
|
-
// Search in sys_hub_flow
|
|
4325
|
-
const modernFlows = await this.client.get('/api/now/table/sys_hub_flow', {
|
|
4326
|
-
sysparm_query: `name=${identifier}^ORnameSTARTSWITH${identifier}`,
|
|
4327
|
-
sysparm_limit: 1,
|
|
4328
|
-
sysparm_fields: 'name,description,active,type,status,sys_id,latest_snapshot'
|
|
4329
|
-
});
|
|
4330
|
-
|
|
4331
|
-
if (modernFlows.result && modernFlows.result.length > 0) {
|
|
4332
|
-
modernFlows.result[0].sys_class_name = 'sys_hub_flow';
|
|
4333
|
-
return modernFlows.result[0];
|
|
4334
|
-
}
|
|
4335
|
-
} catch (error) {
|
|
4336
|
-
// Continue to legacy search
|
|
4337
|
-
}
|
|
4338
|
-
|
|
4339
|
-
try {
|
|
4340
|
-
// Search in wf_workflow
|
|
4341
|
-
const legacyFlows = await this.client.get('/api/now/table/wf_workflow', {
|
|
4342
|
-
sysparm_query: `name=${identifier}^ORnameSTARTSWITH${identifier}`,
|
|
4343
|
-
sysparm_limit: 1,
|
|
4344
|
-
sysparm_fields: 'name,description,active,table,sys_id'
|
|
4345
|
-
});
|
|
4346
|
-
|
|
4347
|
-
if (legacyFlows.result && legacyFlows.result.length > 0) {
|
|
4348
|
-
legacyFlows.result[0].sys_class_name = 'wf_workflow';
|
|
4349
|
-
return legacyFlows.result[0];
|
|
4350
|
-
}
|
|
4351
|
-
} catch (error) {
|
|
4352
|
-
// No results found
|
|
4353
|
-
}
|
|
4354
|
-
|
|
4355
|
-
return null;
|
|
4356
|
-
}
|
|
4357
|
-
|
|
4358
|
-
/**
|
|
4359
|
-
* 🔴 SNOW-002 FIX: Verify artifact is searchable after creation
|
|
4360
|
-
* This method is called by other MCP servers after creating artifacts
|
|
4361
|
-
*/
|
|
4362
|
-
private async generateDocumentation(args: any) {
|
|
4363
|
-
try {
|
|
4364
|
-
this.logger.info('📚 Generating autonomous documentation', args);
|
|
4365
|
-
|
|
4366
|
-
const result = await this.documentationSystem.generateDocumentation({
|
|
4367
|
-
scope: args.scope || 'full',
|
|
4368
|
-
components: args.components,
|
|
4369
|
-
format: args.format || 'markdown',
|
|
4370
|
-
includePrivate: false,
|
|
4371
|
-
includeDiagrams: args.include_diagrams !== false,
|
|
4372
|
-
includeExamples: args.include_examples !== false,
|
|
4373
|
-
});
|
|
4374
|
-
|
|
4375
|
-
return {
|
|
4376
|
-
content: [
|
|
4377
|
-
{
|
|
4378
|
-
type: 'text',
|
|
4379
|
-
text: `✅ Documentation generated successfully!
|
|
4380
|
-
|
|
4381
|
-
📊 Summary:
|
|
4382
|
-
- Quality Score: ${result.profile.analytics.qualityScore}/100
|
|
4383
|
-
- Completeness: ${result.profile.analytics.completeness}%
|
|
4384
|
-
- Sections: ${result.profile.sections.length}
|
|
4385
|
-
- Diagrams: ${result.profile.diagrams.length}
|
|
4386
|
-
- APIs Documented: ${result.profile.apiDocumentation.length}
|
|
4387
|
-
|
|
4388
|
-
📁 Output: ${result.outputPath || 'Generated in memory'}
|
|
4389
|
-
|
|
4390
|
-
⚠️ Warnings: ${result.warnings.length > 0 ? result.warnings.join('\n') : 'None'}
|
|
4391
|
-
|
|
4392
|
-
💡 Suggestions:
|
|
4393
|
-
${result.suggestions.map(s => `- ${s}`).join('\n')}`,
|
|
4394
|
-
},
|
|
4395
|
-
],
|
|
4396
|
-
};
|
|
4397
|
-
} catch (error) {
|
|
4398
|
-
this.logger.error('❌ Documentation generation failed', error);
|
|
4399
|
-
return {
|
|
4400
|
-
content: [
|
|
4401
|
-
{
|
|
4402
|
-
type: 'text',
|
|
4403
|
-
text: `❌ Documentation generation failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
4404
|
-
},
|
|
4405
|
-
],
|
|
4406
|
-
};
|
|
4407
|
-
}
|
|
4408
|
-
}
|
|
4409
|
-
|
|
4410
|
-
private async getDocumentationSuggestions(args: any) {
|
|
4411
|
-
try {
|
|
4412
|
-
this.logger.info('💡 Getting documentation suggestions', args);
|
|
4413
|
-
|
|
4414
|
-
// Get latest profile if not specified
|
|
4415
|
-
let profileId = args.profile_id;
|
|
4416
|
-
if (!profileId) {
|
|
4417
|
-
const profiles = this.documentationSystem.getDocumentationProfiles();
|
|
4418
|
-
if (profiles.length === 0) {
|
|
4419
|
-
return {
|
|
4420
|
-
content: [
|
|
4421
|
-
{
|
|
4422
|
-
type: 'text',
|
|
4423
|
-
text: '⚠️ No documentation profiles found. Please generate documentation first using snow_generate_documentation.',
|
|
4424
|
-
},
|
|
4425
|
-
],
|
|
4426
|
-
};
|
|
4427
|
-
}
|
|
4428
|
-
profileId = profiles[0].id;
|
|
4429
|
-
}
|
|
4430
|
-
|
|
4431
|
-
const suggestions = await this.documentationSystem.suggestDocumentationImprovements(profileId);
|
|
4432
|
-
|
|
4433
|
-
return {
|
|
4434
|
-
content: [
|
|
4435
|
-
{
|
|
4436
|
-
type: 'text',
|
|
4437
|
-
text: `📊 Documentation Improvement Suggestions
|
|
4438
|
-
|
|
4439
|
-
Priority: ${suggestions.priority.toUpperCase()}
|
|
4440
|
-
Estimated Time: ${suggestions.estimatedTime} minutes
|
|
4441
|
-
|
|
4442
|
-
📋 Suggestions:
|
|
4443
|
-
${suggestions.suggestions.map((s, i) => `
|
|
4444
|
-
${i + 1}. ${s.title} (${s.impact} impact, ${s.effort} effort)
|
|
4445
|
-
${s.description}
|
|
4446
|
-
Components: ${s.components.join(', ')}
|
|
4447
|
-
${s.automated ? '✅ Can be automated' : '⚠️ Manual intervention required'}`).join('\n')}`,
|
|
4448
|
-
},
|
|
4449
|
-
],
|
|
4450
|
-
};
|
|
4451
|
-
} catch (error) {
|
|
4452
|
-
this.logger.error('❌ Failed to get documentation suggestions', error);
|
|
4453
|
-
return {
|
|
4454
|
-
content: [
|
|
4455
|
-
{
|
|
4456
|
-
type: 'text',
|
|
4457
|
-
text: `❌ Failed to get suggestions: ${error instanceof Error ? error.message : String(error)}`,
|
|
4458
|
-
},
|
|
4459
|
-
],
|
|
4460
|
-
};
|
|
4461
|
-
}
|
|
4462
|
-
}
|
|
4463
|
-
|
|
4464
|
-
private async startContinuousDocumentation(args: any) {
|
|
4465
|
-
try {
|
|
4466
|
-
this.logger.info('🔄 Starting continuous documentation', args);
|
|
4467
|
-
|
|
4468
|
-
await this.documentationSystem.startContinuousDocumentation({
|
|
4469
|
-
interval: args.interval,
|
|
4470
|
-
scope: args.scope,
|
|
4471
|
-
autoCommit: args.auto_commit || false,
|
|
4472
|
-
});
|
|
4473
|
-
|
|
4474
|
-
return {
|
|
4475
|
-
content: [
|
|
4476
|
-
{
|
|
4477
|
-
type: 'text',
|
|
4478
|
-
text: `✅ Continuous documentation started!
|
|
4479
|
-
|
|
4480
|
-
🔄 Configuration:
|
|
4481
|
-
- Update Interval: ${args.interval ? `${args.interval / 1000 / 60} minutes` : '60 minutes (default)'}
|
|
4482
|
-
- Monitored Components: ${args.scope ? args.scope.join(', ') : 'All components'}
|
|
4483
|
-
- Auto-commit: ${args.auto_commit ? 'Enabled' : 'Disabled'}
|
|
4484
|
-
|
|
4485
|
-
📝 The system will now automatically:
|
|
4486
|
-
- Monitor for changes
|
|
4487
|
-
- Update documentation incrementally
|
|
4488
|
-
- Generate diagrams for new components
|
|
4489
|
-
- Track API changes
|
|
4490
|
-
- Maintain change logs
|
|
4491
|
-
|
|
4492
|
-
Use snow_generate_documentation to manually trigger a full update at any time.`,
|
|
4493
|
-
},
|
|
4494
|
-
],
|
|
4495
|
-
};
|
|
4496
|
-
} catch (error) {
|
|
4497
|
-
this.logger.error('❌ Failed to start continuous documentation', error);
|
|
4498
|
-
return {
|
|
4499
|
-
content: [
|
|
4500
|
-
{
|
|
4501
|
-
type: 'text',
|
|
4502
|
-
text: `❌ Failed to start continuous documentation: ${error instanceof Error ? error.message : String(error)}`,
|
|
4503
|
-
},
|
|
4504
|
-
],
|
|
4505
|
-
};
|
|
4506
|
-
}
|
|
4507
|
-
}
|
|
4508
|
-
|
|
4509
|
-
private async verifyArtifactSearchable(args: any) {
|
|
4510
|
-
// Check authentication first
|
|
4511
|
-
const authResult = await mcpAuth.ensureAuthenticated();
|
|
4512
|
-
if (!authResult.success) {
|
|
4513
|
-
return {
|
|
4514
|
-
content: [
|
|
4515
|
-
{
|
|
4516
|
-
type: 'text',
|
|
4517
|
-
text: authResult.error || '❌ Not authenticated with ServiceNow.\n\nPlease run: snow-flow auth login\n\nOr configure your .env file with ServiceNow OAuth credentials.',
|
|
4518
|
-
},
|
|
4519
|
-
],
|
|
4520
|
-
};
|
|
4521
|
-
}
|
|
4522
|
-
|
|
4523
|
-
try {
|
|
4524
|
-
this.logger.info('🔴 SNOW-002 FIX: Verifying artifact searchability', {
|
|
4525
|
-
name: args.artifact_name,
|
|
4526
|
-
type: args.artifact_type,
|
|
4527
|
-
sys_id: args.expected_sys_id
|
|
4528
|
-
});
|
|
4529
|
-
|
|
4530
|
-
const maxWaitTime = args.max_wait_time || 30; // seconds
|
|
4531
|
-
const startTime = Date.now();
|
|
4532
|
-
|
|
4533
|
-
// Use the specialized search method for newly created artifacts
|
|
4534
|
-
const results = await this.searchForRecentlyCreatedArtifact(
|
|
4535
|
-
args.artifact_name,
|
|
4536
|
-
args.artifact_type,
|
|
4537
|
-
args.expected_sys_id
|
|
4538
|
-
);
|
|
4539
|
-
|
|
4540
|
-
const elapsedTime = Math.round((Date.now() - startTime) / 1000);
|
|
4541
|
-
|
|
4542
|
-
if (results && results.length > 0) {
|
|
4543
|
-
const artifact = results[0];
|
|
4544
|
-
|
|
4545
|
-
return {
|
|
4546
|
-
content: [
|
|
4547
|
-
{
|
|
4548
|
-
type: 'text',
|
|
4549
|
-
text: `✅ SNOW-002 RESOLVED: Artifact is now searchable!
|
|
4550
|
-
|
|
4551
|
-
🎯 **Verification Results:**
|
|
4552
|
-
- **Artifact**: ${args.artifact_name}
|
|
4553
|
-
- **Type**: ${args.artifact_type}
|
|
4554
|
-
- **Sys ID**: ${artifact.sys_id}
|
|
4555
|
-
- **Search Time**: ${elapsedTime} seconds
|
|
4556
|
-
- **Status**: ✅ Searchable and indexed
|
|
4557
|
-
|
|
4558
|
-
🔍 **Search Verification:**
|
|
4559
|
-
- Found via: ${artifact.search_fallback ? 'Fallback search' : 'Standard search'}
|
|
4560
|
-
- Table: ${artifact.table_name || 'Auto-detected'}
|
|
4561
|
-
- Results: ${results.length} matching record(s)
|
|
4562
|
-
|
|
4563
|
-
💡 **SNOW-002 Fix Status**: Search system timing issues resolved - artifact indexing delay successfully handled with retry logic.
|
|
4564
|
-
|
|
4565
|
-
The artifact is now fully searchable and indexed in ServiceNow! 🎉`,
|
|
4566
|
-
},
|
|
4567
|
-
],
|
|
4568
|
-
};
|
|
4569
|
-
} else {
|
|
4570
|
-
return {
|
|
4571
|
-
content: [
|
|
4572
|
-
{
|
|
4573
|
-
type: 'text',
|
|
4574
|
-
text: `❌ SNOW-002 UNRESOLVED: Artifact still not searchable
|
|
4575
|
-
|
|
4576
|
-
🔍 **Verification Results:**
|
|
4577
|
-
- **Artifact**: ${args.artifact_name}
|
|
4578
|
-
- **Type**: ${args.artifact_type}
|
|
4579
|
-
- **Search Time**: ${elapsedTime} seconds (timeout: ${maxWaitTime}s)
|
|
4580
|
-
- **Status**: ❌ Not found in search indexes
|
|
4581
|
-
|
|
4582
|
-
🚨 **Possible Issues:**
|
|
4583
|
-
1. ServiceNow search indexes may need more time to update
|
|
4584
|
-
2. Artifact may have been created with different name/scope
|
|
4585
|
-
3. ServiceNow instance may have search indexing issues
|
|
4586
|
-
4. Artifact may not be active or may be in wrong scope
|
|
4587
|
-
|
|
4588
|
-
💡 **Recommendations:**
|
|
4589
|
-
1. Wait a few more minutes and try again
|
|
4590
|
-
2. Check artifact directly in ServiceNow UI
|
|
4591
|
-
3. Use snow_get_by_sysid if you have the sys_id
|
|
4592
|
-
4. Contact ServiceNow administrator if issue persists
|
|
4593
|
-
|
|
4594
|
-
**Manual Verification Steps:**
|
|
4595
|
-
1. Log into ServiceNow
|
|
4596
|
-
2. Navigate to the appropriate module
|
|
4597
|
-
3. Search for "${args.artifact_name}" manually
|
|
4598
|
-
4. Check if artifact exists but under different name`,
|
|
4599
|
-
},
|
|
4600
|
-
],
|
|
4601
|
-
};
|
|
4602
|
-
}
|
|
4603
|
-
} catch (error) {
|
|
4604
|
-
this.logger.error('🔴 SNOW-002: Artifact verification failed:', error);
|
|
4605
|
-
return {
|
|
4606
|
-
content: [
|
|
4607
|
-
{
|
|
4608
|
-
type: 'text',
|
|
4609
|
-
text: `❌ SNOW-002: Artifact verification failed
|
|
4610
|
-
|
|
4611
|
-
**Error**: ${error instanceof Error ? error.message : String(error)}
|
|
4612
|
-
|
|
4613
|
-
This may indicate a deeper ServiceNow connectivity issue or authentication problem.
|
|
4614
|
-
Please check your ServiceNow connection and try again.`,
|
|
4615
|
-
},
|
|
4616
|
-
],
|
|
4617
|
-
};
|
|
4618
|
-
}
|
|
4619
|
-
}
|
|
4620
|
-
|
|
4621
|
-
async run() {
|
|
4622
|
-
try {
|
|
4623
|
-
// Initialize systems first
|
|
4624
|
-
await this.initializeSystems();
|
|
4625
|
-
|
|
4626
|
-
// Connect transport
|
|
4627
|
-
const transport = new StdioServerTransport();
|
|
4628
|
-
await this.server.connect(transport);
|
|
4629
|
-
this.logger.info('ServiceNow Intelligent MCP Server running on stdio');
|
|
4630
|
-
} catch (error) {
|
|
4631
|
-
this.logger.error('Failed to start ServiceNow Intelligent MCP:', error);
|
|
4632
|
-
process.exit(1);
|
|
4633
|
-
}
|
|
4634
|
-
}
|
|
4635
|
-
}
|
|
4636
|
-
|
|
4637
|
-
const server = new ServiceNowIntelligentMCP();
|
|
4638
|
-
server.run().catch(console.error);
|