snow-flow 1.1.73 → 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.
@@ -3665,6 +3665,11 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
3665
3665
  async unifiedDeploy(args) {
3666
3666
  try {
3667
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
+ }
3668
3673
  const { type, instruction, config, auto_update_set = true, fallback_strategy = 'manual_steps', permission_escalation = 'auto_request', deployment_context } = args;
3669
3674
  // Step 1: Ensure Update Set session if requested
3670
3675
  let updateSetSession = null;
@@ -3722,10 +3727,16 @@ Use \`snow_preview_widget\` to see a detailed preview of the widget rendering.`,
3722
3727
  try {
3723
3728
  const escalationResult = await this.requestPermissionEscalation(error, strategy.scope);
3724
3729
  if (escalationResult.granted) {
3725
- // Retry with escalated permissions
3726
- deploymentResult = await this.attemptDirectDeployment(type, deploymentConfig, strategy.scope);
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);
3727
3738
  if (deploymentResult.success) {
3728
- return this.formatSuccessResponse(deploymentResult, strategy.scope, updateSetSession, 'escalated');
3739
+ return this.formatSuccessResponse(deploymentResult, effectiveScope, updateSetSession, 'escalated');
3729
3740
  }
3730
3741
  }
3731
3742
  }
@@ -3838,13 +3849,128 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
3838
3849
  * Request permission escalation
3839
3850
  */
3840
3851
  async requestPermissionEscalation(error, scope) {
3841
- // This would integrate with the permission escalation system
3842
- // For now, return a structured response
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
3843
3919
  return {
3844
3920
  granted: false,
3845
- message: `Permission escalation requested for ${scope} scope. Contact your ServiceNow administrator.`
3921
+ message: this.generatePermissionGuidance(requiredRoles, scope, error)
3846
3922
  };
3847
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
+ }
3848
3974
  /**
3849
3975
  * Format successful deployment response
3850
3976
  */
@@ -3881,7 +4007,19 @@ Your artifact has been deployed and is ready for testing.` : ''}
3881
4007
  * Generate manual deployment steps as fallback
3882
4008
  */
3883
4009
  async generateManualDeploymentSteps(type, config, error, updateSetSession) {
3884
- const steps = this.generateTypeSpecificManualSteps(type, config);
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
+ }
3885
4023
  return {
3886
4024
  content: [{
3887
4025
  type: 'text',
@@ -3890,42 +4028,123 @@ Your artifact has been deployed and is ready for testing.` : ''}
3890
4028
  🚨 **Deployment Error**: ${error instanceof Error ? error.message : String(error)}
3891
4029
 
3892
4030
  ${updateSetSession ? `📋 **Update Set Ready**: ${updateSetSession.name} (${updateSetSession.update_set_id})
3893
- ✅ Manual changes will be automatically tracked in this Update Set.` : ''}
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}` : ''}
3894
4033
 
3895
- 🔧 **Manual Deployment Steps:**
4034
+ 🔧 **Manual Deployment Steps with Direct URLs:**
3896
4035
 
3897
4036
  ${steps}
3898
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
+
3899
4045
  📊 **After Manual Deployment:**
3900
4046
  ${updateSetSession ? '- Changes are automatically tracked in your active Update Set' : '- Consider creating an Update Set to track your changes'}
3901
4047
  - Test thoroughly in your development environment
3902
4048
  - Complete Update Set when ready for deployment
3903
4049
 
3904
- 💡 **Alternative**: Try individual deployment tools like \`snow_deploy_${type}\` with specific parameters.`
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\``
3905
4054
  }]
3906
4055
  };
3907
4056
  }
3908
4057
  /**
3909
4058
  * Generate type-specific manual steps
3910
4059
  */
3911
- generateTypeSpecificManualSteps(type, config) {
4060
+ async generateTypeSpecificManualSteps(type, config, instance) {
3912
4061
  switch (type) {
3913
4062
  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`;
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`;
3921
4105
  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`;
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`;
3929
4148
  case 'application':
3930
4149
  return `1. Navigate to System Applications > Applications in ServiceNow
3931
4150
  2. Click "New" to create a new application