snow-flow 2.0.4 → 2.0.5

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.
@@ -0,0 +1,560 @@
1
+ "use strict";
2
+ /**
3
+ * ServiceNow Artifact Indexer
4
+ * Intelligent indexing system for large ServiceNow artifacts
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ServiceNowArtifactIndexer = void 0;
8
+ const fs_1 = require("fs");
9
+ const path_1 = require("path");
10
+ const logger_js_1 = require("../utils/logger.js");
11
+ class ServiceNowArtifactIndexer {
12
+ constructor(memoryPath = (0, path_1.join)(process.cwd(), 'memory', 'servicenow_artifacts')) {
13
+ this.logger = new logger_js_1.Logger('ServiceNowArtifactIndexer');
14
+ this.memoryPath = memoryPath;
15
+ }
16
+ async intelligentlyIndex(artifact) {
17
+ this.logger.info('Indexing ServiceNow artifact', {
18
+ sys_id: artifact.sys_id,
19
+ type: artifact.sys_class_name
20
+ });
21
+ const structure = await this.decomposeArtifact(artifact);
22
+ const context = await this.extractContext(artifact);
23
+ const relationships = await this.mapRelationships(artifact);
24
+ const claudeSummary = await this.createClaudeSummary(artifact);
25
+ const modificationPoints = await this.identifyModificationPoints(artifact);
26
+ const searchTerms = await this.generateSearchTerms(artifact);
27
+ const indexed = {
28
+ meta: {
29
+ sys_id: artifact.sys_id,
30
+ name: artifact.name || artifact.title || 'Unknown',
31
+ type: artifact.sys_class_name || artifact.table,
32
+ last_updated: artifact.sys_updated_on,
33
+ size_estimate: this.estimateSize(artifact),
34
+ },
35
+ structure,
36
+ context,
37
+ relationships,
38
+ claudeSummary,
39
+ modificationPoints,
40
+ searchTerms,
41
+ editHistory: [],
42
+ };
43
+ await this.storeInMemory(indexed);
44
+ return indexed;
45
+ }
46
+ async decomposeArtifact(artifact) {
47
+ switch (artifact.sys_class_name) {
48
+ case 'sp_widget':
49
+ return this.decomposeWidget(artifact);
50
+ case 'sys_hub_flow':
51
+ return this.decomposeFlow(artifact);
52
+ case 'sys_script_include':
53
+ return this.decomposeScript(artifact);
54
+ case 'sys_app_application':
55
+ return this.decomposeApplication(artifact);
56
+ default:
57
+ return this.decomposeGeneric(artifact);
58
+ }
59
+ }
60
+ async decomposeWidget(widget) {
61
+ const components = {
62
+ template: {
63
+ present: !!widget.template,
64
+ complexity: widget.template ? this.assessHTMLComplexity(widget.template) : 'none',
65
+ size: widget.template?.length || 0,
66
+ features: widget.template ? this.extractHTMLFeatures(widget.template) : [],
67
+ },
68
+ css: {
69
+ present: !!widget.css,
70
+ complexity: widget.css ? this.assessCSSComplexity(widget.css) : 'none',
71
+ size: widget.css?.length || 0,
72
+ },
73
+ client_script: {
74
+ present: !!widget.client_script,
75
+ complexity: widget.client_script ? this.assessJSComplexity(widget.client_script) : 'none',
76
+ size: widget.client_script?.length || 0,
77
+ functions: widget.client_script ? this.extractJSFunctions(widget.client_script) : [],
78
+ },
79
+ server_script: {
80
+ present: !!widget.server_script,
81
+ complexity: widget.server_script ? this.assessJSComplexity(widget.server_script) : 'none',
82
+ size: widget.server_script?.length || 0,
83
+ functions: widget.server_script ? this.extractJSFunctions(widget.server_script) : [],
84
+ },
85
+ options: {
86
+ present: !!widget.option_schema,
87
+ schema: widget.option_schema ? this.parseOptionSchema(widget.option_schema) : null,
88
+ },
89
+ };
90
+ return {
91
+ type: 'widget',
92
+ components,
93
+ complexity: this.assessOverallComplexity(components),
94
+ editableFields: [
95
+ 'template',
96
+ 'css',
97
+ 'client_script',
98
+ 'server_script',
99
+ 'option_schema',
100
+ 'title',
101
+ 'description',
102
+ ],
103
+ };
104
+ }
105
+ async decomposeFlow(flow) {
106
+ const flowDefinition = this.parseFlowDefinition(flow.flow_definition);
107
+ const components = {
108
+ trigger: {
109
+ type: flowDefinition.trigger?.type || 'unknown',
110
+ table: flowDefinition.trigger?.table || 'unknown',
111
+ conditions: flowDefinition.trigger?.conditions || 'none',
112
+ description: this.describeTrigger(flowDefinition.trigger),
113
+ },
114
+ steps: flowDefinition.steps?.map((step) => ({
115
+ id: step.id,
116
+ type: step.type,
117
+ name: step.name,
118
+ configuration: step.config,
119
+ description: this.generateStepDescription(step),
120
+ editableFields: this.identifyEditableFields(step),
121
+ })) || [],
122
+ variables: flowDefinition.variables || [],
123
+ errorHandling: flowDefinition.errorHandling || 'none',
124
+ };
125
+ return {
126
+ type: 'flow',
127
+ components,
128
+ complexity: this.assessFlowComplexity(components),
129
+ editableFields: [
130
+ 'name',
131
+ 'description',
132
+ 'trigger_conditions',
133
+ 'active',
134
+ 'flow_definition',
135
+ ],
136
+ };
137
+ }
138
+ async decomposeScript(script) {
139
+ const scriptContent = script.script || '';
140
+ const components = {
141
+ functions: this.extractJSFunctions(scriptContent),
142
+ variables: this.extractJSVariables(scriptContent),
143
+ apis: this.extractServiceNowAPIs(scriptContent),
144
+ complexity: this.assessJSComplexity(scriptContent),
145
+ dependencies: this.extractDependencies(scriptContent),
146
+ };
147
+ return {
148
+ type: 'script',
149
+ components,
150
+ complexity: this.assessScriptComplexity(components),
151
+ editableFields: ['script', 'description', 'api_name'],
152
+ };
153
+ }
154
+ async decomposeApplication(app) {
155
+ const components = {
156
+ scope: app.scope,
157
+ version: app.version,
158
+ vendor: app.vendor,
159
+ tables: [], // Would be populated by querying related tables
160
+ modules: [], // Would be populated by querying sys_app_module
161
+ roles: [], // Would be populated by querying sys_user_role
162
+ };
163
+ return {
164
+ type: 'application',
165
+ components,
166
+ complexity: 'medium',
167
+ editableFields: ['name', 'description', 'version', 'vendor'],
168
+ };
169
+ }
170
+ async decomposeGeneric(artifact) {
171
+ return {
172
+ type: 'generic',
173
+ components: {
174
+ fields: Object.keys(artifact).filter(key => !key.startsWith('sys_')),
175
+ },
176
+ complexity: 'low',
177
+ editableFields: ['name', 'description'],
178
+ };
179
+ }
180
+ async extractContext(artifact) {
181
+ return {
182
+ usage: this.determineUsage(artifact),
183
+ dependencies: await this.findDependencies(artifact),
184
+ impact: this.assessImpact(artifact),
185
+ commonModifications: this.getCommonModifications(artifact.sys_class_name),
186
+ };
187
+ }
188
+ async mapRelationships(artifact) {
189
+ return {
190
+ relatedArtifacts: await this.findRelatedArtifacts(artifact),
191
+ dependencies: await this.findDependencies(artifact),
192
+ usage: await this.findUsage(artifact),
193
+ };
194
+ }
195
+ async createClaudeSummary(artifact) {
196
+ const name = artifact.name || artifact.title || 'Unknown';
197
+ const type = this.getReadableType(artifact.sys_class_name);
198
+ const purpose = this.inferPurpose(artifact);
199
+ const modificationSuggestions = this.getModificationSuggestions(artifact);
200
+ return `${name} is a ${type} in ServiceNow that ${purpose}.
201
+
202
+ 🎯 Key Functions:
203
+ ${this.extractKeyFunctions(artifact).join('\n')}
204
+
205
+ 🔧 Common Modifications:
206
+ ${modificationSuggestions.join('\n')}
207
+
208
+ 💡 Claude can help you modify this artifact using natural language instructions like:
209
+ - "Add email notification after approval"
210
+ - "Change the approval threshold to €1000"
211
+ - "Update the widget to show real-time data"`;
212
+ }
213
+ async identifyModificationPoints(artifact) {
214
+ const points = [];
215
+ switch (artifact.sys_class_name) {
216
+ case 'sys_hub_flow':
217
+ points.push({
218
+ location: 'after_approval_step',
219
+ type: 'insert_activity',
220
+ description: 'Perfect place to add email notifications, task creation, or additional approvals',
221
+ examples: [
222
+ 'Add email notification to manager',
223
+ 'Create task for procurement team',
224
+ 'Add second-level approval for high amounts',
225
+ ],
226
+ }, {
227
+ location: 'approval_step_config',
228
+ type: 'modify_settings',
229
+ description: 'Modify approval criteria, timeout, and assignees',
230
+ examples: [
231
+ 'Change approval threshold',
232
+ 'Modify timeout duration',
233
+ 'Update approval group',
234
+ ],
235
+ });
236
+ break;
237
+ case 'sp_widget':
238
+ points.push({
239
+ location: 'template_structure',
240
+ type: 'modify_html',
241
+ description: 'Modify the widget layout and structure',
242
+ examples: [
243
+ 'Add new data fields',
244
+ 'Change color scheme',
245
+ 'Add interactive elements',
246
+ ],
247
+ }, {
248
+ location: 'client_script_functions',
249
+ type: 'modify_javascript',
250
+ description: 'Modify client-side behavior and interactions',
251
+ examples: [
252
+ 'Add click handlers',
253
+ 'Implement real-time updates',
254
+ 'Add form validation',
255
+ ],
256
+ });
257
+ break;
258
+ }
259
+ return points;
260
+ }
261
+ async generateSearchTerms(artifact) {
262
+ const terms = [
263
+ artifact.name || '',
264
+ artifact.title || '',
265
+ artifact.sys_class_name,
266
+ this.getReadableType(artifact.sys_class_name),
267
+ ];
268
+ // Add purpose-based terms
269
+ const purpose = this.inferPurpose(artifact);
270
+ terms.push(...purpose.split(' '));
271
+ // Add content-based terms
272
+ if (artifact.description) {
273
+ terms.push(...artifact.description.split(' '));
274
+ }
275
+ return terms.filter(term => term.length > 2).map(term => term.toLowerCase());
276
+ }
277
+ parseFlowDefinition(flowDefinition) {
278
+ try {
279
+ return JSON.parse(flowDefinition);
280
+ }
281
+ catch {
282
+ // If not JSON, return a basic structure
283
+ return {
284
+ trigger: { type: 'unknown', table: 'unknown', conditions: 'unknown' },
285
+ steps: [],
286
+ variables: [],
287
+ };
288
+ }
289
+ }
290
+ assessHTMLComplexity(html) {
291
+ const elementCount = (html.match(/<[^>]+>/g) || []).length;
292
+ if (elementCount < 10)
293
+ return 'low';
294
+ if (elementCount < 50)
295
+ return 'medium';
296
+ return 'high';
297
+ }
298
+ assessCSSComplexity(css) {
299
+ const ruleCount = (css.match(/\{[^}]+\}/g) || []).length;
300
+ if (ruleCount < 5)
301
+ return 'low';
302
+ if (ruleCount < 20)
303
+ return 'medium';
304
+ return 'high';
305
+ }
306
+ assessJSComplexity(js) {
307
+ const functionCount = (js.match(/function\s+\w+/g) || []).length;
308
+ const lines = js.split('\n').length;
309
+ if (functionCount < 3 && lines < 50)
310
+ return 'low';
311
+ if (functionCount < 10 && lines < 200)
312
+ return 'medium';
313
+ return 'high';
314
+ }
315
+ assessOverallComplexity(components) {
316
+ const complexities = Object.values(components)
317
+ .map((comp) => comp.complexity)
318
+ .filter(c => c !== 'none');
319
+ if (complexities.includes('high'))
320
+ return 'high';
321
+ if (complexities.includes('medium'))
322
+ return 'medium';
323
+ return 'low';
324
+ }
325
+ assessFlowComplexity(components) {
326
+ const stepCount = components.steps?.length || 0;
327
+ if (stepCount < 3)
328
+ return 'low';
329
+ if (stepCount < 10)
330
+ return 'medium';
331
+ return 'high';
332
+ }
333
+ assessScriptComplexity(components) {
334
+ const functionCount = components.functions?.length || 0;
335
+ if (functionCount < 3)
336
+ return 'low';
337
+ if (functionCount < 10)
338
+ return 'medium';
339
+ return 'high';
340
+ }
341
+ extractHTMLFeatures(html) {
342
+ const features = [];
343
+ if (html.includes('ng-'))
344
+ features.push('AngularJS');
345
+ if (html.includes('bootstrap'))
346
+ features.push('Bootstrap');
347
+ if (html.includes('table'))
348
+ features.push('Table');
349
+ if (html.includes('form'))
350
+ features.push('Form');
351
+ if (html.includes('chart'))
352
+ features.push('Chart');
353
+ return features;
354
+ }
355
+ extractJSFunctions(js) {
356
+ const matches = js.match(/function\s+(\w+)/g) || [];
357
+ return matches.map(match => match.replace('function ', ''));
358
+ }
359
+ extractJSVariables(js) {
360
+ const matches = js.match(/var\s+(\w+)/g) || [];
361
+ return matches.map(match => match.replace('var ', ''));
362
+ }
363
+ extractServiceNowAPIs(js) {
364
+ const apis = [];
365
+ if (js.includes('GlideRecord'))
366
+ apis.push('GlideRecord');
367
+ if (js.includes('GlideSystem'))
368
+ apis.push('GlideSystem');
369
+ if (js.includes('GlideUser'))
370
+ apis.push('GlideUser');
371
+ if (js.includes('GlideDateTime'))
372
+ apis.push('GlideDateTime');
373
+ return apis;
374
+ }
375
+ extractDependencies(js) {
376
+ const deps = [];
377
+ const includeMatches = js.match(/gs\.include\(['"]([^'"]+)['"]\)/g) || [];
378
+ deps.push(...includeMatches.map(match => match.match(/['"]([^'"]+)['"]/)?.[1] || ''));
379
+ return deps.filter(dep => dep.length > 0);
380
+ }
381
+ parseOptionSchema(schema) {
382
+ try {
383
+ return JSON.parse(schema);
384
+ }
385
+ catch {
386
+ return null;
387
+ }
388
+ }
389
+ describeTrigger(trigger) {
390
+ if (!trigger)
391
+ return 'Unknown trigger';
392
+ return `Triggers on ${trigger.table} when ${trigger.type}`;
393
+ }
394
+ generateStepDescription(step) {
395
+ return `${step.type} step: ${step.name}`;
396
+ }
397
+ identifyEditableFields(step) {
398
+ const baseFields = ['name', 'description'];
399
+ switch (step.type) {
400
+ case 'approval':
401
+ return [...baseFields, 'approver', 'timeout', 'condition'];
402
+ case 'script':
403
+ return [...baseFields, 'script', 'inputs', 'outputs'];
404
+ case 'notification':
405
+ return [...baseFields, 'recipients', 'template', 'condition'];
406
+ default:
407
+ return baseFields;
408
+ }
409
+ }
410
+ determineUsage(artifact) {
411
+ const type = this.getReadableType(artifact.sys_class_name);
412
+ return `This ${type} is used in ServiceNow for ${this.inferPurpose(artifact)}`;
413
+ }
414
+ async findDependencies(artifact) {
415
+ // This would query ServiceNow for actual dependencies
416
+ return [];
417
+ }
418
+ assessImpact(artifact) {
419
+ switch (artifact.sys_class_name) {
420
+ case 'sys_hub_flow':
421
+ return 'High - Flow modifications can affect business processes';
422
+ case 'sp_widget':
423
+ return 'Medium - Widget changes affect user interface';
424
+ case 'sys_script_include':
425
+ return 'High - Script changes can affect multiple applications';
426
+ default:
427
+ return 'Medium - Standard ServiceNow artifact';
428
+ }
429
+ }
430
+ getCommonModifications(type) {
431
+ const modifications = {
432
+ 'sys_hub_flow': [
433
+ 'Add email notification steps',
434
+ 'Modify approval criteria',
435
+ 'Add task creation',
436
+ 'Update timeout settings',
437
+ ],
438
+ 'sp_widget': [
439
+ 'Update styling and colors',
440
+ 'Add new data fields',
441
+ 'Improve mobile responsiveness',
442
+ 'Add interactive features',
443
+ ],
444
+ 'sys_script_include': [
445
+ 'Add error handling',
446
+ 'Optimize performance',
447
+ 'Add logging',
448
+ 'Update API calls',
449
+ ],
450
+ };
451
+ return modifications[type] || ['General configuration updates'];
452
+ }
453
+ async findRelatedArtifacts(artifact) {
454
+ // This would query ServiceNow for related artifacts
455
+ return [];
456
+ }
457
+ async findUsage(artifact) {
458
+ // This would query ServiceNow for usage patterns
459
+ return [];
460
+ }
461
+ getReadableType(sysClassName) {
462
+ const typeMap = {
463
+ 'sp_widget': 'Service Portal Widget',
464
+ 'sys_hub_flow': 'Flow Designer Flow',
465
+ 'sys_script_include': 'Script Include',
466
+ 'sys_app_application': 'Scoped Application',
467
+ 'sys_script': 'Business Rule',
468
+ 'sys_ui_script': 'UI Script',
469
+ };
470
+ return typeMap[sysClassName] || sysClassName;
471
+ }
472
+ inferPurpose(artifact) {
473
+ const name = (artifact.name || artifact.title || '').toLowerCase();
474
+ if (name.includes('incident'))
475
+ return 'incident management';
476
+ if (name.includes('approval'))
477
+ return 'approval processes';
478
+ if (name.includes('request'))
479
+ return 'request management';
480
+ if (name.includes('user'))
481
+ return 'user management';
482
+ if (name.includes('dashboard'))
483
+ return 'data visualization';
484
+ return 'business process automation';
485
+ }
486
+ getModificationSuggestions(artifact) {
487
+ return this.getCommonModifications(artifact.sys_class_name);
488
+ }
489
+ extractKeyFunctions(artifact) {
490
+ switch (artifact.sys_class_name) {
491
+ case 'sys_hub_flow':
492
+ return ['- Automates business processes', '- Handles approvals and notifications', '- Integrates with ServiceNow tables'];
493
+ case 'sp_widget':
494
+ return ['- Displays data in Service Portal', '- Provides user interaction', '- Integrates with ServiceNow data'];
495
+ default:
496
+ return ['- Provides ServiceNow functionality'];
497
+ }
498
+ }
499
+ estimateSize(artifact) {
500
+ let totalSize = 0;
501
+ Object.values(artifact).forEach(value => {
502
+ if (typeof value === 'string') {
503
+ totalSize += value.length;
504
+ }
505
+ });
506
+ if (totalSize < 1000)
507
+ return 'Small';
508
+ if (totalSize < 10000)
509
+ return 'Medium';
510
+ return 'Large';
511
+ }
512
+ async storeInMemory(artifact) {
513
+ await fs_1.promises.mkdir(this.memoryPath, { recursive: true });
514
+ const filePath = (0, path_1.join)(this.memoryPath, `${artifact.meta.sys_id}.json`);
515
+ await fs_1.promises.writeFile(filePath, JSON.stringify(artifact, null, 2));
516
+ this.logger.info('Artifact indexed and stored in memory', {
517
+ sys_id: artifact.meta.sys_id,
518
+ name: artifact.meta.name,
519
+ type: artifact.meta.type,
520
+ });
521
+ }
522
+ async loadFromMemory(sys_id) {
523
+ try {
524
+ const filePath = (0, path_1.join)(this.memoryPath, `${sys_id}.json`);
525
+ const content = await fs_1.promises.readFile(filePath, 'utf8');
526
+ return JSON.parse(content);
527
+ }
528
+ catch (error) {
529
+ return null;
530
+ }
531
+ }
532
+ async searchMemory(query) {
533
+ try {
534
+ const files = await fs_1.promises.readdir(this.memoryPath);
535
+ const results = [];
536
+ for (const file of files) {
537
+ if (file.endsWith('.json')) {
538
+ const content = await fs_1.promises.readFile((0, path_1.join)(this.memoryPath, file), 'utf8');
539
+ const artifact = JSON.parse(content);
540
+ if (this.matchesQuery(artifact, query)) {
541
+ results.push(artifact);
542
+ }
543
+ }
544
+ }
545
+ return results;
546
+ }
547
+ catch (error) {
548
+ this.logger.error('Memory search failed', error);
549
+ return [];
550
+ }
551
+ }
552
+ matchesQuery(artifact, query) {
553
+ const searchTerms = query.toLowerCase().split(' ');
554
+ const artifactTerms = artifact.searchTerms.join(' ').toLowerCase();
555
+ const summaryTerms = artifact.claudeSummary.toLowerCase();
556
+ return searchTerms.some(term => artifactTerms.includes(term) || summaryTerms.includes(term));
557
+ }
558
+ }
559
+ exports.ServiceNowArtifactIndexer = ServiceNowArtifactIndexer;
560
+ //# sourceMappingURL=servicenow-artifact-indexer.js.map