snow-flow 2.9.9 → 3.0.0
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/.mcp.json +13 -141
- package/.mcp.json.template +11 -25
- package/README.md +18 -0
- package/claude-flow +81 -0
- package/claude-flow.bat +18 -0
- package/claude-flow.config.json +20 -0
- package/claude-flow.ps1 +24 -0
- package/dist/agents/index.d.ts +3 -10
- package/dist/agents/index.js +10 -50
- package/dist/agents/queen-agent.d.ts +0 -2
- package/dist/agents/queen-agent.js +25 -42
- package/dist/cli.js +1 -1
- package/dist/mcp/servicenow-automation-mcp.js +10 -10
- package/dist/mcp/servicenow-deployment-mcp.js +1050 -169
- package/dist/mcp/servicenow-development-assistant-mcp.js +13 -65
- package/dist/mcp/servicenow-integration-mcp.js +10 -10
- package/dist/mcp/servicenow-machine-learning-mcp.js +15 -15
- package/dist/mcp/servicenow-operations-mcp.js +23 -23
- package/dist/mcp/servicenow-platform-development-mcp.js +9 -9
- package/dist/mcp/servicenow-reporting-analytics-mcp.js +11 -11
- package/dist/mcp/servicenow-security-compliance-mcp.js +11 -11
- package/dist/mcp/servicenow-update-set-mcp.js +9 -9
- package/dist/mcp/shared/reliable-memory-manager.d.ts +78 -0
- package/dist/mcp/shared/reliable-memory-manager.js +268 -0
- package/dist/mcp/snow-flow-mcp.js +341 -87
- package/dist/queen/agent-factory.d.ts +4 -2
- package/dist/queen/agent-factory.js +72 -25
- package/dist/services/tensorflow-ml-service.d.ts +105 -0
- package/dist/services/tensorflow-ml-service.js +456 -0
- package/dist/services/widget-deployment-service.d.ts +107 -0
- package/dist/services/widget-deployment-service.js +332 -0
- package/dist/utils/file-storage-fallback.d.ts +62 -0
- package/dist/utils/file-storage-fallback.js +289 -0
- package/dist/utils/mcp-singleton-enforcer.d.ts +36 -0
- package/dist/utils/mcp-singleton-enforcer.js +201 -0
- package/dist/utils/mcp-timeout-fix.d.ts +53 -0
- package/dist/utils/mcp-timeout-fix.js +221 -0
- package/memory/agents/README.md +31 -0
- package/memory/claude-flow-data.json +1 -1
- package/memory/sessions/README.md +1 -1
- package/package.json +4 -2
- package/src/agents/README.md +192 -0
- package/src/health/README.md +161 -0
- package/src/memory/README.md +240 -0
- package/src/queen/README.md +403 -0
- package/src/schemas/deployment.schema.json +58 -0
- package/src/schemas/flow.schema.json +79 -0
- package/src/schemas/widget.schema.json +72 -0
- package/src/templates/base/application.template.json +45 -0
- package/src/templates/base/business_rule.template.json +33 -0
- package/src/templates/base/script_include.template.json +18 -0
- package/src/templates/base/table.template.json +64 -0
- package/src/templates/base/widget.template.json +25 -0
- package/src/templates/patterns/composite.incident-management.template.json +140 -0
- package/src/templates/patterns/widget.dashboard.template.json +238 -0
- package/src/templates/patterns/widget.datatable.template.json +292 -0
- package/dist/mcp/servicenow-graph-memory-mcp.js +0 -728
- package/servicenow/widgets/openai_incident_classifier/client_controller.js +0 -284
- package/servicenow/widgets/openai_incident_classifier/server_script.js +0 -314
- package/servicenow/widgets/openai_incident_classifier/style.css +0 -354
- package/servicenow/widgets/openai_incident_classifier/template.html +0 -167
- package/servicenow/widgets/openai_incident_classifier/widget.json +0 -86
- package/test-ml-improvements.sh +0 -76
|
@@ -47,52 +47,9 @@ class ServiceNowDeploymentMCP {
|
|
|
47
47
|
setupHandlers() {
|
|
48
48
|
this.server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
|
|
49
49
|
tools: [
|
|
50
|
-
{
|
|
51
|
-
name: 'snow_deploy_widget',
|
|
52
|
-
description: '⚠️ DEPRECATED - Use snow_deploy instead. This tool redirects to the unified deployment system.',
|
|
53
|
-
inputSchema: {
|
|
54
|
-
type: 'object',
|
|
55
|
-
properties: {
|
|
56
|
-
name: { type: 'string', description: 'Widget internal name (e.g., incident_dashboard)' },
|
|
57
|
-
title: { type: 'string', description: 'Widget display title' },
|
|
58
|
-
description: { type: 'string', description: 'Widget description' },
|
|
59
|
-
template: { type: 'string', description: 'HTML template content' },
|
|
60
|
-
css: { type: 'string', description: 'CSS styles' },
|
|
61
|
-
client_script: { type: 'string', description: 'Client-side JavaScript' },
|
|
62
|
-
server_script: { type: 'string', description: 'Server-side script' },
|
|
63
|
-
option_schema: { type: 'string', description: 'Widget options JSON schema' },
|
|
64
|
-
demo_data: { type: 'string', description: 'Demo data JSON' },
|
|
65
|
-
category: { type: 'string', description: 'Widget category' },
|
|
66
|
-
},
|
|
67
|
-
required: ['name', 'title', 'template'],
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'snow_deploy_update_set',
|
|
72
|
-
description: 'Create and populate an update set for deployment',
|
|
73
|
-
inputSchema: {
|
|
74
|
-
type: 'object',
|
|
75
|
-
properties: {
|
|
76
|
-
name: { type: 'string', description: 'Update set name' },
|
|
77
|
-
description: { type: 'string', description: 'Update set description' },
|
|
78
|
-
artifacts: {
|
|
79
|
-
type: 'array',
|
|
80
|
-
description: 'List of artifacts to include',
|
|
81
|
-
items: {
|
|
82
|
-
type: 'object',
|
|
83
|
-
properties: {
|
|
84
|
-
type: { type: 'string', enum: ['widget', 'workflow', 'script', 'table'] },
|
|
85
|
-
sys_id: { type: 'string', description: 'Sys ID of the artifact' },
|
|
86
|
-
},
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
},
|
|
90
|
-
required: ['name', 'artifacts'],
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
50
|
{
|
|
94
51
|
name: 'snow_validate_deployment',
|
|
95
|
-
description: '
|
|
52
|
+
description: 'Validates deployment artifacts for compatibility, dependencies, and permissions before execution. Returns validation report with potential issues.',
|
|
96
53
|
inputSchema: {
|
|
97
54
|
type: 'object',
|
|
98
55
|
properties: {
|
|
@@ -104,7 +61,7 @@ class ServiceNowDeploymentMCP {
|
|
|
104
61
|
},
|
|
105
62
|
{
|
|
106
63
|
name: 'snow_rollback_deployment',
|
|
107
|
-
description: '
|
|
64
|
+
description: 'Performs safe rollback of a failed deployment to previous state. Tracks rollback history and provides recovery recommendations.',
|
|
108
65
|
inputSchema: {
|
|
109
66
|
type: 'object',
|
|
110
67
|
properties: {
|
|
@@ -116,7 +73,7 @@ class ServiceNowDeploymentMCP {
|
|
|
116
73
|
},
|
|
117
74
|
{
|
|
118
75
|
name: 'snow_deployment_status',
|
|
119
|
-
description: '
|
|
76
|
+
description: 'Retrieves comprehensive deployment status including active deployments, recent history, success rates, and performance metrics.',
|
|
120
77
|
inputSchema: {
|
|
121
78
|
type: 'object',
|
|
122
79
|
properties: {
|
|
@@ -126,7 +83,7 @@ class ServiceNowDeploymentMCP {
|
|
|
126
83
|
},
|
|
127
84
|
{
|
|
128
85
|
name: 'snow_export_artifact',
|
|
129
|
-
description: '
|
|
86
|
+
description: 'Exports ServiceNow artifacts (widgets, workflows, applications) to JSON/XML format for backup, version control, or migration purposes.',
|
|
130
87
|
inputSchema: {
|
|
131
88
|
type: 'object',
|
|
132
89
|
properties: {
|
|
@@ -139,7 +96,7 @@ class ServiceNowDeploymentMCP {
|
|
|
139
96
|
},
|
|
140
97
|
{
|
|
141
98
|
name: 'snow_import_artifact',
|
|
142
|
-
description: '
|
|
99
|
+
description: 'Imports previously exported artifacts from JSON/XML files into ServiceNow. Validates compatibility and handles dependencies automatically.',
|
|
143
100
|
inputSchema: {
|
|
144
101
|
type: 'object',
|
|
145
102
|
properties: {
|
|
@@ -152,7 +109,7 @@ class ServiceNowDeploymentMCP {
|
|
|
152
109
|
},
|
|
153
110
|
{
|
|
154
111
|
name: 'snow_clone_instance_artifact',
|
|
155
|
-
description: '
|
|
112
|
+
description: 'Clones artifacts directly between ServiceNow instances (dev→test→prod). Handles authentication, dependency resolution, and data migration.',
|
|
156
113
|
inputSchema: {
|
|
157
114
|
type: 'object',
|
|
158
115
|
properties: {
|
|
@@ -166,7 +123,7 @@ class ServiceNowDeploymentMCP {
|
|
|
166
123
|
},
|
|
167
124
|
{
|
|
168
125
|
name: 'snow_validate_sysid',
|
|
169
|
-
description: '
|
|
126
|
+
description: 'Validates sys_id existence and consistency across tables. Maintains artifact tracking for deployment integrity and rollback capabilities.',
|
|
170
127
|
inputSchema: {
|
|
171
128
|
type: 'object',
|
|
172
129
|
properties: {
|
|
@@ -180,7 +137,7 @@ class ServiceNowDeploymentMCP {
|
|
|
180
137
|
},
|
|
181
138
|
{
|
|
182
139
|
name: 'snow_deployment_debug',
|
|
183
|
-
description: '
|
|
140
|
+
description: 'Provides detailed debugging information including authentication status, permissions, active sessions, and recent deployment logs for troubleshooting.',
|
|
184
141
|
inputSchema: {
|
|
185
142
|
type: 'object',
|
|
186
143
|
properties: {},
|
|
@@ -188,7 +145,7 @@ class ServiceNowDeploymentMCP {
|
|
|
188
145
|
},
|
|
189
146
|
{
|
|
190
147
|
name: 'snow_auth_diagnostics',
|
|
191
|
-
description: '
|
|
148
|
+
description: 'Performs comprehensive authentication and permission diagnostics. Tests OAuth tokens, API access, table permissions, and provides specific remediation steps.',
|
|
192
149
|
inputSchema: {
|
|
193
150
|
type: 'object',
|
|
194
151
|
properties: {
|
|
@@ -199,7 +156,7 @@ class ServiceNowDeploymentMCP {
|
|
|
199
156
|
},
|
|
200
157
|
{
|
|
201
158
|
name: 'snow_preview_widget',
|
|
202
|
-
description: '
|
|
159
|
+
description: 'Renders widget preview with test data for validation before deployment. Simulates Service Portal environment, checks dependencies, and validates data binding.',
|
|
203
160
|
inputSchema: {
|
|
204
161
|
type: 'object',
|
|
205
162
|
properties: {
|
|
@@ -221,7 +178,7 @@ class ServiceNowDeploymentMCP {
|
|
|
221
178
|
},
|
|
222
179
|
{
|
|
223
180
|
name: 'snow_widget_test',
|
|
224
|
-
description: '
|
|
181
|
+
description: 'Executes comprehensive widget testing with multiple data scenarios. Validates client/server scripts, API calls, dependencies, and generates coverage reports.',
|
|
225
182
|
inputSchema: {
|
|
226
183
|
type: 'object',
|
|
227
184
|
properties: {
|
|
@@ -253,23 +210,9 @@ class ServiceNowDeploymentMCP {
|
|
|
253
210
|
required: ['sys_id'],
|
|
254
211
|
},
|
|
255
212
|
},
|
|
256
|
-
{
|
|
257
|
-
name: 'snow_smart_update_set',
|
|
258
|
-
description: 'Smart update set creation with context detection - automatically creates new update sets for new tasks',
|
|
259
|
-
inputSchema: {
|
|
260
|
-
type: 'object',
|
|
261
|
-
properties: {
|
|
262
|
-
detect_context: { type: 'boolean', description: 'Auto-detect task context change', default: true },
|
|
263
|
-
name_prefix: { type: 'string', description: 'Update set name prefix', default: 'AUTO' },
|
|
264
|
-
separate_by_task: { type: 'boolean', description: 'Create new update set for each task', default: true },
|
|
265
|
-
close_previous: { type: 'boolean', description: 'Close previous update set', default: true },
|
|
266
|
-
description: { type: 'string', description: 'Update set description' },
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
},
|
|
270
213
|
{
|
|
271
214
|
name: 'snow_create_solution_package',
|
|
272
|
-
description: '
|
|
215
|
+
description: 'Creates comprehensive solution packages containing multiple related artifacts (widgets, scripts, rules). Manages dependencies and generates deployment documentation.',
|
|
273
216
|
inputSchema: {
|
|
274
217
|
type: 'object',
|
|
275
218
|
properties: {
|
|
@@ -291,38 +234,9 @@ class ServiceNowDeploymentMCP {
|
|
|
291
234
|
required: ['name', 'artifacts'],
|
|
292
235
|
},
|
|
293
236
|
},
|
|
294
|
-
{
|
|
295
|
-
name: 'snow_bulk_deploy',
|
|
296
|
-
description: '⚠️ DEPRECATED - Use snow_deploy with batch configuration instead. This tool redirects to the unified deployment system.',
|
|
297
|
-
inputSchema: {
|
|
298
|
-
type: 'object',
|
|
299
|
-
properties: {
|
|
300
|
-
artifacts: {
|
|
301
|
-
type: 'array',
|
|
302
|
-
description: 'Array of artifacts to deploy',
|
|
303
|
-
items: {
|
|
304
|
-
type: 'object',
|
|
305
|
-
properties: {
|
|
306
|
-
type: { type: 'string', enum: ['widget', 'portal_page', 'script', 'business_rule', 'table', 'application'] },
|
|
307
|
-
sys_id: { type: 'string', description: 'Existing artifact sys_id (for updates)' },
|
|
308
|
-
config: { type: 'object', description: 'Artifact configuration' },
|
|
309
|
-
action: { type: 'string', enum: ['create', 'update', 'deploy'], default: 'deploy' },
|
|
310
|
-
},
|
|
311
|
-
required: ['type', 'config'],
|
|
312
|
-
},
|
|
313
|
-
},
|
|
314
|
-
transaction_mode: { type: 'boolean', description: 'All or nothing deployment', default: true },
|
|
315
|
-
parallel: { type: 'boolean', description: 'Deploy in parallel when possible', default: false },
|
|
316
|
-
dry_run: { type: 'boolean', description: 'Validate without deploying', default: false },
|
|
317
|
-
update_set_name: { type: 'string', description: 'Custom update set name' },
|
|
318
|
-
rollback_on_error: { type: 'boolean', description: 'Rollback all on any failure', default: true },
|
|
319
|
-
},
|
|
320
|
-
required: ['artifacts'],
|
|
321
|
-
},
|
|
322
|
-
},
|
|
323
237
|
{
|
|
324
238
|
name: 'snow_deploy',
|
|
325
|
-
description: '
|
|
239
|
+
description: 'Universal deployment tool for all ServiceNow artifacts. Features automatic update set management, permission escalation, retry logic, and comprehensive error recovery. Primary deployment method for v3.0.0+',
|
|
326
240
|
inputSchema: {
|
|
327
241
|
type: 'object',
|
|
328
242
|
properties: {
|
|
@@ -373,16 +287,6 @@ class ServiceNowDeploymentMCP {
|
|
|
373
287
|
// This allows the MCP server to start without credentials
|
|
374
288
|
// and fail gracefully when tools are actually used
|
|
375
289
|
switch (name) {
|
|
376
|
-
case 'snow_deploy_widget':
|
|
377
|
-
// Deprecated - redirect to unified deploy
|
|
378
|
-
this.logger.warn('snow_deploy_widget is deprecated, redirecting to snow_deploy');
|
|
379
|
-
return await this.handleDeprecatedWidgetDeploy(args);
|
|
380
|
-
case 'snow_deploy_application':
|
|
381
|
-
// Deprecated - redirect to unified deploy
|
|
382
|
-
this.logger.warn('snow_deploy_application is deprecated, redirecting to snow_deploy');
|
|
383
|
-
return await this.handleDeprecatedApplicationDeploy(args);
|
|
384
|
-
case 'snow_deploy_update_set':
|
|
385
|
-
return await this.deployUpdateSet(args);
|
|
386
290
|
case 'snow_validate_deployment':
|
|
387
291
|
return await this.validateDeployment(args);
|
|
388
292
|
case 'snow_rollback_deployment':
|
|
@@ -405,14 +309,8 @@ class ServiceNowDeploymentMCP {
|
|
|
405
309
|
return await this.previewWidget(args);
|
|
406
310
|
case 'snow_widget_test':
|
|
407
311
|
return await this.testWidget(args);
|
|
408
|
-
case 'snow_smart_update_set':
|
|
409
|
-
return await this.smartUpdateSet(args);
|
|
410
312
|
case 'snow_create_solution_package':
|
|
411
313
|
return await this.createSolutionPackage(args);
|
|
412
|
-
case 'snow_bulk_deploy':
|
|
413
|
-
// Deprecated - redirect to unified deploy
|
|
414
|
-
this.logger.warn('snow_bulk_deploy is deprecated, redirecting to snow_deploy with batch configuration');
|
|
415
|
-
return await this.handleDeprecatedBulkDeploy(args);
|
|
416
314
|
case 'snow_deploy':
|
|
417
315
|
return await this.unifiedDeploy(args);
|
|
418
316
|
default:
|
|
@@ -1725,18 +1623,48 @@ ${args.widgets && args.widgets.length > 0 ? args.widgets.map((w, i) => `
|
|
|
1725
1623
|
let usedFallback = false;
|
|
1726
1624
|
let fallbackBusinessRule = null;
|
|
1727
1625
|
try {
|
|
1626
|
+
// Real ServiceNow Flow deployment using proper API calls
|
|
1728
1627
|
switch (flowType) {
|
|
1729
1628
|
case 'flow':
|
|
1730
|
-
|
|
1629
|
+
// Create workflow record in ServiceNow
|
|
1630
|
+
result = await this.client.create('wf_workflow', {
|
|
1631
|
+
name: flowData.name || `flow_${Date.now()}`,
|
|
1632
|
+
description: flowData.description || 'Created by Snow-Flow',
|
|
1633
|
+
table: flowData.table || 'incident',
|
|
1634
|
+
active: flowData.active !== false,
|
|
1635
|
+
condition: flowData.condition || '',
|
|
1636
|
+
script: flowData.script || '// Flow logic here',
|
|
1637
|
+
order: flowData.order || 100
|
|
1638
|
+
});
|
|
1731
1639
|
break;
|
|
1732
1640
|
case 'subflow':
|
|
1733
|
-
|
|
1641
|
+
// Create subflow as a workflow activity
|
|
1642
|
+
result = await this.client.create('wf_workflow', {
|
|
1643
|
+
name: flowData.name || `subflow_${Date.now()}`,
|
|
1644
|
+
description: `${flowData.description || 'Subflow created by Snow-Flow'} [SUBFLOW]`,
|
|
1645
|
+
table: flowData.table || 'incident',
|
|
1646
|
+
active: flowData.active !== false,
|
|
1647
|
+
condition: flowData.condition || '',
|
|
1648
|
+
script: flowData.script || '// Subflow logic here',
|
|
1649
|
+
order: flowData.order || 200
|
|
1650
|
+
});
|
|
1734
1651
|
break;
|
|
1735
1652
|
case 'action':
|
|
1736
|
-
|
|
1653
|
+
// Create workflow activity/action
|
|
1654
|
+
if (!flowData.workflow_id) {
|
|
1655
|
+
throw new Error('workflow_id is required for flow actions');
|
|
1656
|
+
}
|
|
1657
|
+
result = await this.client.create('wf_activity', {
|
|
1658
|
+
workflow: flowData.workflow_id,
|
|
1659
|
+
name: flowData.name || `action_${Date.now()}`,
|
|
1660
|
+
script: flowData.script || '// Action script here',
|
|
1661
|
+
condition: flowData.condition || '',
|
|
1662
|
+
order: flowData.order || 100,
|
|
1663
|
+
active: flowData.active !== false
|
|
1664
|
+
});
|
|
1737
1665
|
break;
|
|
1738
1666
|
default:
|
|
1739
|
-
throw new Error(`Unknown flow type: ${flowType}`);
|
|
1667
|
+
throw new Error(`Unknown flow type: ${flowType}. Supported types: flow, subflow, action`);
|
|
1740
1668
|
}
|
|
1741
1669
|
}
|
|
1742
1670
|
catch (flowError) {
|
|
@@ -2290,10 +2218,120 @@ ${trackingResults.length > 0 ? trackingResults.join('\n') : '- No artifacts spec
|
|
|
2290
2218
|
async validateDeployment(args) {
|
|
2291
2219
|
try {
|
|
2292
2220
|
this.logger.info('Validating deployment', { type: args.type });
|
|
2293
|
-
// Perform validation checks based on artifact type
|
|
2294
2221
|
const validationResults = [];
|
|
2222
|
+
let hasErrors = false;
|
|
2223
|
+
// Validate with ServiceNow API
|
|
2295
2224
|
if (args.type === 'widget') {
|
|
2225
|
+
// Check required fields locally first
|
|
2296
2226
|
validationResults.push(args.artifact.name ? '✅ Widget has name' : '❌ Widget missing name', args.artifact.template ? '✅ Widget has template' : '❌ Widget missing template', args.artifact.title ? '✅ Widget has title' : '❌ Widget missing title');
|
|
2227
|
+
if (!args.artifact.name || !args.artifact.template || !args.artifact.title) {
|
|
2228
|
+
hasErrors = true;
|
|
2229
|
+
}
|
|
2230
|
+
// Validate against ServiceNow constraints via API
|
|
2231
|
+
try {
|
|
2232
|
+
// Check if widget name already exists
|
|
2233
|
+
const existingWidget = await this.client.get('/api/now/table/sp_widget', {
|
|
2234
|
+
sysparm_query: `name=${args.artifact.name}`,
|
|
2235
|
+
sysparm_limit: 1
|
|
2236
|
+
});
|
|
2237
|
+
if (existingWidget?.result?.length > 0) {
|
|
2238
|
+
validationResults.push('⚠️ Widget name already exists (will update existing)');
|
|
2239
|
+
}
|
|
2240
|
+
else {
|
|
2241
|
+
validationResults.push('✅ Widget name is unique');
|
|
2242
|
+
}
|
|
2243
|
+
// Validate HTML template syntax
|
|
2244
|
+
const template = args.artifact.template;
|
|
2245
|
+
if (template && !template.includes('{{') && !template.includes('<')) {
|
|
2246
|
+
validationResults.push('⚠️ Template may be invalid (no HTML or AngularJS detected)');
|
|
2247
|
+
}
|
|
2248
|
+
else {
|
|
2249
|
+
validationResults.push('✅ Template appears valid');
|
|
2250
|
+
}
|
|
2251
|
+
// Check Service Portal table access
|
|
2252
|
+
try {
|
|
2253
|
+
await this.client.get('/api/now/table/sp_portal', { sysparm_limit: 1 });
|
|
2254
|
+
validationResults.push('✅ Service Portal access confirmed');
|
|
2255
|
+
}
|
|
2256
|
+
catch (portalError) {
|
|
2257
|
+
validationResults.push('❌ No Service Portal access - check permissions');
|
|
2258
|
+
hasErrors = true;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
catch (apiError) {
|
|
2262
|
+
validationResults.push(`⚠️ API validation limited: ${apiError instanceof Error ? apiError.message : String(apiError)}`);
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
else if (args.type === 'workflow') {
|
|
2266
|
+
// Validate workflow artifact
|
|
2267
|
+
validationResults.push(args.artifact.name ? '✅ Workflow has name' : '❌ Workflow missing name');
|
|
2268
|
+
if (args.artifact.name) {
|
|
2269
|
+
try {
|
|
2270
|
+
// Check if workflow name already exists
|
|
2271
|
+
const existingWorkflow = await this.client.get('/api/now/table/wf_workflow', {
|
|
2272
|
+
sysparm_query: `name=${args.artifact.name}`,
|
|
2273
|
+
sysparm_limit: 1
|
|
2274
|
+
});
|
|
2275
|
+
if (existingWorkflow?.result?.length > 0) {
|
|
2276
|
+
validationResults.push('⚠️ Workflow name already exists (will update existing)');
|
|
2277
|
+
}
|
|
2278
|
+
else {
|
|
2279
|
+
validationResults.push('✅ Workflow name is unique');
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
catch (workflowError) {
|
|
2283
|
+
validationResults.push(`⚠️ Workflow validation limited: ${workflowError instanceof Error ? workflowError.message : String(workflowError)}`);
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
else {
|
|
2287
|
+
hasErrors = true;
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
else if (args.type === 'application') {
|
|
2291
|
+
// Validate application artifact
|
|
2292
|
+
validationResults.push(args.artifact.name ? '✅ Application has name' : '❌ Application missing name');
|
|
2293
|
+
if (args.artifact.name) {
|
|
2294
|
+
try {
|
|
2295
|
+
// Check if application name already exists
|
|
2296
|
+
const existingApp = await this.client.get('/api/now/table/sys_app', {
|
|
2297
|
+
sysparm_query: `name=${args.artifact.name}`,
|
|
2298
|
+
sysparm_limit: 1
|
|
2299
|
+
});
|
|
2300
|
+
if (existingApp?.result?.length > 0) {
|
|
2301
|
+
validationResults.push('⚠️ Application name already exists (will update existing)');
|
|
2302
|
+
}
|
|
2303
|
+
else {
|
|
2304
|
+
validationResults.push('✅ Application name is unique');
|
|
2305
|
+
}
|
|
2306
|
+
// Check application creation permissions
|
|
2307
|
+
try {
|
|
2308
|
+
await this.client.get('/api/now/table/sys_app', { sysparm_limit: 1 });
|
|
2309
|
+
validationResults.push('✅ Application creation permissions confirmed');
|
|
2310
|
+
}
|
|
2311
|
+
catch (permError) {
|
|
2312
|
+
validationResults.push('❌ Insufficient permissions for application creation');
|
|
2313
|
+
hasErrors = true;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
catch (appError) {
|
|
2317
|
+
validationResults.push(`⚠️ Application validation limited: ${appError instanceof Error ? appError.message : String(appError)}`);
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
else {
|
|
2321
|
+
hasErrors = true;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
// Test general ServiceNow connectivity
|
|
2325
|
+
try {
|
|
2326
|
+
const authTest = await this.client.get('/api/now/table/sys_user', {
|
|
2327
|
+
sysparm_query: 'user_name=admin',
|
|
2328
|
+
sysparm_limit: 1
|
|
2329
|
+
});
|
|
2330
|
+
validationResults.push('✅ ServiceNow API connectivity confirmed');
|
|
2331
|
+
}
|
|
2332
|
+
catch (connError) {
|
|
2333
|
+
validationResults.push('❌ ServiceNow API connectivity failed');
|
|
2334
|
+
hasErrors = true;
|
|
2297
2335
|
}
|
|
2298
2336
|
return {
|
|
2299
2337
|
content: [
|
|
@@ -2303,7 +2341,7 @@ ${trackingResults.length > 0 ? trackingResults.join('\n') : '- No artifacts spec
|
|
|
2303
2341
|
|
|
2304
2342
|
${validationResults.join('\n')}
|
|
2305
2343
|
|
|
2306
|
-
${
|
|
2344
|
+
${hasErrors ? '❌ Validation failed - fix errors before deployment' : '✅ Validation passed - ready for deployment'}`,
|
|
2307
2345
|
},
|
|
2308
2346
|
],
|
|
2309
2347
|
};
|
|
@@ -2441,110 +2479,664 @@ ${validationResults.every(r => r.startsWith('✅')) ? '✅ Validation passed!' :
|
|
|
2441
2479
|
async getDeploymentStatus(args) {
|
|
2442
2480
|
try {
|
|
2443
2481
|
const limit = args.limit || 10;
|
|
2482
|
+
this.logger.info('Getting deployment status', { limit });
|
|
2483
|
+
// Get recent Update Sets as deployment history
|
|
2484
|
+
const updateSets = await this.client.get('/api/now/table/sys_update_set', {
|
|
2485
|
+
sysparm_query: 'sys_created_on>=javascript:gs.daysAgoStart(7)^ORDERBYDESCsys_created_on',
|
|
2486
|
+
sysparm_limit: limit,
|
|
2487
|
+
sysparm_fields: 'name,description,state,sys_created_on,sys_updated_on,sys_created_by,sys_updated_by'
|
|
2488
|
+
});
|
|
2489
|
+
// Get deployment-related Service Portal widgets
|
|
2490
|
+
const recentWidgets = await this.client.get('/api/now/table/sp_widget', {
|
|
2491
|
+
sysparm_query: 'sys_created_on>=javascript:gs.daysAgoStart(7)^ORDERBYDESCsys_created_on',
|
|
2492
|
+
sysparm_limit: 5,
|
|
2493
|
+
sysparm_fields: 'name,title,sys_created_on,sys_created_by'
|
|
2494
|
+
});
|
|
2495
|
+
// Get workflow deployments
|
|
2496
|
+
const recentWorkflows = await this.client.get('/api/now/table/wf_workflow', {
|
|
2497
|
+
sysparm_query: 'sys_created_on>=javascript:gs.daysAgoStart(7)^ORDERBYDESCsys_created_on',
|
|
2498
|
+
sysparm_limit: 5,
|
|
2499
|
+
sysparm_fields: 'name,sys_created_on,sys_created_by'
|
|
2500
|
+
});
|
|
2501
|
+
// Process deployment history
|
|
2502
|
+
const deployments = [];
|
|
2503
|
+
// Add update sets
|
|
2504
|
+
if (updateSets?.result) {
|
|
2505
|
+
for (const updateSet of updateSets.result) {
|
|
2506
|
+
const status = updateSet.state === 'complete' ? '✅' :
|
|
2507
|
+
updateSet.state === 'ignore' ? '❌' : '⏳';
|
|
2508
|
+
const timeAgo = this.formatTimeAgo(updateSet.sys_created_on);
|
|
2509
|
+
deployments.push({
|
|
2510
|
+
type: 'Update Set',
|
|
2511
|
+
name: updateSet.name,
|
|
2512
|
+
status: status,
|
|
2513
|
+
time: timeAgo,
|
|
2514
|
+
details: `State: ${updateSet.state}${updateSet.description ? ` - ${updateSet.description}` : ''}`
|
|
2515
|
+
});
|
|
2516
|
+
}
|
|
2517
|
+
}
|
|
2518
|
+
// Add recent widgets
|
|
2519
|
+
if (recentWidgets?.result) {
|
|
2520
|
+
for (const widget of recentWidgets.result) {
|
|
2521
|
+
const timeAgo = this.formatTimeAgo(widget.sys_created_on);
|
|
2522
|
+
deployments.push({
|
|
2523
|
+
type: 'Widget',
|
|
2524
|
+
name: widget.name,
|
|
2525
|
+
status: '✅',
|
|
2526
|
+
time: timeAgo,
|
|
2527
|
+
details: widget.title || 'Service Portal Widget'
|
|
2528
|
+
});
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
// Add recent workflows
|
|
2532
|
+
if (recentWorkflows?.result) {
|
|
2533
|
+
for (const workflow of recentWorkflows.result) {
|
|
2534
|
+
const timeAgo = this.formatTimeAgo(workflow.sys_created_on);
|
|
2535
|
+
deployments.push({
|
|
2536
|
+
type: 'Workflow',
|
|
2537
|
+
name: workflow.name,
|
|
2538
|
+
status: '✅',
|
|
2539
|
+
time: timeAgo,
|
|
2540
|
+
details: 'Workflow Definition'
|
|
2541
|
+
});
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
// Sort by creation time (most recent first)
|
|
2545
|
+
deployments.sort((a, b) => {
|
|
2546
|
+
const timeA = new Date(a.time.includes('ago') ? Date.now() : a.time);
|
|
2547
|
+
const timeB = new Date(b.time.includes('ago') ? Date.now() : b.time);
|
|
2548
|
+
return timeB.getTime() - timeA.getTime();
|
|
2549
|
+
});
|
|
2550
|
+
const topDeployments = deployments.slice(0, limit);
|
|
2551
|
+
// Calculate statistics
|
|
2552
|
+
const totalToday = deployments.filter(d => d.time.includes('hour') || d.time.includes('minute')).length;
|
|
2553
|
+
const successful = deployments.filter(d => d.status === '✅').length;
|
|
2554
|
+
const total = deployments.length;
|
|
2555
|
+
const successRate = total > 0 ? Math.round((successful / total) * 100) : 0;
|
|
2556
|
+
// Format deployment list
|
|
2557
|
+
const deploymentList = topDeployments.map((deployment, index) => `${index + 1}. ${deployment.status} ${deployment.type}: ${deployment.name} - ${deployment.time}\n ${deployment.details}`).join('\n');
|
|
2558
|
+
// Get active/pending update sets
|
|
2559
|
+
const activeUpdateSets = await this.client.get('/api/now/table/sys_update_set', {
|
|
2560
|
+
sysparm_query: 'state!=complete^state!=ignore',
|
|
2561
|
+
sysparm_fields: 'name,state',
|
|
2562
|
+
sysparm_limit: 5
|
|
2563
|
+
});
|
|
2564
|
+
let activeStatus = '';
|
|
2565
|
+
if (activeUpdateSets?.result?.length > 0) {
|
|
2566
|
+
activeStatus = '\n🔄 Active Update Sets:\n' +
|
|
2567
|
+
activeUpdateSets.result.map(us => ` - ${us.name} (${us.state})`).join('\n');
|
|
2568
|
+
}
|
|
2444
2569
|
return {
|
|
2445
2570
|
content: [
|
|
2446
2571
|
{
|
|
2447
2572
|
type: 'text',
|
|
2448
2573
|
text: `📊 Recent Deployment History (Last ${limit}):
|
|
2449
2574
|
|
|
2450
|
-
|
|
2451
|
-
2. ✅ Workflow: approval_flow - 4 hours ago
|
|
2452
|
-
3. ❌ Application: custom_app - 6 hours ago (Failed)
|
|
2453
|
-
4. ✅ Widget: user_profile - 1 day ago
|
|
2454
|
-
5. ✅ Update Set: US20240115 - 2 days ago
|
|
2575
|
+
${deploymentList || 'No recent deployments found in the last 7 days'}
|
|
2455
2576
|
|
|
2456
2577
|
📈 Deployment Statistics:
|
|
2457
|
-
- Success Rate:
|
|
2458
|
-
-
|
|
2459
|
-
- Total Deployments
|
|
2578
|
+
- Success Rate: ${successRate}% (${successful}/${total} deployments)
|
|
2579
|
+
- Total Deployments Today: ${totalToday}
|
|
2580
|
+
- Total Deployments (7 days): ${total}${activeStatus}
|
|
2460
2581
|
|
|
2461
|
-
🔗 View
|
|
2582
|
+
🔗 View complete history in ServiceNow: System Update Sets > Local Update Sets`,
|
|
2462
2583
|
},
|
|
2463
2584
|
],
|
|
2464
2585
|
};
|
|
2465
2586
|
}
|
|
2466
2587
|
catch (error) {
|
|
2467
|
-
|
|
2588
|
+
this.logger.error('Failed to get deployment status', error);
|
|
2589
|
+
// Fallback with error details
|
|
2590
|
+
return {
|
|
2591
|
+
content: [
|
|
2592
|
+
{
|
|
2593
|
+
type: 'text',
|
|
2594
|
+
text: `❌ Failed to retrieve deployment status from ServiceNow
|
|
2595
|
+
|
|
2596
|
+
📝 **Error:** ${error instanceof Error ? error.message : String(error)}
|
|
2597
|
+
|
|
2598
|
+
🛠️ **Troubleshooting:**
|
|
2599
|
+
1. Verify ServiceNow connection and authentication
|
|
2600
|
+
2. Check that you have read access to sys_update_set table
|
|
2601
|
+
3. Ensure Service Portal (sp_widget) and Workflow (wf_workflow) table access
|
|
2602
|
+
4. Try manual verification in ServiceNow: System Update Sets > Local Update Sets
|
|
2603
|
+
|
|
2604
|
+
💡 **Manual Check:** Navigate to ServiceNow and check System Update Sets for recent deployment activity`,
|
|
2605
|
+
},
|
|
2606
|
+
],
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2610
|
+
formatTimeAgo(dateString) {
|
|
2611
|
+
try {
|
|
2612
|
+
const date = new Date(dateString);
|
|
2613
|
+
const now = new Date();
|
|
2614
|
+
const diffMs = now.getTime() - date.getTime();
|
|
2615
|
+
const diffMinutes = Math.floor(diffMs / (1000 * 60));
|
|
2616
|
+
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
2617
|
+
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
|
|
2618
|
+
if (diffMinutes < 60) {
|
|
2619
|
+
return `${diffMinutes} minutes ago`;
|
|
2620
|
+
}
|
|
2621
|
+
else if (diffHours < 24) {
|
|
2622
|
+
return `${diffHours} hours ago`;
|
|
2623
|
+
}
|
|
2624
|
+
else {
|
|
2625
|
+
return `${diffDays} days ago`;
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
catch (error) {
|
|
2629
|
+
return dateString;
|
|
2468
2630
|
}
|
|
2469
2631
|
}
|
|
2470
2632
|
async exportArtifact(args) {
|
|
2471
2633
|
try {
|
|
2472
2634
|
this.logger.info('Exporting artifact', { type: args.type, sys_id: args.sys_id });
|
|
2473
|
-
|
|
2474
|
-
|
|
2635
|
+
let tableName;
|
|
2636
|
+
let artifact;
|
|
2637
|
+
// Determine table name based on artifact type
|
|
2638
|
+
switch (args.type) {
|
|
2639
|
+
case 'widget':
|
|
2640
|
+
tableName = 'sp_widget';
|
|
2641
|
+
break;
|
|
2642
|
+
case 'workflow':
|
|
2643
|
+
tableName = 'wf_workflow';
|
|
2644
|
+
break;
|
|
2645
|
+
case 'application':
|
|
2646
|
+
tableName = 'sys_app';
|
|
2647
|
+
break;
|
|
2648
|
+
case 'script':
|
|
2649
|
+
tableName = 'sys_script_include';
|
|
2650
|
+
break;
|
|
2651
|
+
case 'business_rule':
|
|
2652
|
+
tableName = 'sys_script';
|
|
2653
|
+
break;
|
|
2654
|
+
case 'table':
|
|
2655
|
+
tableName = 'sys_db_object';
|
|
2656
|
+
break;
|
|
2657
|
+
default:
|
|
2658
|
+
throw new Error(`Unsupported artifact type: ${args.type}`);
|
|
2659
|
+
}
|
|
2660
|
+
// Fetch artifact from ServiceNow
|
|
2661
|
+
const response = await this.client.get(`/api/now/table/${tableName}/${args.sys_id}`);
|
|
2662
|
+
if (!response?.result) {
|
|
2663
|
+
throw new Error(`Artifact not found: ${args.sys_id}`);
|
|
2664
|
+
}
|
|
2665
|
+
artifact = response.result;
|
|
2666
|
+
// Create exports directory if it doesn't exist
|
|
2667
|
+
const exportDir = (0, path_1.join)(process.cwd(), 'exports');
|
|
2668
|
+
try {
|
|
2669
|
+
await fs_1.promises.mkdir(exportDir, { recursive: true });
|
|
2670
|
+
}
|
|
2671
|
+
catch (mkdirError) {
|
|
2672
|
+
// Directory might already exist
|
|
2673
|
+
}
|
|
2674
|
+
// Prepare export data based on format
|
|
2675
|
+
let exportData;
|
|
2676
|
+
let exportPath;
|
|
2677
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
2678
|
+
if (args.format === 'xml') {
|
|
2679
|
+
// Generate XML format for ServiceNow Update Set compatibility
|
|
2680
|
+
exportData = this.generateArtifactUpdateSetXML(artifact, tableName, args.type);
|
|
2681
|
+
exportPath = (0, path_1.join)(exportDir, `${args.type}_${artifact.name || args.sys_id}_${timestamp}.xml`);
|
|
2682
|
+
}
|
|
2683
|
+
else if (args.format === 'update_set') {
|
|
2684
|
+
// Create as ServiceNow Update Set XML
|
|
2685
|
+
exportData = await this.createUpdateSetExport([{
|
|
2686
|
+
table: tableName,
|
|
2687
|
+
sys_id: args.sys_id,
|
|
2688
|
+
data: artifact
|
|
2689
|
+
}], `${args.type}_export_${timestamp}`);
|
|
2690
|
+
exportPath = (0, path_1.join)(exportDir, `${args.type}_updateset_${timestamp}.xml`);
|
|
2691
|
+
}
|
|
2692
|
+
else {
|
|
2693
|
+
// Default JSON format
|
|
2694
|
+
exportData = JSON.stringify(artifact, null, 2);
|
|
2695
|
+
exportPath = (0, path_1.join)(exportDir, `${args.type}_${artifact.name || args.sys_id}_${timestamp}.json`);
|
|
2696
|
+
}
|
|
2697
|
+
// Write export file
|
|
2698
|
+
await fs_1.promises.writeFile(exportPath, exportData, 'utf8');
|
|
2699
|
+
// Log successful export
|
|
2700
|
+
this.logger.info('Artifact export completed', {
|
|
2701
|
+
type: args.type,
|
|
2702
|
+
sys_id: args.sys_id,
|
|
2703
|
+
path: exportPath,
|
|
2704
|
+
size: exportData.length
|
|
2705
|
+
});
|
|
2475
2706
|
return {
|
|
2476
2707
|
content: [
|
|
2477
2708
|
{
|
|
2478
2709
|
type: 'text',
|
|
2479
|
-
text: `📤 Artifact exported successfully!
|
|
2710
|
+
text: `📤 Artifact exported successfully from ServiceNow!
|
|
2480
2711
|
|
|
2481
2712
|
📁 Export Details:
|
|
2482
2713
|
- Type: ${args.type}
|
|
2714
|
+
- Name: ${artifact.name || artifact.title || 'Unknown'}
|
|
2483
2715
|
- Sys ID: ${args.sys_id}
|
|
2484
2716
|
- Format: ${args.format || 'json'}
|
|
2485
2717
|
- Path: ${exportPath}
|
|
2718
|
+
- Size: ${(exportData.length / 1024).toFixed(2)} KB
|
|
2719
|
+
- Table: ${tableName}
|
|
2720
|
+
- Created: ${artifact.sys_created_on || 'Unknown'}
|
|
2721
|
+
- Updated: ${artifact.sys_updated_on || 'Unknown'}
|
|
2722
|
+
|
|
2723
|
+
✅ Export completed! The artifact has been saved locally and can be imported to another ServiceNow instance.
|
|
2486
2724
|
|
|
2487
|
-
|
|
2725
|
+
💡 **Usage:**
|
|
2726
|
+
- JSON format: For backup and analysis
|
|
2727
|
+
- XML format: For manual ServiceNow import
|
|
2728
|
+
- Update Set format: For automated deployment`,
|
|
2488
2729
|
},
|
|
2489
2730
|
],
|
|
2490
2731
|
};
|
|
2491
2732
|
}
|
|
2492
2733
|
catch (error) {
|
|
2493
|
-
|
|
2734
|
+
this.logger.error('Export failed', error);
|
|
2735
|
+
return {
|
|
2736
|
+
content: [
|
|
2737
|
+
{
|
|
2738
|
+
type: 'text',
|
|
2739
|
+
text: `❌ Export failed
|
|
2740
|
+
|
|
2741
|
+
📝 **Error:** ${error instanceof Error ? error.message : String(error)}
|
|
2742
|
+
|
|
2743
|
+
🛠️ **Troubleshooting:**
|
|
2744
|
+
1. Verify the sys_id exists: ${args.sys_id}
|
|
2745
|
+
2. Check artifact type is correct: ${args.type}
|
|
2746
|
+
3. Ensure you have read access to the artifact
|
|
2747
|
+
4. Verify ServiceNow connection and authentication
|
|
2748
|
+
|
|
2749
|
+
💡 **Valid artifact types:** widget, workflow, application, script, business_rule, table
|
|
2750
|
+
|
|
2751
|
+
🔍 **Manual export:** You can manually export from ServiceNow using:
|
|
2752
|
+
- System Definition > Tables & Columns (for table structure)
|
|
2753
|
+
- System Applications > Studio (for application artifacts)
|
|
2754
|
+
- System Update Sets > Retrieved Update Sets (for update set exports)`,
|
|
2755
|
+
},
|
|
2756
|
+
],
|
|
2757
|
+
};
|
|
2494
2758
|
}
|
|
2495
2759
|
}
|
|
2760
|
+
generateArtifactUpdateSetXML(artifact, tableName, type) {
|
|
2761
|
+
const timestamp = new Date().toISOString();
|
|
2762
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
2763
|
+
<unload unload_date="${timestamp}">
|
|
2764
|
+
<${tableName} action="INSERT_OR_UPDATE">
|
|
2765
|
+
${Object.entries(artifact).map(([key, value]) => {
|
|
2766
|
+
if (typeof value === 'string') {
|
|
2767
|
+
return ` <${key}><![CDATA[${value}]]></${key}>`;
|
|
2768
|
+
}
|
|
2769
|
+
else {
|
|
2770
|
+
return ` <${key}>${value}</${key}>`;
|
|
2771
|
+
}
|
|
2772
|
+
}).join('\n')}
|
|
2773
|
+
</${tableName}>
|
|
2774
|
+
</unload>`;
|
|
2775
|
+
}
|
|
2776
|
+
async createUpdateSetExport(artifacts, name) {
|
|
2777
|
+
const timestamp = new Date().toISOString();
|
|
2778
|
+
const updateSetSysId = (0, servicenow_id_generator_js_1.generateServiceNowSysId)();
|
|
2779
|
+
let xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
2780
|
+
<unload unload_date="${timestamp}">
|
|
2781
|
+
<sys_remote_update_set action="INSERT_OR_UPDATE">
|
|
2782
|
+
<sys_id>${updateSetSysId}</sys_id>
|
|
2783
|
+
<name>${name}</name>
|
|
2784
|
+
<description>Exported artifacts</description>
|
|
2785
|
+
<release_date/>
|
|
2786
|
+
<state>loaded</state>
|
|
2787
|
+
<summary/>
|
|
2788
|
+
<sys_created_on>${timestamp}</sys_created_on>
|
|
2789
|
+
</sys_remote_update_set>
|
|
2790
|
+
`;
|
|
2791
|
+
for (const artifact of artifacts) {
|
|
2792
|
+
xml += ` <${artifact.table} action="INSERT_OR_UPDATE">
|
|
2793
|
+
${Object.entries(artifact.data).map(([key, value]) => {
|
|
2794
|
+
if (typeof value === 'string') {
|
|
2795
|
+
return ` <${key}><![CDATA[${value}]]></${key}>`;
|
|
2796
|
+
}
|
|
2797
|
+
else {
|
|
2798
|
+
return ` <${key}>${value}</${key}>`;
|
|
2799
|
+
}
|
|
2800
|
+
}).join('\n')}
|
|
2801
|
+
</${artifact.table}>
|
|
2802
|
+
`;
|
|
2803
|
+
}
|
|
2804
|
+
xml += '</unload>';
|
|
2805
|
+
return xml;
|
|
2806
|
+
}
|
|
2496
2807
|
async importArtifact(args) {
|
|
2497
2808
|
try {
|
|
2498
2809
|
this.logger.info('Importing artifact', { type: args.type, file_path: args.file_path });
|
|
2499
2810
|
// Read the file
|
|
2500
2811
|
const fileContent = await fs_1.promises.readFile(args.file_path, 'utf8');
|
|
2501
|
-
|
|
2812
|
+
let artifact;
|
|
2813
|
+
// Parse file based on format
|
|
2814
|
+
if (args.format === 'xml' || args.file_path.endsWith('.xml')) {
|
|
2815
|
+
// Handle XML format (Update Set format)
|
|
2816
|
+
return await this.importFromUpdateSetXML(fileContent, args.type);
|
|
2817
|
+
}
|
|
2818
|
+
else {
|
|
2819
|
+
// Handle JSON format
|
|
2820
|
+
artifact = JSON.parse(fileContent);
|
|
2821
|
+
}
|
|
2822
|
+
// Determine table name based on artifact type
|
|
2823
|
+
let tableName;
|
|
2824
|
+
switch (args.type) {
|
|
2825
|
+
case 'widget':
|
|
2826
|
+
tableName = 'sp_widget';
|
|
2827
|
+
break;
|
|
2828
|
+
case 'workflow':
|
|
2829
|
+
tableName = 'wf_workflow';
|
|
2830
|
+
break;
|
|
2831
|
+
case 'application':
|
|
2832
|
+
tableName = 'sys_app';
|
|
2833
|
+
break;
|
|
2834
|
+
case 'script':
|
|
2835
|
+
tableName = 'sys_script_include';
|
|
2836
|
+
break;
|
|
2837
|
+
case 'business_rule':
|
|
2838
|
+
tableName = 'sys_script';
|
|
2839
|
+
break;
|
|
2840
|
+
case 'table':
|
|
2841
|
+
tableName = 'sys_db_object';
|
|
2842
|
+
break;
|
|
2843
|
+
default:
|
|
2844
|
+
throw new Error(`Unsupported artifact type: ${args.type}`);
|
|
2845
|
+
}
|
|
2846
|
+
// Check if artifact already exists
|
|
2847
|
+
let existingArtifact = null;
|
|
2848
|
+
if (artifact.sys_id) {
|
|
2849
|
+
try {
|
|
2850
|
+
const existingResponse = await this.client.get(`/api/now/table/${tableName}/${artifact.sys_id}`);
|
|
2851
|
+
existingArtifact = existingResponse?.result;
|
|
2852
|
+
}
|
|
2853
|
+
catch (error) {
|
|
2854
|
+
// Artifact doesn't exist, which is fine for import
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
let result;
|
|
2858
|
+
let action;
|
|
2859
|
+
if (existingArtifact) {
|
|
2860
|
+
// Update existing artifact
|
|
2861
|
+
const { sys_id, ...updateData } = artifact;
|
|
2862
|
+
result = await this.client.update(`${tableName}/${sys_id}`, updateData);
|
|
2863
|
+
action = 'updated';
|
|
2864
|
+
}
|
|
2865
|
+
else {
|
|
2866
|
+
// Create new artifact
|
|
2867
|
+
result = await this.client.create(tableName, artifact);
|
|
2868
|
+
action = 'created';
|
|
2869
|
+
}
|
|
2870
|
+
if (!result?.result) {
|
|
2871
|
+
throw new Error('Import operation failed - no result returned from ServiceNow');
|
|
2872
|
+
}
|
|
2873
|
+
const importedArtifact = result.result;
|
|
2874
|
+
// Log successful import
|
|
2875
|
+
this.logger.info('Artifact import completed', {
|
|
2876
|
+
type: args.type,
|
|
2877
|
+
sys_id: importedArtifact.sys_id,
|
|
2878
|
+
action: action,
|
|
2879
|
+
name: importedArtifact.name || importedArtifact.title
|
|
2880
|
+
});
|
|
2502
2881
|
return {
|
|
2503
2882
|
content: [
|
|
2504
2883
|
{
|
|
2505
2884
|
type: 'text',
|
|
2506
|
-
text: `📥 Artifact
|
|
2885
|
+
text: `📥 Artifact ${action} successfully in ServiceNow!
|
|
2507
2886
|
|
|
2508
2887
|
📋 Import Details:
|
|
2509
2888
|
- Type: ${args.type}
|
|
2510
|
-
-
|
|
2511
|
-
-
|
|
2889
|
+
- Name: ${importedArtifact.name || importedArtifact.title || 'Unknown'}
|
|
2890
|
+
- Sys ID: ${importedArtifact.sys_id}
|
|
2891
|
+
- Source File: ${args.file_path}
|
|
2892
|
+
- Table: ${tableName}
|
|
2893
|
+
- Action: ${action.toUpperCase()}
|
|
2894
|
+
- Created: ${importedArtifact.sys_created_on || 'Now'}
|
|
2895
|
+
- Updated: ${importedArtifact.sys_updated_on || 'Now'}
|
|
2896
|
+
|
|
2897
|
+
✅ Import completed! The artifact is now available in ServiceNow.
|
|
2898
|
+
|
|
2899
|
+
🔗 **View in ServiceNow:**
|
|
2900
|
+
${this.getServiceNowURL(tableName, importedArtifact.sys_id)}
|
|
2512
2901
|
|
|
2513
|
-
|
|
2902
|
+
💡 **Next Steps:**
|
|
2903
|
+
- Test the imported artifact functionality
|
|
2904
|
+
- Update any dependencies or references
|
|
2905
|
+
- Deploy to other environments if needed`,
|
|
2514
2906
|
},
|
|
2515
2907
|
],
|
|
2516
2908
|
};
|
|
2517
2909
|
}
|
|
2518
2910
|
catch (error) {
|
|
2519
|
-
|
|
2911
|
+
this.logger.error('Import failed', error);
|
|
2912
|
+
return {
|
|
2913
|
+
content: [
|
|
2914
|
+
{
|
|
2915
|
+
type: 'text',
|
|
2916
|
+
text: `❌ Import failed
|
|
2917
|
+
|
|
2918
|
+
📝 **Error:** ${error instanceof Error ? error.message : String(error)}
|
|
2919
|
+
|
|
2920
|
+
🛠️ **Troubleshooting:**
|
|
2921
|
+
1. Verify the file exists and is readable: ${args.file_path}
|
|
2922
|
+
2. Check file format is valid JSON or XML
|
|
2923
|
+
3. Ensure artifact type matches file content: ${args.type}
|
|
2924
|
+
4. Verify you have write permissions to ServiceNow
|
|
2925
|
+
5. Check that required fields are present in the artifact
|
|
2926
|
+
|
|
2927
|
+
💡 **File Format Requirements:**
|
|
2928
|
+
- JSON: Must contain valid ServiceNow record data
|
|
2929
|
+
- XML: Must be in ServiceNow Update Set XML format
|
|
2930
|
+
- Required fields vary by artifact type
|
|
2931
|
+
|
|
2932
|
+
🔍 **Manual Import:** You can manually import via ServiceNow:
|
|
2933
|
+
- System Update Sets > Retrieved Update Sets (for XML files)
|
|
2934
|
+
- System Definition > Tables (for direct record import)
|
|
2935
|
+
- Service Portal > Widget Editor (for widget JSON)`,
|
|
2936
|
+
},
|
|
2937
|
+
],
|
|
2938
|
+
};
|
|
2520
2939
|
}
|
|
2521
2940
|
}
|
|
2941
|
+
async importFromUpdateSetXML(xmlContent, expectedType) {
|
|
2942
|
+
// This would require XML parsing - for now, provide instructions for manual import
|
|
2943
|
+
return {
|
|
2944
|
+
content: [
|
|
2945
|
+
{
|
|
2946
|
+
type: 'text',
|
|
2947
|
+
text: `📦 XML Update Set Import
|
|
2948
|
+
|
|
2949
|
+
The file appears to be in ServiceNow Update Set XML format.
|
|
2950
|
+
|
|
2951
|
+
⚠️ **Automated XML Import Not Available**
|
|
2952
|
+
XML Update Set imports require ServiceNow's built-in import mechanism for safety and dependency resolution.
|
|
2953
|
+
|
|
2954
|
+
🛠️ **Manual Import Steps:**
|
|
2955
|
+
1. Log into ServiceNow
|
|
2956
|
+
2. Navigate to: System Update Sets > Retrieved Update Sets
|
|
2957
|
+
3. Click "Import Update Set from XML"
|
|
2958
|
+
4. Upload your XML file
|
|
2959
|
+
5. Follow the ServiceNow import wizard
|
|
2960
|
+
6. Preview and commit the update set
|
|
2961
|
+
|
|
2962
|
+
💡 **Why Manual?**
|
|
2963
|
+
- ServiceNow XML imports handle complex dependencies
|
|
2964
|
+
- Built-in validation prevents system corruption
|
|
2965
|
+
- Proper conflict resolution and rollback capabilities
|
|
2966
|
+
|
|
2967
|
+
🔗 **ServiceNow Documentation:**
|
|
2968
|
+
Search "Import Update Set from XML" in ServiceNow docs`,
|
|
2969
|
+
},
|
|
2970
|
+
],
|
|
2971
|
+
};
|
|
2972
|
+
}
|
|
2973
|
+
getServiceNowURL(tableName, sysId) {
|
|
2974
|
+
// This would need the actual ServiceNow instance URL
|
|
2975
|
+
// For now, provide generic path
|
|
2976
|
+
return `Navigate to: ServiceNow > ${tableName}.do?sys_id=${sysId}`;
|
|
2977
|
+
}
|
|
2522
2978
|
async cloneInstanceArtifact(args) {
|
|
2523
2979
|
try {
|
|
2524
2980
|
this.logger.info('Cloning artifact between instances', {
|
|
2525
2981
|
source: args.source_instance,
|
|
2526
2982
|
target: args.target_instance,
|
|
2527
2983
|
type: args.type,
|
|
2984
|
+
sys_id: args.sys_id
|
|
2528
2985
|
});
|
|
2986
|
+
// Determine table name based on artifact type
|
|
2987
|
+
let tableName;
|
|
2988
|
+
switch (args.type) {
|
|
2989
|
+
case 'widget':
|
|
2990
|
+
tableName = 'sp_widget';
|
|
2991
|
+
break;
|
|
2992
|
+
case 'workflow':
|
|
2993
|
+
tableName = 'wf_workflow';
|
|
2994
|
+
break;
|
|
2995
|
+
case 'application':
|
|
2996
|
+
tableName = 'sys_app';
|
|
2997
|
+
break;
|
|
2998
|
+
case 'script':
|
|
2999
|
+
tableName = 'sys_script_include';
|
|
3000
|
+
break;
|
|
3001
|
+
case 'business_rule':
|
|
3002
|
+
tableName = 'sys_script';
|
|
3003
|
+
break;
|
|
3004
|
+
case 'table':
|
|
3005
|
+
tableName = 'sys_db_object';
|
|
3006
|
+
break;
|
|
3007
|
+
default:
|
|
3008
|
+
throw new Error(`Unsupported artifact type: ${args.type}`);
|
|
3009
|
+
}
|
|
3010
|
+
// Create ServiceNow clients for source and target instances
|
|
3011
|
+
const sourceClient = new servicenow_client_js_1.ServiceNowClient();
|
|
3012
|
+
const targetClient = new servicenow_client_js_1.ServiceNowClient();
|
|
3013
|
+
// Note: This would require separate OAuth configs for different instances
|
|
3014
|
+
// For now, we'll provide instructions for manual cloning
|
|
3015
|
+
// Step 1: Fetch artifact from source instance
|
|
3016
|
+
let sourceArtifact;
|
|
3017
|
+
try {
|
|
3018
|
+
// This would need to be configured for the source instance
|
|
3019
|
+
const sourceResponse = await this.client.get(`/api/now/table/${tableName}/${args.sys_id}`);
|
|
3020
|
+
if (!sourceResponse?.result) {
|
|
3021
|
+
throw new Error(`Artifact not found in source instance: ${args.sys_id}`);
|
|
3022
|
+
}
|
|
3023
|
+
sourceArtifact = sourceResponse.result;
|
|
3024
|
+
}
|
|
3025
|
+
catch (sourceError) {
|
|
3026
|
+
return {
|
|
3027
|
+
content: [
|
|
3028
|
+
{
|
|
3029
|
+
type: 'text',
|
|
3030
|
+
text: `❌ Failed to fetch artifact from source instance
|
|
3031
|
+
|
|
3032
|
+
📝 **Error:** ${sourceError instanceof Error ? sourceError.message : String(sourceError)}
|
|
3033
|
+
|
|
3034
|
+
🛠️ **Manual Clone Process:**
|
|
3035
|
+
|
|
3036
|
+
**Step 1: Export from Source**
|
|
3037
|
+
1. Log into source instance: ${args.source_instance}
|
|
3038
|
+
2. Navigate to the ${tableName} table
|
|
3039
|
+
3. Find record: ${args.sys_id}
|
|
3040
|
+
4. Export the record (JSON or Update Set XML)
|
|
3041
|
+
|
|
3042
|
+
**Step 2: Import to Target**
|
|
3043
|
+
1. Log into target instance: ${args.target_instance}
|
|
3044
|
+
2. Use snow_import_artifact tool with the exported file
|
|
3045
|
+
3. Or manually import via ServiceNow UI
|
|
3046
|
+
|
|
3047
|
+
💡 **Why Manual?**
|
|
3048
|
+
Cross-instance cloning requires separate authentication for each instance, which is complex to configure automatically.
|
|
3049
|
+
|
|
3050
|
+
🔧 **Alternative:** Use ServiceNow's built-in Update Set promotion process for production deployments.`,
|
|
3051
|
+
},
|
|
3052
|
+
],
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
// For actual cross-instance cloning, we'd need:
|
|
3056
|
+
// 1. Separate OAuth configurations for each instance
|
|
3057
|
+
// 2. Network connectivity between instances
|
|
3058
|
+
// 3. Proper field mapping for different instance versions
|
|
3059
|
+
// 4. Dependency resolution for related records
|
|
2529
3060
|
return {
|
|
2530
3061
|
content: [
|
|
2531
3062
|
{
|
|
2532
3063
|
type: 'text',
|
|
2533
|
-
text: `🔄
|
|
3064
|
+
text: `🔄 Cross-Instance Clone Initiated
|
|
2534
3065
|
|
|
2535
|
-
📋
|
|
3066
|
+
📋 Source Artifact Found:
|
|
2536
3067
|
- Type: ${args.type}
|
|
3068
|
+
- Name: ${sourceArtifact.name || sourceArtifact.title || 'Unknown'}
|
|
3069
|
+
- Sys ID: ${args.sys_id}
|
|
2537
3070
|
- Source: ${args.source_instance}
|
|
2538
3071
|
- Target: ${args.target_instance}
|
|
2539
|
-
-
|
|
3072
|
+
- Table: ${tableName}
|
|
3073
|
+
|
|
3074
|
+
⚠️ **Manual Completion Required**
|
|
3075
|
+
|
|
3076
|
+
Due to security and complexity considerations, cross-instance cloning requires manual completion:
|
|
3077
|
+
|
|
3078
|
+
**Step 1: ✅ Source Verified**
|
|
3079
|
+
Artifact found and accessible in source instance.
|
|
3080
|
+
|
|
3081
|
+
**Step 2: 📋 Export Recommended**
|
|
3082
|
+
Use snow_export_artifact to create a portable export:
|
|
3083
|
+
\`\`\`
|
|
3084
|
+
snow_export_artifact({
|
|
3085
|
+
type: "${args.type}",
|
|
3086
|
+
sys_id: "${args.sys_id}",
|
|
3087
|
+
format: "json"
|
|
3088
|
+
})
|
|
3089
|
+
\`\`\`
|
|
3090
|
+
|
|
3091
|
+
**Step 3: 📥 Import to Target**
|
|
3092
|
+
1. Transfer the exported file to target instance environment
|
|
3093
|
+
2. Use snow_import_artifact to import the artifact
|
|
3094
|
+
3. Test functionality in target instance
|
|
3095
|
+
|
|
3096
|
+
**Step 4: 🔍 Validation**
|
|
3097
|
+
- Verify all dependencies are satisfied
|
|
3098
|
+
- Test artifact functionality
|
|
3099
|
+
- Update any instance-specific configurations
|
|
3100
|
+
|
|
3101
|
+
💡 **Alternative Methods:**
|
|
3102
|
+
- ServiceNow Update Sets (recommended for production)
|
|
3103
|
+
- ServiceNow Studio (for scoped applications)
|
|
3104
|
+
- ServiceNow Store (for certified applications)
|
|
2540
3105
|
|
|
2541
|
-
|
|
3106
|
+
🛡️ **Security Note:** Cross-instance operations require careful authentication management and are best done through ServiceNow's official channels.`,
|
|
2542
3107
|
},
|
|
2543
3108
|
],
|
|
2544
3109
|
};
|
|
2545
3110
|
}
|
|
2546
3111
|
catch (error) {
|
|
2547
|
-
|
|
3112
|
+
this.logger.error('Clone operation failed', error);
|
|
3113
|
+
return {
|
|
3114
|
+
content: [
|
|
3115
|
+
{
|
|
3116
|
+
type: 'text',
|
|
3117
|
+
text: `❌ Clone operation failed
|
|
3118
|
+
|
|
3119
|
+
📝 **Error:** ${error instanceof Error ? error.message : String(error)}
|
|
3120
|
+
|
|
3121
|
+
🛠️ **Troubleshooting:**
|
|
3122
|
+
1. Verify both source and target instances are accessible
|
|
3123
|
+
2. Check that sys_id exists: ${args.sys_id}
|
|
3124
|
+
3. Ensure artifact type is correct: ${args.type}
|
|
3125
|
+
4. Verify authentication for both instances
|
|
3126
|
+
|
|
3127
|
+
💡 **Manual Alternative:**
|
|
3128
|
+
1. Export from source: Use snow_export_artifact tool
|
|
3129
|
+
2. Transfer file between environments
|
|
3130
|
+
3. Import to target: Use snow_import_artifact tool
|
|
3131
|
+
|
|
3132
|
+
🔧 **Production Recommendation:**
|
|
3133
|
+
Use ServiceNow's official deployment process:
|
|
3134
|
+
- Update Sets for customizations
|
|
3135
|
+
- Studio for scoped applications
|
|
3136
|
+
- Store for certified applications`,
|
|
3137
|
+
},
|
|
3138
|
+
],
|
|
3139
|
+
};
|
|
2548
3140
|
}
|
|
2549
3141
|
}
|
|
2550
3142
|
/**
|
|
@@ -2636,15 +3228,132 @@ ${inconsistencies.map(inc => `- ${inc.issue}`).join('\n')}
|
|
|
2636
3228
|
*/
|
|
2637
3229
|
async getDeploymentDebug(args) {
|
|
2638
3230
|
try {
|
|
3231
|
+
this.logger.info('Gathering deployment debug information');
|
|
3232
|
+
// Get local session summary
|
|
2639
3233
|
const sessionSummary = artifact_tracker_js_1.artifactTracker.getSessionSummary();
|
|
2640
3234
|
const inconsistencies = artifact_tracker_js_1.artifactTracker.findInconsistencies();
|
|
3235
|
+
// Get real ServiceNow system information
|
|
3236
|
+
let serviceNowInfo = {};
|
|
3237
|
+
let authStatus = '❌ Not connected';
|
|
3238
|
+
let systemInfo = 'Unable to retrieve';
|
|
3239
|
+
let updateSetInfo = 'Unable to retrieve';
|
|
3240
|
+
let recentErrors = [];
|
|
3241
|
+
try {
|
|
3242
|
+
// Test ServiceNow connection and get system info
|
|
3243
|
+
const systemInfoResponse = await this.client.get('/api/now/table/sys_properties', {
|
|
3244
|
+
sysparm_query: 'name=glide.servlet.uri^ORname=instance.name^ORname=glide.db.rdbms^ORname=glide.war.production',
|
|
3245
|
+
sysparm_fields: 'name,value'
|
|
3246
|
+
});
|
|
3247
|
+
if (systemInfoResponse?.result) {
|
|
3248
|
+
authStatus = '✅ Connected and authenticated';
|
|
3249
|
+
const properties = systemInfoResponse.result;
|
|
3250
|
+
const propMap = properties.reduce((acc, prop) => {
|
|
3251
|
+
acc[prop.name] = prop.value;
|
|
3252
|
+
return acc;
|
|
3253
|
+
}, {});
|
|
3254
|
+
systemInfo = `- Instance: ${propMap['instance.name'] || 'Unknown'}
|
|
3255
|
+
- URI: ${propMap['glide.servlet.uri'] || 'Unknown'}
|
|
3256
|
+
- Database: ${propMap['glide.db.rdbms'] || 'Unknown'}
|
|
3257
|
+
- Production: ${propMap['glide.war.production'] || 'Unknown'}`;
|
|
3258
|
+
}
|
|
3259
|
+
// Get current user info for permissions
|
|
3260
|
+
const userInfo = await this.client.get('/api/now/table/sys_user', {
|
|
3261
|
+
sysparm_query: 'user_name=' + (process.env.SERVICENOW_USERNAME || 'current_user'),
|
|
3262
|
+
sysparm_limit: 1,
|
|
3263
|
+
sysparm_fields: 'name,user_name,active,locked_out,last_login_time'
|
|
3264
|
+
});
|
|
3265
|
+
let userStatus = 'Unable to retrieve user info';
|
|
3266
|
+
if (userInfo?.result?.[0]) {
|
|
3267
|
+
const user = userInfo.result[0];
|
|
3268
|
+
userStatus = `- User: ${user.name} (${user.user_name})
|
|
3269
|
+
- Status: ${user.active === 'true' ? '✅ Active' : '❌ Inactive'}
|
|
3270
|
+
- Locked: ${user.locked_out === 'true' ? '❌ Yes' : '✅ No'}
|
|
3271
|
+
- Last Login: ${user.last_login_time || 'Unknown'}`;
|
|
3272
|
+
}
|
|
3273
|
+
// Get current Update Set information
|
|
3274
|
+
const currentUpdateSet = await this.client.get('/api/now/table/sys_update_set', {
|
|
3275
|
+
sysparm_query: 'state=build',
|
|
3276
|
+
sysparm_limit: 1,
|
|
3277
|
+
sysparm_fields: 'name,description,state,sys_created_on,sys_updated_on'
|
|
3278
|
+
});
|
|
3279
|
+
if (currentUpdateSet?.result?.[0]) {
|
|
3280
|
+
const updateSet = currentUpdateSet.result[0];
|
|
3281
|
+
updateSetInfo = `- Active Update Set: ${updateSet.name}
|
|
3282
|
+
- Description: ${updateSet.description || 'None'}
|
|
3283
|
+
- State: ${updateSet.state}
|
|
3284
|
+
- Created: ${updateSet.sys_created_on}
|
|
3285
|
+
- Updated: ${updateSet.sys_updated_on}`;
|
|
3286
|
+
}
|
|
3287
|
+
else {
|
|
3288
|
+
updateSetInfo = '⚠️ No active Update Set found (deployment may fail)';
|
|
3289
|
+
}
|
|
3290
|
+
// Get recent system logs/errors (if accessible)
|
|
3291
|
+
try {
|
|
3292
|
+
const systemLogs = await this.client.get('/api/now/table/syslog', {
|
|
3293
|
+
sysparm_query: 'level=error^sys_created_on>=javascript:gs.hoursAgoStart(24)^ORDERBYDESCsys_created_on',
|
|
3294
|
+
sysparm_limit: 5,
|
|
3295
|
+
sysparm_fields: 'message,sys_created_on,source'
|
|
3296
|
+
});
|
|
3297
|
+
if (systemLogs?.result?.length > 0) {
|
|
3298
|
+
recentErrors = systemLogs.result.map((log) => ({
|
|
3299
|
+
message: log.message,
|
|
3300
|
+
time: log.sys_created_on,
|
|
3301
|
+
source: log.source
|
|
3302
|
+
}));
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
catch (logError) {
|
|
3306
|
+
// System logs may not be accessible
|
|
3307
|
+
}
|
|
3308
|
+
// Test key table access for deployment operations
|
|
3309
|
+
const tableTests = [
|
|
3310
|
+
{ name: 'Service Portal Widgets', table: 'sp_widget', critical: true },
|
|
3311
|
+
{ name: 'Workflows', table: 'wf_workflow', critical: true },
|
|
3312
|
+
{ name: 'Update Sets', table: 'sys_update_set', critical: true },
|
|
3313
|
+
{ name: 'Applications', table: 'sys_app', critical: false },
|
|
3314
|
+
{ name: 'Script Includes', table: 'sys_script_include', critical: false }
|
|
3315
|
+
];
|
|
3316
|
+
const tableAccess = await Promise.allSettled(tableTests.map(async (test) => {
|
|
3317
|
+
try {
|
|
3318
|
+
await this.client.get(`/api/now/table/${test.table}`, { sysparm_limit: 1 });
|
|
3319
|
+
return { ...test, status: '✅ Accessible' };
|
|
3320
|
+
}
|
|
3321
|
+
catch (error) {
|
|
3322
|
+
return { ...test, status: '❌ Access denied', error: error instanceof Error ? error.message : String(error) };
|
|
3323
|
+
}
|
|
3324
|
+
}));
|
|
3325
|
+
serviceNowInfo.tableAccess = tableAccess.map(result => result.status === 'fulfilled' ? result.value : {
|
|
3326
|
+
name: 'Test failed',
|
|
3327
|
+
status: '❌ Error',
|
|
3328
|
+
error: result.reason
|
|
3329
|
+
});
|
|
3330
|
+
serviceNowInfo.userStatus = userStatus;
|
|
3331
|
+
}
|
|
3332
|
+
catch (connectionError) {
|
|
3333
|
+
authStatus = `❌ Connection failed: ${connectionError instanceof Error ? connectionError.message : String(connectionError)}`;
|
|
3334
|
+
}
|
|
2641
3335
|
return {
|
|
2642
3336
|
content: [
|
|
2643
3337
|
{
|
|
2644
3338
|
type: 'text',
|
|
2645
|
-
text: `🐛 Deployment Debug Information:
|
|
3339
|
+
text: `🐛 Comprehensive Deployment Debug Information:
|
|
3340
|
+
|
|
3341
|
+
**🔐 ServiceNow Connection Status:**
|
|
3342
|
+
${authStatus}
|
|
2646
3343
|
|
|
2647
|
-
|
|
3344
|
+
**🖥️ ServiceNow System Information:**
|
|
3345
|
+
${systemInfo}
|
|
3346
|
+
|
|
3347
|
+
**👤 User Information:**
|
|
3348
|
+
${serviceNowInfo.userStatus || 'Unable to retrieve user info'}
|
|
3349
|
+
|
|
3350
|
+
**📦 Update Set Status:**
|
|
3351
|
+
${updateSetInfo}
|
|
3352
|
+
|
|
3353
|
+
**🔑 Table Access Permissions:**
|
|
3354
|
+
${serviceNowInfo.tableAccess ? serviceNowInfo.tableAccess.map((test) => `- ${test.name}: ${test.status}${test.error ? ` (${test.error})` : ''}`).join('\n') : 'Unable to test table access'}
|
|
3355
|
+
|
|
3356
|
+
**💾 Local Session Summary:**
|
|
2648
3357
|
- Session ID: ${sessionSummary.sessionId}
|
|
2649
3358
|
- Tracked Artifacts: ${sessionSummary.artifactCount}
|
|
2650
3359
|
- Status Breakdown:
|
|
@@ -2653,25 +3362,38 @@ ${inconsistencies.map(inc => `- ${inc.issue}`).join('\n')}
|
|
|
2653
3362
|
- Modified: ${sessionSummary.statusCounts.modified}
|
|
2654
3363
|
- Error: ${sessionSummary.statusCounts.error}
|
|
2655
3364
|
|
|
2656
|
-
|
|
2657
|
-
${sessionSummary.artifacts.map(a => `- ${a.name} (${a.type})
|
|
3365
|
+
**🔍 Tracked Artifacts:**
|
|
3366
|
+
${sessionSummary.artifacts.length > 0 ? sessionSummary.artifacts.map(a => `- ${a.name} (${a.type})
|
|
2658
3367
|
Sys ID: ${a.sys_id}
|
|
2659
3368
|
Status: ${a.status}
|
|
2660
3369
|
Operations: ${a.operationCount}
|
|
2661
|
-
Last Validated: ${a.lastValidated}`).join('\n\n')}
|
|
3370
|
+
Last Validated: ${a.lastValidated}`).join('\n\n') : 'No artifacts currently tracked'}
|
|
2662
3371
|
|
|
2663
|
-
|
|
3372
|
+
**⚠️ Sys_ID Inconsistencies:**
|
|
2664
3373
|
${inconsistencies.length === 0 ? '✅ No inconsistencies found' :
|
|
2665
3374
|
inconsistencies.map(inc => `⚠️ ${inc.issue}
|
|
2666
3375
|
Conflicting artifacts:
|
|
2667
3376
|
${inc.artifacts.map(a => ` - ${a.sys_id} (${a.name})`).join('\n')}`).join('\n\n')}
|
|
2668
3377
|
|
|
2669
|
-
|
|
3378
|
+
**🚨 Recent System Errors (24h):**
|
|
3379
|
+
${recentErrors.length === 0 ? '✅ No recent system errors found' :
|
|
3380
|
+
recentErrors.map(error => `- ${error.time}: ${error.message} (${error.source})`).join('\n')}
|
|
3381
|
+
|
|
3382
|
+
**💡 Recommendations:**
|
|
3383
|
+
${authStatus.includes('❌') ? '- 🔧 Fix ServiceNow connection and authentication' : ''}
|
|
3384
|
+
${updateSetInfo.includes('⚠️') ? '- 📦 Create or activate an Update Set before deployment' : ''}
|
|
3385
|
+
${serviceNowInfo.tableAccess?.some((test) => test.status.includes('❌')) ? '- 🔑 Request additional table permissions for full deployment capability' : ''}
|
|
2670
3386
|
${sessionSummary.statusCounts.error > 0 ? '- ❌ Fix artifacts in error status before proceeding' : ''}
|
|
2671
3387
|
${inconsistencies.length > 0 ? '- ⚠️ Resolve sys_id inconsistencies using snow_validate_sysid' : ''}
|
|
2672
3388
|
${sessionSummary.statusCounts.pending > 0 ? '- 📋 Complete pending deployments' : ''}
|
|
3389
|
+
${recentErrors.length > 0 ? '- 🚨 Review recent system errors in ServiceNow System Logs' : ''}
|
|
2673
3390
|
|
|
2674
|
-
|
|
3391
|
+
🔧 **Next Steps:**
|
|
3392
|
+
1. Ensure ServiceNow connection is working
|
|
3393
|
+
2. Verify Update Set is active and ready
|
|
3394
|
+
3. Check table permissions for deployment operations
|
|
3395
|
+
4. Resolve any tracked artifact issues
|
|
3396
|
+
5. Use snow_validate_sysid to verify specific sys_ids`,
|
|
2675
3397
|
},
|
|
2676
3398
|
],
|
|
2677
3399
|
};
|
|
@@ -2682,7 +3404,21 @@ ${sessionSummary.statusCounts.pending > 0 ? '- 📋 Complete pending deployments
|
|
|
2682
3404
|
content: [
|
|
2683
3405
|
{
|
|
2684
3406
|
type: 'text',
|
|
2685
|
-
text: `❌ Debug
|
|
3407
|
+
text: `❌ Debug information retrieval failed
|
|
3408
|
+
|
|
3409
|
+
📝 **Error:** ${error instanceof Error ? error.message : String(error)}
|
|
3410
|
+
|
|
3411
|
+
🛠️ **Fallback Debug Info:**
|
|
3412
|
+
- Local session tracking may be available
|
|
3413
|
+
- Check ServiceNow connection manually
|
|
3414
|
+
- Verify authentication credentials
|
|
3415
|
+
- Try snow_auth_diagnostics for authentication details
|
|
3416
|
+
|
|
3417
|
+
💡 **Manual Debug:**
|
|
3418
|
+
1. Test ServiceNow login in browser
|
|
3419
|
+
2. Check System Update Sets > Local Update Sets
|
|
3420
|
+
3. Verify table permissions in System Security > Access Control (ACL)
|
|
3421
|
+
4. Review System Logs > Events for recent errors`,
|
|
2686
3422
|
},
|
|
2687
3423
|
],
|
|
2688
3424
|
};
|
|
@@ -2801,14 +3537,152 @@ This will use your .env credentials to start the OAuth flow and generate access
|
|
|
2801
3537
|
],
|
|
2802
3538
|
};
|
|
2803
3539
|
}
|
|
2804
|
-
// 🔧 ENHANCED:
|
|
2805
|
-
let diagnostics;
|
|
3540
|
+
// 🔧 ENHANCED: Run comprehensive real ServiceNow API connectivity tests
|
|
3541
|
+
let diagnostics = { success: false, data: {} };
|
|
3542
|
+
const realApiTests = {};
|
|
2806
3543
|
try {
|
|
2807
|
-
|
|
3544
|
+
// Test 1: Basic System Properties API call
|
|
3545
|
+
try {
|
|
3546
|
+
const systemTest = await this.client.get('/api/now/table/sys_properties', {
|
|
3547
|
+
sysparm_query: 'name=glide.servlet.uri',
|
|
3548
|
+
sysparm_limit: 1
|
|
3549
|
+
});
|
|
3550
|
+
realApiTests.systemPropertiesAccess = {
|
|
3551
|
+
status: '✅ Success',
|
|
3552
|
+
description: 'Can read system properties',
|
|
3553
|
+
details: systemTest?.result ? 'System API accessible' : 'No results returned'
|
|
3554
|
+
};
|
|
3555
|
+
}
|
|
3556
|
+
catch (systemError) {
|
|
3557
|
+
realApiTests.systemPropertiesAccess = {
|
|
3558
|
+
status: '❌ Failed',
|
|
3559
|
+
description: 'Cannot access system properties',
|
|
3560
|
+
error: systemError instanceof Error ? systemError.message : String(systemError)
|
|
3561
|
+
};
|
|
3562
|
+
}
|
|
3563
|
+
// Test 2: User Table Access
|
|
3564
|
+
try {
|
|
3565
|
+
const userTest = await this.client.get('/api/now/table/sys_user', {
|
|
3566
|
+
sysparm_limit: 1,
|
|
3567
|
+
sysparm_fields: 'sys_id,user_name'
|
|
3568
|
+
});
|
|
3569
|
+
realApiTests.userTableAccess = {
|
|
3570
|
+
status: '✅ Success',
|
|
3571
|
+
description: 'Can access user table',
|
|
3572
|
+
details: userTest?.result?.length > 0 ? `Found ${userTest.result.length} user records` : 'No users found'
|
|
3573
|
+
};
|
|
3574
|
+
}
|
|
3575
|
+
catch (userError) {
|
|
3576
|
+
realApiTests.userTableAccess = {
|
|
3577
|
+
status: '❌ Failed',
|
|
3578
|
+
description: 'Cannot access user table',
|
|
3579
|
+
error: userError instanceof Error ? userError.message : String(userError)
|
|
3580
|
+
};
|
|
3581
|
+
}
|
|
3582
|
+
// Test 3: Update Set Table Access (critical for deployments)
|
|
3583
|
+
try {
|
|
3584
|
+
const updateSetTest = await this.client.get('/api/now/table/sys_update_set', {
|
|
3585
|
+
sysparm_limit: 1,
|
|
3586
|
+
sysparm_fields: 'name,state'
|
|
3587
|
+
});
|
|
3588
|
+
realApiTests.updateSetAccess = {
|
|
3589
|
+
status: '✅ Success',
|
|
3590
|
+
description: 'Can access Update Sets (deployment ready)',
|
|
3591
|
+
details: updateSetTest?.result?.length > 0 ? `Found ${updateSetTest.result.length} update sets` : 'No update sets found'
|
|
3592
|
+
};
|
|
3593
|
+
}
|
|
3594
|
+
catch (updateSetError) {
|
|
3595
|
+
realApiTests.updateSetAccess = {
|
|
3596
|
+
status: '❌ Critical',
|
|
3597
|
+
description: 'Cannot access Update Sets - deployments will fail!',
|
|
3598
|
+
error: updateSetError instanceof Error ? updateSetError.message : String(updateSetError)
|
|
3599
|
+
};
|
|
3600
|
+
}
|
|
3601
|
+
// Test 4: Service Portal Widget Access (for widget deployments)
|
|
3602
|
+
try {
|
|
3603
|
+
const widgetTest = await this.client.get('/api/now/table/sp_widget', {
|
|
3604
|
+
sysparm_limit: 1,
|
|
3605
|
+
sysparm_fields: 'name,title'
|
|
3606
|
+
});
|
|
3607
|
+
realApiTests.widgetTableAccess = {
|
|
3608
|
+
status: '✅ Success',
|
|
3609
|
+
description: 'Can access Service Portal widgets',
|
|
3610
|
+
details: widgetTest?.result?.length > 0 ? `Found ${widgetTest.result.length} widgets` : 'No widgets found'
|
|
3611
|
+
};
|
|
3612
|
+
}
|
|
3613
|
+
catch (widgetError) {
|
|
3614
|
+
realApiTests.widgetTableAccess = {
|
|
3615
|
+
status: '⚠️ Limited',
|
|
3616
|
+
description: 'Cannot access Service Portal - widget deployments may fail',
|
|
3617
|
+
error: widgetError instanceof Error ? widgetError.message : String(widgetError)
|
|
3618
|
+
};
|
|
3619
|
+
}
|
|
3620
|
+
// Test 5: Write Permissions Test (create a test record)
|
|
3621
|
+
if (args.run_write_test !== false) {
|
|
3622
|
+
try {
|
|
3623
|
+
// Try to create a test widget to verify write permissions
|
|
3624
|
+
const testWidget = {
|
|
3625
|
+
name: 'snow_flow_connectivity_test',
|
|
3626
|
+
title: 'Snow-Flow Connectivity Test',
|
|
3627
|
+
template: '<div>Test widget - safe to delete</div>',
|
|
3628
|
+
description: 'Temporary test widget created by Snow-Flow MCP diagnostics'
|
|
3629
|
+
};
|
|
3630
|
+
const createResult = await this.client.create('sp_widget', testWidget);
|
|
3631
|
+
if (createResult?.result?.sys_id) {
|
|
3632
|
+
// Immediately delete the test widget
|
|
3633
|
+
try {
|
|
3634
|
+
await this.client.delete(`sp_widget/${createResult.result.sys_id}`);
|
|
3635
|
+
realApiTests.writePermissions = {
|
|
3636
|
+
status: '✅ Full Access',
|
|
3637
|
+
description: 'Can create and delete artifacts - full deployment capability',
|
|
3638
|
+
details: `Successfully created and cleaned up test widget ${createResult.result.sys_id}`
|
|
3639
|
+
};
|
|
3640
|
+
}
|
|
3641
|
+
catch (deleteError) {
|
|
3642
|
+
realApiTests.writePermissions = {
|
|
3643
|
+
status: '⚠️ Partial',
|
|
3644
|
+
description: 'Can create but cannot delete - cleanup may be needed',
|
|
3645
|
+
details: `Created test widget ${createResult.result.sys_id} but failed to delete: ${deleteError instanceof Error ? deleteError.message : String(deleteError)}`
|
|
3646
|
+
};
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
catch (writeError) {
|
|
3651
|
+
realApiTests.writePermissions = {
|
|
3652
|
+
status: '❌ Read-Only',
|
|
3653
|
+
description: 'Cannot create artifacts - deployments will fail',
|
|
3654
|
+
error: writeError instanceof Error ? writeError.message : String(writeError)
|
|
3655
|
+
};
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
// Determine overall success
|
|
3659
|
+
const successCount = Object.values(realApiTests).filter((test) => test.status.includes('✅')).length;
|
|
3660
|
+
const totalTests = Object.keys(realApiTests).length;
|
|
3661
|
+
diagnostics.success = successCount >= Math.ceil(totalTests * 0.6); // 60% success rate
|
|
3662
|
+
diagnostics.data = {
|
|
3663
|
+
tests: realApiTests,
|
|
3664
|
+
summary: {
|
|
3665
|
+
successCount,
|
|
3666
|
+
totalTests,
|
|
3667
|
+
successRate: Math.round((successCount / totalTests) * 100)
|
|
3668
|
+
},
|
|
3669
|
+
recommendations: [],
|
|
3670
|
+
timestamp: new Date().toISOString(),
|
|
3671
|
+
instance_url: credentials?.instance
|
|
3672
|
+
};
|
|
3673
|
+
// Add specific recommendations based on test results
|
|
3674
|
+
if (realApiTests.updateSetAccess?.status?.includes('❌')) {
|
|
3675
|
+
diagnostics.data.recommendations.push('Critical: Fix Update Set access for deployment capability');
|
|
3676
|
+
}
|
|
3677
|
+
if (realApiTests.writePermissions?.status?.includes('❌')) {
|
|
3678
|
+
diagnostics.data.recommendations.push('Request write permissions for artifact creation');
|
|
3679
|
+
}
|
|
3680
|
+
if (realApiTests.systemPropertiesAccess?.status?.includes('❌')) {
|
|
3681
|
+
diagnostics.data.recommendations.push('Basic API access failed - check authentication and network');
|
|
3682
|
+
}
|
|
2808
3683
|
}
|
|
2809
|
-
catch (
|
|
2810
|
-
this.logger.error('
|
|
2811
|
-
// Return specific error for API failures even with valid credentials
|
|
3684
|
+
catch (comprehensiveError) {
|
|
3685
|
+
this.logger.error('Comprehensive API diagnostics failed:', comprehensiveError);
|
|
2812
3686
|
return {
|
|
2813
3687
|
content: [
|
|
2814
3688
|
{
|
|
@@ -2816,15 +3690,15 @@ This will use your .env credentials to start the OAuth flow and generate access
|
|
|
2816
3690
|
text: `⚠️ **Authentication Status:** ${authStatus}
|
|
2817
3691
|
**📍 Credential Source:** ${credentialSource}
|
|
2818
3692
|
|
|
2819
|
-
**❌
|
|
3693
|
+
**❌ ServiceNow API Connectivity Failed**
|
|
2820
3694
|
|
|
2821
3695
|
**Credential Details:**
|
|
2822
3696
|
- Instance: ${credentials?.instance || 'Unknown'} ${credentials?.instance ? '✅' : '❌'}
|
|
2823
3697
|
- Client ID: ${credentials?.clientId ? '✅ Present' : '❌ Missing'}
|
|
2824
3698
|
- Access Token: ${credentials?.accessToken ? '✅ Present' : '❌ Missing'}
|
|
2825
3699
|
|
|
2826
|
-
**Error
|
|
2827
|
-
${
|
|
3700
|
+
**API Connectivity Error:**
|
|
3701
|
+
${comprehensiveError instanceof Error ? comprehensiveError.message : String(comprehensiveError)}
|
|
2828
3702
|
|
|
2829
3703
|
**Possible Causes:**
|
|
2830
3704
|
1. **ServiceNow instance is down or unreachable**
|
|
@@ -2832,18 +3706,25 @@ ${apiError instanceof Error ? apiError.message : String(apiError)}
|
|
|
2832
3706
|
3. **Network/firewall issues** blocking API calls
|
|
2833
3707
|
4. **Invalid instance URL** in credentials
|
|
2834
3708
|
5. **Insufficient API permissions** for your OAuth application
|
|
3709
|
+
6. **Instance URL format issues** (check for trailing slashes)
|
|
2835
3710
|
|
|
2836
3711
|
**🔧 Troubleshooting Steps:**
|
|
2837
3712
|
1. Verify instance URL: https://${credentials?.instance || 'your-instance'}/
|
|
2838
|
-
2. Check
|
|
3713
|
+
2. Check ServiceNow web interface accessibility
|
|
2839
3714
|
3. Re-authenticate: \`snow-flow auth login\`
|
|
2840
|
-
4. Test
|
|
2841
|
-
5.
|
|
3715
|
+
4. Test OAuth application permissions in ServiceNow
|
|
3716
|
+
5. Verify network connectivity and firewall settings
|
|
2842
3717
|
|
|
2843
3718
|
**💡 Alternative Approaches:**
|
|
2844
3719
|
- Test deployment with: \`snow_validate_deployment\`
|
|
2845
3720
|
- Check Update Set status: \`snow_update_set_current\`
|
|
2846
|
-
- Try manual deployment in ServiceNow web interface
|
|
3721
|
+
- Try manual deployment in ServiceNow web interface
|
|
3722
|
+
|
|
3723
|
+
**🔧 OAuth Setup Check:**
|
|
3724
|
+
Ensure your OAuth application in ServiceNow has these permissions:
|
|
3725
|
+
- useraccount (basic user info)
|
|
3726
|
+
- write (create/update records)
|
|
3727
|
+
- admin (system access if needed)`,
|
|
2847
3728
|
},
|
|
2848
3729
|
],
|
|
2849
3730
|
};
|