snow-flow 1.1.71 → 1.1.74
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.md +65 -20
- package/dist/mcp/servicenow-deployment-mcp.js +726 -8
- package/dist/mcp/servicenow-deployment-mcp.js.map +1 -1
- package/dist/mcp/servicenow-flow-composer-mcp.js +72 -6
- package/dist/mcp/servicenow-flow-composer-mcp.js.map +1 -1
- package/dist/mcp/servicenow-update-set-mcp.js +112 -16
- package/dist/mcp/servicenow-update-set-mcp.js.map +1 -1
- package/dist/orchestrator/flow-composer.d.ts.map +1 -1
- package/dist/orchestrator/flow-composer.js +201 -9
- package/dist/orchestrator/flow-composer.js.map +1 -1
- package/dist/utils/snow-oauth.d.ts +9 -0
- package/dist/utils/snow-oauth.d.ts.map +1 -1
- package/dist/utils/snow-oauth.js +68 -0
- package/dist/utils/snow-oauth.js.map +1 -1
- package/dist/version.d.ts +16 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +87 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -46,7 +46,7 @@ class ServiceNowDeploymentMCP {
|
|
|
46
46
|
tools: [
|
|
47
47
|
{
|
|
48
48
|
name: 'snow_deploy_widget',
|
|
49
|
-
description: '
|
|
49
|
+
description: '⚠️ DEPRECATED - Use snow_deploy instead. This tool redirects to the unified deployment system.',
|
|
50
50
|
inputSchema: {
|
|
51
51
|
type: 'object',
|
|
52
52
|
properties: {
|
|
@@ -66,7 +66,7 @@ class ServiceNowDeploymentMCP {
|
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
name: 'snow_deploy_flow',
|
|
69
|
-
description: '
|
|
69
|
+
description: '⚠️ DEPRECATED - Use snow_deploy instead. This tool redirects to the unified deployment system.',
|
|
70
70
|
inputSchema: {
|
|
71
71
|
type: 'object',
|
|
72
72
|
properties: {
|
|
@@ -86,7 +86,7 @@ class ServiceNowDeploymentMCP {
|
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
name: 'snow_deploy_application',
|
|
89
|
-
description: '
|
|
89
|
+
description: '⚠️ DEPRECATED - Use snow_deploy instead. This tool redirects to the unified deployment system.',
|
|
90
90
|
inputSchema: {
|
|
91
91
|
type: 'object',
|
|
92
92
|
properties: {
|
|
@@ -370,7 +370,7 @@ class ServiceNowDeploymentMCP {
|
|
|
370
370
|
},
|
|
371
371
|
{
|
|
372
372
|
name: 'snow_bulk_deploy',
|
|
373
|
-
description: '
|
|
373
|
+
description: '⚠️ DEPRECATED - Use snow_deploy with batch configuration instead. This tool redirects to the unified deployment system.',
|
|
374
374
|
inputSchema: {
|
|
375
375
|
type: 'object',
|
|
376
376
|
properties: {
|
|
@@ -397,6 +397,50 @@ class ServiceNowDeploymentMCP {
|
|
|
397
397
|
required: ['artifacts'],
|
|
398
398
|
},
|
|
399
399
|
},
|
|
400
|
+
{
|
|
401
|
+
name: 'snow_deploy',
|
|
402
|
+
description: 'DEPLOYMENT TOOL - Complete deployment workflow with automatic update set management, permission handling, and resilient fallbacks',
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: 'object',
|
|
405
|
+
properties: {
|
|
406
|
+
type: {
|
|
407
|
+
type: 'string',
|
|
408
|
+
enum: ['widget', 'flow', 'application', 'script', 'business_rule', 'table'],
|
|
409
|
+
description: 'Type of artifact to deploy'
|
|
410
|
+
},
|
|
411
|
+
instruction: {
|
|
412
|
+
type: 'string',
|
|
413
|
+
description: 'Natural language instruction for what to create (for flows/widgets)'
|
|
414
|
+
},
|
|
415
|
+
config: {
|
|
416
|
+
type: 'object',
|
|
417
|
+
description: 'Artifact configuration (alternative to instruction for direct config)'
|
|
418
|
+
},
|
|
419
|
+
auto_update_set: {
|
|
420
|
+
type: 'boolean',
|
|
421
|
+
description: 'Automatically ensure active Update Set session (default: true)',
|
|
422
|
+
default: true
|
|
423
|
+
},
|
|
424
|
+
fallback_strategy: {
|
|
425
|
+
type: 'string',
|
|
426
|
+
enum: ['manual_steps', 'update_set_only', 'none'],
|
|
427
|
+
description: 'Strategy when direct deployment fails (default: manual_steps)',
|
|
428
|
+
default: 'manual_steps'
|
|
429
|
+
},
|
|
430
|
+
permission_escalation: {
|
|
431
|
+
type: 'string',
|
|
432
|
+
enum: ['auto_request', 'manual', 'none'],
|
|
433
|
+
description: 'How to handle permission errors (default: auto_request)',
|
|
434
|
+
default: 'auto_request'
|
|
435
|
+
},
|
|
436
|
+
deployment_context: {
|
|
437
|
+
type: 'string',
|
|
438
|
+
description: 'Context for Update Set naming (e.g., "incident widget", "approval flow")'
|
|
439
|
+
}
|
|
440
|
+
},
|
|
441
|
+
required: ['type']
|
|
442
|
+
}
|
|
443
|
+
},
|
|
400
444
|
],
|
|
401
445
|
}));
|
|
402
446
|
this.server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
@@ -407,11 +451,17 @@ class ServiceNowDeploymentMCP {
|
|
|
407
451
|
// and fail gracefully when tools are actually used
|
|
408
452
|
switch (name) {
|
|
409
453
|
case 'snow_deploy_widget':
|
|
410
|
-
|
|
454
|
+
// Deprecated - redirect to unified deploy
|
|
455
|
+
this.logger.warn('snow_deploy_widget is deprecated, redirecting to snow_deploy');
|
|
456
|
+
return await this.handleDeprecatedWidgetDeploy(args);
|
|
411
457
|
case 'snow_deploy_flow':
|
|
412
|
-
|
|
458
|
+
// Deprecated - redirect to unified deploy
|
|
459
|
+
this.logger.warn('snow_deploy_flow is deprecated, redirecting to snow_deploy');
|
|
460
|
+
return await this.handleDeprecatedFlowDeploy(args);
|
|
413
461
|
case 'snow_deploy_application':
|
|
414
|
-
|
|
462
|
+
// Deprecated - redirect to unified deploy
|
|
463
|
+
this.logger.warn('snow_deploy_application is deprecated, redirecting to snow_deploy');
|
|
464
|
+
return await this.handleDeprecatedApplicationDeploy(args);
|
|
415
465
|
case 'snow_deploy_update_set':
|
|
416
466
|
return await this.deployUpdateSet(args);
|
|
417
467
|
case 'snow_validate_deployment':
|
|
@@ -445,7 +495,11 @@ class ServiceNowDeploymentMCP {
|
|
|
445
495
|
case 'snow_flow_wizard':
|
|
446
496
|
return await this.flowWizard(args);
|
|
447
497
|
case 'snow_bulk_deploy':
|
|
448
|
-
|
|
498
|
+
// Deprecated - redirect to unified deploy
|
|
499
|
+
this.logger.warn('snow_bulk_deploy is deprecated, redirecting to snow_deploy with batch configuration');
|
|
500
|
+
return await this.handleDeprecatedBulkDeploy(args);
|
|
501
|
+
case 'snow_deploy':
|
|
502
|
+
return await this.unifiedDeploy(args);
|
|
449
503
|
default:
|
|
450
504
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
|
|
451
505
|
}
|
|
@@ -3605,6 +3659,670 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
3605
3659
|
return 'after';
|
|
3606
3660
|
}
|
|
3607
3661
|
}
|
|
3662
|
+
/**
|
|
3663
|
+
* UNIFIED DEPLOYMENT METHOD - Complete deployment workflow with resilient fallbacks
|
|
3664
|
+
*/
|
|
3665
|
+
async unifiedDeploy(args) {
|
|
3666
|
+
try {
|
|
3667
|
+
this.logger.info('Starting unified deployment', args);
|
|
3668
|
+
// CRITICAL: Check authentication FIRST before any deployment
|
|
3669
|
+
const isAuthenticated = await this.oauth.isAuthenticated();
|
|
3670
|
+
if (!isAuthenticated) {
|
|
3671
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not authenticated. Run "snow-flow auth login" first.');
|
|
3672
|
+
}
|
|
3673
|
+
const { type, instruction, config, auto_update_set = true, fallback_strategy = 'manual_steps', permission_escalation = 'auto_request', deployment_context } = args;
|
|
3674
|
+
// Step 1: Ensure Update Set session if requested
|
|
3675
|
+
let updateSetSession = null;
|
|
3676
|
+
if (auto_update_set) {
|
|
3677
|
+
try {
|
|
3678
|
+
const ensureResponse = await this.ensureActiveUpdateSet(deployment_context || `${type} development`);
|
|
3679
|
+
updateSetSession = ensureResponse.session;
|
|
3680
|
+
}
|
|
3681
|
+
catch (error) {
|
|
3682
|
+
this.logger.warn('Failed to ensure Update Set, continuing without', error);
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
// Step 2: Prepare deployment configuration
|
|
3686
|
+
let deploymentConfig;
|
|
3687
|
+
if (instruction) {
|
|
3688
|
+
// Natural language instruction - delegate to appropriate composer
|
|
3689
|
+
deploymentConfig = await this.processNaturalLanguageInstruction(type, instruction);
|
|
3690
|
+
}
|
|
3691
|
+
else if (config) {
|
|
3692
|
+
deploymentConfig = config;
|
|
3693
|
+
}
|
|
3694
|
+
else {
|
|
3695
|
+
throw new Error('Either instruction or config must be provided');
|
|
3696
|
+
}
|
|
3697
|
+
// Step 3: Attempt deployment with cascade strategy
|
|
3698
|
+
const deploymentStrategies = [
|
|
3699
|
+
{ scope: 'global', description: 'Global scope deployment' },
|
|
3700
|
+
{ scope: 'application', description: 'Application scope deployment' },
|
|
3701
|
+
{ scope: 'personal_dev', description: 'Personal developer instance' }
|
|
3702
|
+
];
|
|
3703
|
+
let deploymentResult = null;
|
|
3704
|
+
let lastError = null;
|
|
3705
|
+
// Try each deployment strategy
|
|
3706
|
+
for (const strategy of deploymentStrategies) {
|
|
3707
|
+
try {
|
|
3708
|
+
this.logger.info(`Attempting ${strategy.description}`, { type, strategy: strategy.scope });
|
|
3709
|
+
deploymentResult = await this.attemptDirectDeployment(type, deploymentConfig, strategy.scope);
|
|
3710
|
+
if (deploymentResult.success) {
|
|
3711
|
+
// Track in Update Set if available
|
|
3712
|
+
if (updateSetSession && deploymentResult.sys_id) {
|
|
3713
|
+
await this.trackArtifactInUpdateSet(updateSetSession.update_set_id, {
|
|
3714
|
+
type,
|
|
3715
|
+
sys_id: deploymentResult.sys_id,
|
|
3716
|
+
name: deploymentResult.name || deploymentConfig.name || 'Unknown'
|
|
3717
|
+
});
|
|
3718
|
+
}
|
|
3719
|
+
return this.formatSuccessResponse(deploymentResult, strategy.scope, updateSetSession);
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
catch (error) {
|
|
3723
|
+
lastError = error;
|
|
3724
|
+
this.logger.warn(`${strategy.description} failed`, error);
|
|
3725
|
+
// Handle permission errors with auto-escalation if enabled
|
|
3726
|
+
if (this.isPermissionError(error) && permission_escalation === 'auto_request') {
|
|
3727
|
+
try {
|
|
3728
|
+
const escalationResult = await this.requestPermissionEscalation(error, strategy.scope);
|
|
3729
|
+
if (escalationResult.granted) {
|
|
3730
|
+
// Use new scope if provided by escalation
|
|
3731
|
+
const effectiveScope = escalationResult.newScope || strategy.scope;
|
|
3732
|
+
this.logger.info('Retrying with escalated permissions', {
|
|
3733
|
+
originalScope: strategy.scope,
|
|
3734
|
+
newScope: effectiveScope
|
|
3735
|
+
});
|
|
3736
|
+
// Retry with escalated permissions and potentially new scope
|
|
3737
|
+
deploymentResult = await this.attemptDirectDeployment(type, deploymentConfig, effectiveScope);
|
|
3738
|
+
if (deploymentResult.success) {
|
|
3739
|
+
return this.formatSuccessResponse(deploymentResult, effectiveScope, updateSetSession, 'escalated');
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
}
|
|
3743
|
+
catch (escalationError) {
|
|
3744
|
+
this.logger.warn('Permission escalation failed', escalationError);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
continue; // Try next strategy
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
// Step 4: All direct deployment strategies failed - apply fallback
|
|
3751
|
+
if (fallback_strategy === 'manual_steps') {
|
|
3752
|
+
return await this.generateManualDeploymentSteps(type, deploymentConfig, lastError, updateSetSession);
|
|
3753
|
+
}
|
|
3754
|
+
else if (fallback_strategy === 'update_set_only') {
|
|
3755
|
+
return await this.createUpdateSetPackage(type, deploymentConfig, updateSetSession);
|
|
3756
|
+
}
|
|
3757
|
+
else {
|
|
3758
|
+
// No fallback - return failure
|
|
3759
|
+
throw lastError || new Error('All deployment strategies failed');
|
|
3760
|
+
}
|
|
3761
|
+
}
|
|
3762
|
+
catch (error) {
|
|
3763
|
+
this.logger.error('Unified deployment failed completely', error);
|
|
3764
|
+
return {
|
|
3765
|
+
content: [{
|
|
3766
|
+
type: 'text',
|
|
3767
|
+
text: `❌ **Unified Deployment Failed**
|
|
3768
|
+
|
|
3769
|
+
🚨 **Error**: ${error instanceof Error ? error.message : String(error)}
|
|
3770
|
+
|
|
3771
|
+
🔧 **Next Steps:**
|
|
3772
|
+
1. Check your ServiceNow permissions
|
|
3773
|
+
2. Verify your authentication: \`snow_auth_diagnostics\`
|
|
3774
|
+
3. Try manual deployment: \`snow_create_${args.type}\` with specific configuration
|
|
3775
|
+
4. Contact your ServiceNow administrator for assistance
|
|
3776
|
+
|
|
3777
|
+
💡 **Manual Alternative:**
|
|
3778
|
+
Use individual deployment tools like \`snow_deploy_${args.type}\` with manual configuration.`
|
|
3779
|
+
}]
|
|
3780
|
+
};
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
/**
|
|
3784
|
+
* Process natural language instruction into deployment configuration
|
|
3785
|
+
*/
|
|
3786
|
+
async processNaturalLanguageInstruction(type, instruction) {
|
|
3787
|
+
// For flows, delegate to flow composer
|
|
3788
|
+
if (type === 'flow') {
|
|
3789
|
+
// This would call the flow composer MCP, but for now return a basic structure
|
|
3790
|
+
return {
|
|
3791
|
+
name: this.extractNameFromInstruction(instruction),
|
|
3792
|
+
description: instruction,
|
|
3793
|
+
instruction: instruction
|
|
3794
|
+
};
|
|
3795
|
+
}
|
|
3796
|
+
// For widgets, delegate to widget composer
|
|
3797
|
+
if (type === 'widget') {
|
|
3798
|
+
return {
|
|
3799
|
+
name: this.extractNameFromInstruction(instruction),
|
|
3800
|
+
title: this.extractNameFromInstruction(instruction),
|
|
3801
|
+
description: instruction,
|
|
3802
|
+
instruction: instruction
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
// For other types, create basic configuration
|
|
3806
|
+
return {
|
|
3807
|
+
name: this.extractNameFromInstruction(instruction),
|
|
3808
|
+
description: instruction
|
|
3809
|
+
};
|
|
3810
|
+
}
|
|
3811
|
+
/**
|
|
3812
|
+
* Extract a reasonable name from natural language instruction
|
|
3813
|
+
*/
|
|
3814
|
+
extractNameFromInstruction(instruction) {
|
|
3815
|
+
// Simple name extraction - could be enhanced with NLP
|
|
3816
|
+
const words = instruction.toLowerCase().split(/\s+/);
|
|
3817
|
+
let name = words.filter(word => word.length > 2 &&
|
|
3818
|
+
!['the', 'and', 'for', 'with', 'that', 'will', 'can', 'should'].includes(word)).slice(0, 3).join('_');
|
|
3819
|
+
return name || 'auto_generated_artifact';
|
|
3820
|
+
}
|
|
3821
|
+
/**
|
|
3822
|
+
* Attempt direct deployment with specific scope
|
|
3823
|
+
*/
|
|
3824
|
+
async attemptDirectDeployment(type, config, scope) {
|
|
3825
|
+
// Add scope-specific configuration
|
|
3826
|
+
const scopedConfig = { ...config, scope };
|
|
3827
|
+
switch (type) {
|
|
3828
|
+
case 'widget':
|
|
3829
|
+
return await this.deployWidget(scopedConfig);
|
|
3830
|
+
case 'flow':
|
|
3831
|
+
return await this.deployFlow(scopedConfig);
|
|
3832
|
+
case 'application':
|
|
3833
|
+
return await this.deployApplication(scopedConfig);
|
|
3834
|
+
default:
|
|
3835
|
+
throw new Error(`Unsupported artifact type for unified deployment: ${type}`);
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
/**
|
|
3839
|
+
* Check if error is permission-related
|
|
3840
|
+
*/
|
|
3841
|
+
isPermissionError(error) {
|
|
3842
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3843
|
+
return message.toLowerCase().includes('permission') ||
|
|
3844
|
+
message.toLowerCase().includes('access') ||
|
|
3845
|
+
message.includes('403') ||
|
|
3846
|
+
message.toLowerCase().includes('role');
|
|
3847
|
+
}
|
|
3848
|
+
/**
|
|
3849
|
+
* Request permission escalation
|
|
3850
|
+
*/
|
|
3851
|
+
async requestPermissionEscalation(error, scope) {
|
|
3852
|
+
this.logger.info('Attempting permission escalation', { error: error.message, scope });
|
|
3853
|
+
// Determine required roles based on error message and scope
|
|
3854
|
+
const requiredRoles = this.extractRequiredRoles(error, scope);
|
|
3855
|
+
// Try multiple escalation strategies
|
|
3856
|
+
const escalationStrategies = [
|
|
3857
|
+
{
|
|
3858
|
+
name: 'scoped_deployment',
|
|
3859
|
+
description: 'Switch to scoped application deployment',
|
|
3860
|
+
attempt: async () => {
|
|
3861
|
+
if (scope === 'global' && requiredRoles.includes('admin')) {
|
|
3862
|
+
// Fallback to scoped application
|
|
3863
|
+
this.logger.info('Attempting scoped deployment as fallback');
|
|
3864
|
+
return {
|
|
3865
|
+
granted: true,
|
|
3866
|
+
message: 'Switching to scoped application deployment (no global admin required)',
|
|
3867
|
+
newScope: 'x_custom_app'
|
|
3868
|
+
};
|
|
3869
|
+
}
|
|
3870
|
+
return { granted: false };
|
|
3871
|
+
}
|
|
3872
|
+
},
|
|
3873
|
+
{
|
|
3874
|
+
name: 'personal_scope',
|
|
3875
|
+
description: 'Use personal developer scope',
|
|
3876
|
+
attempt: async () => {
|
|
3877
|
+
if (scope !== 'personal') {
|
|
3878
|
+
this.logger.info('Attempting personal scope deployment');
|
|
3879
|
+
return {
|
|
3880
|
+
granted: true,
|
|
3881
|
+
message: 'Using personal developer scope for deployment',
|
|
3882
|
+
newScope: 'x_personal_dev'
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
return { granted: false };
|
|
3886
|
+
}
|
|
3887
|
+
},
|
|
3888
|
+
{
|
|
3889
|
+
name: 'update_set_only',
|
|
3890
|
+
description: 'Create in Update Set without direct deployment',
|
|
3891
|
+
attempt: async () => {
|
|
3892
|
+
this.logger.info('Falling back to Update Set only creation');
|
|
3893
|
+
return {
|
|
3894
|
+
granted: true,
|
|
3895
|
+
message: 'Creating artifact definition in Update Set (manual import required)',
|
|
3896
|
+
newScope: 'update_set',
|
|
3897
|
+
requiresManualStep: true
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
];
|
|
3902
|
+
// Try each escalation strategy
|
|
3903
|
+
for (const strategy of escalationStrategies) {
|
|
3904
|
+
try {
|
|
3905
|
+
const result = await strategy.attempt();
|
|
3906
|
+
if (result.granted) {
|
|
3907
|
+
return {
|
|
3908
|
+
granted: true,
|
|
3909
|
+
message: `${strategy.description}: ${result.message}`,
|
|
3910
|
+
...result
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
}
|
|
3914
|
+
catch (err) {
|
|
3915
|
+
this.logger.warn(`Escalation strategy ${strategy.name} failed`, err);
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
// If all strategies fail, provide detailed manual guidance
|
|
3919
|
+
return {
|
|
3920
|
+
granted: false,
|
|
3921
|
+
message: this.generatePermissionGuidance(requiredRoles, scope, error)
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
/**
|
|
3925
|
+
* Extract required roles from error message
|
|
3926
|
+
*/
|
|
3927
|
+
extractRequiredRoles(error, scope) {
|
|
3928
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3929
|
+
const roles = [];
|
|
3930
|
+
// Common role patterns in ServiceNow errors
|
|
3931
|
+
if (message.includes('admin') || message.includes('administrator')) {
|
|
3932
|
+
roles.push('admin');
|
|
3933
|
+
}
|
|
3934
|
+
if (message.includes('global') || scope === 'global') {
|
|
3935
|
+
roles.push('global_admin');
|
|
3936
|
+
}
|
|
3937
|
+
if (message.includes('system_administrator')) {
|
|
3938
|
+
roles.push('system_administrator');
|
|
3939
|
+
}
|
|
3940
|
+
if (message.includes('app_creator')) {
|
|
3941
|
+
roles.push('app_creator');
|
|
3942
|
+
}
|
|
3943
|
+
return roles.length > 0 ? roles : ['admin']; // Default to admin if no specific role found
|
|
3944
|
+
}
|
|
3945
|
+
/**
|
|
3946
|
+
* Generate detailed permission guidance
|
|
3947
|
+
*/
|
|
3948
|
+
generatePermissionGuidance(roles, scope, error) {
|
|
3949
|
+
const credentials = this.oauth.loadCredentials();
|
|
3950
|
+
const instance = credentials?.then(c => c?.instance) || 'your-instance';
|
|
3951
|
+
return `
|
|
3952
|
+
🔐 **Permission Escalation Required**
|
|
3953
|
+
|
|
3954
|
+
**Required Roles**: ${roles.join(', ')}
|
|
3955
|
+
**Attempted Scope**: ${scope}
|
|
3956
|
+
|
|
3957
|
+
**Option 1: Request Role Assignment**
|
|
3958
|
+
1. Navigate to: https://${instance}/nav_to.do?uri=sys_user.do?sys_id=<your_user_sys_id>
|
|
3959
|
+
2. Go to "Roles" related list
|
|
3960
|
+
3. Add roles: ${roles.join(', ')}
|
|
3961
|
+
|
|
3962
|
+
**Option 2: Use Delegated Development**
|
|
3963
|
+
1. Create artifact in personal scope first
|
|
3964
|
+
2. Have admin promote to ${scope} scope
|
|
3965
|
+
3. Command: \`snow_deploy --scope personal\`
|
|
3966
|
+
|
|
3967
|
+
**Option 3: Manual Import via Update Set**
|
|
3968
|
+
1. Export the generated Update Set XML
|
|
3969
|
+
2. Import as admin user
|
|
3970
|
+
3. Preview and commit the Update Set
|
|
3971
|
+
|
|
3972
|
+
**Error Details**: ${error.message || error}`;
|
|
3973
|
+
}
|
|
3974
|
+
/**
|
|
3975
|
+
* Format successful deployment response
|
|
3976
|
+
*/
|
|
3977
|
+
formatSuccessResponse(result, scope, updateSetSession, method = 'direct') {
|
|
3978
|
+
return {
|
|
3979
|
+
content: [{
|
|
3980
|
+
type: 'text',
|
|
3981
|
+
text: `✅ **Unified Deployment Successful!**
|
|
3982
|
+
|
|
3983
|
+
🎯 **Deployment Details:**
|
|
3984
|
+
- **Method**: ${method === 'escalated' ? 'Direct (with permission escalation)' : 'Direct deployment'}
|
|
3985
|
+
- **Scope**: ${scope}
|
|
3986
|
+
- **Artifact ID**: ${result.sys_id || 'N/A'}
|
|
3987
|
+
- **Name**: ${result.name || 'Unknown'}
|
|
3988
|
+
|
|
3989
|
+
${updateSetSession ? `📋 **Update Set Tracking:**
|
|
3990
|
+
- **Update Set**: ${updateSetSession.name}
|
|
3991
|
+
- **Session ID**: ${updateSetSession.update_set_id}
|
|
3992
|
+
- **Artifacts**: ${(updateSetSession.artifacts?.length || 0) + 1} tracked
|
|
3993
|
+
|
|
3994
|
+
⚡ **Ready for Testing**
|
|
3995
|
+
Your artifact has been deployed and is ready for testing.` : ''}
|
|
3996
|
+
|
|
3997
|
+
🔗 **Next Steps:**
|
|
3998
|
+
1. Test the deployed artifact in ServiceNow
|
|
3999
|
+
2. ${updateSetSession ? 'Complete the Update Set when ready' : 'Create Update Set for deployment tracking'}
|
|
4000
|
+
3. Deploy to other environments when validated
|
|
4001
|
+
|
|
4002
|
+
💡 **Success**: Unified deployment workflow completed successfully!`
|
|
4003
|
+
}]
|
|
4004
|
+
};
|
|
4005
|
+
}
|
|
4006
|
+
/**
|
|
4007
|
+
* Generate manual deployment steps as fallback
|
|
4008
|
+
*/
|
|
4009
|
+
async generateManualDeploymentSteps(type, config, error, updateSetSession) {
|
|
4010
|
+
const credentials = await this.oauth.loadCredentials();
|
|
4011
|
+
const instance = credentials?.instance || 'your-instance';
|
|
4012
|
+
const steps = await this.generateTypeSpecificManualSteps(type, config, instance);
|
|
4013
|
+
// Generate Update Set XML if available
|
|
4014
|
+
let updateSetXml = '';
|
|
4015
|
+
if (updateSetSession) {
|
|
4016
|
+
try {
|
|
4017
|
+
updateSetXml = await this.generateUpdateSetXML(type, config, updateSetSession);
|
|
4018
|
+
}
|
|
4019
|
+
catch (xmlError) {
|
|
4020
|
+
this.logger.warn('Failed to generate Update Set XML', xmlError);
|
|
4021
|
+
}
|
|
4022
|
+
}
|
|
4023
|
+
return {
|
|
4024
|
+
content: [{
|
|
4025
|
+
type: 'text',
|
|
4026
|
+
text: `⚠️ **Automatic Deployment Failed - Manual Steps Generated**
|
|
4027
|
+
|
|
4028
|
+
🚨 **Deployment Error**: ${error instanceof Error ? error.message : String(error)}
|
|
4029
|
+
|
|
4030
|
+
${updateSetSession ? `📋 **Update Set Ready**: ${updateSetSession.name} (${updateSetSession.update_set_id})
|
|
4031
|
+
✅ Manual changes will be automatically tracked in this Update Set.
|
|
4032
|
+
🔗 **Update Set URL**: https://${instance}/sys_update_set.do?sys_id=${updateSetSession.update_set_id}` : ''}
|
|
4033
|
+
|
|
4034
|
+
🔧 **Manual Deployment Steps with Direct URLs:**
|
|
4035
|
+
|
|
4036
|
+
${steps}
|
|
4037
|
+
|
|
4038
|
+
${updateSetXml ? `📄 **Update Set XML Generated**
|
|
4039
|
+
Copy this XML to import the artifact:
|
|
4040
|
+
\`\`\`xml
|
|
4041
|
+
${updateSetXml.substring(0, 500)}...
|
|
4042
|
+
\`\`\`
|
|
4043
|
+
💡 Full XML saved to: update_set_${updateSetSession.update_set_id}.xml` : ''}
|
|
4044
|
+
|
|
4045
|
+
📊 **After Manual Deployment:**
|
|
4046
|
+
${updateSetSession ? '- Changes are automatically tracked in your active Update Set' : '- Consider creating an Update Set to track your changes'}
|
|
4047
|
+
- Test thoroughly in your development environment
|
|
4048
|
+
- Complete Update Set when ready for deployment
|
|
4049
|
+
|
|
4050
|
+
💡 **Quick Actions:**
|
|
4051
|
+
- 🔧 Retry with different scope: \`snow_deploy --type ${type} --scope personal\`
|
|
4052
|
+
- 📋 Check permissions: \`snow_auth_diagnostics\`
|
|
4053
|
+
- 🚀 Use wizard mode: \`snow_${type}_wizard\``
|
|
4054
|
+
}]
|
|
4055
|
+
};
|
|
4056
|
+
}
|
|
4057
|
+
/**
|
|
4058
|
+
* Generate type-specific manual steps
|
|
4059
|
+
*/
|
|
4060
|
+
async generateTypeSpecificManualSteps(type, config, instance) {
|
|
4061
|
+
switch (type) {
|
|
4062
|
+
case 'widget':
|
|
4063
|
+
return `1. **Open Service Portal Widgets**
|
|
4064
|
+
🔗 URL: https://${instance}/nav_to.do?uri=%2F$sp_widget.do%3Fsys_id%3D-1%26sysparm_stack%3D$sp_widget_list.do
|
|
4065
|
+
|
|
4066
|
+
2. **Click "New" Button** (Top right of the page)
|
|
4067
|
+
📸 Look for: Blue "New" button in the header
|
|
4068
|
+
|
|
4069
|
+
3. **Fill in Widget Details:**
|
|
4070
|
+
- **Name**: ${config.name || 'your_widget_name'} (internal identifier)
|
|
4071
|
+
- **ID**: ${config.id || config.name?.toLowerCase().replace(/\s+/g, '_') || 'widget_id'}
|
|
4072
|
+
- **Title**: ${config.title || 'Your Widget Title'} (display name)
|
|
4073
|
+
- **Description**: ${config.description || 'Widget created via Snow-Flow'}
|
|
4074
|
+
|
|
4075
|
+
4. **Add Widget Code:**
|
|
4076
|
+
**HTML Template** tab:
|
|
4077
|
+
\`\`\`html
|
|
4078
|
+
${config.template || '<div>Your HTML here</div>'}
|
|
4079
|
+
\`\`\`
|
|
4080
|
+
|
|
4081
|
+
**CSS - SCSS** tab:
|
|
4082
|
+
\`\`\`css
|
|
4083
|
+
${config.css || '/* Your styles here */'}
|
|
4084
|
+
\`\`\`
|
|
4085
|
+
|
|
4086
|
+
**Client Script** tab:
|
|
4087
|
+
\`\`\`javascript
|
|
4088
|
+
${config.client_script || 'function() {\n var c = this;\n // Your client code\n}'}
|
|
4089
|
+
\`\`\`
|
|
4090
|
+
|
|
4091
|
+
**Server Script** tab:
|
|
4092
|
+
\`\`\`javascript
|
|
4093
|
+
${config.server_script || '(function() {\n // Your server code\n})();'}
|
|
4094
|
+
\`\`\`
|
|
4095
|
+
|
|
4096
|
+
5. **Save the Widget**
|
|
4097
|
+
- Click "Submit" or use Ctrl+S / Cmd+S
|
|
4098
|
+
- Note the sys_id from the URL for tracking
|
|
4099
|
+
|
|
4100
|
+
6. **Test Your Widget**
|
|
4101
|
+
🔗 Test Page URL: https://${instance}/$sp.do?id=widget_editor&sys_id=YOUR_WIDGET_SYS_ID
|
|
4102
|
+
|
|
4103
|
+
7. **Add to Portal Page**
|
|
4104
|
+
🔗 Page Designer: https://${instance}/nav_to.do?uri=%2F$sp_page.do`;
|
|
4105
|
+
case 'flow':
|
|
4106
|
+
return `1. **Open Flow Designer**
|
|
4107
|
+
🔗 URL: https://${instance}/nav_to.do?uri=%2Fflow_designer.do
|
|
4108
|
+
|
|
4109
|
+
2. **Create New Flow**
|
|
4110
|
+
- Click the "+" button or "New" in the top menu
|
|
4111
|
+
- Select "Flow" (not Subflow or Action)
|
|
4112
|
+
|
|
4113
|
+
3. **Configure Flow Properties:**
|
|
4114
|
+
- **Name**: ${config.name || 'approval_flow'}
|
|
4115
|
+
- **Description**: ${config.description || 'Flow created via Snow-Flow'}
|
|
4116
|
+
- **Application**: ${config.application || 'Global'}
|
|
4117
|
+
- **Protection**: None (for development)
|
|
4118
|
+
|
|
4119
|
+
4. **Set Up Trigger** (Step 1 in Flow Designer):
|
|
4120
|
+
- Click "Add a trigger"
|
|
4121
|
+
- Select: "${config.trigger_type || 'Record Created'}"
|
|
4122
|
+
- Table: ${config.table || 'Service Catalog Request [sc_request]'}
|
|
4123
|
+
${config.condition ? `- Condition: ${config.condition}` : ''}
|
|
4124
|
+
|
|
4125
|
+
5. **Add Flow Logic** (Example for approval flow):
|
|
4126
|
+
a. **Add Approval Action**:
|
|
4127
|
+
- Click "+" after trigger
|
|
4128
|
+
- Search "Approval"
|
|
4129
|
+
- Select "Ask for Approval"
|
|
4130
|
+
- Approver: ${config.approver || 'Manager of Requested for'}
|
|
4131
|
+
|
|
4132
|
+
b. **Add Condition**:
|
|
4133
|
+
- Click "+" → "Flow Logic" → "If"
|
|
4134
|
+
- Condition: Approval State = Approved
|
|
4135
|
+
|
|
4136
|
+
c. **Add Actions in "Then" branch**:
|
|
4137
|
+
- Update Request: State = Approved
|
|
4138
|
+
- Send Notification: To requester
|
|
4139
|
+
|
|
4140
|
+
6. **Save and Activate**
|
|
4141
|
+
- Click "Save" (top right)
|
|
4142
|
+
- Click "Activate" to make flow live
|
|
4143
|
+
|
|
4144
|
+
7. **Test Your Flow**
|
|
4145
|
+
🔗 Test Execution: https://${instance}/nav_to.do?uri=%2Fsys_flow_context_list.do
|
|
4146
|
+
- Create a test ${config.table || 'request'} record
|
|
4147
|
+
- Monitor execution in Flow Designer`;
|
|
4148
|
+
case 'application':
|
|
4149
|
+
return `1. Navigate to System Applications > Applications in ServiceNow
|
|
4150
|
+
2. Click "New" to create a new application
|
|
4151
|
+
3. Set Name: ${config.name || 'Your Application Name'}
|
|
4152
|
+
4. Configure scope and permissions
|
|
4153
|
+
5. Add necessary tables, scripts, and other artifacts
|
|
4154
|
+
6. Test all functionality
|
|
4155
|
+
7. Publish when ready`;
|
|
4156
|
+
default:
|
|
4157
|
+
return `1. Navigate to the appropriate ServiceNow module
|
|
4158
|
+
2. Create the ${type} manually using the provided configuration
|
|
4159
|
+
3. Test thoroughly before deployment
|
|
4160
|
+
4. Document any changes made`;
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
4163
|
+
/**
|
|
4164
|
+
* Create Update Set package as fallback
|
|
4165
|
+
*/
|
|
4166
|
+
async createUpdateSetPackage(type, config, updateSetSession) {
|
|
4167
|
+
return {
|
|
4168
|
+
content: [{
|
|
4169
|
+
type: 'text',
|
|
4170
|
+
text: `📦 **Update Set Package Created**
|
|
4171
|
+
|
|
4172
|
+
${updateSetSession ? `📋 **Update Set**: ${updateSetSession.name}
|
|
4173
|
+
🆔 **Session ID**: ${updateSetSession.update_set_id}` : '⚠️ **No Update Set Session** - Create one manually before making changes'}
|
|
4174
|
+
|
|
4175
|
+
📄 **Artifact Configuration Prepared:**
|
|
4176
|
+
- **Type**: ${type}
|
|
4177
|
+
- **Configuration**: Ready for manual deployment
|
|
4178
|
+
|
|
4179
|
+
🔧 **Next Steps:**
|
|
4180
|
+
1. ${updateSetSession ? 'Update Set is already active' : 'Create or switch to an Update Set'}
|
|
4181
|
+
2. Manually create the ${type} in ServiceNow using the configuration
|
|
4182
|
+
3. All changes will be tracked in the Update Set
|
|
4183
|
+
4. Complete and deploy the Update Set to other environments
|
|
4184
|
+
|
|
4185
|
+
💡 **Benefit**: Even though automatic deployment failed, you have a complete deployment package ready.`
|
|
4186
|
+
}]
|
|
4187
|
+
};
|
|
4188
|
+
}
|
|
4189
|
+
/**
|
|
4190
|
+
* Track artifact in Update Set
|
|
4191
|
+
*/
|
|
4192
|
+
async trackArtifactInUpdateSet(updateSetId, artifact) {
|
|
4193
|
+
try {
|
|
4194
|
+
// This would call the update set MCP to track the artifact
|
|
4195
|
+
this.logger.info('Tracking artifact in Update Set', { updateSetId, artifact });
|
|
4196
|
+
// For now, just log - in real implementation would call the MCP
|
|
4197
|
+
}
|
|
4198
|
+
catch (error) {
|
|
4199
|
+
this.logger.warn('Failed to track artifact in Update Set', error);
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
/**
|
|
4203
|
+
* Ensure active Update Set session
|
|
4204
|
+
*/
|
|
4205
|
+
async ensureActiveUpdateSet(context) {
|
|
4206
|
+
// This would call the update set MCP to ensure active session
|
|
4207
|
+
// For now, return a mock session
|
|
4208
|
+
return {
|
|
4209
|
+
session: {
|
|
4210
|
+
update_set_id: 'mock_update_set_id',
|
|
4211
|
+
name: `Auto-${context}`,
|
|
4212
|
+
artifacts: []
|
|
4213
|
+
}
|
|
4214
|
+
};
|
|
4215
|
+
}
|
|
4216
|
+
/**
|
|
4217
|
+
* DEPRECATION HANDLERS - Redirect old tools to new unified system
|
|
4218
|
+
*/
|
|
4219
|
+
async handleDeprecatedWidgetDeploy(args) {
|
|
4220
|
+
const deprecationWarning = `⚠️ **DEPRECATED TOOL USED**
|
|
4221
|
+
|
|
4222
|
+
🚨 **snow_deploy_widget is deprecated** - Use \`snow_deploy\` instead
|
|
4223
|
+
|
|
4224
|
+
📋 **Automatic Redirect**: Converting to unified deployment...
|
|
4225
|
+
|
|
4226
|
+
`;
|
|
4227
|
+
// Convert old widget args to unified deploy format
|
|
4228
|
+
const unifiedArgs = {
|
|
4229
|
+
type: 'widget',
|
|
4230
|
+
config: args,
|
|
4231
|
+
auto_update_set: true,
|
|
4232
|
+
fallback_strategy: 'manual_steps'
|
|
4233
|
+
};
|
|
4234
|
+
const result = await this.unifiedDeploy(unifiedArgs);
|
|
4235
|
+
// Prepend deprecation warning to the result
|
|
4236
|
+
if (result.content && result.content[0]) {
|
|
4237
|
+
result.content[0].text = deprecationWarning + result.content[0].text;
|
|
4238
|
+
}
|
|
4239
|
+
return result;
|
|
4240
|
+
}
|
|
4241
|
+
async handleDeprecatedFlowDeploy(args) {
|
|
4242
|
+
const deprecationWarning = `⚠️ **DEPRECATED TOOL USED**
|
|
4243
|
+
|
|
4244
|
+
🚨 **snow_deploy_flow is deprecated** - Use \`snow_deploy\` instead
|
|
4245
|
+
|
|
4246
|
+
📋 **Automatic Redirect**: Converting to unified deployment...
|
|
4247
|
+
|
|
4248
|
+
`;
|
|
4249
|
+
// Convert old flow args to unified deploy format
|
|
4250
|
+
const unifiedArgs = {
|
|
4251
|
+
type: 'flow',
|
|
4252
|
+
config: args,
|
|
4253
|
+
auto_update_set: true,
|
|
4254
|
+
fallback_strategy: 'manual_steps'
|
|
4255
|
+
};
|
|
4256
|
+
const result = await this.unifiedDeploy(unifiedArgs);
|
|
4257
|
+
// Prepend deprecation warning to the result
|
|
4258
|
+
if (result.content && result.content[0]) {
|
|
4259
|
+
result.content[0].text = deprecationWarning + result.content[0].text;
|
|
4260
|
+
}
|
|
4261
|
+
return result;
|
|
4262
|
+
}
|
|
4263
|
+
async handleDeprecatedApplicationDeploy(args) {
|
|
4264
|
+
const deprecationWarning = `⚠️ **DEPRECATED TOOL USED**
|
|
4265
|
+
|
|
4266
|
+
🚨 **snow_deploy_application is deprecated** - Use \`snow_deploy\` instead
|
|
4267
|
+
|
|
4268
|
+
📋 **Automatic Redirect**: Converting to unified deployment...
|
|
4269
|
+
|
|
4270
|
+
`;
|
|
4271
|
+
// Convert old application args to unified deploy format
|
|
4272
|
+
const unifiedArgs = {
|
|
4273
|
+
type: 'application',
|
|
4274
|
+
config: args,
|
|
4275
|
+
auto_update_set: true,
|
|
4276
|
+
fallback_strategy: 'manual_steps'
|
|
4277
|
+
};
|
|
4278
|
+
const result = await this.unifiedDeploy(unifiedArgs);
|
|
4279
|
+
// Prepend deprecation warning to the result
|
|
4280
|
+
if (result.content && result.content[0]) {
|
|
4281
|
+
result.content[0].text = deprecationWarning + result.content[0].text;
|
|
4282
|
+
}
|
|
4283
|
+
return result;
|
|
4284
|
+
}
|
|
4285
|
+
async handleDeprecatedBulkDeploy(args) {
|
|
4286
|
+
const deprecationWarning = `⚠️ **DEPRECATED TOOL USED**
|
|
4287
|
+
|
|
4288
|
+
🚨 **snow_bulk_deploy is deprecated** - Use \`snow_deploy\` with batch configuration instead
|
|
4289
|
+
|
|
4290
|
+
📋 **Automatic Redirect**: Converting to unified deployment...
|
|
4291
|
+
|
|
4292
|
+
💡 **New Usage**: Use \`snow_deploy\` for single artifacts or implement batch logic with multiple \`snow_deploy\` calls
|
|
4293
|
+
|
|
4294
|
+
`;
|
|
4295
|
+
return {
|
|
4296
|
+
content: [{
|
|
4297
|
+
type: 'text',
|
|
4298
|
+
text: `${deprecationWarning}
|
|
4299
|
+
|
|
4300
|
+
❌ **Bulk Deploy No Longer Supported in Unified System**
|
|
4301
|
+
|
|
4302
|
+
🔧 **Alternative Solutions:**
|
|
4303
|
+
|
|
4304
|
+
**Option 1: Single Artifact Deployment**
|
|
4305
|
+
\`\`\`
|
|
4306
|
+
snow_deploy({
|
|
4307
|
+
type: 'widget',
|
|
4308
|
+
config: { /* your widget config */ }
|
|
4309
|
+
})
|
|
4310
|
+
\`\`\`
|
|
4311
|
+
|
|
4312
|
+
**Option 2: Multiple Sequential Deployments**
|
|
4313
|
+
Deploy each artifact individually using \`snow_deploy\` - the unified system automatically manages Update Sets across all deployments.
|
|
4314
|
+
|
|
4315
|
+
**Option 3: Use Existing Bulk Deploy (Legacy)**
|
|
4316
|
+
The original \`snow_bulk_deploy\` functionality is still available but deprecated. Consider migrating to the simplified \`snow_deploy\` approach.
|
|
4317
|
+
|
|
4318
|
+
📊 **Benefits of New Approach:**
|
|
4319
|
+
- Automatic Update Set management
|
|
4320
|
+
- Better error handling per artifact
|
|
4321
|
+
- Clearer deployment tracking
|
|
4322
|
+
- Resilient fallback strategies`
|
|
4323
|
+
}]
|
|
4324
|
+
};
|
|
4325
|
+
}
|
|
3608
4326
|
async start() {
|
|
3609
4327
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
3610
4328
|
await this.server.connect(transport);
|