snow-flow 2.10.0 → 3.0.1
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/.mcp.json +13 -141
- package/.mcp.json.template +11 -25
- package/README.md +23 -0
- package/claude-flow +81 -0
- package/claude-flow.bat +18 -0
- package/claude-flow.config.json +20 -0
- package/claude-flow.ps1 +24 -0
- package/dist/agents/index.d.ts +3 -10
- package/dist/agents/index.js +10 -50
- package/dist/agents/queen-agent.d.ts +0 -2
- package/dist/agents/queen-agent.js +25 -42
- package/dist/cli.js +1 -1
- package/dist/mcp/servicenow-automation-mcp.js +10 -10
- package/dist/mcp/servicenow-deployment-mcp.js +1050 -169
- package/dist/mcp/servicenow-development-assistant-mcp.js +13 -65
- package/dist/mcp/servicenow-integration-mcp.js +10 -10
- package/dist/mcp/servicenow-machine-learning-mcp.js +15 -15
- package/dist/mcp/servicenow-operations-mcp.js +23 -23
- package/dist/mcp/servicenow-platform-development-mcp.js +9 -9
- package/dist/mcp/servicenow-reporting-analytics-mcp.js +11 -11
- package/dist/mcp/servicenow-security-compliance-mcp.js +11 -11
- package/dist/mcp/servicenow-update-set-mcp.js +9 -9
- package/dist/mcp/shared/reliable-memory-manager.d.ts +78 -0
- package/dist/mcp/shared/reliable-memory-manager.js +268 -0
- package/dist/mcp/snow-flow-mcp.js +348 -87
- package/dist/queen/agent-factory.d.ts +4 -2
- package/dist/queen/agent-factory.js +72 -25
- package/dist/services/tensorflow-ml-service.d.ts +105 -0
- package/dist/services/tensorflow-ml-service.js +456 -0
- package/dist/services/widget-deployment-service.d.ts +107 -0
- package/dist/services/widget-deployment-service.js +332 -0
- package/dist/utils/file-storage-fallback.d.ts +62 -0
- package/dist/utils/file-storage-fallback.js +289 -0
- package/dist/utils/mcp-server-manager.js +4 -7
- package/dist/utils/mcp-singleton-enforcer.d.ts +7 -1
- package/dist/utils/mcp-singleton-enforcer.js +28 -4
- package/dist/utils/mcp-timeout-fix.d.ts +10 -1
- package/dist/utils/mcp-timeout-fix.js +80 -6
- package/memory/agents/README.md +31 -0
- package/memory/claude-flow-data.json +1 -1
- package/memory/sessions/README.md +1 -1
- package/package.json +4 -2
- package/src/agents/README.md +192 -0
- package/src/health/README.md +161 -0
- package/src/memory/README.md +240 -0
- package/src/queen/README.md +403 -0
- package/src/schemas/deployment.schema.json +58 -0
- package/src/schemas/flow.schema.json +79 -0
- package/src/schemas/widget.schema.json +72 -0
- package/src/templates/base/application.template.json +45 -0
- package/src/templates/base/business_rule.template.json +33 -0
- package/src/templates/base/script_include.template.json +18 -0
- package/src/templates/base/table.template.json +64 -0
- package/src/templates/base/widget.template.json +25 -0
- package/src/templates/patterns/composite.incident-management.template.json +140 -0
- package/src/templates/patterns/widget.dashboard.template.json +238 -0
- package/src/templates/patterns/widget.datatable.template.json +292 -0
- package/.env.example +0 -64
- package/dist/mcp/servicenow-graph-memory-mcp.js +0 -728
- package/servicenow/widgets/openai_incident_classifier/client_controller.js +0 -284
- package/servicenow/widgets/openai_incident_classifier/server_script.js +0 -314
- package/servicenow/widgets/openai_incident_classifier/style.css +0 -354
- package/servicenow/widgets/openai_incident_classifier/template.html +0 -167
- package/servicenow/widgets/openai_incident_classifier/widget.json +0 -86
- package/test-ml-improvements.sh +0 -76
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "widget",
|
|
3
|
+
"name": "Dashboard Widget Template",
|
|
4
|
+
"description": "Template for creating dashboard widgets with charts and metrics",
|
|
5
|
+
"category": "patterns/dashboard",
|
|
6
|
+
"config": {
|
|
7
|
+
"name": "{{WIDGET_NAME|custom_dashboard}}",
|
|
8
|
+
"id": "{{WIDGET_ID|custom_dashboard}}",
|
|
9
|
+
"instance_id": "{{INSTANCE_ID|dashboard_}}",
|
|
10
|
+
"data": {
|
|
11
|
+
"title": "{{WIDGET_TITLE|Dashboard}}",
|
|
12
|
+
"short_description": "{{WIDGET_DESCRIPTION|Real-time dashboard with key metrics}}",
|
|
13
|
+
"roles": "{{WIDGET_ROLES|}}"
|
|
14
|
+
},
|
|
15
|
+
"options": [
|
|
16
|
+
{
|
|
17
|
+
"name": "refresh_interval",
|
|
18
|
+
"section": "Behavior",
|
|
19
|
+
"label": "Refresh Interval (seconds)",
|
|
20
|
+
"type": "integer",
|
|
21
|
+
"value": "30",
|
|
22
|
+
"help": "How often to refresh the dashboard data"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "color_scheme",
|
|
26
|
+
"section": "Appearance",
|
|
27
|
+
"label": "Color Scheme",
|
|
28
|
+
"type": "choice",
|
|
29
|
+
"value": "default",
|
|
30
|
+
"choices": [
|
|
31
|
+
{"label": "Default", "value": "default"},
|
|
32
|
+
{"label": "Dark", "value": "dark"},
|
|
33
|
+
{"label": "High Contrast", "value": "high-contrast"}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"name": "chart_type",
|
|
38
|
+
"section": "Appearance",
|
|
39
|
+
"label": "Default Chart Type",
|
|
40
|
+
"type": "choice",
|
|
41
|
+
"value": "bar",
|
|
42
|
+
"choices": [
|
|
43
|
+
{"label": "Bar Chart", "value": "bar"},
|
|
44
|
+
{"label": "Line Chart", "value": "line"},
|
|
45
|
+
{"label": "Pie Chart", "value": "pie"},
|
|
46
|
+
{"label": "Donut Chart", "value": "donut"}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"html": [
|
|
51
|
+
"<div class=\"dashboard-widget\" ng-class=\"{'dark-theme': options.color_scheme === 'dark', 'high-contrast': options.color_scheme === 'high-contrast'}\">",
|
|
52
|
+
" <div class=\"dashboard-header\">",
|
|
53
|
+
" <h2 class=\"dashboard-title\">{{data.title}}</h2>",
|
|
54
|
+
" <div class=\"dashboard-controls\">",
|
|
55
|
+
" <button class=\"btn btn-sm\" ng-click=\"c.refresh()\" title=\"Refresh\">",
|
|
56
|
+
" <i class=\"fa fa-refresh\" ng-class=\"{'fa-spin': c.loading}\"></i>",
|
|
57
|
+
" </button>",
|
|
58
|
+
" <button class=\"btn btn-sm\" ng-click=\"c.toggleFullscreen()\" title=\"Fullscreen\">",
|
|
59
|
+
" <i class=\"fa\" ng-class=\"c.fullscreen ? 'fa-compress' : 'fa-expand'\"></i>",
|
|
60
|
+
" </button>",
|
|
61
|
+
" </div>",
|
|
62
|
+
" </div>",
|
|
63
|
+
" ",
|
|
64
|
+
" <div class=\"dashboard-metrics\" ng-if=\"!c.loading\">",
|
|
65
|
+
" <div class=\"metric-card\" ng-repeat=\"metric in c.metrics\">",
|
|
66
|
+
" <div class=\"metric-value\" ng-style=\"{'color': metric.color}\">{{metric.value}}</div>",
|
|
67
|
+
" <div class=\"metric-label\">{{metric.label}}</div>",
|
|
68
|
+
" <div class=\"metric-trend\" ng-if=\"metric.trend\">",
|
|
69
|
+
" <i class=\"fa\" ng-class=\"metric.trend > 0 ? 'fa-arrow-up text-success' : 'fa-arrow-down text-danger'\"></i>",
|
|
70
|
+
" <span>{{metric.trend}}%</span>",
|
|
71
|
+
" </div>",
|
|
72
|
+
" </div>",
|
|
73
|
+
" </div>",
|
|
74
|
+
" ",
|
|
75
|
+
" <div class=\"dashboard-charts\" ng-if=\"!c.loading\">",
|
|
76
|
+
" <div class=\"chart-container\" ng-repeat=\"chart in c.charts\">",
|
|
77
|
+
" <h3>{{chart.title}}</h3>",
|
|
78
|
+
" <canvas id=\"chart-{{$index}}\" width=\"400\" height=\"200\"></canvas>",
|
|
79
|
+
" </div>",
|
|
80
|
+
" </div>",
|
|
81
|
+
" ",
|
|
82
|
+
" <div class=\"dashboard-loading\" ng-if=\"c.loading\">",
|
|
83
|
+
" <i class=\"fa fa-spinner fa-spin fa-3x\"></i>",
|
|
84
|
+
" <p>Loading dashboard data...</p>",
|
|
85
|
+
" </div>",
|
|
86
|
+
"</div>"
|
|
87
|
+
],
|
|
88
|
+
"css": [
|
|
89
|
+
".dashboard-widget {",
|
|
90
|
+
" padding: 20px;",
|
|
91
|
+
" background: #fff;",
|
|
92
|
+
" border-radius: 8px;",
|
|
93
|
+
" box-shadow: 0 2px 4px rgba(0,0,0,0.1);",
|
|
94
|
+
"}",
|
|
95
|
+
"",
|
|
96
|
+
".dashboard-widget.dark-theme {",
|
|
97
|
+
" background: #1a1a1a;",
|
|
98
|
+
" color: #fff;",
|
|
99
|
+
"}",
|
|
100
|
+
"",
|
|
101
|
+
".dashboard-widget.high-contrast {",
|
|
102
|
+
" border: 2px solid #000;",
|
|
103
|
+
"}",
|
|
104
|
+
"",
|
|
105
|
+
".dashboard-header {",
|
|
106
|
+
" display: flex;",
|
|
107
|
+
" justify-content: space-between;",
|
|
108
|
+
" align-items: center;",
|
|
109
|
+
" margin-bottom: 20px;",
|
|
110
|
+
"}",
|
|
111
|
+
"",
|
|
112
|
+
".dashboard-title {",
|
|
113
|
+
" margin: 0;",
|
|
114
|
+
" font-size: 24px;",
|
|
115
|
+
" font-weight: 600;",
|
|
116
|
+
"}",
|
|
117
|
+
"",
|
|
118
|
+
".dashboard-controls button {",
|
|
119
|
+
" margin-left: 10px;",
|
|
120
|
+
"}",
|
|
121
|
+
"",
|
|
122
|
+
".dashboard-metrics {",
|
|
123
|
+
" display: grid;",
|
|
124
|
+
" grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));",
|
|
125
|
+
" gap: 20px;",
|
|
126
|
+
" margin-bottom: 30px;",
|
|
127
|
+
"}",
|
|
128
|
+
"",
|
|
129
|
+
".metric-card {",
|
|
130
|
+
" background: #f5f5f5;",
|
|
131
|
+
" padding: 20px;",
|
|
132
|
+
" border-radius: 8px;",
|
|
133
|
+
" text-align: center;",
|
|
134
|
+
" transition: transform 0.2s;",
|
|
135
|
+
"}",
|
|
136
|
+
"",
|
|
137
|
+
".dark-theme .metric-card {",
|
|
138
|
+
" background: #2a2a2a;",
|
|
139
|
+
"}",
|
|
140
|
+
"",
|
|
141
|
+
".metric-card:hover {",
|
|
142
|
+
" transform: translateY(-2px);",
|
|
143
|
+
" box-shadow: 0 4px 8px rgba(0,0,0,0.1);",
|
|
144
|
+
"}",
|
|
145
|
+
"",
|
|
146
|
+
".metric-value {",
|
|
147
|
+
" font-size: 36px;",
|
|
148
|
+
" font-weight: bold;",
|
|
149
|
+
" margin-bottom: 5px;",
|
|
150
|
+
"}",
|
|
151
|
+
"",
|
|
152
|
+
".metric-label {",
|
|
153
|
+
" font-size: 14px;",
|
|
154
|
+
" color: #666;",
|
|
155
|
+
" text-transform: uppercase;",
|
|
156
|
+
"}",
|
|
157
|
+
"",
|
|
158
|
+
".dark-theme .metric-label {",
|
|
159
|
+
" color: #999;",
|
|
160
|
+
"}",
|
|
161
|
+
"",
|
|
162
|
+
".metric-trend {",
|
|
163
|
+
" margin-top: 10px;",
|
|
164
|
+
" font-size: 14px;",
|
|
165
|
+
"}",
|
|
166
|
+
"",
|
|
167
|
+
".dashboard-charts {",
|
|
168
|
+
" display: grid;",
|
|
169
|
+
" grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));",
|
|
170
|
+
" gap: 30px;",
|
|
171
|
+
"}",
|
|
172
|
+
"",
|
|
173
|
+
".chart-container {",
|
|
174
|
+
" background: #f9f9f9;",
|
|
175
|
+
" padding: 20px;",
|
|
176
|
+
" border-radius: 8px;",
|
|
177
|
+
"}",
|
|
178
|
+
"",
|
|
179
|
+
".dark-theme .chart-container {",
|
|
180
|
+
" background: #2a2a2a;",
|
|
181
|
+
"}",
|
|
182
|
+
"",
|
|
183
|
+
".dashboard-loading {",
|
|
184
|
+
" text-align: center;",
|
|
185
|
+
" padding: 60px 20px;",
|
|
186
|
+
" color: #666;",
|
|
187
|
+
"}",
|
|
188
|
+
"",
|
|
189
|
+
".dark-theme .dashboard-loading {",
|
|
190
|
+
" color: #999;",
|
|
191
|
+
"}",
|
|
192
|
+
"",
|
|
193
|
+
"@media (max-width: 768px) {",
|
|
194
|
+
" .dashboard-metrics {",
|
|
195
|
+
" grid-template-columns: 1fr;",
|
|
196
|
+
" }",
|
|
197
|
+
" .dashboard-charts {",
|
|
198
|
+
" grid-template-columns: 1fr;",
|
|
199
|
+
" }",
|
|
200
|
+
"}"
|
|
201
|
+
],
|
|
202
|
+
"client_controller": "{{CLIENT_CONTROLLER|dashboard_controller}}",
|
|
203
|
+
"script": "{{SERVER_SCRIPT|dashboard_server_script}}",
|
|
204
|
+
"link_function": "{{LINK_FUNCTION|}}",
|
|
205
|
+
"demo_data": {
|
|
206
|
+
"metrics": [
|
|
207
|
+
{"label": "Total Items", "value": "1,234", "color": "#007bff", "trend": 12},
|
|
208
|
+
{"label": "Active Users", "value": "456", "color": "#28a745", "trend": 5},
|
|
209
|
+
{"label": "Pending Tasks", "value": "78", "color": "#ffc107", "trend": -3},
|
|
210
|
+
{"label": "Completed", "value": "89%", "color": "#17a2b8", "trend": 2}
|
|
211
|
+
],
|
|
212
|
+
"charts": [
|
|
213
|
+
{
|
|
214
|
+
"title": "Weekly Trend",
|
|
215
|
+
"type": "line",
|
|
216
|
+
"data": {
|
|
217
|
+
"labels": ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
|
218
|
+
"datasets": [{
|
|
219
|
+
"label": "Items",
|
|
220
|
+
"data": [65, 59, 80, 81, 56, 55, 40]
|
|
221
|
+
}]
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"variables": {
|
|
228
|
+
"WIDGET_NAME": "custom_dashboard",
|
|
229
|
+
"WIDGET_ID": "custom_dashboard",
|
|
230
|
+
"INSTANCE_ID": "dashboard_",
|
|
231
|
+
"WIDGET_TITLE": "Dashboard",
|
|
232
|
+
"WIDGET_DESCRIPTION": "Real-time dashboard with key metrics",
|
|
233
|
+
"WIDGET_ROLES": "",
|
|
234
|
+
"CLIENT_CONTROLLER": "function($scope, $http, $timeout) {\n var c = this;\n \n c.loading = true;\n c.fullscreen = false;\n c.metrics = [];\n c.charts = [];\n \n c.refresh = function() {\n c.loading = true;\n c.server.get({\n action: 'get_dashboard_data'\n }).then(function(response) {\n c.metrics = response.data.metrics || [];\n c.charts = response.data.charts || [];\n c.loading = false;\n \n // Render charts\n $timeout(function() {\n c.renderCharts();\n }, 100);\n });\n };\n \n c.renderCharts = function() {\n c.charts.forEach(function(chart, index) {\n var ctx = document.getElementById('chart-' + index);\n if (ctx) {\n new Chart(ctx.getContext('2d'), {\n type: c.options.chart_type || chart.type || 'bar',\n data: chart.data,\n options: {\n responsive: true,\n maintainAspectRatio: false\n }\n });\n }\n });\n };\n \n c.toggleFullscreen = function() {\n c.fullscreen = !c.fullscreen;\n // Implement fullscreen logic\n };\n \n // Initial load\n c.refresh();\n \n // Auto-refresh\n if (c.options.refresh_interval > 0) {\n var refreshTimer = $timeout(function refresh() {\n c.refresh();\n refreshTimer = $timeout(refresh, c.options.refresh_interval * 1000);\n }, c.options.refresh_interval * 1000);\n \n $scope.$on('$destroy', function() {\n $timeout.cancel(refreshTimer);\n });\n }\n}",
|
|
235
|
+
"SERVER_SCRIPT": "(function() {\n try {\n data.metrics = [];\n data.charts = [];\n gs.log('Dashboard widget server script started', 'dashboard_widget');\n \n if (input && input.action === 'get_dashboard_data') {\n // Example: Fetch incident metrics\n var gr = new GlideRecord('incident');\n gr.addQuery('active', 'true'); // ✅ FIXED: Use string instead of boolean\n gr.query();\n \n var totalCount = gr.getRowCount();\n gs.log('Found ' + totalCount + ' active incidents', 'dashboard_widget');\n \n // Get priority breakdown\n var priorities = {};\n gr = new GlideRecord('incident');\n gr.addQuery('active', 'true'); // ✅ FIXED: Use string instead of boolean\n gr.query();\n \n while (gr.next()) {\n var priority = gr.getValue('priority') || 'undefined';\n priorities[priority] = (priorities[priority] || 0) + 1;\n }\n \n // Build metrics\n data.metrics = [\n {\n label: 'Total Incidents',\n value: totalCount.toString(),\n color: '#007bff',\n trend: 5\n },\n {\n label: 'High Priority',\n value: (priorities['1'] || 0).toString(),\n color: '#dc3545',\n trend: -2\n },\n {\n label: 'Medium Priority',\n value: (priorities['2'] || 0).toString(),\n color: '#ffc107',\n trend: 3\n },\n {\n label: 'Low Priority',\n value: (priorities['3'] || 0).toString(),\n color: '#28a745',\n trend: 1\n }\n ];\n \n // Build chart data\n data.charts = [\n {\n title: 'Incidents by Priority',\n type: 'bar',\n data: {\n labels: ['High', 'Medium', 'Low'],\n datasets: [{\n label: 'Incidents',\n data: [\n priorities['1'] || 0,\n priorities['2'] || 0,\n priorities['3'] || 0\n ],\n backgroundColor: ['#dc3545', '#ffc107', '#28a745']\n }]\n }\n }\n ];\n \n gs.log('Dashboard data prepared successfully', 'dashboard_widget');\n }\n } catch (error) {\n gs.error('Dashboard widget error: ' + error.message, 'dashboard_widget');\n data.error = 'Error loading dashboard: ' + error.message;\n data.success = false;\n }\n})()",
|
|
236
|
+
"LINK_FUNCTION": ""
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "widget",
|
|
3
|
+
"name": "Data Table Widget Template",
|
|
4
|
+
"description": "Template for creating data table widgets with sorting, filtering, and pagination",
|
|
5
|
+
"category": "patterns/datatable",
|
|
6
|
+
"config": {
|
|
7
|
+
"name": "{{WIDGET_NAME|data_table}}",
|
|
8
|
+
"id": "{{WIDGET_ID|data_table}}",
|
|
9
|
+
"instance_id": "{{INSTANCE_ID|table_}}",
|
|
10
|
+
"data": {
|
|
11
|
+
"title": "{{WIDGET_TITLE|Data Table}}",
|
|
12
|
+
"short_description": "{{WIDGET_DESCRIPTION|Interactive data table with advanced features}}",
|
|
13
|
+
"roles": "{{WIDGET_ROLES|}}"
|
|
14
|
+
},
|
|
15
|
+
"options": [
|
|
16
|
+
{
|
|
17
|
+
"name": "table_name",
|
|
18
|
+
"section": "Data",
|
|
19
|
+
"label": "Table",
|
|
20
|
+
"type": "reference",
|
|
21
|
+
"value": "{{TABLE_NAME|incident}}",
|
|
22
|
+
"ed": "sys_db_object",
|
|
23
|
+
"help": "Select the table to display data from"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "max_records",
|
|
27
|
+
"section": "Data",
|
|
28
|
+
"label": "Maximum Records",
|
|
29
|
+
"type": "integer",
|
|
30
|
+
"value": "100",
|
|
31
|
+
"help": "Maximum number of records to display"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"name": "enable_filter",
|
|
35
|
+
"section": "Features",
|
|
36
|
+
"label": "Enable Filtering",
|
|
37
|
+
"type": "boolean",
|
|
38
|
+
"value": "true"
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "enable_export",
|
|
42
|
+
"section": "Features",
|
|
43
|
+
"label": "Enable Export",
|
|
44
|
+
"type": "boolean",
|
|
45
|
+
"value": "true"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "enable_inline_edit",
|
|
49
|
+
"section": "Features",
|
|
50
|
+
"label": "Enable Inline Editing",
|
|
51
|
+
"type": "boolean",
|
|
52
|
+
"value": "false"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "row_actions",
|
|
56
|
+
"section": "Features",
|
|
57
|
+
"label": "Row Actions",
|
|
58
|
+
"type": "json",
|
|
59
|
+
"value": "[{\"label\":\"View\",\"action\":\"view\",\"icon\":\"eye\"},{\"label\":\"Edit\",\"action\":\"edit\",\"icon\":\"pencil\"}]"
|
|
60
|
+
}
|
|
61
|
+
],
|
|
62
|
+
"html": [
|
|
63
|
+
"<div class=\"data-table-widget\">",
|
|
64
|
+
" <div class=\"table-header\">",
|
|
65
|
+
" <h2 class=\"table-title\">{{data.title}}</h2>",
|
|
66
|
+
" <div class=\"table-controls\">",
|
|
67
|
+
" <div class=\"search-box\" ng-if=\"options.enable_filter\">",
|
|
68
|
+
" <i class=\"fa fa-search\"></i>",
|
|
69
|
+
" <input type=\"text\" ng-model=\"c.searchTerm\" ng-change=\"c.filterData()\" placeholder=\"Search...\">",
|
|
70
|
+
" </div>",
|
|
71
|
+
" <button class=\"btn btn-primary\" ng-if=\"options.enable_export\" ng-click=\"c.exportData()\">",
|
|
72
|
+
" <i class=\"fa fa-download\"></i> Export",
|
|
73
|
+
" </button>",
|
|
74
|
+
" </div>",
|
|
75
|
+
" </div>",
|
|
76
|
+
" ",
|
|
77
|
+
" <div class=\"table-container\" ng-if=\"!c.loading\">",
|
|
78
|
+
" <table class=\"table table-striped table-hover\">",
|
|
79
|
+
" <thead>",
|
|
80
|
+
" <tr>",
|
|
81
|
+
" <th ng-repeat=\"column in c.columns\" ng-click=\"c.sort(column.field)\" class=\"sortable\">",
|
|
82
|
+
" {{column.label}}",
|
|
83
|
+
" <i class=\"fa fa-sort\" ng-if=\"c.sortField !== column.field\"></i>",
|
|
84
|
+
" <i class=\"fa fa-sort-asc\" ng-if=\"c.sortField === column.field && c.sortDirection === 'asc'\"></i>",
|
|
85
|
+
" <i class=\"fa fa-sort-desc\" ng-if=\"c.sortField === column.field && c.sortDirection === 'desc'\"></i>",
|
|
86
|
+
" </th>",
|
|
87
|
+
" <th ng-if=\"c.rowActions.length > 0\">Actions</th>",
|
|
88
|
+
" </tr>",
|
|
89
|
+
" </thead>",
|
|
90
|
+
" <tbody>",
|
|
91
|
+
" <tr ng-repeat=\"row in c.displayData\">",
|
|
92
|
+
" <td ng-repeat=\"column in c.columns\">",
|
|
93
|
+
" <span ng-if=\"!c.isEditing(row, column)\" ng-click=\"c.startEdit(row, column)\">",
|
|
94
|
+
" {{c.getCellValue(row, column)}}",
|
|
95
|
+
" </span>",
|
|
96
|
+
" <input ng-if=\"c.isEditing(row, column)\" ",
|
|
97
|
+
" type=\"text\" ",
|
|
98
|
+
" ng-model=\"row[column.field]\" ",
|
|
99
|
+
" ng-blur=\"c.saveEdit(row, column)\"",
|
|
100
|
+
" ng-keypress=\"c.handleEditKeypress($event, row, column)\">",
|
|
101
|
+
" </td>",
|
|
102
|
+
" <td ng-if=\"c.rowActions.length > 0\" class=\"actions-cell\">",
|
|
103
|
+
" <button ng-repeat=\"action in c.rowActions\" ",
|
|
104
|
+
" class=\"btn btn-sm btn-default\" ",
|
|
105
|
+
" ng-click=\"c.performAction(action, row)\"",
|
|
106
|
+
" title=\"{{action.label}}\">",
|
|
107
|
+
" <i class=\"fa fa-{{action.icon}}\"></i>",
|
|
108
|
+
" </button>",
|
|
109
|
+
" </td>",
|
|
110
|
+
" </tr>",
|
|
111
|
+
" </tbody>",
|
|
112
|
+
" </table>",
|
|
113
|
+
" </div>",
|
|
114
|
+
" ",
|
|
115
|
+
" <div class=\"table-pagination\" ng-if=\"c.totalPages > 1\">",
|
|
116
|
+
" <button class=\"btn btn-sm\" ng-click=\"c.previousPage()\" ng-disabled=\"c.currentPage === 1\">",
|
|
117
|
+
" <i class=\"fa fa-chevron-left\"></i>",
|
|
118
|
+
" </button>",
|
|
119
|
+
" <span class=\"page-info\">Page {{c.currentPage}} of {{c.totalPages}}</span>",
|
|
120
|
+
" <button class=\"btn btn-sm\" ng-click=\"c.nextPage()\" ng-disabled=\"c.currentPage === c.totalPages\">",
|
|
121
|
+
" <i class=\"fa fa-chevron-right\"></i>",
|
|
122
|
+
" </button>",
|
|
123
|
+
" </div>",
|
|
124
|
+
" ",
|
|
125
|
+
" <div class=\"table-loading\" ng-if=\"c.loading\">",
|
|
126
|
+
" <i class=\"fa fa-spinner fa-spin fa-3x\"></i>",
|
|
127
|
+
" <p>Loading data...</p>",
|
|
128
|
+
" </div>",
|
|
129
|
+
"</div>"
|
|
130
|
+
],
|
|
131
|
+
"css": [
|
|
132
|
+
".data-table-widget {",
|
|
133
|
+
" background: #fff;",
|
|
134
|
+
" border-radius: 8px;",
|
|
135
|
+
" box-shadow: 0 2px 4px rgba(0,0,0,0.1);",
|
|
136
|
+
" overflow: hidden;",
|
|
137
|
+
"}",
|
|
138
|
+
"",
|
|
139
|
+
".table-header {",
|
|
140
|
+
" display: flex;",
|
|
141
|
+
" justify-content: space-between;",
|
|
142
|
+
" align-items: center;",
|
|
143
|
+
" padding: 20px;",
|
|
144
|
+
" border-bottom: 1px solid #e0e0e0;",
|
|
145
|
+
"}",
|
|
146
|
+
"",
|
|
147
|
+
".table-title {",
|
|
148
|
+
" margin: 0;",
|
|
149
|
+
" font-size: 20px;",
|
|
150
|
+
" font-weight: 600;",
|
|
151
|
+
"}",
|
|
152
|
+
"",
|
|
153
|
+
".table-controls {",
|
|
154
|
+
" display: flex;",
|
|
155
|
+
" align-items: center;",
|
|
156
|
+
" gap: 15px;",
|
|
157
|
+
"}",
|
|
158
|
+
"",
|
|
159
|
+
".search-box {",
|
|
160
|
+
" position: relative;",
|
|
161
|
+
" display: flex;",
|
|
162
|
+
" align-items: center;",
|
|
163
|
+
"}",
|
|
164
|
+
"",
|
|
165
|
+
".search-box i {",
|
|
166
|
+
" position: absolute;",
|
|
167
|
+
" left: 10px;",
|
|
168
|
+
" color: #666;",
|
|
169
|
+
"}",
|
|
170
|
+
"",
|
|
171
|
+
".search-box input {",
|
|
172
|
+
" padding: 8px 8px 8px 35px;",
|
|
173
|
+
" border: 1px solid #ddd;",
|
|
174
|
+
" border-radius: 4px;",
|
|
175
|
+
" width: 250px;",
|
|
176
|
+
"}",
|
|
177
|
+
"",
|
|
178
|
+
".table-container {",
|
|
179
|
+
" overflow-x: auto;",
|
|
180
|
+
"}",
|
|
181
|
+
"",
|
|
182
|
+
"table {",
|
|
183
|
+
" width: 100%;",
|
|
184
|
+
" border-collapse: collapse;",
|
|
185
|
+
"}",
|
|
186
|
+
"",
|
|
187
|
+
"th {",
|
|
188
|
+
" background: #f5f5f5;",
|
|
189
|
+
" padding: 12px;",
|
|
190
|
+
" text-align: left;",
|
|
191
|
+
" font-weight: 600;",
|
|
192
|
+
" color: #333;",
|
|
193
|
+
" white-space: nowrap;",
|
|
194
|
+
"}",
|
|
195
|
+
"",
|
|
196
|
+
"th.sortable {",
|
|
197
|
+
" cursor: pointer;",
|
|
198
|
+
" user-select: none;",
|
|
199
|
+
"}",
|
|
200
|
+
"",
|
|
201
|
+
"th.sortable:hover {",
|
|
202
|
+
" background: #e8e8e8;",
|
|
203
|
+
"}",
|
|
204
|
+
"",
|
|
205
|
+
"th i {",
|
|
206
|
+
" margin-left: 5px;",
|
|
207
|
+
" color: #999;",
|
|
208
|
+
"}",
|
|
209
|
+
"",
|
|
210
|
+
"td {",
|
|
211
|
+
" padding: 12px;",
|
|
212
|
+
" border-bottom: 1px solid #f0f0f0;",
|
|
213
|
+
"}",
|
|
214
|
+
"",
|
|
215
|
+
"td input {",
|
|
216
|
+
" width: 100%;",
|
|
217
|
+
" padding: 4px 8px;",
|
|
218
|
+
" border: 1px solid #007bff;",
|
|
219
|
+
" border-radius: 4px;",
|
|
220
|
+
"}",
|
|
221
|
+
"",
|
|
222
|
+
"tr:hover {",
|
|
223
|
+
" background: #f9f9f9;",
|
|
224
|
+
"}",
|
|
225
|
+
"",
|
|
226
|
+
".actions-cell {",
|
|
227
|
+
" white-space: nowrap;",
|
|
228
|
+
"}",
|
|
229
|
+
"",
|
|
230
|
+
".actions-cell button {",
|
|
231
|
+
" margin-right: 5px;",
|
|
232
|
+
"}",
|
|
233
|
+
"",
|
|
234
|
+
".table-pagination {",
|
|
235
|
+
" display: flex;",
|
|
236
|
+
" justify-content: center;",
|
|
237
|
+
" align-items: center;",
|
|
238
|
+
" padding: 20px;",
|
|
239
|
+
" gap: 15px;",
|
|
240
|
+
" border-top: 1px solid #e0e0e0;",
|
|
241
|
+
"}",
|
|
242
|
+
"",
|
|
243
|
+
".page-info {",
|
|
244
|
+
" color: #666;",
|
|
245
|
+
"}",
|
|
246
|
+
"",
|
|
247
|
+
".table-loading {",
|
|
248
|
+
" text-align: center;",
|
|
249
|
+
" padding: 60px 20px;",
|
|
250
|
+
" color: #666;",
|
|
251
|
+
"}",
|
|
252
|
+
"",
|
|
253
|
+
"@media (max-width: 768px) {",
|
|
254
|
+
" .table-header {",
|
|
255
|
+
" flex-direction: column;",
|
|
256
|
+
" align-items: flex-start;",
|
|
257
|
+
" gap: 15px;",
|
|
258
|
+
" }",
|
|
259
|
+
" ",
|
|
260
|
+
" .search-box input {",
|
|
261
|
+
" width: 200px;",
|
|
262
|
+
" }",
|
|
263
|
+
"}"
|
|
264
|
+
],
|
|
265
|
+
"client_controller": "{{CLIENT_CONTROLLER|datatable_controller}}",
|
|
266
|
+
"script": "{{SERVER_SCRIPT|datatable_server_script}}",
|
|
267
|
+
"link_function": "{{LINK_FUNCTION|}}",
|
|
268
|
+
"demo_data": {
|
|
269
|
+
"columns": [
|
|
270
|
+
{"field": "number", "label": "Number"},
|
|
271
|
+
{"field": "short_description", "label": "Description"},
|
|
272
|
+
{"field": "priority", "label": "Priority"},
|
|
273
|
+
{"field": "state", "label": "State"}
|
|
274
|
+
],
|
|
275
|
+
"data": [
|
|
276
|
+
{"number": "INC0001", "short_description": "Sample incident", "priority": "High", "state": "Open"}
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
"variables": {
|
|
281
|
+
"WIDGET_NAME": "data_table",
|
|
282
|
+
"WIDGET_ID": "data_table",
|
|
283
|
+
"INSTANCE_ID": "table_",
|
|
284
|
+
"WIDGET_TITLE": "Data Table",
|
|
285
|
+
"WIDGET_DESCRIPTION": "Interactive data table with advanced features",
|
|
286
|
+
"WIDGET_ROLES": "",
|
|
287
|
+
"TABLE_NAME": "incident",
|
|
288
|
+
"CLIENT_CONTROLLER": "function($scope, $http, $timeout) {\n var c = this;\n \n c.loading = true;\n c.data = [];\n c.displayData = [];\n c.columns = [];\n c.searchTerm = '';\n c.sortField = null;\n c.sortDirection = 'asc';\n c.currentPage = 1;\n c.pageSize = 20;\n c.totalPages = 1;\n c.editingCell = null;\n c.rowActions = [];\n \n // Parse row actions from options\n try {\n c.rowActions = JSON.parse(c.options.row_actions || '[]');\n } catch(e) {\n c.rowActions = [];\n }\n \n c.getData = function() {\n c.loading = true;\n c.server.get({\n action: 'get_table_data',\n table: c.options.table_name,\n max_records: c.options.max_records\n }).then(function(response) {\n c.columns = response.data.columns || [];\n c.data = response.data.data || [];\n c.filterData();\n c.loading = false;\n });\n };\n \n c.filterData = function() {\n var filtered = c.data;\n \n if (c.searchTerm) {\n filtered = c.data.filter(function(row) {\n return Object.values(row).some(function(value) {\n return String(value).toLowerCase().includes(c.searchTerm.toLowerCase());\n });\n });\n }\n \n // Apply sorting\n if (c.sortField) {\n filtered.sort(function(a, b) {\n var aVal = a[c.sortField] || '';\n var bVal = b[c.sortField] || '';\n var result = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;\n return c.sortDirection === 'desc' ? -result : result;\n });\n }\n \n // Apply pagination\n c.totalPages = Math.ceil(filtered.length / c.pageSize);\n var start = (c.currentPage - 1) * c.pageSize;\n c.displayData = filtered.slice(start, start + c.pageSize);\n };\n \n c.sort = function(field) {\n if (c.sortField === field) {\n c.sortDirection = c.sortDirection === 'asc' ? 'desc' : 'asc';\n } else {\n c.sortField = field;\n c.sortDirection = 'asc';\n }\n c.filterData();\n };\n \n c.previousPage = function() {\n if (c.currentPage > 1) {\n c.currentPage--;\n c.filterData();\n }\n };\n \n c.nextPage = function() {\n if (c.currentPage < c.totalPages) {\n c.currentPage++;\n c.filterData();\n }\n };\n \n c.getCellValue = function(row, column) {\n var value = row[column.field];\n // Format based on field type\n if (column.type === 'date') {\n return value ? new Date(value).toLocaleDateString() : '';\n }\n return value || '';\n };\n \n c.isEditing = function(row, column) {\n return c.options.enable_inline_edit && \n c.editingCell && \n c.editingCell.row === row && \n c.editingCell.column === column;\n };\n \n c.startEdit = function(row, column) {\n if (c.options.enable_inline_edit) {\n c.editingCell = {row: row, column: column};\n }\n };\n \n c.saveEdit = function(row, column) {\n c.editingCell = null;\n // Save to server\n c.server.get({\n action: 'update_record',\n table: c.options.table_name,\n sys_id: row.sys_id,\n field: column.field,\n value: row[column.field]\n });\n };\n \n c.handleEditKeypress = function(event, row, column) {\n if (event.keyCode === 13) { // Enter key\n c.saveEdit(row, column);\n }\n };\n \n c.performAction = function(action, row) {\n switch(action.action) {\n case 'view':\n window.open('/' + c.options.table_name + '.do?sys_id=' + row.sys_id, '_blank');\n break;\n case 'edit':\n window.open('/' + c.options.table_name + '.do?sys_id=' + row.sys_id + '&sysparm_view=edit', '_blank');\n break;\n default:\n // Custom action\n $scope.$emit('widget-action', {action: action, row: row});\n }\n };\n \n c.exportData = function() {\n var csv = c.columns.map(function(col) { return col.label; }).join(',') + '\\n';\n c.displayData.forEach(function(row) {\n csv += c.columns.map(function(col) {\n var val = row[col.field] || '';\n return '\"' + String(val).replace(/\"/g, '\"\"') + '\"';\n }).join(',') + '\\n';\n });\n \n var blob = new Blob([csv], {type: 'text/csv'});\n var url = window.URL.createObjectURL(blob);\n var a = document.createElement('a');\n a.href = url;\n a.download = c.data.title + '.csv';\n a.click();\n };\n \n // Initial load\n c.getData();\n}",
|
|
289
|
+
"SERVER_SCRIPT": "(function() {\n try {\n data.columns = [];\n data.data = [];\n gs.log('DataTable widget server script started', 'datatable_widget');\n \n if (input && input.action === 'get_table_data') {\n var tableName = input.table || 'incident';\n var maxRecords = parseInt(input.max_records) || 100;\n \n gs.log('Getting table data: table=' + tableName + ', maxRecords=' + maxRecords, 'datatable_widget');\n \n // Get table metadata\n var fields = [];\n var gd = new GlideRecord('sys_dictionary');\n gd.addQuery('name', tableName);\n gd.addQuery('internal_type', '!=', 'collection');\n gd.orderBy('column_label');\n gd.setLimit(10); // Limit columns for performance\n gd.query();\n \n while (gd.next()) {\n if (gd.element && !gd.element.startsWith('sys_')) {\n fields.push({\n field: gd.element.toString(),\n label: gd.column_label.toString() || gd.element.toString(),\n type: gd.internal_type.toString()\n });\n }\n }\n \n // Default fields if none found\n if (fields.length === 0) {\n fields = [\n {field: 'number', label: 'Number', type: 'string'},\n {field: 'short_description', label: 'Short Description', type: 'string'},\n {field: 'sys_created_on', label: 'Created', type: 'glide_date_time'}\n ];\n gs.log('Using default fields for table: ' + tableName, 'datatable_widget');\n }\n \n data.columns = fields;\n gs.log('Found ' + fields.length + ' columns for table', 'datatable_widget');\n \n // Get table data\n var gr = new GlideRecord(tableName);\n gr.orderByDesc('sys_created_on');\n gr.setLimit(maxRecords);\n gr.query();\n \n var recordCount = 0;\n while (gr.next()) {\n var row = {sys_id: gr.getUniqueValue()};\n fields.forEach(function(field) {\n row[field.field] = gr.getDisplayValue(field.field) || '';\n });\n data.data.push(row);\n recordCount++;\n }\n \n gs.log('Retrieved ' + recordCount + ' records from ' + tableName, 'datatable_widget');\n \n } else if (input && input.action === 'update_record') {\n // ✅ FIXED: Enhanced update with validation and error handling\n if (!input.table || !input.sys_id || !input.field) {\n throw new Error('Missing required parameters for update: table, sys_id, field');\n }\n \n gs.log('Updating record: table=' + input.table + ', sys_id=' + input.sys_id + ', field=' + input.field, 'datatable_widget');\n \n var gr = new GlideRecord(input.table);\n if (gr.get(input.sys_id)) {\n gr.setValue(input.field, input.value || '');\n gr.update();\n data.success = true;\n data.message = 'Record updated successfully';\n gs.log('Record updated successfully', 'datatable_widget');\n } else {\n data.success = false;\n data.error = 'Record not found: ' + input.sys_id;\n gs.warn('Record not found for update: ' + input.sys_id, 'datatable_widget');\n }\n }\n } catch (error) {\n gs.error('DataTable widget error: ' + error.message, 'datatable_widget');\n data.error = 'Error processing request: ' + error.message;\n data.success = false;\n }\n})()",
|
|
290
|
+
"LINK_FUNCTION": ""
|
|
291
|
+
}
|
|
292
|
+
}
|
package/.env.example
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
# ServiceNow OAuth Configuration (recommended)
|
|
2
|
-
SNOW_INSTANCE=dev12345.service-now.com
|
|
3
|
-
SNOW_CLIENT_ID=your_oauth_client_id
|
|
4
|
-
SNOW_CLIENT_SECRET=your_oauth_client_secret
|
|
5
|
-
|
|
6
|
-
# OAuth URLs (these are automatically generated)
|
|
7
|
-
OAUTH_TOKEN_URL=https://dev12345.service-now.com/oauth_token.do
|
|
8
|
-
OAUTH_REDIRECT_URI=http://localhost:3000/callback
|
|
9
|
-
|
|
10
|
-
# Basic Auth Configuration (deprecated - use OAuth instead)
|
|
11
|
-
# Uncomment these lines ONLY if you need to use basic auth:
|
|
12
|
-
# SNOW_USERNAME=your_username
|
|
13
|
-
# SNOW_PASSWORD=your_password
|
|
14
|
-
|
|
15
|
-
# ===========================================
|
|
16
|
-
# Snow-Flow Configuration
|
|
17
|
-
# ===========================================
|
|
18
|
-
|
|
19
|
-
# Enable debug logging (true/false)
|
|
20
|
-
SNOW_FLOW_DEBUG=false
|
|
21
|
-
|
|
22
|
-
# Default coordination strategy
|
|
23
|
-
SNOW_FLOW_STRATEGY=development
|
|
24
|
-
|
|
25
|
-
# Maximum number of agents
|
|
26
|
-
SNOW_FLOW_MAX_AGENTS=5
|
|
27
|
-
|
|
28
|
-
# Claude Code timeout configuration (in minutes)
|
|
29
|
-
# Set to 0 to disable timeout (infinite execution time)
|
|
30
|
-
# Default: 60 minutes
|
|
31
|
-
SNOW_FLOW_TIMEOUT_MINUTES=0
|
|
32
|
-
|
|
33
|
-
# ===========================================
|
|
34
|
-
# Deployment Timeout Configuration
|
|
35
|
-
# ===========================================
|
|
36
|
-
|
|
37
|
-
# ServiceNow API timeout for regular operations (milliseconds)
|
|
38
|
-
# Default: 60000 (60 seconds)
|
|
39
|
-
SNOW_REQUEST_TIMEOUT=60000
|
|
40
|
-
|
|
41
|
-
# ServiceNow API timeout for DEPLOYMENT operations (milliseconds)
|
|
42
|
-
# Deployments need more time for complex widgets
|
|
43
|
-
# Default: 300000 (5 minutes)
|
|
44
|
-
SNOW_DEPLOYMENT_TIMEOUT=300000
|
|
45
|
-
|
|
46
|
-
# MCP transport timeout for deployment operations (milliseconds)
|
|
47
|
-
# Should be higher than SNOW_DEPLOYMENT_TIMEOUT
|
|
48
|
-
# Default: 360000 (6 minutes)
|
|
49
|
-
MCP_DEPLOYMENT_TIMEOUT=360000
|
|
50
|
-
|
|
51
|
-
# ===========================================
|
|
52
|
-
# How to Set Up ServiceNow OAuth
|
|
53
|
-
# ===========================================
|
|
54
|
-
# 1. Log into your ServiceNow instance as admin
|
|
55
|
-
# 2. Navigate to: System OAuth > Application Registry
|
|
56
|
-
# 3. Click "New" > "Create an OAuth application"
|
|
57
|
-
# 4. Fill in:
|
|
58
|
-
# - Name: Snow-Flow Development
|
|
59
|
-
# - Client ID: (will be generated)
|
|
60
|
-
# - Client Secret: (will be generated)
|
|
61
|
-
# - Redirect URL: http://${SNOW_REDIRECT_HOST:-localhost}:${SNOW_REDIRECT_PORT:-3000}/callback
|
|
62
|
-
# - Grant Type: Authorization Code
|
|
63
|
-
# 5. Copy the Client ID and Client Secret to this file
|
|
64
|
-
# 6. Run: snow-flow auth login
|