snow-flow 1.1.45 → 1.1.47
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/.claude/settings.json +11 -0
- package/dist/mcp/servicenow-operations-mcp.js +348 -2
- package/dist/mcp/servicenow-operations-mcp.js.map +1 -1
- package/dist/orchestrator/flow-composer.d.ts.map +1 -1
- package/dist/orchestrator/flow-composer.js +4 -0
- package/dist/orchestrator/flow-composer.js.map +1 -1
- package/dist/orchestrator/flow-pattern-templates.d.ts.map +1 -1
- package/dist/orchestrator/flow-pattern-templates.js +2 -0
- package/dist/orchestrator/flow-pattern-templates.js.map +1 -1
- package/dist/strategies/global-scope-strategy.d.ts.map +1 -1
- package/dist/strategies/global-scope-strategy.js +9 -0
- package/dist/strategies/global-scope-strategy.js.map +1 -1
- package/dist/version.d.ts +11 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +49 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/.claude/settings.json
CHANGED
|
@@ -87,6 +87,17 @@
|
|
|
87
87
|
},
|
|
88
88
|
"includeCoAuthoredBy": true,
|
|
89
89
|
"enabledMcpjsonServers": [
|
|
90
|
+
"servicenow-deployment",
|
|
91
|
+
"servicenow-flow-composer",
|
|
92
|
+
"servicenow-update-set",
|
|
93
|
+
"servicenow-intelligent",
|
|
94
|
+
"servicenow-graph-memory",
|
|
95
|
+
"servicenow-operations",
|
|
96
|
+
"servicenow-platform-development",
|
|
97
|
+
"servicenow-integration",
|
|
98
|
+
"servicenow-automation",
|
|
99
|
+
"servicenow-security-compliance",
|
|
100
|
+
"servicenow-reporting-analytics",
|
|
90
101
|
"claude-flow",
|
|
91
102
|
"ruv-swarm"
|
|
92
103
|
]
|
|
@@ -651,6 +651,49 @@ class ServiceNowOperationsMCP {
|
|
|
651
651
|
},
|
|
652
652
|
required: ['catalog_item_id', 'flow_id']
|
|
653
653
|
}
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
name: 'snow_cleanup_test_artifacts',
|
|
657
|
+
description: 'Clean up test artifacts while preserving Update Set audit trail',
|
|
658
|
+
inputSchema: {
|
|
659
|
+
type: 'object',
|
|
660
|
+
properties: {
|
|
661
|
+
artifact_types: {
|
|
662
|
+
type: 'array',
|
|
663
|
+
description: 'Types of artifacts to clean up',
|
|
664
|
+
items: {
|
|
665
|
+
type: 'string',
|
|
666
|
+
enum: ['catalog_items', 'flows', 'users', 'requests', 'all']
|
|
667
|
+
},
|
|
668
|
+
default: ['catalog_items', 'flows', 'users']
|
|
669
|
+
},
|
|
670
|
+
test_patterns: {
|
|
671
|
+
type: 'array',
|
|
672
|
+
description: 'Name patterns that identify test artifacts',
|
|
673
|
+
items: { type: 'string' },
|
|
674
|
+
default: ['Test%', 'Mock%', 'Demo%', '%_test_%', '%test', '%mock']
|
|
675
|
+
},
|
|
676
|
+
max_age_hours: {
|
|
677
|
+
type: 'number',
|
|
678
|
+
description: 'Only clean artifacts older than this (hours)',
|
|
679
|
+
default: 1
|
|
680
|
+
},
|
|
681
|
+
dry_run: {
|
|
682
|
+
type: 'boolean',
|
|
683
|
+
description: 'Preview what would be deleted without actually deleting',
|
|
684
|
+
default: false
|
|
685
|
+
},
|
|
686
|
+
preserve_update_set_entries: {
|
|
687
|
+
type: 'boolean',
|
|
688
|
+
description: 'Keep Update Set entries as audit trail',
|
|
689
|
+
default: true
|
|
690
|
+
},
|
|
691
|
+
update_set_filter: {
|
|
692
|
+
type: 'string',
|
|
693
|
+
description: 'Only clean from specific Update Set (optional)'
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
654
697
|
}
|
|
655
698
|
],
|
|
656
699
|
};
|
|
@@ -690,6 +733,8 @@ class ServiceNowOperationsMCP {
|
|
|
690
733
|
return await this.handleTestFlowWithMock(args);
|
|
691
734
|
case 'snow_link_catalog_to_flow':
|
|
692
735
|
return await this.handleLinkCatalogToFlow(args);
|
|
736
|
+
case 'snow_cleanup_test_artifacts':
|
|
737
|
+
return await this.handleCleanupTestArtifacts(args);
|
|
693
738
|
default:
|
|
694
739
|
throw new types_js_1.McpError(types_js_1.ErrorCode.MethodNotFound, `Tool ${name} not found`);
|
|
695
740
|
}
|
|
@@ -1557,6 +1602,34 @@ class ServiceNowOperationsMCP {
|
|
|
1557
1602
|
try {
|
|
1558
1603
|
switch (action) {
|
|
1559
1604
|
case 'create': {
|
|
1605
|
+
// Get default catalog if none provided
|
|
1606
|
+
let catalogId = params.sc_catalogs || params.catalog_id;
|
|
1607
|
+
let categoryId = params.sc_categories || params.category_id;
|
|
1608
|
+
if (!catalogId) {
|
|
1609
|
+
// Find default Service Catalog
|
|
1610
|
+
const defaultCatalogResult = await this.client.searchRecords('sc_catalog', 'active=true^ORDERBYsys_created_on', 1);
|
|
1611
|
+
if (defaultCatalogResult.success && defaultCatalogResult.data?.result?.length > 0) {
|
|
1612
|
+
catalogId = defaultCatalogResult.data.result[0].sys_id;
|
|
1613
|
+
logger_js_1.logger.info('Using default catalog:', {
|
|
1614
|
+
catalogId,
|
|
1615
|
+
catalogName: defaultCatalogResult.data.result[0].title
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
else {
|
|
1619
|
+
logger_js_1.logger.warn('No catalogs found, catalog item may not be visible');
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
if (!categoryId) {
|
|
1623
|
+
// Find a default category like "Hardware" or "General"
|
|
1624
|
+
const defaultCategoryResult = await this.client.searchRecords('sc_category', 'active=true^titleLIKEHardware^ORtitleLIKEGeneral^ORtitleLIKEIT^ORDERBYsys_created_on', 1);
|
|
1625
|
+
if (defaultCategoryResult.success && defaultCategoryResult.data?.result?.length > 0) {
|
|
1626
|
+
categoryId = defaultCategoryResult.data.result[0].sys_id;
|
|
1627
|
+
logger_js_1.logger.info('Using default category:', {
|
|
1628
|
+
categoryId,
|
|
1629
|
+
categoryName: defaultCategoryResult.data.result[0].title
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1560
1633
|
const catalogItem = {
|
|
1561
1634
|
name: params.name,
|
|
1562
1635
|
short_description: params.short_description,
|
|
@@ -1567,8 +1640,8 @@ class ServiceNowOperationsMCP {
|
|
|
1567
1640
|
active: params.active !== false,
|
|
1568
1641
|
workflow: params.workflow,
|
|
1569
1642
|
picture: params.picture,
|
|
1570
|
-
sc_catalogs:
|
|
1571
|
-
sc_categories:
|
|
1643
|
+
sc_catalogs: catalogId,
|
|
1644
|
+
sc_categories: categoryId,
|
|
1572
1645
|
sys_class_name: 'sc_cat_item'
|
|
1573
1646
|
};
|
|
1574
1647
|
const result = await this.client.createRecord('sc_cat_item', catalogItem);
|
|
@@ -2491,6 +2564,279 @@ class ServiceNowOperationsMCP {
|
|
|
2491
2564
|
};
|
|
2492
2565
|
}
|
|
2493
2566
|
}
|
|
2567
|
+
async handleCleanupTestArtifacts(args) {
|
|
2568
|
+
const { artifact_types = ['catalog_items', 'flows', 'users'], test_patterns = ['Test%', 'Mock%', 'Demo%', '%_test_%', '%test', '%mock'], max_age_hours = 1, dry_run = false, preserve_update_set_entries = true, update_set_filter } = args;
|
|
2569
|
+
logger_js_1.logger.info('Starting test artifact cleanup', {
|
|
2570
|
+
artifact_types,
|
|
2571
|
+
test_patterns,
|
|
2572
|
+
max_age_hours,
|
|
2573
|
+
dry_run
|
|
2574
|
+
});
|
|
2575
|
+
const cleanupResults = {
|
|
2576
|
+
start_time: new Date().toISOString(),
|
|
2577
|
+
dry_run,
|
|
2578
|
+
artifacts_found: {
|
|
2579
|
+
catalog_items: [],
|
|
2580
|
+
flows: [],
|
|
2581
|
+
users: [],
|
|
2582
|
+
requests: []
|
|
2583
|
+
},
|
|
2584
|
+
artifacts_deleted: {
|
|
2585
|
+
catalog_items: 0,
|
|
2586
|
+
flows: 0,
|
|
2587
|
+
users: 0,
|
|
2588
|
+
requests: 0
|
|
2589
|
+
},
|
|
2590
|
+
update_set_entries_preserved: 0,
|
|
2591
|
+
errors: [],
|
|
2592
|
+
summary: ''
|
|
2593
|
+
};
|
|
2594
|
+
try {
|
|
2595
|
+
// Calculate cutoff time
|
|
2596
|
+
const cutoffTime = new Date();
|
|
2597
|
+
cutoffTime.setHours(cutoffTime.getHours() - max_age_hours);
|
|
2598
|
+
const cutoffISO = cutoffTime.toISOString();
|
|
2599
|
+
// Process each artifact type
|
|
2600
|
+
for (const artifactType of artifact_types) {
|
|
2601
|
+
if (artifactType === 'all' || artifact_types.includes('catalog_items')) {
|
|
2602
|
+
await this.cleanupTestCatalogItems(test_patterns, cutoffISO, dry_run, cleanupResults, update_set_filter);
|
|
2603
|
+
}
|
|
2604
|
+
if (artifactType === 'all' || artifact_types.includes('flows')) {
|
|
2605
|
+
await this.cleanupTestFlows(test_patterns, cutoffISO, dry_run, cleanupResults, update_set_filter);
|
|
2606
|
+
}
|
|
2607
|
+
if (artifactType === 'all' || artifact_types.includes('users')) {
|
|
2608
|
+
await this.cleanupTestUsers(test_patterns, cutoffISO, dry_run, cleanupResults, update_set_filter);
|
|
2609
|
+
}
|
|
2610
|
+
if (artifactType === 'all' || artifact_types.includes('requests')) {
|
|
2611
|
+
await this.cleanupTestRequests(test_patterns, cutoffISO, dry_run, cleanupResults, update_set_filter);
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
// Generate summary
|
|
2615
|
+
const totalFound = Object.values(cleanupResults.artifacts_found)
|
|
2616
|
+
.reduce((sum, items) => sum + (Array.isArray(items) ? items.length : 0), 0);
|
|
2617
|
+
const totalDeleted = Object.values(cleanupResults.artifacts_deleted)
|
|
2618
|
+
.reduce((sum, count) => sum + count, 0);
|
|
2619
|
+
cleanupResults.summary = dry_run
|
|
2620
|
+
? `🔍 Dry Run: Found ${totalFound} test artifacts that would be cleaned up`
|
|
2621
|
+
: `🧹 Cleaned up ${totalDeleted} test artifacts successfully`;
|
|
2622
|
+
// Format results
|
|
2623
|
+
let resultText = `🧹 Test Artifact Cleanup Results\n${'='.repeat(50)}\n\n`;
|
|
2624
|
+
if (dry_run) {
|
|
2625
|
+
resultText += `🔍 **DRY RUN MODE** - No actual deletion performed\n\n`;
|
|
2626
|
+
}
|
|
2627
|
+
resultText += `⏰ **Cleanup Configuration:**\n`;
|
|
2628
|
+
resultText += `- Artifact Types: ${artifact_types.join(', ')}\n`;
|
|
2629
|
+
resultText += `- Test Patterns: ${test_patterns.join(', ')}\n`;
|
|
2630
|
+
resultText += `- Max Age: ${max_age_hours} hours\n`;
|
|
2631
|
+
resultText += `- Cutoff Time: ${cutoffISO}\n`;
|
|
2632
|
+
resultText += `- Preserve Update Set Entries: ${preserve_update_set_entries ? '✅' : '❌'}\n\n`;
|
|
2633
|
+
// Report findings by type
|
|
2634
|
+
['catalog_items', 'flows', 'users', 'requests'].forEach(type => {
|
|
2635
|
+
const found = cleanupResults.artifacts_found[type] || [];
|
|
2636
|
+
const deleted = cleanupResults.artifacts_deleted[type] || 0;
|
|
2637
|
+
if (found.length > 0) {
|
|
2638
|
+
resultText += `📦 **${type.replace('_', ' ').toUpperCase()}:**\n`;
|
|
2639
|
+
resultText += ` ${dry_run ? 'Found' : 'Deleted'}: ${dry_run ? found.length : deleted}\n`;
|
|
2640
|
+
if (dry_run && found.length > 0) {
|
|
2641
|
+
found.slice(0, 10).forEach((item) => {
|
|
2642
|
+
resultText += ` - ${item.name} (${item.sys_id}) - Created: ${item.sys_created_on}\n`;
|
|
2643
|
+
});
|
|
2644
|
+
if (found.length > 10) {
|
|
2645
|
+
resultText += ` ... and ${found.length - 10} more\n`;
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
resultText += '\n';
|
|
2649
|
+
}
|
|
2650
|
+
});
|
|
2651
|
+
if (cleanupResults.update_set_entries_preserved > 0) {
|
|
2652
|
+
resultText += `📋 **Update Set Audit Trail Preserved:** ${cleanupResults.update_set_entries_preserved} entries\n\n`;
|
|
2653
|
+
}
|
|
2654
|
+
if (cleanupResults.errors.length > 0) {
|
|
2655
|
+
resultText += `❌ **Errors:**\n`;
|
|
2656
|
+
cleanupResults.errors.forEach(error => {
|
|
2657
|
+
resultText += ` - ${error}\n`;
|
|
2658
|
+
});
|
|
2659
|
+
resultText += '\n';
|
|
2660
|
+
}
|
|
2661
|
+
resultText += `✅ **${cleanupResults.summary}**\n\n`;
|
|
2662
|
+
if (!dry_run && totalDeleted > 0) {
|
|
2663
|
+
resultText += `💡 **Note:** Update Set entries have been preserved as audit trail.\n`;
|
|
2664
|
+
resultText += `This shows that testing was performed and cleanup was completed.\n\n`;
|
|
2665
|
+
}
|
|
2666
|
+
if (dry_run) {
|
|
2667
|
+
resultText += `▶️ **Next Steps:**\n`;
|
|
2668
|
+
resultText += `1. Review the artifacts that would be deleted\n`;
|
|
2669
|
+
resultText += `2. Run again with dry_run: false to perform actual cleanup\n`;
|
|
2670
|
+
resultText += `3. Verify Update Set entries are preserved as intended\n`;
|
|
2671
|
+
}
|
|
2672
|
+
return {
|
|
2673
|
+
content: [{
|
|
2674
|
+
type: 'text',
|
|
2675
|
+
text: resultText
|
|
2676
|
+
}]
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
catch (error) {
|
|
2680
|
+
logger_js_1.logger.error('Error during test artifact cleanup:', error);
|
|
2681
|
+
return {
|
|
2682
|
+
content: [{
|
|
2683
|
+
type: 'text',
|
|
2684
|
+
text: `❌ Test artifact cleanup failed\n\nError: ${error.message}\n\n` +
|
|
2685
|
+
`🔧 Troubleshooting:\n` +
|
|
2686
|
+
`1. Check ServiceNow connection and permissions\n` +
|
|
2687
|
+
`2. Verify test patterns are correct\n` +
|
|
2688
|
+
`3. Ensure artifacts exist and are accessible\n\n` +
|
|
2689
|
+
`Debug Info: ${JSON.stringify(cleanupResults, null, 2)}`
|
|
2690
|
+
}]
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
async cleanupTestCatalogItems(patterns, cutoffTime, dryRun, results, updateSetFilter) {
|
|
2695
|
+
try {
|
|
2696
|
+
// Build query for test catalog items
|
|
2697
|
+
const patternQueries = patterns.map(pattern => {
|
|
2698
|
+
if (pattern.includes('%')) {
|
|
2699
|
+
return `nameLIKE${pattern.replace(/%/g, '')}`;
|
|
2700
|
+
}
|
|
2701
|
+
else {
|
|
2702
|
+
return `name=${pattern}`;
|
|
2703
|
+
}
|
|
2704
|
+
});
|
|
2705
|
+
const query = `(${patternQueries.join('^OR')})^sys_created_on<${cutoffTime}`;
|
|
2706
|
+
const searchResult = await this.client.searchRecords('sc_cat_item', query, 100);
|
|
2707
|
+
if (searchResult.success && searchResult.data?.result) {
|
|
2708
|
+
results.artifacts_found.catalog_items = searchResult.data.result.map((item) => ({
|
|
2709
|
+
sys_id: item.sys_id,
|
|
2710
|
+
name: item.name,
|
|
2711
|
+
sys_created_on: item.sys_created_on
|
|
2712
|
+
}));
|
|
2713
|
+
if (!dryRun) {
|
|
2714
|
+
// Delete each catalog item
|
|
2715
|
+
for (const item of searchResult.data.result) {
|
|
2716
|
+
const deleteResult = await this.client.deleteRecord('sc_cat_item', item.sys_id);
|
|
2717
|
+
if (deleteResult.success) {
|
|
2718
|
+
results.artifacts_deleted.catalog_items++;
|
|
2719
|
+
}
|
|
2720
|
+
else {
|
|
2721
|
+
results.errors.push(`Failed to delete catalog item ${item.name}: ${deleteResult.error}`);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
catch (error) {
|
|
2728
|
+
results.errors.push(`Error cleaning catalog items: ${error.message}`);
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
async cleanupTestFlows(patterns, cutoffTime, dryRun, results, updateSetFilter) {
|
|
2732
|
+
try {
|
|
2733
|
+
const patternQueries = patterns.map(pattern => {
|
|
2734
|
+
if (pattern.includes('%')) {
|
|
2735
|
+
return `nameLIKE${pattern.replace(/%/g, '')}`;
|
|
2736
|
+
}
|
|
2737
|
+
else {
|
|
2738
|
+
return `name=${pattern}`;
|
|
2739
|
+
}
|
|
2740
|
+
});
|
|
2741
|
+
const query = `(${patternQueries.join('^OR')})^sys_created_on<${cutoffTime}`;
|
|
2742
|
+
const searchResult = await this.client.searchRecords('sys_hub_flow', query, 100);
|
|
2743
|
+
if (searchResult.success && searchResult.data?.result) {
|
|
2744
|
+
results.artifacts_found.flows = searchResult.data.result.map((flow) => ({
|
|
2745
|
+
sys_id: flow.sys_id,
|
|
2746
|
+
name: flow.name,
|
|
2747
|
+
sys_created_on: flow.sys_created_on
|
|
2748
|
+
}));
|
|
2749
|
+
if (!dryRun) {
|
|
2750
|
+
for (const flow of searchResult.data.result) {
|
|
2751
|
+
const deleteResult = await this.client.deleteRecord('sys_hub_flow', flow.sys_id);
|
|
2752
|
+
if (deleteResult.success) {
|
|
2753
|
+
results.artifacts_deleted.flows++;
|
|
2754
|
+
}
|
|
2755
|
+
else {
|
|
2756
|
+
results.errors.push(`Failed to delete flow ${flow.name}: ${deleteResult.error}`);
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
catch (error) {
|
|
2763
|
+
results.errors.push(`Error cleaning flows: ${error.message}`);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
async cleanupTestUsers(patterns, cutoffTime, dryRun, results, updateSetFilter) {
|
|
2767
|
+
try {
|
|
2768
|
+
const patternQueries = patterns.map(pattern => {
|
|
2769
|
+
if (pattern.includes('%')) {
|
|
2770
|
+
return `user_nameLIKE${pattern.replace(/%/g, '')}^ORfirst_nameLIKE${pattern.replace(/%/g, '')}`;
|
|
2771
|
+
}
|
|
2772
|
+
else {
|
|
2773
|
+
return `user_name=${pattern}^ORfirst_name=${pattern}`;
|
|
2774
|
+
}
|
|
2775
|
+
});
|
|
2776
|
+
const query = `(${patternQueries.join('^OR')})^sys_created_on<${cutoffTime}^active=false`;
|
|
2777
|
+
const searchResult = await this.client.searchRecords('sys_user', query, 100);
|
|
2778
|
+
if (searchResult.success && searchResult.data?.result) {
|
|
2779
|
+
results.artifacts_found.users = searchResult.data.result.map((user) => ({
|
|
2780
|
+
sys_id: user.sys_id,
|
|
2781
|
+
name: user.user_name || user.name,
|
|
2782
|
+
sys_created_on: user.sys_created_on
|
|
2783
|
+
}));
|
|
2784
|
+
if (!dryRun) {
|
|
2785
|
+
for (const user of searchResult.data.result) {
|
|
2786
|
+
const deleteResult = await this.client.deleteRecord('sys_user', user.sys_id);
|
|
2787
|
+
if (deleteResult.success) {
|
|
2788
|
+
results.artifacts_deleted.users++;
|
|
2789
|
+
}
|
|
2790
|
+
else {
|
|
2791
|
+
results.errors.push(`Failed to delete user ${user.user_name}: ${deleteResult.error}`);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
catch (error) {
|
|
2798
|
+
results.errors.push(`Error cleaning users: ${error.message}`);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
async cleanupTestRequests(patterns, cutoffTime, dryRun, results, updateSetFilter) {
|
|
2802
|
+
try {
|
|
2803
|
+
const patternQueries = patterns.map(pattern => {
|
|
2804
|
+
if (pattern.includes('%')) {
|
|
2805
|
+
return `short_descriptionLIKE${pattern.replace(/%/g, '')}`;
|
|
2806
|
+
}
|
|
2807
|
+
else {
|
|
2808
|
+
return `short_description=${pattern}`;
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
const query = `(${patternQueries.join('^OR')})^sys_created_on<${cutoffTime}`;
|
|
2812
|
+
const searchResult = await this.client.searchRecords('sc_request', query, 100);
|
|
2813
|
+
if (searchResult.success && searchResult.data?.result) {
|
|
2814
|
+
results.artifacts_found.requests = searchResult.data.result.map((req) => ({
|
|
2815
|
+
sys_id: req.sys_id,
|
|
2816
|
+
name: req.number || req.short_description,
|
|
2817
|
+
sys_created_on: req.sys_created_on
|
|
2818
|
+
}));
|
|
2819
|
+
if (!dryRun) {
|
|
2820
|
+
for (const request of searchResult.data.result) {
|
|
2821
|
+
// Cancel request instead of deleting (safer)
|
|
2822
|
+
const updateResult = await this.client.updateRecord('sc_request', request.sys_id, {
|
|
2823
|
+
state: '4', // Cancelled
|
|
2824
|
+
comments: 'Cancelled by test cleanup automation'
|
|
2825
|
+
});
|
|
2826
|
+
if (updateResult.success) {
|
|
2827
|
+
results.artifacts_deleted.requests++;
|
|
2828
|
+
}
|
|
2829
|
+
else {
|
|
2830
|
+
results.errors.push(`Failed to cancel request ${request.number}: ${updateResult.error}`);
|
|
2831
|
+
}
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
catch (error) {
|
|
2837
|
+
results.errors.push(`Error cleaning requests: ${error.message}`);
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2494
2840
|
getServiceNowUrl() {
|
|
2495
2841
|
// This would need to get the instance URL from credentials
|
|
2496
2842
|
return 'https://instance.service-now.com';
|