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,1292 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* ServiceNow Deployment MCP Server - Agent-Integrated Version
|
|
4
|
-
* Provides specialized deployment tools with full agent coordination
|
|
5
|
-
* NEW v1.3.1: Complete XML Update Set auto-import with preview and commit controls
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
9
|
-
import {
|
|
10
|
-
CallToolRequestSchema,
|
|
11
|
-
ListToolsRequestSchema,
|
|
12
|
-
} from '@modelcontextprotocol/sdk/types.js';
|
|
13
|
-
import { BaseMCPServer, MCPToolResult } from './shared/base-mcp-server.js';
|
|
14
|
-
import { AgentContext } from './shared/mcp-memory-manager.js';
|
|
15
|
-
import { ScopeManager, DeploymentContext } from '../managers/scope-manager.js';
|
|
16
|
-
import { GlobalScopeStrategy, ScopeType } from '../strategies/global-scope-strategy.js';
|
|
17
|
-
import { artifactTracker } from '../utils/artifact-tracker.js';
|
|
18
|
-
import { promises as fs } from 'fs';
|
|
19
|
-
|
|
20
|
-
export class ServiceNowDeploymentMCP extends BaseMCPServer {
|
|
21
|
-
private scopeManager: ScopeManager;
|
|
22
|
-
private globalScopeStrategy: GlobalScopeStrategy;
|
|
23
|
-
|
|
24
|
-
constructor() {
|
|
25
|
-
super('servicenow-deployment', '2.0.0');
|
|
26
|
-
|
|
27
|
-
// Initialize scope management
|
|
28
|
-
this.scopeManager = new ScopeManager({
|
|
29
|
-
defaultScope: ScopeType.GLOBAL,
|
|
30
|
-
allowFallback: true,
|
|
31
|
-
validatePermissions: true,
|
|
32
|
-
enableMigration: false
|
|
33
|
-
});
|
|
34
|
-
this.globalScopeStrategy = new GlobalScopeStrategy();
|
|
35
|
-
|
|
36
|
-
// Start artifact tracking session
|
|
37
|
-
artifactTracker.startSession();
|
|
38
|
-
|
|
39
|
-
this.setupHandlers();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
private setupHandlers() {
|
|
43
|
-
// Define available tools
|
|
44
|
-
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
45
|
-
tools: [
|
|
46
|
-
{
|
|
47
|
-
name: 'snow_deploy',
|
|
48
|
-
description: 'AGENT-AWARE DEPLOYMENT - Complete deployment workflow with automatic update set management, agent coordination, and memory integration',
|
|
49
|
-
inputSchema: {
|
|
50
|
-
type: 'object',
|
|
51
|
-
properties: {
|
|
52
|
-
type: {
|
|
53
|
-
type: 'string',
|
|
54
|
-
enum: ['widget', 'flow', 'application', 'script', 'business_rule', 'table', 'batch', 'xml_update_set'],
|
|
55
|
-
description: 'Type of artifact to deploy (use xml_update_set for XML import)'
|
|
56
|
-
},
|
|
57
|
-
xml_file_path: {
|
|
58
|
-
type: 'string',
|
|
59
|
-
description: 'Path to XML update set file (for xml_update_set type)'
|
|
60
|
-
},
|
|
61
|
-
auto_preview: {
|
|
62
|
-
type: 'boolean',
|
|
63
|
-
description: 'Automatically preview the update set after import (default: true)',
|
|
64
|
-
default: true
|
|
65
|
-
},
|
|
66
|
-
auto_commit: {
|
|
67
|
-
type: 'boolean',
|
|
68
|
-
description: 'Automatically commit the update set if preview is clean (default: true)',
|
|
69
|
-
default: true
|
|
70
|
-
},
|
|
71
|
-
instruction: {
|
|
72
|
-
type: 'string',
|
|
73
|
-
description: 'Natural language instruction for what to create (for flows/widgets)'
|
|
74
|
-
},
|
|
75
|
-
config: {
|
|
76
|
-
type: 'object',
|
|
77
|
-
description: 'Artifact configuration (alternative to instruction for direct config)'
|
|
78
|
-
},
|
|
79
|
-
artifacts: {
|
|
80
|
-
type: 'array',
|
|
81
|
-
description: 'For batch deployments - array of artifacts',
|
|
82
|
-
items: {
|
|
83
|
-
type: 'object',
|
|
84
|
-
properties: {
|
|
85
|
-
type: { type: 'string' },
|
|
86
|
-
config: { type: 'object' },
|
|
87
|
-
instruction: { type: 'string' }
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
// Agent context parameters
|
|
92
|
-
session_id: {
|
|
93
|
-
type: 'string',
|
|
94
|
-
description: 'Agent session ID for coordination'
|
|
95
|
-
},
|
|
96
|
-
agent_id: {
|
|
97
|
-
type: 'string',
|
|
98
|
-
description: 'Deploying agent ID'
|
|
99
|
-
},
|
|
100
|
-
agent_type: {
|
|
101
|
-
type: 'string',
|
|
102
|
-
description: 'Type of agent performing deployment'
|
|
103
|
-
},
|
|
104
|
-
// Deployment options
|
|
105
|
-
auto_update_set: {
|
|
106
|
-
type: 'boolean',
|
|
107
|
-
description: 'Automatically ensure active Update Set session (default: true)',
|
|
108
|
-
default: true
|
|
109
|
-
},
|
|
110
|
-
fallback_strategy: {
|
|
111
|
-
type: 'string',
|
|
112
|
-
enum: ['manual_steps', 'update_set_only', 'none'],
|
|
113
|
-
description: 'Strategy when direct deployment fails (default: manual_steps)',
|
|
114
|
-
default: 'manual_steps'
|
|
115
|
-
},
|
|
116
|
-
permission_escalation: {
|
|
117
|
-
type: 'string',
|
|
118
|
-
enum: ['auto_request', 'manual', 'none'],
|
|
119
|
-
description: 'How to handle permission errors (default: auto_request)',
|
|
120
|
-
default: 'auto_request'
|
|
121
|
-
},
|
|
122
|
-
deployment_context: {
|
|
123
|
-
type: 'string',
|
|
124
|
-
description: 'Context for Update Set naming (e.g., "incident widget", "approval flow")'
|
|
125
|
-
},
|
|
126
|
-
// Batch options
|
|
127
|
-
parallel: {
|
|
128
|
-
type: 'boolean',
|
|
129
|
-
description: 'Deploy batch artifacts in parallel',
|
|
130
|
-
default: false
|
|
131
|
-
},
|
|
132
|
-
transaction_mode: {
|
|
133
|
-
type: 'boolean',
|
|
134
|
-
description: 'All-or-nothing batch deployment',
|
|
135
|
-
default: true
|
|
136
|
-
},
|
|
137
|
-
dry_run: {
|
|
138
|
-
type: 'boolean',
|
|
139
|
-
description: 'Validate without deploying',
|
|
140
|
-
default: false
|
|
141
|
-
}
|
|
142
|
-
},
|
|
143
|
-
required: ['type']
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
name: 'snow_deployment_status',
|
|
148
|
-
description: 'Check deployment status and history with agent tracking',
|
|
149
|
-
inputSchema: {
|
|
150
|
-
type: 'object',
|
|
151
|
-
properties: {
|
|
152
|
-
session_id: { type: 'string', description: 'Session ID to filter by' },
|
|
153
|
-
limit: { type: 'number', description: 'Number of recent deployments to show', default: 10 },
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
name: 'snow_validate_deployment',
|
|
159
|
-
description: 'Validate a deployment before executing with agent coordination',
|
|
160
|
-
inputSchema: {
|
|
161
|
-
type: 'object',
|
|
162
|
-
properties: {
|
|
163
|
-
type: { type: 'string', enum: ['widget', 'workflow', 'application'] },
|
|
164
|
-
artifact: { type: 'object', description: 'The artifact to validate' },
|
|
165
|
-
session_id: { type: 'string' },
|
|
166
|
-
agent_id: { type: 'string' }
|
|
167
|
-
},
|
|
168
|
-
required: ['type', 'artifact']
|
|
169
|
-
}
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
name: 'snow_rollback_deployment',
|
|
173
|
-
description: 'Rollback a deployment with agent coordination',
|
|
174
|
-
inputSchema: {
|
|
175
|
-
type: 'object',
|
|
176
|
-
properties: {
|
|
177
|
-
update_set_id: { type: 'string', description: 'Update set sys_id to rollback' },
|
|
178
|
-
reason: { type: 'string', description: 'Reason for rollback' },
|
|
179
|
-
session_id: { type: 'string' },
|
|
180
|
-
agent_id: { type: 'string' }
|
|
181
|
-
},
|
|
182
|
-
required: ['update_set_id', 'reason']
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
]
|
|
186
|
-
}));
|
|
187
|
-
|
|
188
|
-
// Handle tool calls
|
|
189
|
-
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
190
|
-
const { name, arguments: args } = request.params;
|
|
191
|
-
|
|
192
|
-
try {
|
|
193
|
-
switch (name) {
|
|
194
|
-
case 'snow_deploy':
|
|
195
|
-
return await this.deployWithAgentCoordination(args);
|
|
196
|
-
case 'snow_deployment_status':
|
|
197
|
-
return await this.getDeploymentStatusWithContext(args);
|
|
198
|
-
case 'snow_validate_deployment':
|
|
199
|
-
return await this.validateDeploymentWithContext(args);
|
|
200
|
-
case 'snow_rollback_deployment':
|
|
201
|
-
return await this.rollbackDeploymentWithContext(args);
|
|
202
|
-
default:
|
|
203
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
204
|
-
}
|
|
205
|
-
} catch (error) {
|
|
206
|
-
return this.createErrorResponse(
|
|
207
|
-
`Tool execution failed: ${name}`,
|
|
208
|
-
error
|
|
209
|
-
);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Main deployment method with full agent integration and intelligent workflow
|
|
216
|
-
*/
|
|
217
|
-
private async deployWithAgentCoordination(args: any): Promise<MCPToolResult> {
|
|
218
|
-
return await this.executeWithAgentContext(
|
|
219
|
-
'snow_deploy',
|
|
220
|
-
args,
|
|
221
|
-
async (context) => {
|
|
222
|
-
// 🔧 STEP 1: MANDATORY Authentication and Connection Validation
|
|
223
|
-
this.logger.info('🔍 Step 1: Validating ServiceNow connection...');
|
|
224
|
-
const connectionResult = await this.validateServiceNowConnection();
|
|
225
|
-
if (!connectionResult.success) {
|
|
226
|
-
return this.createAuthenticationError(connectionResult.error);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Assert no mock data
|
|
230
|
-
this.assertNoMockData('deployment');
|
|
231
|
-
|
|
232
|
-
// 🔧 STEP 2: Update Set Management
|
|
233
|
-
this.logger.info('📦 Step 2: Ensuring Update Set for tracking...');
|
|
234
|
-
await this.reportProgress(context, 15, 'Setting up Update Set');
|
|
235
|
-
|
|
236
|
-
const purpose = args.type === 'batch' ? 'Batch Deployment' : `${args.type} Deployment`;
|
|
237
|
-
const updateSetId = await this.ensureUpdateSet(context, purpose);
|
|
238
|
-
|
|
239
|
-
if (!updateSetId) {
|
|
240
|
-
this.logger.warn('⚠️ No Update Set created - changes will not be tracked');
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 🔧 STEP 3: Smart Artifact Discovery (DRY principle)
|
|
244
|
-
if (args.type !== 'batch' && (args.config?.name || args.instruction)) {
|
|
245
|
-
this.logger.info('🔍 Step 3: Discovering existing artifacts...');
|
|
246
|
-
await this.reportProgress(context, 25, 'Checking for existing artifacts');
|
|
247
|
-
|
|
248
|
-
const artifactName = args.config?.name || this.extractNameFromInstruction(args.instruction);
|
|
249
|
-
if (artifactName) {
|
|
250
|
-
const discovery = await this.discoverExistingArtifacts(
|
|
251
|
-
args.type,
|
|
252
|
-
artifactName,
|
|
253
|
-
this.extractSearchTermsFromInstruction(args.instruction)
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
if (discovery.found) {
|
|
257
|
-
this.logger.info(`🔍 Found ${discovery.artifacts.length} existing artifacts`);
|
|
258
|
-
// Store discovery info in memory for reference
|
|
259
|
-
await this.memory.updateSharedContext({
|
|
260
|
-
session_id: context.session_id,
|
|
261
|
-
context_key: `discovery_${args.type}`,
|
|
262
|
-
context_value: JSON.stringify({
|
|
263
|
-
artifacts: discovery.artifacts,
|
|
264
|
-
suggestions: discovery.suggestions,
|
|
265
|
-
timestamp: new Date().toISOString()
|
|
266
|
-
}),
|
|
267
|
-
created_by_agent: context.agent_id
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Get session context for coordination
|
|
274
|
-
const sessionContext = await this.getSessionContext(context.session_id);
|
|
275
|
-
|
|
276
|
-
// Report progress
|
|
277
|
-
await this.reportProgress(context, 35, 'Starting deployment');
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
// Handle different deployment types
|
|
281
|
-
let result;
|
|
282
|
-
if (args.type === 'batch') {
|
|
283
|
-
result = await this.deployBatch(args, context, updateSetId);
|
|
284
|
-
} else if (args.type === 'xml_update_set') {
|
|
285
|
-
result = await this.deployXMLUpdateSet(args, context);
|
|
286
|
-
} else {
|
|
287
|
-
result = await this.deploySingleArtifact(args, context, updateSetId);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Add discovery information to successful results (from memory context)
|
|
291
|
-
try {
|
|
292
|
-
const discoveryResults = await this.memory.query(
|
|
293
|
-
'SELECT context_value FROM shared_context WHERE session_id = ? AND context_key = ?',
|
|
294
|
-
[context.session_id, `discovery_${args.type}`]
|
|
295
|
-
);
|
|
296
|
-
|
|
297
|
-
if (discoveryResults.length > 0) {
|
|
298
|
-
const discovery = JSON.parse(discoveryResults[0].context_value);
|
|
299
|
-
if (discovery.artifacts && discovery.artifacts.length > 0) {
|
|
300
|
-
result.content.push({
|
|
301
|
-
type: 'text',
|
|
302
|
-
text: `\n📋 Artifact Discovery Results:\n${discovery.suggestions?.join('\n') || ''}`
|
|
303
|
-
});
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
} catch (discoveryError) {
|
|
307
|
-
this.logger.warn('Could not retrieve discovery information', discoveryError);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return result;
|
|
311
|
-
|
|
312
|
-
} catch (error) {
|
|
313
|
-
this.logger.error('Deployment failed, attempting intelligent recovery...', error);
|
|
314
|
-
|
|
315
|
-
// Use enhanced error handling with fallback options
|
|
316
|
-
const fallbackOptions = {
|
|
317
|
-
enableRetry: true,
|
|
318
|
-
enableScopeEscalation: args.permission_escalation === 'auto_request',
|
|
319
|
-
enableManualSteps: args.fallback_strategy === 'manual_steps'
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const errorResult = await this.handleServiceNowError(
|
|
323
|
-
error,
|
|
324
|
-
`${args.type} deployment`,
|
|
325
|
-
context,
|
|
326
|
-
fallbackOptions
|
|
327
|
-
);
|
|
328
|
-
|
|
329
|
-
// If error handling provided a recovery solution
|
|
330
|
-
if (errorResult.content.some(c => c.text?.includes('can_continue: true'))) {
|
|
331
|
-
this.logger.info('✅ Error recovered - retrying deployment...');
|
|
332
|
-
|
|
333
|
-
try {
|
|
334
|
-
// Retry the deployment after recovery
|
|
335
|
-
await this.reportProgress(context, 85, 'Retrying after error recovery');
|
|
336
|
-
|
|
337
|
-
let retryResult;
|
|
338
|
-
if (args.type === 'batch') {
|
|
339
|
-
retryResult = await this.deployBatch(args, context, updateSetId);
|
|
340
|
-
} else {
|
|
341
|
-
retryResult = await this.deploySingleArtifact(args, context, updateSetId);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Add recovery note to success result
|
|
345
|
-
retryResult.content.push({
|
|
346
|
-
type: 'text',
|
|
347
|
-
text: '\n✅ Deployment succeeded after automatic error recovery'
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
return retryResult;
|
|
351
|
-
|
|
352
|
-
} catch (retryError) {
|
|
353
|
-
this.logger.error('Retry after recovery also failed', retryError);
|
|
354
|
-
|
|
355
|
-
// Request Queen intervention for persistent failures
|
|
356
|
-
await this.requestQueenIntervention(context, {
|
|
357
|
-
type: 'deployment_failure_persistent',
|
|
358
|
-
priority: 'critical',
|
|
359
|
-
description: `Failed to deploy ${args.type} even after error recovery: ${retryError instanceof Error ? retryError.message : String(retryError)}`,
|
|
360
|
-
attempted_solutions: ['direct_deployment', 'error_recovery', 'retry_after_recovery']
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
return errorResult; // Return the error recovery guidance
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// If no automatic recovery possible, request Queen intervention
|
|
368
|
-
await this.requestQueenIntervention(context, {
|
|
369
|
-
type: 'deployment_failure',
|
|
370
|
-
priority: 'high',
|
|
371
|
-
description: `Failed to deploy ${args.type}: ${error instanceof Error ? error.message : String(error)}`,
|
|
372
|
-
attempted_solutions: ['direct_deployment', 'intelligent_error_handling']
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
return errorResult; // Return the error recovery guidance instead of throwing
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Deploy a single artifact with memory tracking
|
|
383
|
-
*/
|
|
384
|
-
private async deploySingleArtifact(args: any, context: AgentContext, updateSetId?: string): Promise<MCPToolResult> {
|
|
385
|
-
const { type, config, instruction } = args;
|
|
386
|
-
|
|
387
|
-
// Report planning phase
|
|
388
|
-
await this.reportProgress(context, 20, 'Planning deployment');
|
|
389
|
-
|
|
390
|
-
// Ensure update set if requested
|
|
391
|
-
let finalUpdateSetId = updateSetId;
|
|
392
|
-
if (args.auto_update_set !== false && !finalUpdateSetId) {
|
|
393
|
-
finalUpdateSetId = await this.ensureUpdateSet(context, config?.name || 'artifact');
|
|
394
|
-
|
|
395
|
-
await this.reportProgress(context, 30, 'Update set ready');
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
// Deploy based on type
|
|
399
|
-
let result: any;
|
|
400
|
-
await this.reportProgress(context, 50, `Deploying ${type}`);
|
|
401
|
-
|
|
402
|
-
switch (type) {
|
|
403
|
-
case 'widget':
|
|
404
|
-
result = await this.deployWidget(config, context, finalUpdateSetId);
|
|
405
|
-
break;
|
|
406
|
-
case 'flow':
|
|
407
|
-
result = await this.deployFlow(config || { instruction }, context, finalUpdateSetId);
|
|
408
|
-
break;
|
|
409
|
-
case 'application':
|
|
410
|
-
result = await this.deployApplication(config, context, finalUpdateSetId);
|
|
411
|
-
break;
|
|
412
|
-
case 'script':
|
|
413
|
-
case 'business_rule':
|
|
414
|
-
result = await this.deployScript(type, config, context, finalUpdateSetId);
|
|
415
|
-
break;
|
|
416
|
-
case 'xml_update_set':
|
|
417
|
-
result = await this.deployXMLUpdateSet(config, context);
|
|
418
|
-
break;
|
|
419
|
-
default:
|
|
420
|
-
throw new Error(`Unsupported deployment type: ${type}`);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// Store artifact in memory
|
|
424
|
-
await this.storeArtifact(context, {
|
|
425
|
-
sys_id: result.sys_id,
|
|
426
|
-
type,
|
|
427
|
-
name: result.name || config?.name || 'unnamed',
|
|
428
|
-
description: config?.description,
|
|
429
|
-
config,
|
|
430
|
-
update_set_id: finalUpdateSetId
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
// Record deployment in history
|
|
434
|
-
await this.memory.recordDeployment(
|
|
435
|
-
context.session_id,
|
|
436
|
-
result.sys_id,
|
|
437
|
-
type,
|
|
438
|
-
true,
|
|
439
|
-
context.agent_id
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
await this.reportProgress(context, 90, 'Finalizing deployment');
|
|
443
|
-
|
|
444
|
-
// Notify next agent if needed
|
|
445
|
-
if (result.next_agent) {
|
|
446
|
-
await this.notifyHandoff(context, result.next_agent, {
|
|
447
|
-
type,
|
|
448
|
-
sys_id: result.sys_id,
|
|
449
|
-
next_steps: result.next_steps || []
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
await this.reportProgress(context, 100, 'Deployment complete');
|
|
454
|
-
|
|
455
|
-
return this.createSuccessResponse(
|
|
456
|
-
`Successfully deployed ${type}: ${result.name}`,
|
|
457
|
-
{
|
|
458
|
-
sys_id: result.sys_id,
|
|
459
|
-
name: result.name,
|
|
460
|
-
update_set_id: finalUpdateSetId,
|
|
461
|
-
deployment_details: result
|
|
462
|
-
},
|
|
463
|
-
{
|
|
464
|
-
agent_id: context.agent_id,
|
|
465
|
-
session_id: context.session_id,
|
|
466
|
-
artifacts_created: [result.sys_id]
|
|
467
|
-
}
|
|
468
|
-
);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
/**
|
|
472
|
-
* Deploy multiple artifacts in batch
|
|
473
|
-
*/
|
|
474
|
-
private async deployBatch(args: any, context: AgentContext, updateSetId?: string): Promise<MCPToolResult> {
|
|
475
|
-
const { artifacts, parallel, transaction_mode, dry_run } = args;
|
|
476
|
-
|
|
477
|
-
if (!artifacts || !Array.isArray(artifacts)) {
|
|
478
|
-
throw new Error('Batch deployment requires artifacts array');
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
await this.reportProgress(context, 10, `Preparing batch deployment of ${artifacts.length} artifacts`);
|
|
482
|
-
|
|
483
|
-
// Dry run validation
|
|
484
|
-
if (dry_run) {
|
|
485
|
-
const validationResults = await Promise.all(
|
|
486
|
-
artifacts.map(artifact => this.validateArtifact(artifact, context))
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
return this.createSuccessResponse(
|
|
490
|
-
'Dry run completed - all artifacts validated',
|
|
491
|
-
{ validationResults }
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Create update set for batch
|
|
496
|
-
const batchUpdateSetId = await this.ensureUpdateSet(context, `Batch deployment`);
|
|
497
|
-
|
|
498
|
-
const results: any[] = [];
|
|
499
|
-
const errors: any[] = [];
|
|
500
|
-
|
|
501
|
-
try {
|
|
502
|
-
if (parallel) {
|
|
503
|
-
// Deploy in parallel
|
|
504
|
-
await this.reportProgress(context, 30, 'Deploying artifacts in parallel');
|
|
505
|
-
|
|
506
|
-
const deploymentPromises = artifacts.map((artifact, index) =>
|
|
507
|
-
this.deploySingleArtifact(
|
|
508
|
-
{ ...artifact, auto_update_set: false },
|
|
509
|
-
context
|
|
510
|
-
).catch(error => {
|
|
511
|
-
errors.push({ artifact, error: error.message });
|
|
512
|
-
return null;
|
|
513
|
-
})
|
|
514
|
-
);
|
|
515
|
-
|
|
516
|
-
const parallelResults = await Promise.all(deploymentPromises);
|
|
517
|
-
results.push(...parallelResults.filter(r => r !== null));
|
|
518
|
-
|
|
519
|
-
} else {
|
|
520
|
-
// Deploy sequentially
|
|
521
|
-
for (let i = 0; i < artifacts.length; i++) {
|
|
522
|
-
const artifact = artifacts[i];
|
|
523
|
-
await this.reportProgress(
|
|
524
|
-
context,
|
|
525
|
-
30 + (60 * i / artifacts.length),
|
|
526
|
-
`Deploying artifact ${i + 1} of ${artifacts.length}`
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
try {
|
|
530
|
-
const result = await this.deploySingleArtifact(
|
|
531
|
-
{ ...artifact, auto_update_set: false },
|
|
532
|
-
context
|
|
533
|
-
);
|
|
534
|
-
results.push(result);
|
|
535
|
-
} catch (error) {
|
|
536
|
-
errors.push({ artifact, error: error instanceof Error ? error.message : String(error) });
|
|
537
|
-
|
|
538
|
-
if (transaction_mode) {
|
|
539
|
-
// Rollback on error in transaction mode
|
|
540
|
-
await this.rollbackBatch(results, batchUpdateSetId, context);
|
|
541
|
-
throw new Error(`Batch deployment failed at artifact ${i + 1}: ${error}`);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// Check if we need to rollback
|
|
548
|
-
if (transaction_mode && errors.length > 0) {
|
|
549
|
-
await this.rollbackBatch(results, batchUpdateSetId, context);
|
|
550
|
-
throw new Error(`Batch deployment failed with ${errors.length} errors`);
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
await this.reportProgress(context, 100, 'Batch deployment complete');
|
|
554
|
-
|
|
555
|
-
return this.createSuccessResponse(
|
|
556
|
-
`Batch deployment completed: ${results.length} succeeded, ${errors.length} failed`,
|
|
557
|
-
{
|
|
558
|
-
successful: results.length,
|
|
559
|
-
failed: errors.length,
|
|
560
|
-
errors: errors.length > 0 ? errors : undefined,
|
|
561
|
-
update_set_id: batchUpdateSetId
|
|
562
|
-
}
|
|
563
|
-
);
|
|
564
|
-
|
|
565
|
-
} catch (error) {
|
|
566
|
-
// Record failed deployment
|
|
567
|
-
await this.memory.recordDeployment(
|
|
568
|
-
context.session_id,
|
|
569
|
-
'batch_deployment',
|
|
570
|
-
'batch',
|
|
571
|
-
false,
|
|
572
|
-
context.agent_id,
|
|
573
|
-
error instanceof Error ? error.message : String(error)
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
throw error;
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
/**
|
|
581
|
-
* Deploy XML Update Set to ServiceNow
|
|
582
|
-
*/
|
|
583
|
-
private async deployXMLUpdateSet(args: any, context: AgentContext): Promise<MCPToolResult> {
|
|
584
|
-
const { xml_file_path, auto_preview = true, auto_commit = true } = args;
|
|
585
|
-
|
|
586
|
-
if (!xml_file_path) {
|
|
587
|
-
throw new Error('XML file path is required for xml_update_set deployment');
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
this.logger.info('🚀 Deploying XML Update Set', {
|
|
591
|
-
file: xml_file_path,
|
|
592
|
-
auto_preview,
|
|
593
|
-
auto_commit
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
try {
|
|
597
|
-
// Read XML file
|
|
598
|
-
await this.reportProgress(context, 20, 'Reading XML file');
|
|
599
|
-
const xmlContent = await fs.readFile(xml_file_path, 'utf-8');
|
|
600
|
-
|
|
601
|
-
// Import XML as remote update set
|
|
602
|
-
await this.reportProgress(context, 40, 'Importing XML to ServiceNow');
|
|
603
|
-
const importResponse = await this.client.makeRequest({
|
|
604
|
-
method: 'POST',
|
|
605
|
-
url: '/api/now/table/sys_remote_update_set',
|
|
606
|
-
headers: {
|
|
607
|
-
'Content-Type': 'application/xml',
|
|
608
|
-
'Accept': 'application/json'
|
|
609
|
-
},
|
|
610
|
-
data: xmlContent
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
if (!importResponse.result || !importResponse.result.sys_id) {
|
|
614
|
-
throw new Error('Failed to import XML update set');
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
const remoteUpdateSetId = importResponse.result.sys_id;
|
|
618
|
-
this.logger.info('✅ XML imported successfully', { sys_id: remoteUpdateSetId });
|
|
619
|
-
|
|
620
|
-
// Load the update set
|
|
621
|
-
await this.reportProgress(context, 60, 'Loading update set');
|
|
622
|
-
await this.client.makeRequest({
|
|
623
|
-
method: 'PUT',
|
|
624
|
-
url: `/api/now/table/sys_remote_update_set/${remoteUpdateSetId}`,
|
|
625
|
-
data: {
|
|
626
|
-
state: 'loaded'
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
// Find the loaded update set
|
|
631
|
-
const loadedResponse = await this.client.makeRequest({
|
|
632
|
-
method: 'GET',
|
|
633
|
-
url: '/api/now/table/sys_update_set',
|
|
634
|
-
params: {
|
|
635
|
-
sysparm_query: `remote_sys_id=${remoteUpdateSetId}`,
|
|
636
|
-
sysparm_limit: 1
|
|
637
|
-
}
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
if (!loadedResponse.result || loadedResponse.result.length === 0) {
|
|
641
|
-
throw new Error('Failed to find loaded update set');
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
const updateSetId = loadedResponse.result[0].sys_id;
|
|
645
|
-
const updateSetName = loadedResponse.result[0].name;
|
|
646
|
-
|
|
647
|
-
// Preview if requested
|
|
648
|
-
if (auto_preview) {
|
|
649
|
-
await this.reportProgress(context, 80, 'Previewing update set');
|
|
650
|
-
|
|
651
|
-
const previewResponse = await this.client.makeRequest({
|
|
652
|
-
method: 'POST',
|
|
653
|
-
url: `/api/now/table/sys_update_set/${updateSetId}/preview`
|
|
654
|
-
});
|
|
655
|
-
|
|
656
|
-
// Check preview results
|
|
657
|
-
const previewProblems = await this.client.makeRequest({
|
|
658
|
-
method: 'GET',
|
|
659
|
-
url: '/api/now/table/sys_update_preview_problem',
|
|
660
|
-
params: {
|
|
661
|
-
sysparm_query: `update_set=${updateSetId}`,
|
|
662
|
-
sysparm_limit: 100
|
|
663
|
-
}
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
if (previewProblems.result && previewProblems.result.length > 0) {
|
|
667
|
-
const problems = previewProblems.result.map((p: any) =>
|
|
668
|
-
`- ${p.type}: ${p.description}`
|
|
669
|
-
).join('\n');
|
|
670
|
-
|
|
671
|
-
if (auto_commit) {
|
|
672
|
-
this.logger.warn('Preview found problems, skipping auto-commit', { problems });
|
|
673
|
-
}
|
|
674
|
-
|
|
675
|
-
return this.createSuccessResponse(
|
|
676
|
-
'XML imported and previewed with problems',
|
|
677
|
-
{
|
|
678
|
-
update_set_id: updateSetId,
|
|
679
|
-
update_set_name: updateSetName,
|
|
680
|
-
preview_status: 'problems_found',
|
|
681
|
-
problems: previewProblems.result,
|
|
682
|
-
next_steps: [
|
|
683
|
-
'1. Review preview problems in ServiceNow',
|
|
684
|
-
'2. Resolve any issues',
|
|
685
|
-
'3. Commit manually when ready'
|
|
686
|
-
]
|
|
687
|
-
}
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
// Commit if clean and requested
|
|
692
|
-
if (auto_commit) {
|
|
693
|
-
await this.reportProgress(context, 95, 'Committing update set');
|
|
694
|
-
|
|
695
|
-
await this.client.makeRequest({
|
|
696
|
-
method: 'POST',
|
|
697
|
-
url: `/api/now/table/sys_update_set/${updateSetId}/commit`
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
return this.createSuccessResponse(
|
|
701
|
-
'✅ XML Update Set imported, previewed, and committed successfully!',
|
|
702
|
-
{
|
|
703
|
-
update_set_id: updateSetId,
|
|
704
|
-
update_set_name: updateSetName,
|
|
705
|
-
status: 'committed',
|
|
706
|
-
flow_location: 'Flow Designer > Designer',
|
|
707
|
-
next_steps: [
|
|
708
|
-
'1. Navigate to Flow Designer',
|
|
709
|
-
'2. Your flow should be visible in the list',
|
|
710
|
-
'3. Open the flow to verify all components'
|
|
711
|
-
]
|
|
712
|
-
}
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// Return success without preview/commit
|
|
718
|
-
return this.createSuccessResponse(
|
|
719
|
-
'XML Update Set imported successfully',
|
|
720
|
-
{
|
|
721
|
-
update_set_id: updateSetId,
|
|
722
|
-
update_set_name: updateSetName,
|
|
723
|
-
status: 'imported',
|
|
724
|
-
next_steps: [
|
|
725
|
-
'1. Navigate to System Update Sets > Local Update Sets',
|
|
726
|
-
'2. Find your update set: ' + updateSetName,
|
|
727
|
-
'3. Click Preview Update Set',
|
|
728
|
-
'4. Review and commit when ready'
|
|
729
|
-
]
|
|
730
|
-
}
|
|
731
|
-
);
|
|
732
|
-
|
|
733
|
-
} catch (error) {
|
|
734
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
735
|
-
|
|
736
|
-
if (errorMsg.includes('ENOENT')) {
|
|
737
|
-
throw new Error(`XML file not found: ${xml_file_path}`);
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
throw new Error(`Failed to deploy XML update set: ${errorMsg}`);
|
|
741
|
-
}
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
/**
|
|
745
|
-
* Deploy widget with agent tracking and intelligent error handling
|
|
746
|
-
*/
|
|
747
|
-
private async deployWidget(config: any, context: AgentContext, updateSetId?: string): Promise<any> {
|
|
748
|
-
this.logger.info('Deploying widget', {
|
|
749
|
-
name: config.name,
|
|
750
|
-
agent_id: context.agent_id
|
|
751
|
-
});
|
|
752
|
-
|
|
753
|
-
try {
|
|
754
|
-
// Validate widget configuration first
|
|
755
|
-
const validationErrors = this.validateWidgetConfig(config);
|
|
756
|
-
if (validationErrors.length > 0) {
|
|
757
|
-
throw new Error(`Widget validation failed: ${validationErrors.join(', ')}`);
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
// Create widget in ServiceNow
|
|
761
|
-
const widgetData = {
|
|
762
|
-
name: config.name,
|
|
763
|
-
id: config.name,
|
|
764
|
-
template: config.template || '<div>Widget Template</div>',
|
|
765
|
-
css: config.css || '',
|
|
766
|
-
server_script: config.server_script || '',
|
|
767
|
-
client_script: config.client_script || '',
|
|
768
|
-
public: false,
|
|
769
|
-
roles: '',
|
|
770
|
-
active: true
|
|
771
|
-
};
|
|
772
|
-
|
|
773
|
-
const response = await this.client.createRecord('sp_widget', widgetData);
|
|
774
|
-
|
|
775
|
-
if (!response.success || !response.result) {
|
|
776
|
-
// Try to provide more specific error information
|
|
777
|
-
const errorMsg = response.error || 'Unknown error creating widget';
|
|
778
|
-
|
|
779
|
-
// Check for common widget creation issues
|
|
780
|
-
if (errorMsg.includes('duplicate') || errorMsg.includes('already exists')) {
|
|
781
|
-
throw new Error(`Widget '${config.name}' already exists. Consider using a different name or updating the existing widget.`);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if (errorMsg.includes('permission') || errorMsg.includes('access') || errorMsg.includes('403')) {
|
|
785
|
-
throw new Error(`
|
|
786
|
-
🚫 Service Portal Permission Error (403)
|
|
787
|
-
|
|
788
|
-
Even with sp_admin role, this can happen due to:
|
|
789
|
-
|
|
790
|
-
1. **OAuth Scope Restrictions**
|
|
791
|
-
- Go to: System OAuth > Application Registry
|
|
792
|
-
- Set "Accessible from" to "All application scopes"
|
|
793
|
-
|
|
794
|
-
2. **Cross-Scope Access**
|
|
795
|
-
- Switch to Global scope and try again
|
|
796
|
-
- Check ACLs on sp_widget table
|
|
797
|
-
|
|
798
|
-
3. **Missing System Properties**
|
|
799
|
-
- Ensure: glide.service_portal.enable_api_access = true
|
|
800
|
-
|
|
801
|
-
4. **Update Set Required**
|
|
802
|
-
- Widgets must be created in an active Update Set
|
|
803
|
-
- Use: snow_ensure_active_update_set() first
|
|
804
|
-
|
|
805
|
-
🔧 Quick Fix: Try deployment with global scope:
|
|
806
|
-
await snow_deploy({
|
|
807
|
-
type: "widget",
|
|
808
|
-
config: {...},
|
|
809
|
-
scope_preference: "global"
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
📚 See SERVICE_PORTAL_403_FIX.md for detailed troubleshooting.
|
|
813
|
-
`);
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
throw new Error(`Failed to create widget in ServiceNow: ${errorMsg}`);
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
const result = response.result as any;
|
|
820
|
-
const widgetSysId = Array.isArray(result) ? result[0]?.sys_id : result?.sys_id;
|
|
821
|
-
|
|
822
|
-
// 🔧 Track artifact in Update Set
|
|
823
|
-
if (updateSetId) {
|
|
824
|
-
try {
|
|
825
|
-
await this.trackArtifact(widgetSysId, 'widget', config.name, updateSetId);
|
|
826
|
-
} catch (trackingError) {
|
|
827
|
-
this.logger.warn('Update Set tracking failed (widget created but not tracked)', trackingError);
|
|
828
|
-
// Don't fail the deployment, just warn
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
// Determine next agent based on widget complexity
|
|
833
|
-
let nextAgent: string | undefined;
|
|
834
|
-
let nextSteps: string[] = [];
|
|
835
|
-
|
|
836
|
-
if (config.requires_styling || config.template?.includes('class=')) {
|
|
837
|
-
nextAgent = 'ui_specialist';
|
|
838
|
-
nextSteps = ['responsive_styling', 'accessibility_compliance'];
|
|
839
|
-
} else if (config.requires_testing) {
|
|
840
|
-
nextAgent = 'test_agent';
|
|
841
|
-
nextSteps = ['functional_testing', 'performance_testing'];
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
// Log successful deployment
|
|
845
|
-
this.logger.info(`✅ Widget deployed successfully: ${config.name} (${widgetSysId})`);
|
|
846
|
-
|
|
847
|
-
return {
|
|
848
|
-
sys_id: widgetSysId,
|
|
849
|
-
name: config.name,
|
|
850
|
-
table: 'sp_widget',
|
|
851
|
-
next_agent: nextAgent,
|
|
852
|
-
next_steps: nextSteps,
|
|
853
|
-
success: true
|
|
854
|
-
};
|
|
855
|
-
|
|
856
|
-
} catch (error) {
|
|
857
|
-
this.logger.error('Widget deployment failed', error);
|
|
858
|
-
|
|
859
|
-
// Use intelligent error handling for specific recovery guidance
|
|
860
|
-
const errorResult = await this.handleServiceNowError(
|
|
861
|
-
error,
|
|
862
|
-
'widget deployment',
|
|
863
|
-
context,
|
|
864
|
-
{
|
|
865
|
-
enableRetry: true,
|
|
866
|
-
enableScopeEscalation: true,
|
|
867
|
-
enableManualSteps: true
|
|
868
|
-
}
|
|
869
|
-
);
|
|
870
|
-
|
|
871
|
-
// Re-throw with enhanced error information for higher-level handling
|
|
872
|
-
const enhancedError = new Error(`Widget deployment failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
873
|
-
(enhancedError as any).errorRecoveryGuidance = errorResult;
|
|
874
|
-
throw enhancedError;
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
/**
|
|
879
|
-
* Validate widget configuration before deployment
|
|
880
|
-
*/
|
|
881
|
-
private validateWidgetConfig(config: any): string[] {
|
|
882
|
-
const errors: string[] = [];
|
|
883
|
-
|
|
884
|
-
if (!config.name) {
|
|
885
|
-
errors.push('Widget name is required');
|
|
886
|
-
} else {
|
|
887
|
-
// Check for valid widget naming
|
|
888
|
-
if (!/^[a-zA-Z][a-zA-Z0-9_]*$/.test(config.name)) {
|
|
889
|
-
errors.push('Widget name must start with letter and contain only letters, numbers, and underscores');
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
if (config.name.length > 40) {
|
|
893
|
-
errors.push('Widget name must be 40 characters or less');
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
if (!config.template) {
|
|
898
|
-
errors.push('Widget template is required');
|
|
899
|
-
} else {
|
|
900
|
-
// Basic HTML validation
|
|
901
|
-
if (config.template.length > 100000) {
|
|
902
|
-
errors.push('Widget template is too large (max 100KB)');
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
// Validate scripts if provided
|
|
907
|
-
if (config.server_script && config.server_script.length > 1000000) {
|
|
908
|
-
errors.push('Server script is too large (max 1MB)');
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
if (config.client_script && config.client_script.length > 1000000) {
|
|
912
|
-
errors.push('Client script is too large (max 1MB)');
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
return errors;
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
/**
|
|
919
|
-
* Deploy flow with agent tracking
|
|
920
|
-
*/
|
|
921
|
-
private async deployFlow(config: any, context: AgentContext, updateSetId?: string): Promise<any> {
|
|
922
|
-
// For flows with natural language instructions, delegate to flow composer
|
|
923
|
-
if (config.instruction) {
|
|
924
|
-
// Store instruction in shared context for flow builder agent
|
|
925
|
-
await this.memory.updateSharedContext({
|
|
926
|
-
session_id: context.session_id,
|
|
927
|
-
context_key: 'flow_instruction',
|
|
928
|
-
context_value: config.instruction,
|
|
929
|
-
created_by_agent: context.agent_id
|
|
930
|
-
});
|
|
931
|
-
|
|
932
|
-
return {
|
|
933
|
-
sys_id: `pending_flow_${Date.now()}`,
|
|
934
|
-
name: 'Flow from instruction',
|
|
935
|
-
instruction: config.instruction,
|
|
936
|
-
next_agent: 'flow_builder',
|
|
937
|
-
next_steps: ['analyze_requirements', 'create_flow_structure', 'deploy_flow']
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
// Direct flow deployment
|
|
942
|
-
const flowData = {
|
|
943
|
-
name: config.name,
|
|
944
|
-
description: config.description,
|
|
945
|
-
active: config.active !== false,
|
|
946
|
-
sys_scope: 'global'
|
|
947
|
-
};
|
|
948
|
-
|
|
949
|
-
const response = await this.client.createRecord('sys_hub_flow', flowData);
|
|
950
|
-
|
|
951
|
-
if (!response.success || !response.result) {
|
|
952
|
-
throw new Error('Failed to create flow in ServiceNow');
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
const result = response.result as any;
|
|
956
|
-
return {
|
|
957
|
-
sys_id: Array.isArray(result) ? result[0]?.sys_id : result?.sys_id,
|
|
958
|
-
name: config.name,
|
|
959
|
-
table: 'sys_hub_flow'
|
|
960
|
-
};
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
/**
|
|
964
|
-
* Deploy application with scope management
|
|
965
|
-
*/
|
|
966
|
-
private async deployApplication(config: any, context: AgentContext, updateSetId?: string): Promise<any> {
|
|
967
|
-
// Determine deployment scope (simplified)
|
|
968
|
-
const scopeStrategy = config.scope_strategy || 'auto';
|
|
969
|
-
const targetScope = scopeStrategy === 'auto' ? 'global' : (config.scope || 'global');
|
|
970
|
-
|
|
971
|
-
const appData = {
|
|
972
|
-
name: config.name,
|
|
973
|
-
scope: targetScope,
|
|
974
|
-
short_description: config.short_description,
|
|
975
|
-
version: config.version,
|
|
976
|
-
vendor: config.vendor || 'Custom',
|
|
977
|
-
vendor_prefix: config.vendor_prefix || 'x',
|
|
978
|
-
active: config.active !== false
|
|
979
|
-
};
|
|
980
|
-
|
|
981
|
-
const response = await this.client.createRecord('sys_app', appData);
|
|
982
|
-
|
|
983
|
-
if (!response.success || !response.result) {
|
|
984
|
-
throw new Error('Failed to create application in ServiceNow');
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
const result = response.result as any;
|
|
988
|
-
return {
|
|
989
|
-
sys_id: Array.isArray(result) ? result[0]?.sys_id : result?.sys_id,
|
|
990
|
-
name: config.name,
|
|
991
|
-
scope: targetScope,
|
|
992
|
-
table: 'sys_app'
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
/**
|
|
997
|
-
* Deploy script or business rule
|
|
998
|
-
*/
|
|
999
|
-
private async deployScript(type: string, config: any, context: AgentContext, updateSetId?: string): Promise<any> {
|
|
1000
|
-
const table = type === 'script' ? 'sys_script' : 'sys_script';
|
|
1001
|
-
|
|
1002
|
-
const scriptData = {
|
|
1003
|
-
name: config.name,
|
|
1004
|
-
script: config.script,
|
|
1005
|
-
active: config.active !== false,
|
|
1006
|
-
description: config.description
|
|
1007
|
-
};
|
|
1008
|
-
|
|
1009
|
-
const response = await this.client.createRecord(table, scriptData);
|
|
1010
|
-
|
|
1011
|
-
if (!response.success || !response.result) {
|
|
1012
|
-
throw new Error(`Failed to create ${type} in ServiceNow`);
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// Scripts often need testing
|
|
1016
|
-
const result = response.result as any;
|
|
1017
|
-
return {
|
|
1018
|
-
sys_id: Array.isArray(result) ? result[0]?.sys_id : result?.sys_id,
|
|
1019
|
-
name: config.name,
|
|
1020
|
-
table,
|
|
1021
|
-
next_agent: 'test_agent',
|
|
1022
|
-
next_steps: ['syntax_validation', 'unit_testing']
|
|
1023
|
-
};
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
/**
|
|
1027
|
-
* Get deployment status with session filtering
|
|
1028
|
-
*/
|
|
1029
|
-
private async getDeploymentStatusWithContext(args: any): Promise<MCPToolResult> {
|
|
1030
|
-
return await this.executeWithAgentContext(
|
|
1031
|
-
'snow_deployment_status',
|
|
1032
|
-
args,
|
|
1033
|
-
async (context) => {
|
|
1034
|
-
const session_id = args.session_id || context.session_id;
|
|
1035
|
-
|
|
1036
|
-
// Get artifacts from memory
|
|
1037
|
-
const artifacts = await this.memory.getSessionArtifacts(session_id);
|
|
1038
|
-
|
|
1039
|
-
// Get active agents
|
|
1040
|
-
const activeAgents = await this.memory.query(`
|
|
1041
|
-
SELECT * FROM agent_coordination
|
|
1042
|
-
WHERE session_id = ?
|
|
1043
|
-
ORDER BY last_activity DESC
|
|
1044
|
-
`, [session_id]);
|
|
1045
|
-
|
|
1046
|
-
// Get recent deployments
|
|
1047
|
-
const deployments = await this.memory.query(`
|
|
1048
|
-
SELECT * FROM deployment_history
|
|
1049
|
-
WHERE session_id = ?
|
|
1050
|
-
ORDER BY deployment_time DESC
|
|
1051
|
-
LIMIT ?
|
|
1052
|
-
`, [session_id, args.limit || 10]);
|
|
1053
|
-
|
|
1054
|
-
return this.createSuccessResponse(
|
|
1055
|
-
`Deployment status for session ${session_id}`,
|
|
1056
|
-
{
|
|
1057
|
-
artifacts: artifacts.length,
|
|
1058
|
-
active_agents: activeAgents.length,
|
|
1059
|
-
recent_deployments: deployments,
|
|
1060
|
-
session_summary: {
|
|
1061
|
-
total_artifacts: artifacts.length,
|
|
1062
|
-
artifact_types: this.groupByType(artifacts),
|
|
1063
|
-
success_rate: this.calculateSuccessRate(deployments)
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
);
|
|
1067
|
-
}
|
|
1068
|
-
);
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
/**
|
|
1072
|
-
* Validate deployment with agent context
|
|
1073
|
-
*/
|
|
1074
|
-
private async validateDeploymentWithContext(args: any): Promise<MCPToolResult> {
|
|
1075
|
-
return await this.executeWithAgentContext(
|
|
1076
|
-
'snow_validate_deployment',
|
|
1077
|
-
args,
|
|
1078
|
-
async (context) => {
|
|
1079
|
-
const { type, artifact } = args;
|
|
1080
|
-
|
|
1081
|
-
const validationResult = await this.validateArtifact(
|
|
1082
|
-
{ type, config: artifact },
|
|
1083
|
-
context
|
|
1084
|
-
);
|
|
1085
|
-
|
|
1086
|
-
if (validationResult.valid) {
|
|
1087
|
-
return this.createSuccessResponse(
|
|
1088
|
-
`Validation passed for ${type}`,
|
|
1089
|
-
validationResult
|
|
1090
|
-
);
|
|
1091
|
-
} else {
|
|
1092
|
-
// Store validation failure in context
|
|
1093
|
-
await this.memory.updateSharedContext({
|
|
1094
|
-
session_id: context.session_id,
|
|
1095
|
-
context_key: `validation_failure_${type}`,
|
|
1096
|
-
context_value: JSON.stringify(validationResult),
|
|
1097
|
-
created_by_agent: context.agent_id
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
return this.createErrorResponse(
|
|
1101
|
-
`Validation failed for ${type}`,
|
|
1102
|
-
validationResult.errors
|
|
1103
|
-
);
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
);
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
/**
|
|
1110
|
-
* Rollback deployment with coordination
|
|
1111
|
-
*/
|
|
1112
|
-
private async rollbackDeploymentWithContext(args: any): Promise<MCPToolResult> {
|
|
1113
|
-
return await this.executeWithAgentContext(
|
|
1114
|
-
'snow_rollback_deployment',
|
|
1115
|
-
args,
|
|
1116
|
-
async (context) => {
|
|
1117
|
-
const { update_set_id, reason } = args;
|
|
1118
|
-
|
|
1119
|
-
// Notify all agents about rollback
|
|
1120
|
-
await this.memory.sendAgentMessage({
|
|
1121
|
-
session_id: context.session_id,
|
|
1122
|
-
from_agent: context.agent_id,
|
|
1123
|
-
to_agent: 'all',
|
|
1124
|
-
message_type: 'status_update',
|
|
1125
|
-
content: JSON.stringify({
|
|
1126
|
-
action: 'rollback_initiated',
|
|
1127
|
-
update_set_id,
|
|
1128
|
-
reason
|
|
1129
|
-
})
|
|
1130
|
-
});
|
|
1131
|
-
|
|
1132
|
-
// Perform rollback
|
|
1133
|
-
const rollbackResult = await this.client.updateRecord('sys_update_set', update_set_id, {
|
|
1134
|
-
state: 'reverted',
|
|
1135
|
-
description: `Rolled back: ${reason}`
|
|
1136
|
-
});
|
|
1137
|
-
|
|
1138
|
-
if (rollbackResult.success) {
|
|
1139
|
-
// Update deployment history
|
|
1140
|
-
await this.memory.recordDeployment(
|
|
1141
|
-
context.session_id,
|
|
1142
|
-
update_set_id,
|
|
1143
|
-
'rollback',
|
|
1144
|
-
true,
|
|
1145
|
-
context.agent_id
|
|
1146
|
-
);
|
|
1147
|
-
|
|
1148
|
-
return this.createSuccessResponse(
|
|
1149
|
-
`Successfully rolled back update set ${update_set_id}`,
|
|
1150
|
-
{ reason }
|
|
1151
|
-
);
|
|
1152
|
-
} else {
|
|
1153
|
-
throw new Error('Rollback failed');
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
);
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
// Helper methods
|
|
1160
|
-
|
|
1161
|
-
private async validateArtifact(artifact: any, context: AgentContext): Promise<any> {
|
|
1162
|
-
// Simplified validation logic
|
|
1163
|
-
const errors: string[] = [];
|
|
1164
|
-
|
|
1165
|
-
if (!artifact.type) errors.push('Missing artifact type');
|
|
1166
|
-
if (!artifact.config && !artifact.instruction) errors.push('Missing configuration or instruction');
|
|
1167
|
-
|
|
1168
|
-
if (artifact.type === 'widget') {
|
|
1169
|
-
if (!artifact.config?.name) errors.push('Widget name is required');
|
|
1170
|
-
if (!artifact.config?.template) errors.push('Widget template is required');
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
return {
|
|
1174
|
-
valid: errors.length === 0,
|
|
1175
|
-
errors,
|
|
1176
|
-
warnings: []
|
|
1177
|
-
};
|
|
1178
|
-
}
|
|
1179
|
-
|
|
1180
|
-
private async rollbackBatch(results: any[], updateSetId: string, context: AgentContext): Promise<void> {
|
|
1181
|
-
// Rollback logic for batch deployments
|
|
1182
|
-
for (const result of results) {
|
|
1183
|
-
if (result?.data?.sys_id) {
|
|
1184
|
-
try {
|
|
1185
|
-
await this.client.deleteRecord(result.data.table || 'sys_metadata', result.data.sys_id);
|
|
1186
|
-
} catch (error) {
|
|
1187
|
-
this.logger.error('Failed to rollback artifact', error);
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
private groupByType(artifacts: any[]): Record<string, number> {
|
|
1194
|
-
return artifacts.reduce((acc, artifact) => {
|
|
1195
|
-
acc[artifact.artifact_type] = (acc[artifact.artifact_type] || 0) + 1;
|
|
1196
|
-
return acc;
|
|
1197
|
-
}, {});
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
private calculateSuccessRate(deployments: any[]): number {
|
|
1201
|
-
if (deployments.length === 0) return 0;
|
|
1202
|
-
const successful = deployments.filter(d => d.success).length;
|
|
1203
|
-
return Math.round((successful / deployments.length) * 100);
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
/**
|
|
1207
|
-
* Extract artifact name from natural language instruction
|
|
1208
|
-
*/
|
|
1209
|
-
private extractNameFromInstruction(instruction: string): string | null {
|
|
1210
|
-
if (!instruction) return null;
|
|
1211
|
-
|
|
1212
|
-
// Look for patterns like "create a widget called X" or "make a flow named Y"
|
|
1213
|
-
const patterns = [
|
|
1214
|
-
/(?:create|make|build)\s+(?:a|an)?\s*\w+\s+(?:called|named|for)\s+["']?([^"']+)["']?/i,
|
|
1215
|
-
/(?:create|make|build)\s+["']([^"']+)["']\s+\w+/i,
|
|
1216
|
-
/(?:widget|flow|script|rule|table|application)\s+(?:called|named|for)\s+["']?([^"']+)["']?/i,
|
|
1217
|
-
/"([^"]+)"\s+(?:widget|flow|script|rule|table|application)/i,
|
|
1218
|
-
/'([^']+)'\s+(?:widget|flow|script|rule|table|application)/i
|
|
1219
|
-
];
|
|
1220
|
-
|
|
1221
|
-
for (const pattern of patterns) {
|
|
1222
|
-
const match = instruction.match(pattern);
|
|
1223
|
-
if (match && match[1]) {
|
|
1224
|
-
return match[1].trim();
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
// Fallback: look for quoted text that might be a name
|
|
1229
|
-
const quotedMatch = instruction.match(/["']([^"']{3,30})["']/);
|
|
1230
|
-
if (quotedMatch && quotedMatch[1]) {
|
|
1231
|
-
return quotedMatch[1].trim();
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
return null;
|
|
1235
|
-
}
|
|
1236
|
-
|
|
1237
|
-
/**
|
|
1238
|
-
* Extract search terms from instruction for discovery
|
|
1239
|
-
*/
|
|
1240
|
-
private extractSearchTermsFromInstruction(instruction: string): string[] {
|
|
1241
|
-
if (!instruction) return [];
|
|
1242
|
-
|
|
1243
|
-
const terms: string[] = [];
|
|
1244
|
-
|
|
1245
|
-
// Common keywords that might indicate similar functionality
|
|
1246
|
-
const keywords = [
|
|
1247
|
-
'incident', 'dashboard', 'report', 'approval', 'notification', 'user', 'profile',
|
|
1248
|
-
'management', 'tracking', 'monitoring', 'analytics', 'workflow', 'request',
|
|
1249
|
-
'ticket', 'service', 'catalog', 'portal', 'form', 'table', 'list', 'chart',
|
|
1250
|
-
'graph', 'widget', 'flow', 'automation', 'integration', 'api', 'rest',
|
|
1251
|
-
'email', 'alert', 'escalation', 'assignment', 'routing', 'sla', 'metric'
|
|
1252
|
-
];
|
|
1253
|
-
|
|
1254
|
-
const lowercaseInstruction = instruction.toLowerCase();
|
|
1255
|
-
|
|
1256
|
-
// Extract keywords that appear in the instruction
|
|
1257
|
-
for (const keyword of keywords) {
|
|
1258
|
-
if (lowercaseInstruction.includes(keyword)) {
|
|
1259
|
-
terms.push(keyword);
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
// Extract quoted terms
|
|
1264
|
-
const quotedTerms = instruction.match(/["']([^"']{2,20})["']/g);
|
|
1265
|
-
if (quotedTerms) {
|
|
1266
|
-
quotedTerms.forEach(quoted => {
|
|
1267
|
-
const term = quoted.replace(/["']/g, '').trim();
|
|
1268
|
-
if (term.length >= 2 && !terms.includes(term.toLowerCase())) {
|
|
1269
|
-
terms.push(term.toLowerCase());
|
|
1270
|
-
}
|
|
1271
|
-
});
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
// Extract important words (3+ characters, not common words)
|
|
1275
|
-
const words = lowercaseInstruction.match(/\b[a-z]{3,}\b/g) || [];
|
|
1276
|
-
const commonWords = ['the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'day', 'get', 'has', 'him', 'his', 'how', 'man', 'new', 'now', 'old', 'see', 'two', 'who', 'boy', 'did', 'its', 'let', 'put', 'say', 'she', 'too', 'use'];
|
|
1277
|
-
|
|
1278
|
-
for (const word of words) {
|
|
1279
|
-
if (!commonWords.includes(word) && word.length >= 3 && !terms.includes(word)) {
|
|
1280
|
-
terms.push(word);
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
return terms.slice(0, 5); // Limit to 5 terms to avoid too many searches
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// Start the server
|
|
1289
|
-
if (require.main === module) {
|
|
1290
|
-
const server = new ServiceNowDeploymentMCP();
|
|
1291
|
-
server.start().catch(console.error);
|
|
1292
|
-
}
|