snow-flow 1.1.73 ā 1.1.75
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/dist/mcp/servicenow-deployment-mcp.js +390 -31
- 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-operations-mcp.js +104 -14
- package/dist/mcp/servicenow-operations-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 +7 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +38 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
|
@@ -1073,6 +1073,17 @@ Use \`snow_deployment_debug\` for more information about this session.`,
|
|
|
1073
1073
|
],
|
|
1074
1074
|
};
|
|
1075
1075
|
}
|
|
1076
|
+
// Ensure we have a flow definition
|
|
1077
|
+
if (!args.flow_definition) {
|
|
1078
|
+
return {
|
|
1079
|
+
content: [
|
|
1080
|
+
{
|
|
1081
|
+
type: 'text',
|
|
1082
|
+
text: 'ā Flow deployment failed: No flow_definition provided.\n\nš” You need to provide a flow_definition with activities.\n\nExample:\n```json\n{\n "name": "approval_flow",\n "flow_definition": {\n "activities": [\n {\n "id": "activity_1",\n "name": "Check Condition",\n "type": "condition"\n }\n ]\n }\n}\n```\n\nOr use snow_create_flow with natural language for easier flow creation.',
|
|
1083
|
+
},
|
|
1084
|
+
],
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1076
1087
|
const flowType = args.flow_type || 'flow';
|
|
1077
1088
|
this.logger.info(`Deploying ${flowType} to ServiceNow`, { name: args.name, type: flowType });
|
|
1078
1089
|
// Validate flow definition first if requested
|
|
@@ -2760,6 +2771,17 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
2760
2771
|
]
|
|
2761
2772
|
};
|
|
2762
2773
|
}
|
|
2774
|
+
// Check if definition is null or undefined
|
|
2775
|
+
if (!definition) {
|
|
2776
|
+
return {
|
|
2777
|
+
content: [
|
|
2778
|
+
{
|
|
2779
|
+
type: 'text',
|
|
2780
|
+
text: `ā Flow validation failed: No definition provided.\n\nš” Please provide a flow definition with activities.\n\nExample:\n\`\`\`json\n{\n "activities": [\n {\n "id": "activity_1",\n "name": "Send Notification",\n "type": "notification"\n }\n ]\n}\n\`\`\``
|
|
2781
|
+
}
|
|
2782
|
+
]
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2763
2785
|
const issues = [];
|
|
2764
2786
|
const warnings = [];
|
|
2765
2787
|
const info = [];
|
|
@@ -2768,13 +2790,13 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
2768
2790
|
// Handle multiple JSON structure variations: top-level, nested in "flow", nested in "flow_definition"
|
|
2769
2791
|
let workingDefinition = definition;
|
|
2770
2792
|
// Check if we have a nested structure like { "flow": { "steps": [...] } }
|
|
2771
|
-
if (definition.flow && typeof definition.flow === 'object') {
|
|
2793
|
+
if (definition && definition.flow && typeof definition.flow === 'object') {
|
|
2772
2794
|
workingDefinition = definition.flow;
|
|
2773
2795
|
corrections.push('ā
Processing nested flow structure (definition.flow)');
|
|
2774
2796
|
info.push('š” Detected nested flow definition format - extracting flow content');
|
|
2775
2797
|
}
|
|
2776
2798
|
// Check if we have nested flow_definition
|
|
2777
|
-
if (definition.flow_definition && typeof definition.flow_definition === 'object') {
|
|
2799
|
+
if (definition && definition.flow_definition && typeof definition.flow_definition === 'object') {
|
|
2778
2800
|
workingDefinition = definition.flow_definition;
|
|
2779
2801
|
corrections.push('ā
Processing nested flow_definition structure');
|
|
2780
2802
|
}
|
|
@@ -3665,6 +3687,11 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
3665
3687
|
async unifiedDeploy(args) {
|
|
3666
3688
|
try {
|
|
3667
3689
|
this.logger.info('Starting unified deployment', args);
|
|
3690
|
+
// CRITICAL: Check authentication FIRST before any deployment
|
|
3691
|
+
const isAuthenticated = await this.oauth.isAuthenticated();
|
|
3692
|
+
if (!isAuthenticated) {
|
|
3693
|
+
throw new types_js_1.McpError(types_js_1.ErrorCode.InvalidRequest, 'Not authenticated. Run "snow-flow auth login" first.');
|
|
3694
|
+
}
|
|
3668
3695
|
const { type, instruction, config, auto_update_set = true, fallback_strategy = 'manual_steps', permission_escalation = 'auto_request', deployment_context } = args;
|
|
3669
3696
|
// Step 1: Ensure Update Set session if requested
|
|
3670
3697
|
let updateSetSession = null;
|
|
@@ -3722,10 +3749,16 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
|
|
|
3722
3749
|
try {
|
|
3723
3750
|
const escalationResult = await this.requestPermissionEscalation(error, strategy.scope);
|
|
3724
3751
|
if (escalationResult.granted) {
|
|
3725
|
-
//
|
|
3726
|
-
|
|
3752
|
+
// Use new scope if provided by escalation
|
|
3753
|
+
const effectiveScope = escalationResult.newScope || strategy.scope;
|
|
3754
|
+
this.logger.info('Retrying with escalated permissions', {
|
|
3755
|
+
originalScope: strategy.scope,
|
|
3756
|
+
newScope: effectiveScope
|
|
3757
|
+
});
|
|
3758
|
+
// Retry with escalated permissions and potentially new scope
|
|
3759
|
+
deploymentResult = await this.attemptDirectDeployment(type, deploymentConfig, effectiveScope);
|
|
3727
3760
|
if (deploymentResult.success) {
|
|
3728
|
-
return this.formatSuccessResponse(deploymentResult,
|
|
3761
|
+
return this.formatSuccessResponse(deploymentResult, effectiveScope, updateSetSession, 'escalated');
|
|
3729
3762
|
}
|
|
3730
3763
|
}
|
|
3731
3764
|
}
|
|
@@ -3773,13 +3806,127 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
3773
3806
|
* Process natural language instruction into deployment configuration
|
|
3774
3807
|
*/
|
|
3775
3808
|
async processNaturalLanguageInstruction(type, instruction) {
|
|
3776
|
-
// For flows,
|
|
3809
|
+
// For flows, create a proper flow structure
|
|
3777
3810
|
if (type === 'flow') {
|
|
3778
|
-
//
|
|
3811
|
+
// Extract flow name from instruction
|
|
3812
|
+
const flowName = this.extractNameFromInstruction(instruction);
|
|
3813
|
+
// Create a basic flow definition based on the instruction
|
|
3814
|
+
// This is a simplified version - in production, this would call the flow composer
|
|
3815
|
+
const lowerInstruction = instruction.toLowerCase();
|
|
3816
|
+
// Determine trigger type
|
|
3817
|
+
let triggerType = 'manual';
|
|
3818
|
+
let triggerTable = '';
|
|
3819
|
+
if (lowerInstruction.includes('new service catalog request') ||
|
|
3820
|
+
lowerInstruction.includes('new catalog request')) {
|
|
3821
|
+
triggerType = 'record_created';
|
|
3822
|
+
triggerTable = 'sc_request';
|
|
3823
|
+
}
|
|
3824
|
+
else if (lowerInstruction.includes('new incident')) {
|
|
3825
|
+
triggerType = 'record_created';
|
|
3826
|
+
triggerTable = 'incident';
|
|
3827
|
+
}
|
|
3828
|
+
else if (lowerInstruction.includes('updated') || lowerInstruction.includes('changes')) {
|
|
3829
|
+
triggerType = 'record_updated';
|
|
3830
|
+
}
|
|
3831
|
+
// Create activities based on instruction
|
|
3832
|
+
const activities = [];
|
|
3833
|
+
let activityId = 1;
|
|
3834
|
+
// Check for approval requirement
|
|
3835
|
+
if (lowerInstruction.includes('approval') || lowerInstruction.includes('approve')) {
|
|
3836
|
+
// Check for condition
|
|
3837
|
+
if (lowerInstruction.includes('if') &&
|
|
3838
|
+
(lowerInstruction.includes('monitor') || lowerInstruction.includes('display') ||
|
|
3839
|
+
lowerInstruction.includes('screen') || lowerInstruction.includes('lcd'))) {
|
|
3840
|
+
// Add condition activity
|
|
3841
|
+
activities.push({
|
|
3842
|
+
id: `activity_${activityId++}`,
|
|
3843
|
+
name: 'Check Item Type',
|
|
3844
|
+
type: 'condition',
|
|
3845
|
+
condition: 'current.cat_item.name.toLowerCase().includes("monitor") || current.cat_item.name.toLowerCase().includes("display") || current.cat_item.name.toLowerCase().includes("screen") || current.cat_item.name.toLowerCase().includes("lcd")',
|
|
3846
|
+
outputs: {
|
|
3847
|
+
condition_met: true
|
|
3848
|
+
}
|
|
3849
|
+
});
|
|
3850
|
+
}
|
|
3851
|
+
// Add approval activity
|
|
3852
|
+
activities.push({
|
|
3853
|
+
id: `activity_${activityId++}`,
|
|
3854
|
+
name: 'Request Approval',
|
|
3855
|
+
type: 'approval',
|
|
3856
|
+
approval_type: 'user',
|
|
3857
|
+
approvers: lowerInstruction.includes('admin') ? 'admin' : 'assignment_group.manager',
|
|
3858
|
+
inputs: {
|
|
3859
|
+
record: '${trigger.current}',
|
|
3860
|
+
approvers: lowerInstruction.includes('admin') ? 'admin' : 'assignment_group.manager'
|
|
3861
|
+
},
|
|
3862
|
+
outputs: {
|
|
3863
|
+
approval_state: '${approval.state}',
|
|
3864
|
+
approval_comments: '${approval.comments}'
|
|
3865
|
+
}
|
|
3866
|
+
});
|
|
3867
|
+
// Add wait for approval if mentioned
|
|
3868
|
+
if (lowerInstruction.includes('wait for approval')) {
|
|
3869
|
+
activities.push({
|
|
3870
|
+
id: `activity_${activityId++}`,
|
|
3871
|
+
name: 'Wait for Approval',
|
|
3872
|
+
type: 'wait',
|
|
3873
|
+
wait_type: 'approval',
|
|
3874
|
+
inputs: {
|
|
3875
|
+
approval_record: '${activity_' + (activityId - 2) + '.approval_id}'
|
|
3876
|
+
}
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
}
|
|
3880
|
+
// Add update status if mentioned
|
|
3881
|
+
if (lowerInstruction.includes('update') && lowerInstruction.includes('status')) {
|
|
3882
|
+
activities.push({
|
|
3883
|
+
id: `activity_${activityId++}`,
|
|
3884
|
+
name: 'Update Request Status',
|
|
3885
|
+
type: 'update_record',
|
|
3886
|
+
table: triggerTable || 'sc_request',
|
|
3887
|
+
inputs: {
|
|
3888
|
+
record: '${trigger.current}',
|
|
3889
|
+
fields: {
|
|
3890
|
+
state: '${activity_' + (activityId - 2) + '.approval_state === "approved" ? "approved" : "rejected"}'
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
});
|
|
3894
|
+
}
|
|
3895
|
+
// Create flow definition
|
|
3896
|
+
const flowDefinition = {
|
|
3897
|
+
name: flowName,
|
|
3898
|
+
description: instruction,
|
|
3899
|
+
table: triggerTable,
|
|
3900
|
+
trigger: {
|
|
3901
|
+
type: triggerType,
|
|
3902
|
+
table: triggerTable,
|
|
3903
|
+
condition: ''
|
|
3904
|
+
},
|
|
3905
|
+
activities: activities.length > 0 ? activities : [{
|
|
3906
|
+
id: 'activity_1',
|
|
3907
|
+
name: 'Log Action',
|
|
3908
|
+
type: 'log',
|
|
3909
|
+
message: 'Flow executed for: ' + instruction,
|
|
3910
|
+
level: 'info'
|
|
3911
|
+
}],
|
|
3912
|
+
connections: []
|
|
3913
|
+
};
|
|
3914
|
+
// Generate connections between activities
|
|
3915
|
+
for (let i = 0; i < activities.length - 1; i++) {
|
|
3916
|
+
flowDefinition.connections.push({
|
|
3917
|
+
from: activities[i].id,
|
|
3918
|
+
to: activities[i + 1].id,
|
|
3919
|
+
type: 'always'
|
|
3920
|
+
});
|
|
3921
|
+
}
|
|
3779
3922
|
return {
|
|
3780
|
-
name:
|
|
3923
|
+
name: flowName,
|
|
3781
3924
|
description: instruction,
|
|
3782
|
-
|
|
3925
|
+
flow_definition: flowDefinition,
|
|
3926
|
+
table: triggerTable,
|
|
3927
|
+
trigger_type: triggerType,
|
|
3928
|
+
condition: '',
|
|
3929
|
+
active: true
|
|
3783
3930
|
};
|
|
3784
3931
|
}
|
|
3785
3932
|
// For widgets, delegate to widget composer
|
|
@@ -3788,6 +3935,10 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
3788
3935
|
name: this.extractNameFromInstruction(instruction),
|
|
3789
3936
|
title: this.extractNameFromInstruction(instruction),
|
|
3790
3937
|
description: instruction,
|
|
3938
|
+
template: '<div>Widget placeholder - implement with proper template</div>',
|
|
3939
|
+
css: '',
|
|
3940
|
+
server_script: '',
|
|
3941
|
+
client_script: '',
|
|
3791
3942
|
instruction: instruction
|
|
3792
3943
|
};
|
|
3793
3944
|
}
|
|
@@ -3838,13 +3989,128 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
|
|
|
3838
3989
|
* Request permission escalation
|
|
3839
3990
|
*/
|
|
3840
3991
|
async requestPermissionEscalation(error, scope) {
|
|
3841
|
-
|
|
3842
|
-
//
|
|
3992
|
+
this.logger.info('Attempting permission escalation', { error: error.message, scope });
|
|
3993
|
+
// Determine required roles based on error message and scope
|
|
3994
|
+
const requiredRoles = this.extractRequiredRoles(error, scope);
|
|
3995
|
+
// Try multiple escalation strategies
|
|
3996
|
+
const escalationStrategies = [
|
|
3997
|
+
{
|
|
3998
|
+
name: 'scoped_deployment',
|
|
3999
|
+
description: 'Switch to scoped application deployment',
|
|
4000
|
+
attempt: async () => {
|
|
4001
|
+
if (scope === 'global' && requiredRoles.includes('admin')) {
|
|
4002
|
+
// Fallback to scoped application
|
|
4003
|
+
this.logger.info('Attempting scoped deployment as fallback');
|
|
4004
|
+
return {
|
|
4005
|
+
granted: true,
|
|
4006
|
+
message: 'Switching to scoped application deployment (no global admin required)',
|
|
4007
|
+
newScope: 'x_custom_app'
|
|
4008
|
+
};
|
|
4009
|
+
}
|
|
4010
|
+
return { granted: false };
|
|
4011
|
+
}
|
|
4012
|
+
},
|
|
4013
|
+
{
|
|
4014
|
+
name: 'personal_scope',
|
|
4015
|
+
description: 'Use personal developer scope',
|
|
4016
|
+
attempt: async () => {
|
|
4017
|
+
if (scope !== 'personal') {
|
|
4018
|
+
this.logger.info('Attempting personal scope deployment');
|
|
4019
|
+
return {
|
|
4020
|
+
granted: true,
|
|
4021
|
+
message: 'Using personal developer scope for deployment',
|
|
4022
|
+
newScope: 'x_personal_dev'
|
|
4023
|
+
};
|
|
4024
|
+
}
|
|
4025
|
+
return { granted: false };
|
|
4026
|
+
}
|
|
4027
|
+
},
|
|
4028
|
+
{
|
|
4029
|
+
name: 'update_set_only',
|
|
4030
|
+
description: 'Create in Update Set without direct deployment',
|
|
4031
|
+
attempt: async () => {
|
|
4032
|
+
this.logger.info('Falling back to Update Set only creation');
|
|
4033
|
+
return {
|
|
4034
|
+
granted: true,
|
|
4035
|
+
message: 'Creating artifact definition in Update Set (manual import required)',
|
|
4036
|
+
newScope: 'update_set',
|
|
4037
|
+
requiresManualStep: true
|
|
4038
|
+
};
|
|
4039
|
+
}
|
|
4040
|
+
}
|
|
4041
|
+
];
|
|
4042
|
+
// Try each escalation strategy
|
|
4043
|
+
for (const strategy of escalationStrategies) {
|
|
4044
|
+
try {
|
|
4045
|
+
const result = await strategy.attempt();
|
|
4046
|
+
if (result.granted) {
|
|
4047
|
+
return {
|
|
4048
|
+
granted: true,
|
|
4049
|
+
message: `${strategy.description}: ${result.message}`,
|
|
4050
|
+
...result
|
|
4051
|
+
};
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
catch (err) {
|
|
4055
|
+
this.logger.warn(`Escalation strategy ${strategy.name} failed`, err);
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
// If all strategies fail, provide detailed manual guidance
|
|
3843
4059
|
return {
|
|
3844
4060
|
granted: false,
|
|
3845
|
-
message:
|
|
4061
|
+
message: this.generatePermissionGuidance(requiredRoles, scope, error)
|
|
3846
4062
|
};
|
|
3847
4063
|
}
|
|
4064
|
+
/**
|
|
4065
|
+
* Extract required roles from error message
|
|
4066
|
+
*/
|
|
4067
|
+
extractRequiredRoles(error, scope) {
|
|
4068
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4069
|
+
const roles = [];
|
|
4070
|
+
// Common role patterns in ServiceNow errors
|
|
4071
|
+
if (message.includes('admin') || message.includes('administrator')) {
|
|
4072
|
+
roles.push('admin');
|
|
4073
|
+
}
|
|
4074
|
+
if (message.includes('global') || scope === 'global') {
|
|
4075
|
+
roles.push('global_admin');
|
|
4076
|
+
}
|
|
4077
|
+
if (message.includes('system_administrator')) {
|
|
4078
|
+
roles.push('system_administrator');
|
|
4079
|
+
}
|
|
4080
|
+
if (message.includes('app_creator')) {
|
|
4081
|
+
roles.push('app_creator');
|
|
4082
|
+
}
|
|
4083
|
+
return roles.length > 0 ? roles : ['admin']; // Default to admin if no specific role found
|
|
4084
|
+
}
|
|
4085
|
+
/**
|
|
4086
|
+
* Generate detailed permission guidance
|
|
4087
|
+
*/
|
|
4088
|
+
generatePermissionGuidance(roles, scope, error) {
|
|
4089
|
+
const credentials = this.oauth.loadCredentials();
|
|
4090
|
+
const instance = credentials?.then(c => c?.instance) || 'your-instance';
|
|
4091
|
+
return `
|
|
4092
|
+
š **Permission Escalation Required**
|
|
4093
|
+
|
|
4094
|
+
**Required Roles**: ${roles.join(', ')}
|
|
4095
|
+
**Attempted Scope**: ${scope}
|
|
4096
|
+
|
|
4097
|
+
**Option 1: Request Role Assignment**
|
|
4098
|
+
1. Navigate to: https://${instance}/nav_to.do?uri=sys_user.do?sys_id=<your_user_sys_id>
|
|
4099
|
+
2. Go to "Roles" related list
|
|
4100
|
+
3. Add roles: ${roles.join(', ')}
|
|
4101
|
+
|
|
4102
|
+
**Option 2: Use Delegated Development**
|
|
4103
|
+
1. Create artifact in personal scope first
|
|
4104
|
+
2. Have admin promote to ${scope} scope
|
|
4105
|
+
3. Command: \`snow_deploy --scope personal\`
|
|
4106
|
+
|
|
4107
|
+
**Option 3: Manual Import via Update Set**
|
|
4108
|
+
1. Export the generated Update Set XML
|
|
4109
|
+
2. Import as admin user
|
|
4110
|
+
3. Preview and commit the Update Set
|
|
4111
|
+
|
|
4112
|
+
**Error Details**: ${error.message || error}`;
|
|
4113
|
+
}
|
|
3848
4114
|
/**
|
|
3849
4115
|
* Format successful deployment response
|
|
3850
4116
|
*/
|
|
@@ -3881,7 +4147,19 @@ Your artifact has been deployed and is ready for testing.` : ''}
|
|
|
3881
4147
|
* Generate manual deployment steps as fallback
|
|
3882
4148
|
*/
|
|
3883
4149
|
async generateManualDeploymentSteps(type, config, error, updateSetSession) {
|
|
3884
|
-
const
|
|
4150
|
+
const credentials = await this.oauth.loadCredentials();
|
|
4151
|
+
const instance = credentials?.instance || 'your-instance';
|
|
4152
|
+
const steps = await this.generateTypeSpecificManualSteps(type, config, instance);
|
|
4153
|
+
// Generate Update Set XML if available
|
|
4154
|
+
let updateSetXml = '';
|
|
4155
|
+
if (updateSetSession) {
|
|
4156
|
+
try {
|
|
4157
|
+
updateSetXml = await this.generateUpdateSetXML(type, config, updateSetSession);
|
|
4158
|
+
}
|
|
4159
|
+
catch (xmlError) {
|
|
4160
|
+
this.logger.warn('Failed to generate Update Set XML', xmlError);
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
3885
4163
|
return {
|
|
3886
4164
|
content: [{
|
|
3887
4165
|
type: 'text',
|
|
@@ -3890,42 +4168,123 @@ Your artifact has been deployed and is ready for testing.` : ''}
|
|
|
3890
4168
|
šØ **Deployment Error**: ${error instanceof Error ? error.message : String(error)}
|
|
3891
4169
|
|
|
3892
4170
|
${updateSetSession ? `š **Update Set Ready**: ${updateSetSession.name} (${updateSetSession.update_set_id})
|
|
3893
|
-
ā
Manual changes will be automatically tracked in this Update Set
|
|
4171
|
+
ā
Manual changes will be automatically tracked in this Update Set.
|
|
4172
|
+
š **Update Set URL**: https://${instance}/sys_update_set.do?sys_id=${updateSetSession.update_set_id}` : ''}
|
|
3894
4173
|
|
|
3895
|
-
š§ **Manual Deployment Steps:**
|
|
4174
|
+
š§ **Manual Deployment Steps with Direct URLs:**
|
|
3896
4175
|
|
|
3897
4176
|
${steps}
|
|
3898
4177
|
|
|
4178
|
+
${updateSetXml ? `š **Update Set XML Generated**
|
|
4179
|
+
Copy this XML to import the artifact:
|
|
4180
|
+
\`\`\`xml
|
|
4181
|
+
${updateSetXml.substring(0, 500)}...
|
|
4182
|
+
\`\`\`
|
|
4183
|
+
š” Full XML saved to: update_set_${updateSetSession.update_set_id}.xml` : ''}
|
|
4184
|
+
|
|
3899
4185
|
š **After Manual Deployment:**
|
|
3900
4186
|
${updateSetSession ? '- Changes are automatically tracked in your active Update Set' : '- Consider creating an Update Set to track your changes'}
|
|
3901
4187
|
- Test thoroughly in your development environment
|
|
3902
4188
|
- Complete Update Set when ready for deployment
|
|
3903
4189
|
|
|
3904
|
-
š” **
|
|
4190
|
+
š” **Quick Actions:**
|
|
4191
|
+
- š§ Retry with different scope: \`snow_deploy --type ${type} --scope personal\`
|
|
4192
|
+
- š Check permissions: \`snow_auth_diagnostics\`
|
|
4193
|
+
- š Use wizard mode: \`snow_${type}_wizard\``
|
|
3905
4194
|
}]
|
|
3906
4195
|
};
|
|
3907
4196
|
}
|
|
3908
4197
|
/**
|
|
3909
4198
|
* Generate type-specific manual steps
|
|
3910
4199
|
*/
|
|
3911
|
-
generateTypeSpecificManualSteps(type, config) {
|
|
4200
|
+
async generateTypeSpecificManualSteps(type, config, instance) {
|
|
3912
4201
|
switch (type) {
|
|
3913
4202
|
case 'widget':
|
|
3914
|
-
return `1.
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3920
|
-
|
|
4203
|
+
return `1. **Open Service Portal Widgets**
|
|
4204
|
+
š URL: https://${instance}/nav_to.do?uri=%2F$sp_widget.do%3Fsys_id%3D-1%26sysparm_stack%3D$sp_widget_list.do
|
|
4205
|
+
|
|
4206
|
+
2. **Click "New" Button** (Top right of the page)
|
|
4207
|
+
šø Look for: Blue "New" button in the header
|
|
4208
|
+
|
|
4209
|
+
3. **Fill in Widget Details:**
|
|
4210
|
+
- **Name**: ${config.name || 'your_widget_name'} (internal identifier)
|
|
4211
|
+
- **ID**: ${config.id || config.name?.toLowerCase().replace(/\s+/g, '_') || 'widget_id'}
|
|
4212
|
+
- **Title**: ${config.title || 'Your Widget Title'} (display name)
|
|
4213
|
+
- **Description**: ${config.description || 'Widget created via Snow-Flow'}
|
|
4214
|
+
|
|
4215
|
+
4. **Add Widget Code:**
|
|
4216
|
+
**HTML Template** tab:
|
|
4217
|
+
\`\`\`html
|
|
4218
|
+
${config.template || '<div>Your HTML here</div>'}
|
|
4219
|
+
\`\`\`
|
|
4220
|
+
|
|
4221
|
+
**CSS - SCSS** tab:
|
|
4222
|
+
\`\`\`css
|
|
4223
|
+
${config.css || '/* Your styles here */'}
|
|
4224
|
+
\`\`\`
|
|
4225
|
+
|
|
4226
|
+
**Client Script** tab:
|
|
4227
|
+
\`\`\`javascript
|
|
4228
|
+
${config.client_script || 'function() {\n var c = this;\n // Your client code\n}'}
|
|
4229
|
+
\`\`\`
|
|
4230
|
+
|
|
4231
|
+
**Server Script** tab:
|
|
4232
|
+
\`\`\`javascript
|
|
4233
|
+
${config.server_script || '(function() {\n // Your server code\n})();'}
|
|
4234
|
+
\`\`\`
|
|
4235
|
+
|
|
4236
|
+
5. **Save the Widget**
|
|
4237
|
+
- Click "Submit" or use Ctrl+S / Cmd+S
|
|
4238
|
+
- Note the sys_id from the URL for tracking
|
|
4239
|
+
|
|
4240
|
+
6. **Test Your Widget**
|
|
4241
|
+
š Test Page URL: https://${instance}/$sp.do?id=widget_editor&sys_id=YOUR_WIDGET_SYS_ID
|
|
4242
|
+
|
|
4243
|
+
7. **Add to Portal Page**
|
|
4244
|
+
š Page Designer: https://${instance}/nav_to.do?uri=%2F$sp_page.do`;
|
|
3921
4245
|
case 'flow':
|
|
3922
|
-
return `1.
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
4246
|
+
return `1. **Open Flow Designer**
|
|
4247
|
+
š URL: https://${instance}/nav_to.do?uri=%2Fflow_designer.do
|
|
4248
|
+
|
|
4249
|
+
2. **Create New Flow**
|
|
4250
|
+
- Click the "+" button or "New" in the top menu
|
|
4251
|
+
- Select "Flow" (not Subflow or Action)
|
|
4252
|
+
|
|
4253
|
+
3. **Configure Flow Properties:**
|
|
4254
|
+
- **Name**: ${config.name || 'approval_flow'}
|
|
4255
|
+
- **Description**: ${config.description || 'Flow created via Snow-Flow'}
|
|
4256
|
+
- **Application**: ${config.application || 'Global'}
|
|
4257
|
+
- **Protection**: None (for development)
|
|
4258
|
+
|
|
4259
|
+
4. **Set Up Trigger** (Step 1 in Flow Designer):
|
|
4260
|
+
- Click "Add a trigger"
|
|
4261
|
+
- Select: "${config.trigger_type || 'Record Created'}"
|
|
4262
|
+
- Table: ${config.table || 'Service Catalog Request [sc_request]'}
|
|
4263
|
+
${config.condition ? `- Condition: ${config.condition}` : ''}
|
|
4264
|
+
|
|
4265
|
+
5. **Add Flow Logic** (Example for approval flow):
|
|
4266
|
+
a. **Add Approval Action**:
|
|
4267
|
+
- Click "+" after trigger
|
|
4268
|
+
- Search "Approval"
|
|
4269
|
+
- Select "Ask for Approval"
|
|
4270
|
+
- Approver: ${config.approver || 'Manager of Requested for'}
|
|
4271
|
+
|
|
4272
|
+
b. **Add Condition**:
|
|
4273
|
+
- Click "+" ā "Flow Logic" ā "If"
|
|
4274
|
+
- Condition: Approval State = Approved
|
|
4275
|
+
|
|
4276
|
+
c. **Add Actions in "Then" branch**:
|
|
4277
|
+
- Update Request: State = Approved
|
|
4278
|
+
- Send Notification: To requester
|
|
4279
|
+
|
|
4280
|
+
6. **Save and Activate**
|
|
4281
|
+
- Click "Save" (top right)
|
|
4282
|
+
- Click "Activate" to make flow live
|
|
4283
|
+
|
|
4284
|
+
7. **Test Your Flow**
|
|
4285
|
+
š Test Execution: https://${instance}/nav_to.do?uri=%2Fsys_flow_context_list.do
|
|
4286
|
+
- Create a test ${config.table || 'request'} record
|
|
4287
|
+
- Monitor execution in Flow Designer`;
|
|
3929
4288
|
case 'application':
|
|
3930
4289
|
return `1. Navigate to System Applications > Applications in ServiceNow
|
|
3931
4290
|
2. Click "New" to create a new application
|