snow-flow 1.3.1 → 1.3.2

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.
Files changed (40) hide show
  1. package/.claude-flow/queen/queen-memory.db +0 -0
  2. package/.env.example +249 -14
  3. package/CLAUDE.md +185 -24
  4. package/README.md +55 -5
  5. package/dist/api/error-handling.js +898 -4
  6. package/dist/api/performance-optimizer.js +3 -2
  7. package/dist/cli.js +590 -200
  8. package/dist/config/snow-flow-config.js +320 -8
  9. package/dist/health/system-health.js +288 -21
  10. package/dist/mcp/http-transport-wrapper.js +65 -4
  11. package/dist/mcp/servicenow-automation-mcp-refactored.js +90 -9
  12. package/dist/mcp/servicenow-deployment-mcp-refactored.js +151 -2
  13. package/dist/mcp/servicenow-deployment-mcp.js +828 -15
  14. package/dist/mcp/servicenow-integration-mcp-refactored.js +100 -9
  15. package/dist/mcp/servicenow-intelligent-mcp.js +198 -7
  16. package/dist/mcp/servicenow-memory-mcp.js +2 -1
  17. package/dist/mcp/servicenow-operations-mcp-refactored.js +3 -2
  18. package/dist/mcp/servicenow-xml-flow-mcp.js +671 -0
  19. package/dist/mcp/shared/base-mcp-server.js +1356 -8
  20. package/dist/mcp/shared/mcp-resource-manager.js +304 -0
  21. package/dist/queen/parallel-agent-engine.js +26 -8
  22. package/dist/queen/servicenow-queen.js +47 -44
  23. package/dist/sparc/sparc-help.js +37 -26
  24. package/dist/sparc/team-sparc.js +60 -16
  25. package/dist/utils/action-type-cache.js +2 -1
  26. package/dist/utils/mcp-config-manager.js +7 -3
  27. package/dist/utils/mcp-server-manager.js +7 -3
  28. package/dist/utils/servicenow-client.js +134 -107
  29. package/dist/utils/servicenow-id-generator.js +171 -0
  30. package/dist/utils/snow-oauth.js +13 -8
  31. package/dist/utils/widget-template-generator.js +1690 -0
  32. package/dist/utils/xml-first-flow-generator.js +473 -0
  33. package/flow-update-sets/flow_build_automated_approval_flow_with_notifications_flow.xml +203 -0
  34. package/flow-update-sets/flow_create_approval_flow_for_equipment_requests_flow.xml +206 -0
  35. package/flow-update-sets/flow_create_equipment_approval_flow_with_manager_approv_flow.xml +254 -0
  36. package/flow-update-sets/iphone_15_pro_approval_flow.xml +203 -0
  37. package/flow-update-sets/test_iphone_approval_flow_flow.xml +303 -0
  38. package/package.json +2 -1
  39. package/test-config.js +55 -0
  40. package/test-real-monitoring.js +184 -0
@@ -14,6 +14,7 @@ const logger_js_1 = require("../utils/logger.js");
14
14
  const scope_manager_js_1 = require("../managers/scope-manager.js");
15
15
  const global_scope_strategy_js_1 = require("../strategies/global-scope-strategy.js");
16
16
  const artifact_tracker_js_1 = require("../utils/artifact-tracker.js");
17
+ const servicenow_id_generator_js_1 = require("../utils/servicenow-id-generator.js");
17
18
  const fs_1 = require("fs");
18
19
  const path_1 = require("path");
19
20
  class ServiceNowDeploymentMCP {
@@ -2251,12 +2252,7 @@ Run snow_deployment_debug for basic session info or check the logs for more deta
2251
2252
  * Generate ServiceNow-style GUID
2252
2253
  */
2253
2254
  generateGUID() {
2254
- const chars = '0123456789abcdef';
2255
- let result = '';
2256
- for (let i = 0; i < 32; i++) {
2257
- result += chars.charAt(Math.floor(Math.random() * chars.length));
2258
- }
2259
- return result;
2255
+ return (0, servicenow_id_generator_js_1.generateServiceNowSysId)();
2260
2256
  }
2261
2257
  /**
2262
2258
  * Generate Update Set XML based on artifact type
@@ -2627,14 +2623,117 @@ Run snow_deployment_debug for basic session info or check the logs for more deta
2627
2623
  </sys_update_xml>`;
2628
2624
  }
2629
2625
  /**
2630
- * Generate Update Set XML for applications (stub for now)
2626
+ * Generate Update Set XML for applications
2631
2627
  */
2632
2628
  generateApplicationUpdateSetXML(args) {
2633
- // TODO: Implement application XML generation
2634
- return `<?xml version="1.0" encoding="UTF-8"?>
2635
- <unload>
2636
- <!-- Application Update Set XML not yet implemented -->
2629
+ const timestamp = new Date().toISOString();
2630
+ const updateSetName = `Application_${args.name}_${Date.now()}`;
2631
+ // Generate unique identifiers
2632
+ const updateSetId = this.generateGUID();
2633
+ const applicationId = this.generateGUID();
2634
+ const updateXmlId = this.generateGUID();
2635
+ // Default values
2636
+ const applicationData = {
2637
+ name: args.name || 'New Application',
2638
+ short_description: args.short_description || args.description || 'Custom Application',
2639
+ description: args.description || 'Generated by Snow-Flow',
2640
+ version: args.version || '1.0.0',
2641
+ vendor: args.vendor || 'Snow-Flow',
2642
+ vendor_prefix: args.vendor_prefix || 'x_snf',
2643
+ active: args.active !== false,
2644
+ scope: args.scope || 'x_snf_' + args.name?.toLowerCase().replace(/[^a-z0-9]/g, '_'),
2645
+ logo: args.logo || '',
2646
+ roles: args.roles || ''
2647
+ };
2648
+ const xmlContent = `<?xml version="1.0" encoding="UTF-8"?>
2649
+ <unload unload_date="${timestamp}">
2650
+ <sys_update_set action="INSERT_OR_UPDATE">
2651
+ <application display_value="Global">global</application>
2652
+ <category>customer</category>
2653
+ <description>Auto-generated Update Set for Application: ${applicationData.name}</description>
2654
+ <is_default>false</is_default>
2655
+ <name>${updateSetName}</name>
2656
+ <origin_sys_id/>
2657
+ <release_date/>
2658
+ <state>complete</state>
2659
+ <sys_created_by>snow-flow</sys_created_by>
2660
+ <sys_created_on>${timestamp}</sys_created_on>
2661
+ <sys_id>${updateSetId}</sys_id>
2662
+ <sys_mod_count>0</sys_mod_count>
2663
+ <sys_updated_by>snow-flow</sys_updated_by>
2664
+ <sys_updated_on>${timestamp}</sys_updated_on>
2665
+ <update_count>1</update_count>
2666
+ </sys_update_set>
2667
+
2668
+ <sys_update_xml action="INSERT_OR_UPDATE">
2669
+ <action>INSERT_OR_UPDATE</action>
2670
+ <application display_value="Global">global</application>
2671
+ <category>customer</category>
2672
+ <name>sys_app_${applicationId}</name>
2673
+ <payload><![CDATA[<?xml version="1.0" encoding="UTF-8"?><record_update table="sys_app"><sys_app action="INSERT_OR_UPDATE">
2674
+ <active>${applicationData.active}</active>
2675
+ <can_edit_in_studio>true</can_edit_in_studio>
2676
+ <category>Custom</category>
2677
+ <description>${this.escapeXml(applicationData.description)}</description>
2678
+ <enforce_license>none</enforce_license>
2679
+ <guided_setup_guid/>
2680
+ <hide_on_ui>false</hide_on_ui>
2681
+ <installed_as_dependency>false</installed_as_dependency>
2682
+ <js_level>helsinki_es5</js_level>
2683
+ <licensable>true</licensable>
2684
+ <license_category>none</license_category>
2685
+ <license_definition/>
2686
+ <license_model>none</license_model>
2687
+ <logo display_value="">${applicationData.logo}</logo>
2688
+ <menu/>
2689
+ <name>${this.escapeXml(applicationData.name)}</name>
2690
+ <private>false</private>
2691
+ <restrict_table_access>false</restrict_table_access>
2692
+ <runtime_access_tracking>permissive</runtime_access_tracking>
2693
+ <scope>${applicationData.scope}</scope>
2694
+ <scoped_administration>false</scoped_administration>
2695
+ <short_description>${this.escapeXml(applicationData.short_description)}</short_description>
2696
+ <source>${applicationData.scope}</source>
2697
+ <store_correlation_id/>
2698
+ <store_url/>
2699
+ <sys_class_name>sys_app</sys_class_name>
2700
+ <sys_created_by>snow-flow</sys_created_by>
2701
+ <sys_created_on>${timestamp}</sys_created_on>
2702
+ <sys_id>${applicationId}</sys_id>
2703
+ <sys_mod_count>0</sys_mod_count>
2704
+ <sys_updated_by>snow-flow</sys_updated_by>
2705
+ <sys_updated_on>${timestamp}</sys_updated_on>
2706
+ <template/>
2707
+ <trackable>true</trackable>
2708
+ <uninstall_blocked>false</uninstall_blocked>
2709
+ <user_role display_value="">${applicationData.roles}</user_role>
2710
+ <vendor display_value="${applicationData.vendor}">${applicationData.vendor}</vendor>
2711
+ <vendor_prefix>${applicationData.vendor_prefix}</vendor_prefix>
2712
+ <version>${applicationData.version}</version>
2713
+ </sys_app></record_update>]]></payload>
2714
+ <payload_hash>-1</payload_hash>
2715
+ <record_name>${applicationData.name}</record_name>
2716
+ <reverted_from/>
2717
+ <source display_value="sys_app.${applicationId}">sys_app.${applicationId}</source>
2718
+ <source_table>sys_app</source_table>
2719
+ <state>previous</state>
2720
+ <sys_created_by>snow-flow</sys_created_by>
2721
+ <sys_created_on>${timestamp}</sys_created_on>
2722
+ <sys_id>${updateXmlId}</sys_id>
2723
+ <sys_mod_count>0</sys_mod_count>
2724
+ <sys_updated_by>snow-flow</sys_updated_by>
2725
+ <sys_updated_on>${timestamp}</sys_updated_on>
2726
+ <table>sys_app</table>
2727
+ <target_name>${applicationData.name}</target_name>
2728
+ <type>Application File</type>
2729
+ <update_domain>global</update_domain>
2730
+ <update_guid>${this.generateGUID()}</update_guid>
2731
+ <update_guid_history>${this.generateGUID()}</update_guid_history>
2732
+ <update_set display_value="${updateSetName}">${updateSetId}</update_set>
2733
+ <view/>
2734
+ </sys_update_xml>
2637
2735
  </unload>`;
2736
+ return xmlContent;
2638
2737
  }
2639
2738
  /**
2640
2739
  * Preview widget with test data
@@ -4382,10 +4481,10 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
4382
4481
  name: this.extractNameFromInstruction(instruction),
4383
4482
  title: this.extractNameFromInstruction(instruction),
4384
4483
  description: instruction,
4385
- template: '<div>Widget placeholder - implement with proper template</div>',
4386
- css: '',
4387
- server_script: '',
4388
- client_script: '',
4484
+ template: this.generateWidgetTemplate(instruction),
4485
+ css: this.generateWidgetCss(instruction),
4486
+ server_script: this.generateWidgetServerScript(instruction),
4487
+ client_script: this.generateWidgetClientScript(instruction),
4389
4488
  instruction: instruction
4390
4489
  };
4391
4490
  }
@@ -4405,6 +4504,720 @@ Use individual deployment tools like \`snow_deploy_${args.type}\` with manual co
4405
4504
  !['the', 'and', 'for', 'with', 'that', 'will', 'can', 'should'].includes(word)).slice(0, 3).join('_');
4406
4505
  return name || 'auto_generated_artifact';
4407
4506
  }
4507
+ /**
4508
+ * Generate HTML template based on widget requirements
4509
+ */
4510
+ generateWidgetTemplate(instruction) {
4511
+ const lower = instruction.toLowerCase();
4512
+ const title = this.extractNameFromInstruction(instruction).replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
4513
+ // Chart/Graph widgets
4514
+ if (lower.includes('chart') || lower.includes('graph') || lower.includes('analytics')) {
4515
+ return `
4516
+ <div class="panel panel-default widget-chart">
4517
+ <div class="panel-heading">
4518
+ <h3 class="panel-title">{{data.title || '${title}'}}</h3>
4519
+ <div class="panel-actions" ng-if="data.refresh_enabled">
4520
+ <button class="btn btn-sm btn-default" ng-click="c.refreshData()">
4521
+ <i class="fa fa-refresh"></i> Refresh
4522
+ </button>
4523
+ </div>
4524
+ </div>
4525
+ <div class="panel-body">
4526
+ <div ng-if="data.loading" class="text-center">
4527
+ <i class="fa fa-spinner fa-spin"></i> Loading chart data...
4528
+ </div>
4529
+ <canvas ng-if="!data.loading" id="chart-{{::data.widget_id}}" width="400" height="300"></canvas>
4530
+ <div ng-if="data.error" class="alert alert-danger">
4531
+ <i class="fa fa-exclamation-triangle"></i> {{data.error}}
4532
+ </div>
4533
+ </div>
4534
+ </div>`;
4535
+ }
4536
+ // Dashboard widgets
4537
+ if (lower.includes('dashboard') || lower.includes('metrics') || lower.includes('kpi')) {
4538
+ return `
4539
+ <div class="row widget-dashboard">
4540
+ <div class="col-md-12">
4541
+ <div class="panel panel-default">
4542
+ <div class="panel-heading">
4543
+ <h3 class="panel-title">{{data.title || '${title}'}}</h3>
4544
+ <span class="panel-subtitle" ng-if="data.last_updated">
4545
+ Last updated: {{data.last_updated | date:'medium'}}
4546
+ </span>
4547
+ </div>
4548
+ <div class="panel-body">
4549
+ <div class="row">
4550
+ <div class="col-md-{{12/data.metrics.length}}" ng-repeat="metric in data.metrics track by $index">
4551
+ <div class="metric-card well text-center" ng-class="{'metric-critical': metric.critical, 'metric-warning': metric.warning}">
4552
+ <div class="metric-icon" ng-if="metric.icon">
4553
+ <i class="fa fa-{{metric.icon}}"></i>
4554
+ </div>
4555
+ <h4 class="metric-label">{{metric.label}}</h4>
4556
+ <h2 class="metric-value" ng-class="{'text-danger': metric.critical, 'text-warning': metric.warning, 'text-success': metric.success}">
4557
+ {{metric.value}} <small ng-if="metric.unit">{{metric.unit}}</small>
4558
+ </h2>
4559
+ <div class="metric-trend" ng-if="metric.trend">
4560
+ <i class="fa" ng-class="{'fa-arrow-up text-success': metric.trend > 0, 'fa-arrow-down text-danger': metric.trend < 0, 'fa-minus text-muted': metric.trend === 0}"></i>
4561
+ <span class="trend-value">{{metric.trend_text}}</span>
4562
+ </div>
4563
+ </div>
4564
+ </div>
4565
+ </div>
4566
+ </div>
4567
+ </div>
4568
+ </div>
4569
+ </div>`;
4570
+ }
4571
+ // Table/List widgets
4572
+ if (lower.includes('table') || lower.includes('list') || lower.includes('records')) {
4573
+ return `
4574
+ <div class="panel panel-default widget-table">
4575
+ <div class="panel-heading">
4576
+ <h3 class="panel-title">{{data.title || '${title}'}}</h3>
4577
+ <div class="panel-actions">
4578
+ <input type="text" class="form-control input-sm" placeholder="Search..." ng-model="c.searchTerm" ng-if="data.search_enabled">
4579
+ <button class="btn btn-sm btn-primary" ng-click="c.refresh()" ng-if="data.refresh_enabled">
4580
+ <i class="fa fa-refresh"></i>
4581
+ </button>
4582
+ </div>
4583
+ </div>
4584
+ <div class="panel-body">
4585
+ <div ng-if="data.loading" class="text-center">
4586
+ <i class="fa fa-spinner fa-spin"></i> Loading data...
4587
+ </div>
4588
+ <div ng-if="!data.loading && data.records.length > 0">
4589
+ <table class="table table-striped table-hover">
4590
+ <thead>
4591
+ <tr>
4592
+ <th ng-repeat="column in data.columns" ng-click="c.sortBy(column.field)" class="sortable">
4593
+ {{column.label}}
4594
+ <i class="fa fa-sort" ng-if="c.sortField !== column.field"></i>
4595
+ <i class="fa fa-sort-up" ng-if="c.sortField === column.field && !c.sortReverse"></i>
4596
+ <i class="fa fa-sort-down" ng-if="c.sortField === column.field && c.sortReverse"></i>
4597
+ </th>
4598
+ </tr>
4599
+ </thead>
4600
+ <tbody>
4601
+ <tr ng-repeat="record in data.records | filter:c.searchTerm | orderBy:c.sortField:c.sortReverse track by record.sys_id">
4602
+ <td ng-repeat="column in data.columns">
4603
+ <span ng-if="column.type === 'link'">
4604
+ <a href="{{record[column.field + '_link']}}" target="_blank">{{record[column.field]}}</a>
4605
+ </span>
4606
+ <span ng-if="column.type === 'date'">
4607
+ {{record[column.field] | date:'short'}}
4608
+ </span>
4609
+ <span ng-if="!column.type || (column.type !== 'link' && column.type !== 'date')">
4610
+ {{record[column.field]}}
4611
+ </span>
4612
+ </td>
4613
+ </tr>
4614
+ </tbody>
4615
+ </table>
4616
+ </div>
4617
+ <div ng-if="!data.loading && data.records.length === 0" class="alert alert-info text-center">
4618
+ <i class="fa fa-info-circle"></i> No records found
4619
+ </div>
4620
+ </div>
4621
+ </div>`;
4622
+ }
4623
+ // Form widgets
4624
+ if (lower.includes('form') || lower.includes('input') || lower.includes('create') || lower.includes('submit')) {
4625
+ return `
4626
+ <div class="panel panel-default widget-form">
4627
+ <div class="panel-heading">
4628
+ <h3 class="panel-title">{{data.title || '${title}'}}</h3>
4629
+ </div>
4630
+ <div class="panel-body">
4631
+ <form name="widgetForm" ng-submit="c.submitForm()" novalidate>
4632
+ <div class="form-group" ng-repeat="field in data.fields">
4633
+ <label class="control-label" for="{{field.name}}">
4634
+ {{field.label}}
4635
+ <span class="text-danger" ng-if="field.required">*</span>
4636
+ </label>
4637
+ <input ng-if="field.type === 'text'"
4638
+ type="text"
4639
+ class="form-control"
4640
+ id="{{field.name}}"
4641
+ name="{{field.name}}"
4642
+ ng-model="c.formData[field.name]"
4643
+ ng-required="field.required"
4644
+ placeholder="{{field.placeholder}}">
4645
+ <textarea ng-if="field.type === 'textarea'"
4646
+ class="form-control"
4647
+ id="{{field.name}}"
4648
+ name="{{field.name}}"
4649
+ ng-model="c.formData[field.name]"
4650
+ ng-required="field.required"
4651
+ placeholder="{{field.placeholder}}"
4652
+ rows="3"></textarea>
4653
+ <select ng-if="field.type === 'select'"
4654
+ class="form-control"
4655
+ id="{{field.name}}"
4656
+ name="{{field.name}}"
4657
+ ng-model="c.formData[field.name]"
4658
+ ng-required="field.required">
4659
+ <option value="">Select {{field.label}}</option>
4660
+ <option ng-repeat="option in field.options" value="{{option.value}}">{{option.label}}</option>
4661
+ </select>
4662
+ <div class="text-danger" ng-if="widgetForm[field.name].$invalid && widgetForm[field.name].$touched">
4663
+ <small ng-if="widgetForm[field.name].$error.required">{{field.label}} is required</small>
4664
+ </div>
4665
+ </div>
4666
+ <div class="form-actions">
4667
+ <button type="submit" class="btn btn-primary" ng-disabled="widgetForm.$invalid || c.submitting">
4668
+ <i class="fa fa-spinner fa-spin" ng-if="c.submitting"></i>
4669
+ {{c.submitting ? 'Submitting...' : 'Submit'}}
4670
+ </button>
4671
+ <button type="button" class="btn btn-default" ng-click="c.resetForm()">Reset</button>
4672
+ </div>
4673
+ </form>
4674
+ </div>
4675
+ </div>`;
4676
+ }
4677
+ // Generic/Default widget template
4678
+ return `
4679
+ <div class="panel panel-default widget-generic">
4680
+ <div class="panel-heading">
4681
+ <h3 class="panel-title">{{data.title || '${title}'}}</h3>
4682
+ </div>
4683
+ <div class="panel-body">
4684
+ <div ng-if="data.items && data.items.length > 0">
4685
+ <div class="list-group">
4686
+ <div class="list-group-item" ng-repeat="item in data.items track by $index">
4687
+ <div class="list-group-item-heading" ng-if="item.title">
4688
+ <strong>{{item.title}}</strong>
4689
+ </div>
4690
+ <div class="list-group-item-text" ng-if="item.description">
4691
+ {{item.description}}
4692
+ </div>
4693
+ <div class="item-meta" ng-if="item.meta">
4694
+ <small class="text-muted">{{item.meta}}</small>
4695
+ </div>
4696
+ </div>
4697
+ </div>
4698
+ </div>
4699
+ <div ng-if="!data.items || data.items.length === 0" class="text-center text-muted">
4700
+ <i class="fa fa-inbox fa-3x"></i>
4701
+ <h4>{{data.empty_message || 'No data available'}}</h4>
4702
+ <p ng-if="data.empty_description">{{data.empty_description}}</p>
4703
+ </div>
4704
+ </div>
4705
+ </div>`;
4706
+ }
4707
+ /**
4708
+ * Generate CSS styles based on widget requirements
4709
+ */
4710
+ generateWidgetCss(instruction) {
4711
+ const lower = instruction.toLowerCase();
4712
+ // Base styles for all widgets
4713
+ let css = `
4714
+ .panel {
4715
+ margin-bottom: 20px;
4716
+ border-radius: 6px;
4717
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
4718
+ }
4719
+
4720
+ .panel-heading {
4721
+ background: linear-gradient(to bottom, #f8f9fa 0%, #e9ecef 100%);
4722
+ border-bottom: 1px solid #dee2e6;
4723
+ position: relative;
4724
+ }
4725
+
4726
+ .panel-title {
4727
+ margin: 0;
4728
+ font-size: 16px;
4729
+ font-weight: 600;
4730
+ color: #495057;
4731
+ }
4732
+
4733
+ .panel-actions {
4734
+ position: absolute;
4735
+ right: 15px;
4736
+ top: 50%;
4737
+ transform: translateY(-50%);
4738
+ }
4739
+
4740
+ .panel-body {
4741
+ padding: 15px;
4742
+ }
4743
+ `;
4744
+ // Chart-specific styles
4745
+ if (lower.includes('chart') || lower.includes('graph')) {
4746
+ css += `
4747
+ .widget-chart canvas {
4748
+ max-width: 100%;
4749
+ height: auto;
4750
+ }
4751
+
4752
+ .widget-chart .panel-actions button {
4753
+ margin-left: 5px;
4754
+ }
4755
+ `;
4756
+ }
4757
+ // Dashboard-specific styles
4758
+ if (lower.includes('dashboard') || lower.includes('metrics')) {
4759
+ css += `
4760
+ .widget-dashboard .metric-card {
4761
+ margin-bottom: 15px;
4762
+ transition: all 0.3s ease;
4763
+ }
4764
+
4765
+ .widget-dashboard .metric-card:hover {
4766
+ transform: translateY(-2px);
4767
+ box-shadow: 0 4px 8px rgba(0,0,0,0.15);
4768
+ }
4769
+
4770
+ .metric-icon {
4771
+ font-size: 24px;
4772
+ margin-bottom: 10px;
4773
+ color: #6c757d;
4774
+ }
4775
+
4776
+ .metric-label {
4777
+ font-size: 14px;
4778
+ font-weight: 500;
4779
+ color: #6c757d;
4780
+ margin-bottom: 5px;
4781
+ }
4782
+
4783
+ .metric-value {
4784
+ font-size: 32px;
4785
+ font-weight: 700;
4786
+ margin: 0;
4787
+ line-height: 1;
4788
+ }
4789
+
4790
+ .metric-trend {
4791
+ margin-top: 8px;
4792
+ font-size: 12px;
4793
+ }
4794
+
4795
+ .metric-critical {
4796
+ border-left: 4px solid #dc3545;
4797
+ }
4798
+
4799
+ .metric-warning {
4800
+ border-left: 4px solid #ffc107;
4801
+ }
4802
+
4803
+ .panel-subtitle {
4804
+ position: absolute;
4805
+ right: 15px;
4806
+ top: 50%;
4807
+ transform: translateY(-50%);
4808
+ font-size: 12px;
4809
+ color: #6c757d;
4810
+ }
4811
+ `;
4812
+ }
4813
+ // Table-specific styles
4814
+ if (lower.includes('table') || lower.includes('list')) {
4815
+ css += `
4816
+ .widget-table .panel-actions {
4817
+ display: flex;
4818
+ gap: 10px;
4819
+ align-items: center;
4820
+ }
4821
+
4822
+ .widget-table .panel-actions input {
4823
+ width: 200px;
4824
+ }
4825
+
4826
+ .widget-table .sortable {
4827
+ cursor: pointer;
4828
+ user-select: none;
4829
+ }
4830
+
4831
+ .widget-table .sortable:hover {
4832
+ background-color: #f8f9fa;
4833
+ }
4834
+
4835
+ .widget-table .table > tbody > tr:hover {
4836
+ background-color: #f8f9fa;
4837
+ }
4838
+ `;
4839
+ }
4840
+ // Form-specific styles
4841
+ if (lower.includes('form') || lower.includes('input')) {
4842
+ css += `
4843
+ .widget-form .form-actions {
4844
+ margin-top: 20px;
4845
+ padding-top: 20px;
4846
+ border-top: 1px solid #dee2e6;
4847
+ }
4848
+
4849
+ .widget-form .form-actions button {
4850
+ margin-right: 10px;
4851
+ }
4852
+
4853
+ .widget-form .form-group {
4854
+ margin-bottom: 20px;
4855
+ }
4856
+
4857
+ .widget-form .control-label {
4858
+ font-weight: 500;
4859
+ margin-bottom: 5px;
4860
+ }
4861
+ `;
4862
+ }
4863
+ return css;
4864
+ }
4865
+ /**
4866
+ * Generate server-side script based on widget requirements
4867
+ */
4868
+ generateWidgetServerScript(instruction) {
4869
+ const lower = instruction.toLowerCase();
4870
+ const isChart = lower.includes('chart') || lower.includes('graph');
4871
+ const isDashboard = lower.includes('dashboard') || lower.includes('metrics');
4872
+ const isTable = lower.includes('table') || lower.includes('list');
4873
+ const isForm = lower.includes('form') || lower.includes('input');
4874
+ let script = `(function() {
4875
+ // Widget data initialization
4876
+ data.title = options.title || '${this.extractNameFromInstruction(instruction).replace(/_/g, ' ')}';
4877
+ data.widget_id = 'widget_' + Date.now();
4878
+ data.loading = false;
4879
+ data.error = null;
4880
+
4881
+ // Configuration options
4882
+ data.refresh_enabled = options.refresh_enabled || true;
4883
+ data.search_enabled = options.search_enabled || false;
4884
+
4885
+ `;
4886
+ if (isChart) {
4887
+ script += ` // Chart data initialization
4888
+ data.chart_type = options.chart_type || 'bar';
4889
+ data.chart_data = [];
4890
+ data.chart_labels = [];
4891
+
4892
+ // Sample chart data - replace with real data source
4893
+ try {
4894
+ var gr = new GlideRecord('incident');
4895
+ gr.addQuery('active', true);
4896
+ gr.addAggregate('COUNT');
4897
+ gr.groupBy('priority');
4898
+ gr.query();
4899
+
4900
+ data.chart_labels = [];
4901
+ data.chart_data = [];
4902
+
4903
+ while (gr.next()) {
4904
+ data.chart_labels.push('Priority ' + gr.priority.getDisplayValue());
4905
+ data.chart_data.push(parseInt(gr.getAggregate('COUNT')));
4906
+ }
4907
+ } catch (e) {
4908
+ data.error = 'Failed to load chart data: ' + e.getMessage();
4909
+ }
4910
+ `;
4911
+ }
4912
+ else if (isDashboard) {
4913
+ script += ` // Dashboard metrics initialization
4914
+ data.metrics = [];
4915
+ data.last_updated = new Date();
4916
+
4917
+ // Sample metrics - replace with real data sources
4918
+ try {
4919
+ // Incident metrics
4920
+ var incidentGr = new GlideRecord('incident');
4921
+ incidentGr.addQuery('active', true);
4922
+ incidentGr.query();
4923
+ var totalIncidents = incidentGr.getRowCount();
4924
+
4925
+ var criticalGr = new GlideRecord('incident');
4926
+ criticalGr.addQuery('active', true);
4927
+ criticalGr.addQuery('priority', '1');
4928
+ criticalGr.query();
4929
+ var criticalIncidents = criticalGr.getRowCount();
4930
+
4931
+ data.metrics = [
4932
+ {
4933
+ label: 'Active Incidents',
4934
+ value: totalIncidents,
4935
+ icon: 'exclamation-triangle',
4936
+ critical: totalIncidents > 100,
4937
+ warning: totalIncidents > 50
4938
+ },
4939
+ {
4940
+ label: 'Critical Issues',
4941
+ value: criticalIncidents,
4942
+ icon: 'fire',
4943
+ critical: criticalIncidents > 5,
4944
+ unit: 'critical'
4945
+ },
4946
+ {
4947
+ label: 'Resolution Rate',
4948
+ value: '94%',
4949
+ icon: 'check-circle',
4950
+ success: true,
4951
+ trend: 2,
4952
+ trend_text: '+2% this week'
4953
+ }
4954
+ ];
4955
+ } catch (e) {
4956
+ data.error = 'Failed to load dashboard data: ' + e.getMessage();
4957
+ }
4958
+ `;
4959
+ }
4960
+ else if (isTable) {
4961
+ script += ` // Table data initialization
4962
+ data.records = [];
4963
+ data.columns = [];
4964
+
4965
+ // Define table columns - customize based on requirements
4966
+ data.columns = [
4967
+ { field: 'number', label: 'Number', type: 'link' },
4968
+ { field: 'short_description', label: 'Description' },
4969
+ { field: 'priority', label: 'Priority' },
4970
+ { field: 'state', label: 'State' },
4971
+ { field: 'sys_created_on', label: 'Created', type: 'date' }
4972
+ ];
4973
+
4974
+ // Load table data - replace with appropriate table/query
4975
+ try {
4976
+ var gr = new GlideRecord('incident');
4977
+ gr.addQuery('active', true);
4978
+ gr.orderByDesc('sys_created_on');
4979
+ gr.setLimit(50);
4980
+ gr.query();
4981
+
4982
+ while (gr.next()) {
4983
+ data.records.push({
4984
+ sys_id: gr.getUniqueValue(),
4985
+ number: gr.number.getDisplayValue(),
4986
+ number_link: '/' + gr.getTableName() + '.do?sys_id=' + gr.getUniqueValue(),
4987
+ short_description: gr.short_description.getDisplayValue(),
4988
+ priority: gr.priority.getDisplayValue(),
4989
+ state: gr.state.getDisplayValue(),
4990
+ sys_created_on: gr.sys_created_on.getDisplayValue()
4991
+ });
4992
+ }
4993
+ } catch (e) {
4994
+ data.error = 'Failed to load table data: ' + e.getMessage();
4995
+ }
4996
+ `;
4997
+ }
4998
+ else if (isForm) {
4999
+ script += ` // Form configuration
5000
+ data.fields = [];
5001
+
5002
+ // Define form fields - customize based on requirements
5003
+ data.fields = [
5004
+ {
5005
+ name: 'short_description',
5006
+ label: 'Short Description',
5007
+ type: 'text',
5008
+ required: true,
5009
+ placeholder: 'Enter a brief description'
5010
+ },
5011
+ {
5012
+ name: 'description',
5013
+ label: 'Detailed Description',
5014
+ type: 'textarea',
5015
+ required: false,
5016
+ placeholder: 'Provide additional details'
5017
+ },
5018
+ {
5019
+ name: 'priority',
5020
+ label: 'Priority',
5021
+ type: 'select',
5022
+ required: true,
5023
+ options: [
5024
+ { value: '1', label: '1 - Critical' },
5025
+ { value: '2', label: '2 - High' },
5026
+ { value: '3', label: '3 - Moderate' },
5027
+ { value: '4', label: '4 - Low' }
5028
+ ]
5029
+ }
5030
+ ];
5031
+ `;
5032
+ }
5033
+ else {
5034
+ script += ` // Generic data initialization
5035
+ data.items = [];
5036
+ data.empty_message = 'No items to display';
5037
+ data.empty_description = 'Configure this widget to show your data';
5038
+
5039
+ // Sample data - replace with real data source
5040
+ try {
5041
+ // Add your data loading logic here
5042
+ data.items = [
5043
+ {
5044
+ title: 'Sample Item 1',
5045
+ description: 'This is a sample item for demonstration',
5046
+ meta: 'Sample metadata'
5047
+ },
5048
+ {
5049
+ title: 'Sample Item 2',
5050
+ description: 'Another sample item',
5051
+ meta: 'More metadata'
5052
+ }
5053
+ ];
5054
+ } catch (e) {
5055
+ data.error = 'Failed to load data: ' + e.getMessage();
5056
+ }
5057
+ `;
5058
+ }
5059
+ script += `})();`;
5060
+ return script;
5061
+ }
5062
+ /**
5063
+ * Generate client-side script based on widget requirements
5064
+ */
5065
+ generateWidgetClientScript(instruction) {
5066
+ const lower = instruction.toLowerCase();
5067
+ const isChart = lower.includes('chart') || lower.includes('graph');
5068
+ const isDashboard = lower.includes('dashboard') || lower.includes('metrics');
5069
+ const isTable = lower.includes('table') || lower.includes('list');
5070
+ const isForm = lower.includes('form') || lower.includes('input');
5071
+ let script = `function($scope, $http) {
5072
+ var c = this;
5073
+
5074
+ // Initialize controller data
5075
+ c.loading = false;
5076
+ c.error = null;
5077
+
5078
+ `;
5079
+ if (isChart) {
5080
+ script += ` // Chart initialization
5081
+ c.chartInstance = null;
5082
+
5083
+ c.$onInit = function() {
5084
+ c.initializeChart();
5085
+ };
5086
+
5087
+ c.initializeChart = function() {
5088
+ if (!c.data.chart_data || c.data.chart_data.length === 0) return;
5089
+
5090
+ var ctx = document.getElementById('chart-' + c.data.widget_id);
5091
+ if (!ctx) return;
5092
+
5093
+ c.chartInstance = new Chart(ctx, {
5094
+ type: c.data.chart_type || 'bar',
5095
+ data: {
5096
+ labels: c.data.chart_labels,
5097
+ datasets: [{
5098
+ label: c.data.title,
5099
+ data: c.data.chart_data,
5100
+ backgroundColor: [
5101
+ '#FF6384', '#36A2EB', '#FFCE56',
5102
+ '#4BC0C0', '#9966FF', '#FF9F40'
5103
+ ],
5104
+ borderWidth: 1
5105
+ }]
5106
+ },
5107
+ options: {
5108
+ responsive: true,
5109
+ plugins: {
5110
+ legend: {
5111
+ position: 'top',
5112
+ },
5113
+ title: {
5114
+ display: true,
5115
+ text: c.data.title
5116
+ }
5117
+ }
5118
+ }
5119
+ });
5120
+ };
5121
+
5122
+ c.refreshData = function() {
5123
+ c.loading = true;
5124
+ c.server.update().then(function() {
5125
+ c.loading = false;
5126
+ c.initializeChart();
5127
+ });
5128
+ };
5129
+ `;
5130
+ }
5131
+ else if (isDashboard) {
5132
+ script += ` // Dashboard functionality
5133
+ c.refreshData = function() {
5134
+ c.loading = true;
5135
+ c.server.update().then(function() {
5136
+ c.loading = false;
5137
+ });
5138
+ };
5139
+
5140
+ // Auto-refresh every 5 minutes
5141
+ setInterval(function() {
5142
+ if (c.data.refresh_enabled) {
5143
+ c.refreshData();
5144
+ }
5145
+ }, 300000);
5146
+ `;
5147
+ }
5148
+ else if (isTable) {
5149
+ script += ` // Table functionality
5150
+ c.searchTerm = '';
5151
+ c.sortField = '';
5152
+ c.sortReverse = false;
5153
+
5154
+ c.sortBy = function(field) {
5155
+ if (c.sortField === field) {
5156
+ c.sortReverse = !c.sortReverse;
5157
+ } else {
5158
+ c.sortField = field;
5159
+ c.sortReverse = false;
5160
+ }
5161
+ };
5162
+
5163
+ c.refresh = function() {
5164
+ c.loading = true;
5165
+ c.server.update().then(function() {
5166
+ c.loading = false;
5167
+ });
5168
+ };
5169
+ `;
5170
+ }
5171
+ else if (isForm) {
5172
+ script += ` // Form functionality
5173
+ c.formData = {};
5174
+ c.submitting = false;
5175
+
5176
+ c.submitForm = function() {
5177
+ if ($scope.widgetForm.$invalid) return;
5178
+
5179
+ c.submitting = true;
5180
+
5181
+ // Submit form data to server
5182
+ c.server.update({
5183
+ action: 'submit_form',
5184
+ formData: c.formData
5185
+ }).then(function(response) {
5186
+ c.submitting = false;
5187
+ if (response.success) {
5188
+ // Reset form on success
5189
+ c.resetForm();
5190
+ // Show success message
5191
+ alert('Form submitted successfully!');
5192
+ } else {
5193
+ alert('Error submitting form: ' + (response.error || 'Unknown error'));
5194
+ }
5195
+ }).catch(function(error) {
5196
+ c.submitting = false;
5197
+ alert('Error submitting form: ' + error.message);
5198
+ });
5199
+ };
5200
+
5201
+ c.resetForm = function() {
5202
+ c.formData = {};
5203
+ $scope.widgetForm.$setPristine();
5204
+ $scope.widgetForm.$setUntouched();
5205
+ };
5206
+ `;
5207
+ }
5208
+ else {
5209
+ script += ` // Generic widget functionality
5210
+ c.refresh = function() {
5211
+ c.loading = true;
5212
+ c.server.update().then(function() {
5213
+ c.loading = false;
5214
+ });
5215
+ };
5216
+ `;
5217
+ }
5218
+ script += `}`;
5219
+ return script;
5220
+ }
4408
5221
  /**
4409
5222
  * Attempt direct deployment with specific scope
4410
5223
  */