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.
@@ -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
- // Retry with escalated permissions
3726
- deploymentResult = await this.attemptDirectDeployment(type, deploymentConfig, strategy.scope);
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, strategy.scope, updateSetSession, 'escalated');
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, delegate to flow composer
3809
+ // For flows, create a proper flow structure
3777
3810
  if (type === 'flow') {
3778
- // This would call the flow composer MCP, but for now return a basic structure
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: this.extractNameFromInstruction(instruction),
3923
+ name: flowName,
3781
3924
  description: instruction,
3782
- instruction: instruction
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
- // This would integrate with the permission escalation system
3842
- // For now, return a structured response
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: `Permission escalation requested for ${scope} scope. Contact your ServiceNow administrator.`
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 steps = this.generateTypeSpecificManualSteps(type, config);
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
- šŸ’” **Alternative**: Try individual deployment tools like \`snow_deploy_${type}\` with specific parameters.`
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. Navigate to Service Portal > Widgets in ServiceNow
3915
- 2. Click "New" to create a new widget
3916
- 3. Set Name: ${config.name || 'Your Widget Name'}
3917
- 4. Set Title: ${config.title || config.name || 'Your Widget Title'}
3918
- 5. Add HTML template, CSS, and client script as needed
3919
- 6. Save and test the widget
3920
- 7. Add to a portal page for testing`;
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. Navigate to Process Automation > Flow Designer in ServiceNow
3923
- 2. Click "New" > "Flow" (or "Subflow" if applicable)
3924
- 3. Set Name: ${config.name || 'Your Flow Name'}
3925
- 4. Configure trigger based on your requirements
3926
- 5. Add activities and logic as needed
3927
- 6. Save and activate the flow
3928
- 7. Test with sample data`;
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