snow-flow 2.0.2 → 2.0.4

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 (173) hide show
  1. package/{.claude/mcp-config.json → .mcp.json} +13 -12
  2. package/.releaserc.json +47 -0
  3. package/README.md +48 -1
  4. package/package.json +1 -1
  5. package/.claude/claude-settings.json +0 -15
  6. package/.claude/commands/analysis/README.md +0 -9
  7. package/.claude/commands/automation/README.md +0 -9
  8. package/.claude/commands/github/README.md +0 -11
  9. package/.claude/commands/helpers/standard-checkpoint-hooks.sh +0 -179
  10. package/.claude/commands/hooks/README.md +0 -11
  11. package/.claude/commands/monitoring/README.md +0 -9
  12. package/.claude/commands/optimization/README.md +0 -9
  13. package/.claude/commands/training/README.md +0 -9
  14. package/.claude/commands/workflows/README.md +0 -9
  15. package/.claude/config.json +0 -36
  16. package/.claude/helpers/github-setup.sh +0 -28
  17. package/.claude/helpers/quick-start.sh +0 -19
  18. package/.claude/helpers/setup-mcp.sh +0 -18
  19. package/.claude/settings.json +0 -114
  20. package/.claude/settings.local.json +0 -33
  21. package/.claude-flow/metrics/agent-metrics.json +0 -1
  22. package/.claude-flow/metrics/performance.json +0 -9
  23. package/.claude-flow/metrics/system-metrics.json +0 -15986
  24. package/.claude-flow/metrics/task-metrics.json +0 -10
  25. package/.roo/README.md +0 -402
  26. package/.roo/mcp-list.txt +0 -257
  27. package/.roo/mcp.json +0 -32
  28. package/.roo/workflows/basic-tdd.json +0 -32
  29. package/.roomodes +0 -201
  30. package/CLAUDE.md +0 -393
  31. package/bin/snow-flow.js +0 -3
  32. package/dist/cli.js.bak +0 -2052
  33. package/dist/config/snow-flow-config.d.ts +0 -1492
  34. package/dist/config/snow-flow-config.js +0 -938
  35. package/scripts/create-env.js +0 -91
  36. package/scripts/postinstall.js +0 -30
  37. package/scripts/register-mcp-servers.js +0 -210
  38. package/scripts/reset-mcp-servers.js +0 -246
  39. package/scripts/setup-mcp.js +0 -106
  40. package/scripts/update-version.js +0 -31
  41. package/src/agents/README.md +0 -192
  42. package/src/agents/base-agent.ts +0 -251
  43. package/src/agents/coordinator.ts +0 -971
  44. package/src/agents/index.ts +0 -18
  45. package/src/agents/queen-403-handler.ts +0 -230
  46. package/src/agents/queen-agent.ts +0 -1399
  47. package/src/agents/script-writer-agent.ts +0 -769
  48. package/src/agents/security-agent.ts +0 -726
  49. package/src/agents/widget-creator-agent.ts +0 -568
  50. package/src/api/error-handling.ts +0 -1758
  51. package/src/api/natural-language-mapper.ts +0 -630
  52. package/src/api/performance-optimizer.ts +0 -891
  53. package/src/api/transaction-manager.ts +0 -754
  54. package/src/cli/deploy-artifact.ts +0 -304
  55. package/src/cli/snow-flow-cli-integration.ts +0 -434
  56. package/src/cli.ts +0 -4455
  57. package/src/cli.ts.bak +0 -5616
  58. package/src/compliance/advanced-compliance-system.ts +0 -1268
  59. package/src/compliance/index.ts +0 -19
  60. package/src/config/snow-flow-config.ts +0 -1019
  61. package/src/documentation/index.ts +0 -16
  62. package/src/documentation/self-documenting-system.ts +0 -1421
  63. package/src/dynamic-version.ts +0 -44
  64. package/src/healing/index.ts +0 -20
  65. package/src/healing/self-healing-system.ts +0 -1435
  66. package/src/health/README.md +0 -161
  67. package/src/health/system-health.ts +0 -995
  68. package/src/index.ts +0 -60
  69. package/src/intelligence/acl-analyzer.ts +0 -377
  70. package/src/intelligence/auto-resolution-engine.ts +0 -643
  71. package/src/intelligence/gap-analysis-engine.ts +0 -488
  72. package/src/intelligence/manual-instructions-generator.ts +0 -744
  73. package/src/intelligence/mcp-coverage-analyzer.ts +0 -708
  74. package/src/intelligence/multi-pass-requirements-analyzer.ts +0 -603
  75. package/src/intelligence/performance-recommendations-engine.ts +0 -1223
  76. package/src/intelligence/requirements-analyzer.ts +0 -1148
  77. package/src/intelligence/task-analyzer.ts +0 -628
  78. package/src/managers/scope-manager.ts +0 -555
  79. package/src/mcp/advanced/servicenow-advanced-features-mcp.ts +0 -16110
  80. package/src/mcp/base-mcp-server.ts +0 -660
  81. package/src/mcp/http-transport-wrapper.ts +0 -426
  82. package/src/mcp/service-discovery-client.ts +0 -312
  83. package/src/mcp/servicenow-automation-mcp-refactored.ts +0 -678
  84. package/src/mcp/servicenow-automation-mcp.ts +0 -838
  85. package/src/mcp/servicenow-deployment-mcp-refactored.ts +0 -1292
  86. package/src/mcp/servicenow-deployment-mcp.ts +0 -7244
  87. package/src/mcp/servicenow-graph-memory-mcp-refactored.ts +0 -721
  88. package/src/mcp/servicenow-graph-memory-mcp.ts +0 -832
  89. package/src/mcp/servicenow-integration-mcp-refactored.ts +0 -655
  90. package/src/mcp/servicenow-integration-mcp.ts +0 -819
  91. package/src/mcp/servicenow-intelligent-mcp-refactored.ts +0 -977
  92. package/src/mcp/servicenow-intelligent-mcp.ts +0 -4638
  93. package/src/mcp/servicenow-mcp-server.ts +0 -728
  94. package/src/mcp/servicenow-memory-mcp.ts +0 -474
  95. package/src/mcp/servicenow-operations-mcp-refactored.ts +0 -1995
  96. package/src/mcp/servicenow-operations-mcp.ts +0 -3384
  97. package/src/mcp/servicenow-platform-development-mcp-refactored.ts +0 -611
  98. package/src/mcp/servicenow-platform-development-mcp.ts +0 -970
  99. package/src/mcp/servicenow-progressive-indexer.ts +0 -370
  100. package/src/mcp/servicenow-reporting-analytics-mcp-refactored.ts +0 -695
  101. package/src/mcp/servicenow-reporting-analytics-mcp.ts +0 -1123
  102. package/src/mcp/servicenow-security-compliance-mcp-refactored.ts +0 -642
  103. package/src/mcp/servicenow-security-compliance-mcp.ts +0 -1157
  104. package/src/mcp/servicenow-update-set-mcp-refactored.ts +0 -751
  105. package/src/mcp/servicenow-update-set-mcp.ts +0 -811
  106. package/src/mcp/shared/agent-context-provider.ts +0 -365
  107. package/src/mcp/shared/base-mcp-server.ts +0 -2463
  108. package/src/mcp/shared/mcp-memory-manager.ts +0 -540
  109. package/src/mcp/shared/mcp-resource-manager.ts +0 -367
  110. package/src/mcp/snow-flow-mcp.ts +0 -911
  111. package/src/mcp/start-all-mcp-servers.ts +0 -102
  112. package/src/mcp/start-servicenow-mcp.ts +0 -67
  113. package/src/monitoring/enhanced-monitoring-system.ts +0 -1345
  114. package/src/monitoring/performance-tracker.ts +0 -777
  115. package/src/optimization/cost-optimization-engine.ts +0 -1125
  116. package/src/optimization/index.ts +0 -10
  117. package/src/queen/README.md +0 -403
  118. package/src/queen/agent-factory.ts +0 -574
  119. package/src/queen/index.ts +0 -94
  120. package/src/queen/mcp-execution-bridge.ts +0 -681
  121. package/src/queen/neural-learning.ts +0 -319
  122. package/src/queen/parallel-agent-engine.ts +0 -848
  123. package/src/queen/queen-memory-system.ts +0 -490
  124. package/src/queen/queen-memory.ts +0 -456
  125. package/src/queen/servicenow-queen.ts +0 -1029
  126. package/src/queen/types.ts +0 -87
  127. package/src/rollback/smart-rollback-system.ts +0 -866
  128. package/src/schemas/deployment.schema.json +0 -58
  129. package/src/schemas/flow.schema.json +0 -79
  130. package/src/schemas/widget.schema.json +0 -72
  131. package/src/snow-flow-system.ts +0 -541
  132. package/src/sparc/sparc-help.ts +0 -49
  133. package/src/sparc/team-sparc.ts +0 -88
  134. package/src/strategies/global-scope-strategy.ts +0 -736
  135. package/src/templates/base/application.template.json +0 -45
  136. package/src/templates/base/business_rule.template.json +0 -33
  137. package/src/templates/base/script_include.template.json +0 -18
  138. package/src/templates/base/table.template.json +0 -64
  139. package/src/templates/base/widget.template.json +0 -25
  140. package/src/templates/patterns/composite.incident-management.template.json +0 -140
  141. package/src/templates/patterns/widget.dashboard.template.json +0 -238
  142. package/src/templates/patterns/widget.datatable.template.json +0 -292
  143. package/src/testing/integration-test-suite.ts +0 -1276
  144. package/src/types/index.ts +0 -183
  145. package/src/types/neo4j-driver.d.ts +0 -23
  146. package/src/types/servicenow.types.ts +0 -307
  147. package/src/types/snow-flow.types.ts +0 -186
  148. package/src/types/todo.types.ts +0 -70
  149. package/src/utils/action-type-cache.ts +0 -286
  150. package/src/utils/agent-detector.ts +0 -573
  151. package/src/utils/artifact-tracker.ts +0 -379
  152. package/src/utils/dependency-detector.ts +0 -233
  153. package/src/utils/deployment-metadata-handler.ts +0 -518
  154. package/src/utils/error-recovery.ts +0 -680
  155. package/src/utils/logger.ts +0 -60
  156. package/src/utils/mcp-auth-middleware.ts +0 -262
  157. package/src/utils/mcp-config-manager.ts +0 -270
  158. package/src/utils/mcp-server-manager.ts +0 -446
  159. package/src/utils/mcp-tool-registry.ts +0 -298
  160. package/src/utils/migrate-snow-flow.ts +0 -98
  161. package/src/utils/oauth-html-templates.ts +0 -188
  162. package/src/utils/scope-utils.ts +0 -443
  163. package/src/utils/servicenow-client.ts +0 -3321
  164. package/src/utils/servicenow-id-generator.ts +0 -181
  165. package/src/utils/snow-memory-manager.ts +0 -605
  166. package/src/utils/snow-oauth.ts +0 -797
  167. package/src/utils/template-engine.ts +0 -463
  168. package/src/utils/theme-manager.ts +0 -295
  169. package/src/utils/unified-auth-store.ts +0 -222
  170. package/src/utils/update-set-importer.ts +0 -422
  171. package/src/utils/update-set-xml-packager.ts +0 -371
  172. package/src/utils/widget-template-generator.ts +0 -1744
  173. package/src/version.ts +0 -1950
@@ -1,1758 +0,0 @@
1
- /**
2
- * Comprehensive Error Handling and Rollback System
3
- * Advanced error handling with automatic rollback capabilities
4
- */
5
-
6
- import { ServiceNowClient } from '../utils/servicenow-client.js';
7
- import { Logger } from '../utils/logger.js';
8
-
9
- export interface ErrorContext {
10
- operation: string;
11
- flow_id?: string;
12
- component_type?: string;
13
- component_id?: string;
14
- user_input?: any;
15
- system_state?: any;
16
- timestamp: Date;
17
- }
18
-
19
- export interface ErrorRecoveryAction {
20
- type: 'rollback' | 'retry' | 'skip' | 'fallback' | 'notify';
21
- description: string;
22
- parameters?: Record<string, any>;
23
- priority: 'high' | 'medium' | 'low';
24
- }
25
-
26
- export interface ErrorHandlingResult {
27
- success: boolean;
28
- recovered: boolean;
29
- actions_taken: ErrorRecoveryAction[];
30
- final_state?: any;
31
- user_message?: string;
32
- technical_details?: string;
33
- }
34
-
35
- export enum ErrorType {
36
- VALIDATION_ERROR = 'validation_error',
37
- API_ERROR = 'api_error',
38
- AUTHENTICATION_ERROR = 'authentication_error',
39
- AUTHORIZATION_ERROR = 'authorization_error',
40
- NETWORK_ERROR = 'network_error',
41
- TIMEOUT_ERROR = 'timeout_error',
42
- RATE_LIMIT_ERROR = 'rate_limit_error',
43
- RESOURCE_NOT_FOUND = 'resource_not_found',
44
- CONFLICT_ERROR = 'conflict_error',
45
- SERVICE_UNAVAILABLE = 'service_unavailable',
46
- FLOW_LOGIC_ERROR = 'flow_logic_error',
47
- ROLLBACK_ERROR = 'rollback_error',
48
- UNKNOWN_ERROR = 'unknown_error'
49
- }
50
-
51
- export enum ErrorSeverity {
52
- CRITICAL = 'critical',
53
- HIGH = 'high',
54
- MEDIUM = 'medium',
55
- LOW = 'low'
56
- }
57
-
58
- export interface FlowError {
59
- type: ErrorType;
60
- severity: ErrorSeverity;
61
- message: string;
62
- context: ErrorContext;
63
- original_error?: Error;
64
- recoverable: boolean;
65
- suggested_actions: ErrorRecoveryAction[];
66
- retry_count?: number;
67
- max_retries?: number;
68
- }
69
-
70
- export interface RollbackOperation {
71
- id: string;
72
- type: 'create' | 'update' | 'delete';
73
- table: string;
74
- record_id?: string;
75
- original_data?: any;
76
- new_data?: any;
77
- timestamp: Date;
78
- dependencies: string[];
79
- rollback_function?: () => Promise<void>;
80
- }
81
-
82
- export interface TransactionState {
83
- id: string;
84
- flow_id: string;
85
- operations: RollbackOperation[];
86
- status: 'active' | 'committed' | 'rolled_back' | 'failed';
87
- start_time: Date;
88
- end_time?: Date;
89
- checkpoints: TransactionCheckpoint[];
90
- }
91
-
92
- export interface TransactionCheckpoint {
93
- id: string;
94
- timestamp: Date;
95
- operation_count: number;
96
- state_snapshot: any;
97
- description: string;
98
- }
99
-
100
- export class FlowErrorHandler {
101
- private client: ServiceNowClient;
102
- private logger: Logger;
103
- private transactions: Map<string, TransactionState> = new Map();
104
- private errorHistory: FlowError[] = [];
105
- private maxErrorHistory = 1000;
106
- private retryDelays = [1000, 2000, 5000, 10000, 30000]; // Exponential backoff
107
-
108
- constructor(client: ServiceNowClient) {
109
- this.client = client;
110
- this.logger = new Logger('FlowErrorHandler');
111
- }
112
-
113
- /**
114
- * Handle an error with automatic recovery
115
- */
116
- async handleError(error: Error, context: ErrorContext): Promise<ErrorHandlingResult> {
117
- const flowError = this.classifyError(error, context);
118
- this.errorHistory.push(flowError);
119
-
120
- // Limit error history size
121
- if (this.errorHistory.length > this.maxErrorHistory) {
122
- this.errorHistory.shift();
123
- }
124
-
125
- this.logger.error('Flow error occurred', {
126
- type: flowError.type,
127
- severity: flowError.severity,
128
- message: flowError.message,
129
- context: flowError.context
130
- });
131
-
132
- // Determine recovery strategy
133
- const recoveryActions = this.determineRecoveryActions(flowError);
134
- const result: ErrorHandlingResult = {
135
- success: false,
136
- recovered: false,
137
- actions_taken: []
138
- };
139
-
140
- // Execute recovery actions
141
- for (const action of recoveryActions) {
142
- try {
143
- const actionResult = await this.executeRecoveryAction(action, flowError);
144
- result.actions_taken.push(action);
145
-
146
- if (actionResult.success) {
147
- result.success = true;
148
- result.recovered = true;
149
- result.final_state = actionResult.final_state;
150
- break;
151
- }
152
- } catch (recoveryError) {
153
- this.logger.error('Recovery action failed', {
154
- action: action.type,
155
- error: recoveryError
156
- });
157
- }
158
- }
159
-
160
- // Generate user-friendly message
161
- result.user_message = this.generateUserMessage(flowError, result);
162
- result.technical_details = this.generateTechnicalDetails(flowError, result);
163
-
164
- return result;
165
- }
166
-
167
- /**
168
- * Start a new transaction
169
- */
170
- startTransaction(flowId: string): string {
171
- const transactionId = this.generateTransactionId();
172
- const transaction: TransactionState = {
173
- id: transactionId,
174
- flow_id: flowId,
175
- operations: [],
176
- status: 'active',
177
- start_time: new Date(),
178
- checkpoints: []
179
- };
180
-
181
- this.transactions.set(transactionId, transaction);
182
- this.logger.info('Transaction started', { transactionId, flowId });
183
-
184
- return transactionId;
185
- }
186
-
187
- /**
188
- * Add operation to transaction
189
- */
190
- addOperation(transactionId: string, operation: Omit<RollbackOperation, 'id' | 'timestamp'>): void {
191
- const transaction = this.transactions.get(transactionId);
192
- if (!transaction) {
193
- throw new Error(`Transaction ${transactionId} not found`);
194
- }
195
-
196
- const rollbackOperation: RollbackOperation = {
197
- ...operation,
198
- id: this.generateOperationId(),
199
- timestamp: new Date()
200
- };
201
-
202
- transaction.operations.push(rollbackOperation);
203
- this.logger.debug('Operation added to transaction', { transactionId, operationId: rollbackOperation.id });
204
- }
205
-
206
- /**
207
- * Create checkpoint in transaction
208
- */
209
- createCheckpoint(transactionId: string, description: string): string {
210
- const transaction = this.transactions.get(transactionId);
211
- if (!transaction) {
212
- throw new Error(`Transaction ${transactionId} not found`);
213
- }
214
-
215
- const checkpoint: TransactionCheckpoint = {
216
- id: this.generateCheckpointId(),
217
- timestamp: new Date(),
218
- operation_count: transaction.operations.length,
219
- state_snapshot: this.captureStateSnapshot(transaction),
220
- description
221
- };
222
-
223
- transaction.checkpoints.push(checkpoint);
224
- this.logger.info('Checkpoint created', { transactionId, checkpointId: checkpoint.id, description });
225
-
226
- return checkpoint.id;
227
- }
228
-
229
- /**
230
- * Commit transaction
231
- */
232
- async commitTransaction(transactionId: string): Promise<void> {
233
- const transaction = this.transactions.get(transactionId);
234
- if (!transaction) {
235
- throw new Error(`Transaction ${transactionId} not found`);
236
- }
237
-
238
- transaction.status = 'committed';
239
- transaction.end_time = new Date();
240
-
241
- this.logger.info('Transaction committed', {
242
- transactionId,
243
- operationCount: transaction.operations.length,
244
- duration: transaction.end_time.getTime() - transaction.start_time.getTime()
245
- });
246
-
247
- // Clean up transaction after some time
248
- setTimeout(() => {
249
- this.transactions.delete(transactionId);
250
- }, 300000); // 5 minutes
251
- }
252
-
253
- /**
254
- * Rollback transaction
255
- */
256
- async rollbackTransaction(transactionId: string, toCheckpoint?: string): Promise<void> {
257
- const transaction = this.transactions.get(transactionId);
258
- if (!transaction) {
259
- throw new Error(`Transaction ${transactionId} not found`);
260
- }
261
-
262
- this.logger.info('Starting transaction rollback', { transactionId, toCheckpoint });
263
-
264
- let operationsToRollback = transaction.operations.slice();
265
-
266
- // If rolling back to a checkpoint, only rollback operations after that checkpoint
267
- if (toCheckpoint) {
268
- const checkpoint = transaction.checkpoints.find(cp => cp.id === toCheckpoint);
269
- if (checkpoint) {
270
- operationsToRollback = transaction.operations.slice(checkpoint.operation_count);
271
- }
272
- }
273
-
274
- // Rollback operations in reverse order
275
- operationsToRollback.reverse();
276
-
277
- const rollbackErrors: Error[] = [];
278
-
279
- for (const operation of operationsToRollback) {
280
- try {
281
- await this.rollbackOperation(operation);
282
- this.logger.debug('Operation rolled back', { operationId: operation.id });
283
- } catch (error) {
284
- rollbackErrors.push(error instanceof Error ? error : new Error(String(error)));
285
- this.logger.error('Rollback operation failed', { operationId: operation.id, error });
286
- }
287
- }
288
-
289
- transaction.status = rollbackErrors.length > 0 ? 'failed' : 'rolled_back';
290
- transaction.end_time = new Date();
291
-
292
- if (rollbackErrors.length > 0) {
293
- throw new Error(`Rollback failed with ${rollbackErrors.length} errors: ${rollbackErrors.map(e => e.message).join(', ')}`);
294
- }
295
-
296
- this.logger.info('Transaction rollback completed', { transactionId });
297
- }
298
-
299
- /**
300
- * Rollback a single operation
301
- */
302
- private async rollbackOperation(operation: RollbackOperation): Promise<void> {
303
- switch (operation.type) {
304
- case 'create':
305
- // Delete the created record
306
- if (operation.record_id) {
307
- await this.client.deleteRecord(operation.table, operation.record_id);
308
- }
309
- break;
310
-
311
- case 'update':
312
- // Restore original data
313
- if (operation.record_id && operation.original_data) {
314
- await this.client.updateRecord(operation.table, operation.record_id, operation.original_data);
315
- }
316
- break;
317
-
318
- case 'delete':
319
- // Recreate the deleted record
320
- if (operation.original_data) {
321
- await this.client.createRecord(operation.table, operation.original_data);
322
- }
323
- break;
324
- }
325
-
326
- // Execute custom rollback function if provided
327
- if (operation.rollback_function) {
328
- await operation.rollback_function();
329
- }
330
- }
331
-
332
- /**
333
- * Classify error type and severity
334
- */
335
- private classifyError(error: Error, context: ErrorContext): FlowError {
336
- let type = ErrorType.UNKNOWN_ERROR;
337
- let severity = ErrorSeverity.MEDIUM;
338
- let recoverable = true;
339
- let suggestedActions: ErrorRecoveryAction[] = [];
340
-
341
- const errorMessage = error.message.toLowerCase();
342
-
343
- // Classify by error message patterns
344
- if (errorMessage.includes('validation') || errorMessage.includes('invalid')) {
345
- type = ErrorType.VALIDATION_ERROR;
346
- severity = ErrorSeverity.HIGH;
347
- suggestedActions = [
348
- { type: 'fallback', description: 'Use fallback values', priority: 'high' },
349
- { type: 'notify', description: 'Notify user of validation issues', priority: 'medium' }
350
- ];
351
- } else if (errorMessage.includes('unauthorized') || errorMessage.includes('authentication')) {
352
- type = ErrorType.AUTHENTICATION_ERROR;
353
- severity = ErrorSeverity.CRITICAL;
354
- recoverable = false;
355
- suggestedActions = [
356
- { type: 'notify', description: 'Request user to re-authenticate', priority: 'high' }
357
- ];
358
- } else if (errorMessage.includes('forbidden') || errorMessage.includes('access denied')) {
359
- type = ErrorType.AUTHORIZATION_ERROR;
360
- severity = ErrorSeverity.HIGH;
361
- recoverable = false;
362
- suggestedActions = [
363
- { type: 'notify', description: 'Inform user of insufficient permissions', priority: 'high' }
364
- ];
365
- } else if (errorMessage.includes('network') || errorMessage.includes('connection')) {
366
- type = ErrorType.NETWORK_ERROR;
367
- severity = ErrorSeverity.MEDIUM;
368
- suggestedActions = [
369
- { type: 'retry', description: 'Retry with exponential backoff', priority: 'high' },
370
- { type: 'fallback', description: 'Use cached data if available', priority: 'medium' }
371
- ];
372
- } else if (errorMessage.includes('timeout')) {
373
- type = ErrorType.TIMEOUT_ERROR;
374
- severity = ErrorSeverity.MEDIUM;
375
- suggestedActions = [
376
- { type: 'retry', description: 'Retry with increased timeout', priority: 'high' }
377
- ];
378
- } else if (errorMessage.includes('rate limit') || errorMessage.includes('too many requests')) {
379
- type = ErrorType.RATE_LIMIT_ERROR;
380
- severity = ErrorSeverity.LOW;
381
- suggestedActions = [
382
- { type: 'retry', description: 'Retry after delay', priority: 'high', parameters: { delay: 60000 } }
383
- ];
384
- } else if (errorMessage.includes('not found') || errorMessage.includes('404')) {
385
- type = ErrorType.RESOURCE_NOT_FOUND;
386
- severity = ErrorSeverity.HIGH;
387
- suggestedActions = [
388
- { type: 'fallback', description: 'Create missing resource', priority: 'high' },
389
- { type: 'notify', description: 'Inform user of missing resource', priority: 'medium' }
390
- ];
391
- } else if (errorMessage.includes('conflict') || errorMessage.includes('409')) {
392
- type = ErrorType.CONFLICT_ERROR;
393
- severity = ErrorSeverity.MEDIUM;
394
- suggestedActions = [
395
- { type: 'retry', description: 'Retry after resolving conflict', priority: 'high' },
396
- { type: 'fallback', description: 'Use alternative approach', priority: 'medium' }
397
- ];
398
- } else if (errorMessage.includes('service unavailable') || errorMessage.includes('503')) {
399
- type = ErrorType.SERVICE_UNAVAILABLE;
400
- severity = ErrorSeverity.HIGH;
401
- suggestedActions = [
402
- { type: 'retry', description: 'Retry after service recovery', priority: 'high' },
403
- { type: 'notify', description: 'Inform user of service unavailability', priority: 'medium' }
404
- ];
405
- }
406
-
407
- // Determine if rollback is needed
408
- if (context.flow_id && ['create', 'update', 'delete'].includes(context.operation)) {
409
- suggestedActions.unshift({
410
- type: 'rollback',
411
- description: 'Rollback partial changes',
412
- priority: 'high'
413
- });
414
- }
415
-
416
- return {
417
- type,
418
- severity,
419
- message: error.message,
420
- context,
421
- original_error: error,
422
- recoverable,
423
- suggested_actions: suggestedActions,
424
- retry_count: 0,
425
- max_retries: this.getMaxRetries(type)
426
- };
427
- }
428
-
429
- /**
430
- * Determine recovery actions based on error
431
- */
432
- private determineRecoveryActions(error: FlowError): ErrorRecoveryAction[] {
433
- const actions: ErrorRecoveryAction[] = [];
434
-
435
- // Sort suggested actions by priority
436
- const sortedActions = error.suggested_actions.sort((a, b) => {
437
- const priorityOrder = { high: 3, medium: 2, low: 1 };
438
- return priorityOrder[b.priority] - priorityOrder[a.priority];
439
- });
440
-
441
- // Add retry logic for recoverable errors
442
- if (error.recoverable && error.retry_count < error.max_retries) {
443
- actions.push({
444
- type: 'retry',
445
- description: `Retry operation (attempt ${error.retry_count + 1}/${error.max_retries})`,
446
- priority: 'high',
447
- parameters: {
448
- delay: this.retryDelays[Math.min(error.retry_count, this.retryDelays.length - 1)]
449
- }
450
- });
451
- }
452
-
453
- // Add suggested actions
454
- actions.push(...sortedActions);
455
-
456
- return actions;
457
- }
458
-
459
- /**
460
- * Execute a recovery action
461
- */
462
- private async executeRecoveryAction(action: ErrorRecoveryAction, error: FlowError): Promise<{
463
- success: boolean;
464
- final_state?: any;
465
- }> {
466
- switch (action.type) {
467
- case 'retry':
468
- return await this.executeRetry(action, error);
469
-
470
- case 'rollback':
471
- return await this.executeRollback(action, error);
472
-
473
- case 'fallback':
474
- return await this.executeFallback(action, error);
475
-
476
- case 'skip':
477
- return await this.executeSkip(action, error);
478
-
479
- case 'notify':
480
- return await this.executeNotify(action, error);
481
-
482
- default:
483
- return { success: false };
484
- }
485
- }
486
-
487
- /**
488
- * Execute retry action
489
- */
490
- private async executeRetry(action: ErrorRecoveryAction, error: FlowError): Promise<{
491
- success: boolean;
492
- final_state?: any;
493
- }> {
494
- const delay = action.parameters?.delay || 1000;
495
-
496
- this.logger.info('Executing retry', { delay, retryCount: error.retry_count });
497
-
498
- // Wait before retry
499
- await new Promise(resolve => setTimeout(resolve, delay));
500
-
501
- try {
502
- // Retry the original operation
503
- const result = await this.retryOriginalOperation(error);
504
- return { success: true, final_state: result };
505
- } catch (retryError) {
506
- error.retry_count = (error.retry_count || 0) + 1;
507
- return { success: false };
508
- }
509
- }
510
-
511
- /**
512
- * Execute rollback action
513
- */
514
- private async executeRollback(action: ErrorRecoveryAction, error: FlowError): Promise<{
515
- success: boolean;
516
- final_state?: any;
517
- }> {
518
- if (!error.context.flow_id) {
519
- return { success: false };
520
- }
521
-
522
- // Find transaction for this flow
523
- const transaction = Array.from(this.transactions.values())
524
- .find(t => t.flow_id === error.context.flow_id && t.status === 'active');
525
-
526
- if (!transaction) {
527
- return { success: false };
528
- }
529
-
530
- try {
531
- await this.rollbackTransaction(transaction.id);
532
- return { success: true, final_state: 'rolled_back' };
533
- } catch (rollbackError) {
534
- return { success: false };
535
- }
536
- }
537
-
538
- /**
539
- * Execute fallback action
540
- */
541
- private async executeFallback(action: ErrorRecoveryAction, error: FlowError): Promise<{
542
- success: boolean;
543
- final_state?: any;
544
- }> {
545
- // Implement fallback logic based on error type
546
- switch (error.type) {
547
- case ErrorType.RESOURCE_NOT_FOUND:
548
- return await this.createMissingResource(error);
549
-
550
- case ErrorType.VALIDATION_ERROR:
551
- return await this.useFallbackValues(error);
552
-
553
- case ErrorType.NETWORK_ERROR:
554
- return await this.useCachedData(error);
555
-
556
- default:
557
- return { success: false };
558
- }
559
- }
560
-
561
- /**
562
- * Execute skip action
563
- */
564
- private async executeSkip(action: ErrorRecoveryAction, error: FlowError): Promise<{
565
- success: boolean;
566
- final_state?: any;
567
- }> {
568
- this.logger.info('Skipping failed operation', { error: error.type });
569
- return { success: true, final_state: 'skipped' };
570
- }
571
-
572
- /**
573
- * Execute notify action
574
- */
575
- private async executeNotify(action: ErrorRecoveryAction, error: FlowError): Promise<{
576
- success: boolean;
577
- final_state?: any;
578
- }> {
579
- // Create notification record
580
- try {
581
- await this.client.createRecord('sys_email', {
582
- to: 'admin@company.com',
583
- subject: `Flow Error: ${error.type}`,
584
- body: `An error occurred in flow operation: ${error.message}`
585
- });
586
-
587
- return { success: true, final_state: 'notified' };
588
- } catch (notifyError) {
589
- return { success: false };
590
- }
591
- }
592
-
593
- /**
594
- * Retry original operation with exponential backoff
595
- */
596
- private async retryOriginalOperation(error: FlowError): Promise<any> {
597
- const maxRetries = error.max_retries || 3;
598
- const currentRetryCount = error.retry_count || 0;
599
-
600
- this.logger.info(`Attempting retry ${currentRetryCount + 1}/${maxRetries} for operation`, {
601
- operation: error.context.operation,
602
- error_type: error.type,
603
- previous_attempts: currentRetryCount
604
- });
605
-
606
- // Check if we've exceeded max retries
607
- if (currentRetryCount >= maxRetries) {
608
- this.logger.error('Maximum retry attempts exceeded', {
609
- operation: error.context.operation,
610
- total_attempts: currentRetryCount,
611
- final_error: error.message
612
- });
613
-
614
- return {
615
- success: false,
616
- final_state: null,
617
- retry_exhausted: true,
618
- total_attempts: currentRetryCount,
619
- last_error: error.message,
620
- suggested_actions: [
621
- 'Review the operation configuration',
622
- 'Check ServiceNow system status',
623
- 'Contact administrator for manual intervention',
624
- 'Consider alternative approach'
625
- ]
626
- };
627
- }
628
-
629
- // Calculate retry delay using exponential backoff
630
- const baseDelay = this.retryDelays[Math.min(currentRetryCount, this.retryDelays.length - 1)];
631
- const jitterDelay = baseDelay + Math.random() * 1000; // Add jitter to prevent thundering herd
632
-
633
- this.logger.info(`Waiting ${Math.round(jitterDelay)}ms before retry attempt`, {
634
- retry_count: currentRetryCount + 1,
635
- base_delay: baseDelay,
636
- actual_delay: Math.round(jitterDelay)
637
- });
638
-
639
- // Wait before retry
640
- await this.delay(jitterDelay);
641
-
642
- try {
643
- // Determine retry strategy based on error type
644
- const retryResult = await this.executeRetryBasedOnErrorType(error);
645
-
646
- if (retryResult.success) {
647
- this.logger.info('Retry attempt successful', {
648
- operation: error.context.operation,
649
- retry_count: currentRetryCount + 1,
650
- success_after_attempts: currentRetryCount + 1
651
- });
652
-
653
- return {
654
- success: true,
655
- final_state: retryResult.data,
656
- retry_count: currentRetryCount + 1,
657
- recovery_method: 'retry_with_backoff'
658
- };
659
- } else {
660
- // Update error with new retry count and continue retry cycle
661
- const updatedError: FlowError = {
662
- ...error,
663
- retry_count: currentRetryCount + 1,
664
- message: retryResult.error || error.message
665
- };
666
-
667
- // Recursively retry if not at max attempts
668
- return await this.retryOriginalOperation(updatedError);
669
- }
670
-
671
- } catch (retryError) {
672
- this.logger.error('Error during retry attempt', {
673
- retry_count: currentRetryCount + 1,
674
- error: retryError instanceof Error ? retryError.message : String(retryError)
675
- });
676
-
677
- // Create updated error for next retry or final failure
678
- const updatedError: FlowError = {
679
- ...error,
680
- retry_count: currentRetryCount + 1,
681
- message: retryError instanceof Error ? retryError.message : String(retryError),
682
- original_error: retryError instanceof Error ? retryError : new Error(String(retryError))
683
- };
684
-
685
- // Continue retry cycle
686
- return await this.retryOriginalOperation(updatedError);
687
- }
688
- }
689
-
690
- /**
691
- * Execute retry based on specific error type
692
- */
693
- private async executeRetryBasedOnErrorType(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
694
- try {
695
- switch (error.type) {
696
- case ErrorType.AUTHENTICATION_ERROR:
697
- return await this.retryAuthenticationOperation(error);
698
-
699
- case ErrorType.NETWORK_ERROR:
700
- case ErrorType.TIMEOUT_ERROR:
701
- return await this.retryNetworkOperation(error);
702
-
703
- case ErrorType.RATE_LIMIT_ERROR:
704
- return await this.retryRateLimitedOperation(error);
705
-
706
- case ErrorType.SERVICE_UNAVAILABLE:
707
- return await this.retryServiceUnavailableOperation(error);
708
-
709
- case ErrorType.API_ERROR:
710
- return await this.retryAPIOperation(error);
711
-
712
- case ErrorType.CONFLICT_ERROR:
713
- return await this.retryConflictOperation(error);
714
-
715
- case ErrorType.RESOURCE_NOT_FOUND:
716
- return await this.retryResourceOperation(error);
717
-
718
- default:
719
- return await this.retryGenericOperation(error);
720
- }
721
- } catch (executeError) {
722
- return {
723
- success: false,
724
- error: executeError instanceof Error ? executeError.message : String(executeError)
725
- };
726
- }
727
- }
728
-
729
- /**
730
- * Retry authentication operations
731
- */
732
- private async retryAuthenticationOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
733
- try {
734
- this.logger.info('Retrying authentication operation');
735
-
736
- // Try to refresh authentication
737
- const credentials = this.client.credentialsInstance;
738
- if (credentials?.refreshToken) {
739
- // Attempt token refresh
740
- const refreshResult = await (this.client as any).oauth?.refreshAccessToken();
741
- if (refreshResult?.success) {
742
- // Retry the original operation with new token
743
- return await this.retryWithNewAuthentication(error);
744
- }
745
- }
746
-
747
- return {
748
- success: false,
749
- error: 'Authentication refresh failed - manual re-authentication required'
750
- };
751
- } catch (authError) {
752
- return {
753
- success: false,
754
- error: authError instanceof Error ? authError.message : String(authError)
755
- };
756
- }
757
- }
758
-
759
- /**
760
- * Retry network operations
761
- */
762
- private async retryNetworkOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
763
- try {
764
- this.logger.info('Retrying network operation');
765
-
766
- // For network errors, simply retry the original request
767
- return await this.executeOriginalOperation(error);
768
- } catch (networkError) {
769
- return {
770
- success: false,
771
- error: networkError instanceof Error ? networkError.message : String(networkError)
772
- };
773
- }
774
- }
775
-
776
- /**
777
- * Retry rate limited operations with longer delay
778
- */
779
- private async retryRateLimitedOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
780
- try {
781
- this.logger.info('Retrying rate limited operation with extended delay');
782
-
783
- // Rate limit errors need longer delays
784
- const rateLimitDelay = 30000 + Math.random() * 10000; // 30-40 seconds
785
- await this.delay(rateLimitDelay);
786
-
787
- return await this.executeOriginalOperation(error);
788
- } catch (rateLimitError) {
789
- return {
790
- success: false,
791
- error: rateLimitError instanceof Error ? rateLimitError.message : String(rateLimitError)
792
- };
793
- }
794
- }
795
-
796
- /**
797
- * Retry service unavailable operations
798
- */
799
- private async retryServiceUnavailableOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
800
- try {
801
- this.logger.info('Retrying operation after service unavailability');
802
-
803
- // Check service health before retry
804
- const healthCheck = await this.performServiceHealthCheck();
805
- if (!healthCheck.healthy) {
806
- return {
807
- success: false,
808
- error: `Service still unavailable: ${healthCheck.reason}`
809
- };
810
- }
811
-
812
- return await this.executeOriginalOperation(error);
813
- } catch (serviceError) {
814
- return {
815
- success: false,
816
- error: serviceError instanceof Error ? serviceError.message : String(serviceError)
817
- };
818
- }
819
- }
820
-
821
- /**
822
- * Retry API operations
823
- */
824
- private async retryAPIOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
825
- try {
826
- this.logger.info('Retrying API operation');
827
-
828
- // API errors might be transient, retry with same parameters
829
- return await this.executeOriginalOperation(error);
830
- } catch (apiError) {
831
- return {
832
- success: false,
833
- error: apiError instanceof Error ? apiError.message : String(apiError)
834
- };
835
- }
836
- }
837
-
838
- /**
839
- * Retry conflict operations with modified data
840
- */
841
- private async retryConflictOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
842
- try {
843
- this.logger.info('Retrying conflict operation with modifications');
844
-
845
- // For conflicts, try to modify the operation slightly
846
- const modifiedOperation = await this.modifyOperationForConflictResolution(error);
847
- return await this.executeModifiedOperation(error, modifiedOperation);
848
- } catch (conflictError) {
849
- return {
850
- success: false,
851
- error: conflictError instanceof Error ? conflictError.message : String(conflictError)
852
- };
853
- }
854
- }
855
-
856
- /**
857
- * Retry resource operations
858
- */
859
- private async retryResourceOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
860
- try {
861
- this.logger.info('Retrying resource operation');
862
-
863
- // For resource not found, try to create the resource first
864
- const resourceCreated = await this.ensureResourceExists(error);
865
- if (resourceCreated.success) {
866
- return await this.executeOriginalOperation(error);
867
- }
868
-
869
- return {
870
- success: false,
871
- error: `Failed to create required resource: ${resourceCreated.error}`
872
- };
873
- } catch (resourceError) {
874
- return {
875
- success: false,
876
- error: resourceError instanceof Error ? resourceError.message : String(resourceError)
877
- };
878
- }
879
- }
880
-
881
- /**
882
- * Generic retry operation
883
- */
884
- private async retryGenericOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
885
- try {
886
- this.logger.info('Performing generic retry operation');
887
-
888
- // Generic retry - just execute the original operation again
889
- return await this.executeOriginalOperation(error);
890
- } catch (genericError) {
891
- return {
892
- success: false,
893
- error: genericError instanceof Error ? genericError.message : String(genericError)
894
- };
895
- }
896
- }
897
-
898
- /**
899
- * Execute the original operation that failed
900
- */
901
- private async executeOriginalOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
902
- try {
903
- // This would reconstruct and execute the original operation
904
- // For now, we'll simulate the operation based on context
905
- const operation = error.context.operation;
906
- const operationType = this.extractOperationType(operation);
907
-
908
- switch (operationType) {
909
- case 'deploy':
910
- return await this.retryDeployOperation(error);
911
- case 'search':
912
- return await this.retrySearchOperation(error);
913
- case 'create':
914
- return await this.retryCreateOperation(error);
915
- case 'update':
916
- return await this.retryUpdateOperation(error);
917
- default:
918
- // Generic HTTP request retry
919
- return await this.retryHTTPRequest(error);
920
- }
921
- } catch (executeError) {
922
- return {
923
- success: false,
924
- error: executeError instanceof Error ? executeError.message : String(executeError)
925
- };
926
- }
927
- }
928
-
929
- // Helper methods for specific operation types
930
- private async retryDeployOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
931
- try {
932
- this.logger.info('Retrying deployment operation', {
933
- component_type: error.context.component_type,
934
- component_id: error.context.component_id
935
- });
936
-
937
- // Extract deployment parameters from context
938
- const deploymentData = error.context.user_input;
939
- if (!deploymentData) {
940
- return { success: false, error: 'No deployment data available for retry' };
941
- }
942
-
943
- // Retry based on component type
944
- switch (error.context.component_type) {
945
- case 'widget':
946
- return await this.retryWidgetDeployment(deploymentData, error);
947
- case 'flow':
948
- return await this.retryFlowDeployment(deploymentData, error);
949
- case 'application':
950
- return await this.retryApplicationDeployment(deploymentData, error);
951
- case 'script':
952
- return await this.retryScriptDeployment(deploymentData, error);
953
- default:
954
- return await this.retryGenericDeployment(deploymentData, error);
955
- }
956
- } catch (deployError) {
957
- return {
958
- success: false,
959
- error: deployError instanceof Error ? deployError.message : String(deployError)
960
- };
961
- }
962
- }
963
-
964
- private async retrySearchOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
965
- try {
966
- this.logger.info('Retrying search operation', { operation: error.context.operation });
967
-
968
- const searchParams = error.context.user_input;
969
- if (!searchParams) {
970
- return { success: false, error: 'No search parameters available for retry' };
971
- }
972
-
973
- // Modify search parameters if this is a retry to avoid same failure
974
- const modifiedParams = {
975
- ...searchParams,
976
- sysparm_limit: Math.min(searchParams.sysparm_limit || 100, 50), // Reduce limit
977
- sysparm_offset: searchParams.sysparm_offset || 0,
978
- retry_attempt: (error.retry_count || 0) + 1
979
- };
980
-
981
- // Perform the search request
982
- const result = await this.client.makeRequest({
983
- method: 'GET',
984
- endpoint: searchParams.endpoint || '/api/now/table/sys_metadata',
985
- params: modifiedParams
986
- });
987
-
988
- if (result.status === 200) {
989
- return { success: true, data: result.data };
990
- } else {
991
- return { success: false, error: `Search failed with status: ${result.status}` };
992
- }
993
- } catch (searchError) {
994
- return {
995
- success: false,
996
- error: searchError instanceof Error ? searchError.message : String(searchError)
997
- };
998
- }
999
- }
1000
-
1001
- private async retryCreateOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1002
- try {
1003
- this.logger.info('Retrying create operation', {
1004
- table: error.context.component_type,
1005
- retry_count: error.retry_count
1006
- });
1007
-
1008
- const createData = error.context.user_input;
1009
- if (!createData || !createData.table) {
1010
- return { success: false, error: 'No create data or table specified for retry' };
1011
- }
1012
-
1013
- // Modify data for conflict resolution if needed
1014
- const retryData = { ...createData.data };
1015
-
1016
- // If this is a conflict retry, modify unique fields
1017
- if (error.type === ErrorType.CONFLICT_ERROR && error.retry_count > 0) {
1018
- const timestamp = Date.now();
1019
- if (retryData.name) {
1020
- retryData.name = `${retryData.name}_retry_${timestamp}`;
1021
- }
1022
- if (retryData.sys_name) {
1023
- retryData.sys_name = `${retryData.sys_name}_retry_${timestamp}`;
1024
- }
1025
- }
1026
-
1027
- // Perform the create request
1028
- const result = await this.client.createRecord(createData.table, retryData);
1029
-
1030
- if (result && (result as any).sys_id) {
1031
- this.logger.info('Create operation retry successful', { sys_id: (result as any).sys_id });
1032
- return { success: true, data: result };
1033
- } else {
1034
- return { success: false, error: 'Create operation failed - no sys_id returned' };
1035
- }
1036
- } catch (createError) {
1037
- return {
1038
- success: false,
1039
- error: createError instanceof Error ? createError.message : String(createError)
1040
- };
1041
- }
1042
- }
1043
-
1044
- private async retryUpdateOperation(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1045
- try {
1046
- this.logger.info('Retrying update operation', {
1047
- table: error.context.component_type,
1048
- record_id: error.context.component_id
1049
- });
1050
-
1051
- const updateData = error.context.user_input;
1052
- if (!updateData || !updateData.table || !updateData.record_id) {
1053
- return { success: false, error: 'No update data, table, or record ID available for retry' };
1054
- }
1055
-
1056
- // First, verify the record still exists
1057
- try {
1058
- const existingRecord = await this.client.getRecord(updateData.table, updateData.record_id);
1059
- if (!existingRecord) {
1060
- return { success: false, error: 'Record no longer exists for update' };
1061
- }
1062
- } catch (getError) {
1063
- return { success: false, error: 'Cannot verify record existence for update retry' };
1064
- }
1065
-
1066
- // Perform the update request
1067
- const result = await this.client.updateRecord(
1068
- updateData.table,
1069
- updateData.record_id,
1070
- updateData.data
1071
- );
1072
-
1073
- if (result) {
1074
- this.logger.info('Update operation retry successful', { sys_id: updateData.record_id });
1075
- return { success: true, data: result };
1076
- } else {
1077
- return { success: false, error: 'Update operation failed' };
1078
- }
1079
- } catch (updateError) {
1080
- return {
1081
- success: false,
1082
- error: updateError instanceof Error ? updateError.message : String(updateError)
1083
- };
1084
- }
1085
- }
1086
-
1087
- private async retryHTTPRequest(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1088
- try {
1089
- this.logger.info('Retrying HTTP request', { operation: error.context.operation });
1090
-
1091
- const requestData = error.context.user_input;
1092
- if (!requestData) {
1093
- return { success: false, error: 'No request data available for HTTP retry' };
1094
- }
1095
-
1096
- // Construct request with retry-safe parameters
1097
- const requestConfig = {
1098
- method: requestData.method || 'GET',
1099
- endpoint: requestData.endpoint || requestData.url,
1100
- params: requestData.params,
1101
- data: requestData.data,
1102
- timeout: Math.min((requestData.timeout || 30000) * 1.5, 60000), // Increase timeout
1103
- retry_attempt: (error.retry_count || 0) + 1
1104
- };
1105
-
1106
- // Add retry headers
1107
- const headers = {
1108
- ...requestData.headers,
1109
- 'X-Retry-Attempt': String(requestConfig.retry_attempt),
1110
- 'X-Request-ID': `retry_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
1111
- };
1112
-
1113
- const result = await this.client.makeRequest({
1114
- ...requestConfig,
1115
- headers
1116
- });
1117
-
1118
- if (result.status >= 200 && result.status < 300) {
1119
- return { success: true, data: result.data };
1120
- } else {
1121
- return {
1122
- success: false,
1123
- error: `HTTP request failed with status: ${result.status} - ${result.statusText}`
1124
- };
1125
- }
1126
- } catch (httpError) {
1127
- return {
1128
- success: false,
1129
- error: httpError instanceof Error ? httpError.message : String(httpError)
1130
- };
1131
- }
1132
- }
1133
-
1134
- // Additional helper methods
1135
-
1136
- private async retryWithNewAuthentication(error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1137
- // Retry with refreshed authentication
1138
- return await this.executeOriginalOperation(error);
1139
- }
1140
-
1141
- private async performServiceHealthCheck(): Promise<{healthy: boolean, reason?: string}> {
1142
- try {
1143
- // Basic health check - try to access ServiceNow instance
1144
- const response = await this.client.makeRequest({
1145
- method: 'GET',
1146
- endpoint: '/api/now/v2/table/sys_user',
1147
- params: { sysparm_limit: 1 }
1148
- });
1149
-
1150
- return { healthy: response.status === 200 };
1151
- } catch (healthError) {
1152
- return {
1153
- healthy: false,
1154
- reason: healthError instanceof Error ? healthError.message : 'Unknown health check error'
1155
- };
1156
- }
1157
- }
1158
-
1159
- private async modifyOperationForConflictResolution(error: FlowError): Promise<any> {
1160
- // Modify operation to resolve conflicts (e.g., add timestamp to name)
1161
- const timestamp = Date.now();
1162
- return {
1163
- ...error.context.system_state,
1164
- name: `${error.context.system_state?.name || 'item'}_${timestamp}`,
1165
- modified_for_conflict: true
1166
- };
1167
- }
1168
-
1169
- private async executeModifiedOperation(error: FlowError, modifiedData: any): Promise<{success: boolean, data?: any, error?: string}> {
1170
- try {
1171
- this.logger.info('Executing modified operation for conflict resolution', {
1172
- original_operation: error.context.operation,
1173
- modifications: modifiedData.modified_for_conflict ? 'conflict_resolution' : 'standard'
1174
- });
1175
-
1176
- // Execute the operation with modified data based on operation type
1177
- const operationType = this.extractOperationType(error.context.operation);
1178
-
1179
- switch (operationType) {
1180
- case 'create':
1181
- const table = error.context.component_type || 'sys_metadata';
1182
- const result = await this.client.createRecord(table, modifiedData);
1183
- return { success: true, data: result };
1184
-
1185
- case 'update':
1186
- if (!error.context.component_id) {
1187
- return { success: false, error: 'No component ID for update operation' };
1188
- }
1189
- const updateResult = await this.client.updateRecord(
1190
- error.context.component_type || 'sys_metadata',
1191
- error.context.component_id,
1192
- modifiedData
1193
- );
1194
- return { success: true, data: updateResult };
1195
-
1196
- case 'deploy':
1197
- // For deployment operations, use the deployment data structure
1198
- return await this.retryGenericDeployment(modifiedData, error);
1199
-
1200
- default:
1201
- return { success: false, error: 'Unsupported operation type for modification' };
1202
- }
1203
- } catch (executeError) {
1204
- return {
1205
- success: false,
1206
- error: executeError instanceof Error ? executeError.message : String(executeError)
1207
- };
1208
- }
1209
- }
1210
-
1211
- private async ensureResourceExists(error: FlowError): Promise<{success: boolean, error?: string}> {
1212
- try {
1213
- this.logger.info('Attempting to create missing resource', {
1214
- resource_type: error.context.component_type,
1215
- resource_id: error.context.component_id
1216
- });
1217
-
1218
- // Extract resource information from context
1219
- const resourceData = error.context.user_input;
1220
- if (!resourceData) {
1221
- return { success: false, error: 'No resource data available for creation' };
1222
- }
1223
-
1224
- // Determine resource type and create accordingly
1225
- switch (error.context.component_type) {
1226
- case 'table':
1227
- return await this.createMissingTable(resourceData);
1228
- case 'field':
1229
- return await this.createMissingField(resourceData);
1230
- case 'user':
1231
- return await this.createMissingUser(resourceData);
1232
- case 'group':
1233
- return await this.createMissingGroup(resourceData);
1234
- default:
1235
- // Generic resource creation
1236
- return await this.createGenericResource(resourceData);
1237
- }
1238
- } catch (resourceError) {
1239
- return {
1240
- success: false,
1241
- error: resourceError instanceof Error ? resourceError.message : String(resourceError)
1242
- };
1243
- }
1244
- }
1245
-
1246
- private async delay(ms: number): Promise<void> {
1247
- return new Promise(resolve => setTimeout(resolve, ms));
1248
- }
1249
-
1250
- /**
1251
- * Create missing resource
1252
- */
1253
- private async createMissingResource(error: FlowError): Promise<{
1254
- success: boolean;
1255
- final_state?: any;
1256
- }> {
1257
- // Implementation depends on the specific resource type
1258
- return { success: false };
1259
- }
1260
-
1261
- /**
1262
- * Use fallback values
1263
- */
1264
- private async useFallbackValues(error: FlowError): Promise<{
1265
- success: boolean;
1266
- final_state?: any;
1267
- }> {
1268
- // Implementation depends on the specific validation error
1269
- return { success: false };
1270
- }
1271
-
1272
- /**
1273
- * Use cached data
1274
- */
1275
- private async useCachedData(error: FlowError): Promise<{
1276
- success: boolean;
1277
- final_state?: any;
1278
- }> {
1279
- // Implementation depends on the specific cached data available
1280
- return { success: false };
1281
- }
1282
-
1283
- /**
1284
- * Generate user-friendly error message
1285
- */
1286
- private generateUserMessage(error: FlowError, result: ErrorHandlingResult): string {
1287
- if (result.recovered) {
1288
- return `The operation encountered an issue but was automatically recovered. ${error.message}`;
1289
- }
1290
-
1291
- switch (error.type) {
1292
- case ErrorType.AUTHENTICATION_ERROR:
1293
- return 'Authentication failed. Please check your credentials and try again.';
1294
- case ErrorType.AUTHORIZATION_ERROR:
1295
- return 'You do not have permission to perform this operation.';
1296
- case ErrorType.VALIDATION_ERROR:
1297
- return 'The provided data is invalid. Please check your inputs and try again.';
1298
- case ErrorType.NETWORK_ERROR:
1299
- return 'Network connection failed. Please check your connection and try again.';
1300
- case ErrorType.RESOURCE_NOT_FOUND:
1301
- return 'The requested resource was not found. It may have been deleted or moved.';
1302
- default:
1303
- return `An error occurred: ${error.message}`;
1304
- }
1305
- }
1306
-
1307
- /**
1308
- * Generate technical details
1309
- */
1310
- private generateTechnicalDetails(error: FlowError, result: ErrorHandlingResult): string {
1311
- const details = {
1312
- error_type: error.type,
1313
- severity: error.severity,
1314
- message: error.message,
1315
- context: error.context,
1316
- actions_taken: result.actions_taken.map(a => a.type),
1317
- recovered: result.recovered,
1318
- timestamp: new Date().toISOString()
1319
- };
1320
-
1321
- return JSON.stringify(details, null, 2);
1322
- }
1323
-
1324
- /**
1325
- * Get max retries for error type
1326
- */
1327
- private getMaxRetries(errorType: ErrorType): number {
1328
- switch (errorType) {
1329
- case ErrorType.NETWORK_ERROR:
1330
- case ErrorType.TIMEOUT_ERROR:
1331
- return 3;
1332
- case ErrorType.RATE_LIMIT_ERROR:
1333
- return 2;
1334
- case ErrorType.SERVICE_UNAVAILABLE:
1335
- return 5;
1336
- default:
1337
- return 1;
1338
- }
1339
- }
1340
-
1341
- /**
1342
- * Capture state snapshot
1343
- */
1344
- private captureStateSnapshot(transaction: TransactionState): any {
1345
- return {
1346
- transaction_id: transaction.id,
1347
- flow_id: transaction.flow_id,
1348
- operation_count: transaction.operations.length,
1349
- status: transaction.status,
1350
- timestamp: new Date().toISOString()
1351
- };
1352
- }
1353
-
1354
- /**
1355
- * Generate transaction ID
1356
- */
1357
- private generateTransactionId(): string {
1358
- return `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1359
- }
1360
-
1361
- /**
1362
- * Generate operation ID
1363
- */
1364
- private generateOperationId(): string {
1365
- return `op_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1366
- }
1367
-
1368
- /**
1369
- * Generate checkpoint ID
1370
- */
1371
- private generateCheckpointId(): string {
1372
- return `cp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1373
- }
1374
-
1375
- /**
1376
- * Get error statistics
1377
- */
1378
- getErrorStatistics(): {
1379
- total_errors: number;
1380
- errors_by_type: Record<string, number>;
1381
- errors_by_severity: Record<string, number>;
1382
- recovery_rate: number;
1383
- } {
1384
- const stats = {
1385
- total_errors: this.errorHistory.length,
1386
- errors_by_type: {} as Record<string, number>,
1387
- errors_by_severity: {} as Record<string, number>,
1388
- recovery_rate: 0
1389
- };
1390
-
1391
- let recoveredCount = 0;
1392
-
1393
- for (const error of this.errorHistory) {
1394
- // Count by type
1395
- stats.errors_by_type[error.type] = (stats.errors_by_type[error.type] || 0) + 1;
1396
-
1397
- // Count by severity
1398
- stats.errors_by_severity[error.severity] = (stats.errors_by_severity[error.severity] || 0) + 1;
1399
-
1400
- // Count recovered errors
1401
- if (error.recoverable) {
1402
- recoveredCount++;
1403
- }
1404
- }
1405
-
1406
- stats.recovery_rate = stats.total_errors > 0 ? recoveredCount / stats.total_errors : 0;
1407
-
1408
- return stats;
1409
- }
1410
-
1411
- /**
1412
- * Get active transactions
1413
- */
1414
- getActiveTransactions(): TransactionState[] {
1415
- return Array.from(this.transactions.values())
1416
- .filter(t => t.status === 'active');
1417
- }
1418
-
1419
- // =====================================
1420
- // DEPLOYMENT RETRY METHODS
1421
- // =====================================
1422
-
1423
- /**
1424
- * Retry widget deployment
1425
- */
1426
- private async retryWidgetDeployment(deploymentData: any, error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1427
- try {
1428
- this.logger.info('Retrying widget deployment', { widget_name: deploymentData.name });
1429
-
1430
- // Construct widget data with retry modifications
1431
- const widgetData = {
1432
- name: deploymentData.name,
1433
- title: deploymentData.title,
1434
- template: deploymentData.template,
1435
- server_script: deploymentData.server_script,
1436
- client_script: deploymentData.client_script,
1437
- css: deploymentData.css,
1438
- category: deploymentData.category || 'custom',
1439
- roles: deploymentData.roles || '',
1440
- public: deploymentData.public !== false
1441
- };
1442
-
1443
- // If this is a conflict retry, modify the name
1444
- if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
1445
- widgetData.name = `${widgetData.name}_retry_${Date.now()}`;
1446
- }
1447
-
1448
- const result = await this.client.createRecord('sp_widget', widgetData);
1449
- return { success: true, data: result };
1450
- } catch (widgetError) {
1451
- return {
1452
- success: false,
1453
- error: widgetError instanceof Error ? widgetError.message : String(widgetError)
1454
- };
1455
- }
1456
- }
1457
-
1458
- /**
1459
- * Retry flow deployment
1460
- */
1461
- private async retryFlowDeployment(deploymentData: any, error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1462
- try {
1463
- this.logger.info('Retrying flow deployment', { flow_name: deploymentData.name });
1464
-
1465
- const flowData = {
1466
- name: deploymentData.name,
1467
- description: deploymentData.description,
1468
- active: deploymentData.active !== false,
1469
- flow_designer: deploymentData.flow_definition || '{}',
1470
- trigger_conditions: deploymentData.trigger_conditions,
1471
- table: deploymentData.table
1472
- };
1473
-
1474
- // Handle conflict retry
1475
- if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
1476
- flowData.name = `${flowData.name}_retry_${Date.now()}`;
1477
- }
1478
-
1479
- const result = await this.client.createRecord('sys_hub_flow', flowData);
1480
- return { success: true, data: result };
1481
- } catch (flowError) {
1482
- return {
1483
- success: false,
1484
- error: flowError instanceof Error ? flowError.message : String(flowError)
1485
- };
1486
- }
1487
- }
1488
-
1489
- /**
1490
- * Retry application deployment
1491
- */
1492
- private async retryApplicationDeployment(deploymentData: any, error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1493
- try {
1494
- this.logger.info('Retrying application deployment', { app_name: deploymentData.name });
1495
-
1496
- const appData = {
1497
- name: deploymentData.name,
1498
- short_description: deploymentData.short_description,
1499
- description: deploymentData.description,
1500
- version: deploymentData.version || '1.0.0',
1501
- vendor: deploymentData.vendor || 'Custom',
1502
- vendor_prefix: deploymentData.vendor_prefix,
1503
- active: deploymentData.active !== false
1504
- };
1505
-
1506
- // Handle conflict retry
1507
- if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
1508
- appData.name = `${appData.name}_retry_${Date.now()}`;
1509
- }
1510
-
1511
- const result = await this.client.createRecord('sys_app', appData);
1512
- return { success: true, data: result };
1513
- } catch (appError) {
1514
- return {
1515
- success: false,
1516
- error: appError instanceof Error ? appError.message : String(appError)
1517
- };
1518
- }
1519
- }
1520
-
1521
- /**
1522
- * Retry script deployment
1523
- */
1524
- private async retryScriptDeployment(deploymentData: any, error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1525
- try {
1526
- this.logger.info('Retrying script deployment', { script_name: deploymentData.name });
1527
-
1528
- const scriptData = {
1529
- name: deploymentData.name,
1530
- script: deploymentData.script,
1531
- description: deploymentData.description,
1532
- api_name: deploymentData.api_name,
1533
- client_callable: deploymentData.client_callable || false,
1534
- active: deploymentData.active !== false
1535
- };
1536
-
1537
- // Handle conflict retry
1538
- if (error.type === ErrorType.CONFLICT_ERROR && (error.retry_count || 0) > 0) {
1539
- scriptData.name = `${scriptData.name}_retry_${Date.now()}`;
1540
- }
1541
-
1542
- const result = await this.client.createRecord('sys_script_include', scriptData);
1543
- return { success: true, data: result };
1544
- } catch (scriptError) {
1545
- return {
1546
- success: false,
1547
- error: scriptError instanceof Error ? scriptError.message : String(scriptError)
1548
- };
1549
- }
1550
- }
1551
-
1552
- /**
1553
- * Generic deployment retry
1554
- */
1555
- private async retryGenericDeployment(deploymentData: any, error: FlowError): Promise<{success: boolean, data?: any, error?: string}> {
1556
- try {
1557
- this.logger.info('Retrying generic deployment', {
1558
- component_type: error.context.component_type,
1559
- data_keys: Object.keys(deploymentData)
1560
- });
1561
-
1562
- // Determine table from component type or data
1563
- const table = deploymentData.table ||
1564
- this.getTableFromComponentType(error.context.component_type) ||
1565
- 'sys_metadata';
1566
-
1567
- const result = await this.client.createRecord(table, deploymentData);
1568
- return { success: true, data: result };
1569
- } catch (genericError) {
1570
- return {
1571
- success: false,
1572
- error: genericError instanceof Error ? genericError.message : String(genericError)
1573
- };
1574
- }
1575
- }
1576
-
1577
- // =====================================
1578
- // RESOURCE CREATION METHODS
1579
- // =====================================
1580
-
1581
- /**
1582
- * Create missing table
1583
- */
1584
- private async createMissingTable(resourceData: any): Promise<{success: boolean, error?: string}> {
1585
- try {
1586
- const tableData = {
1587
- name: resourceData.table_name || resourceData.name,
1588
- label: resourceData.label || resourceData.table_name,
1589
- super_class: resourceData.super_class || 'sys_metadata',
1590
- is_extendable: resourceData.is_extendable !== false
1591
- };
1592
-
1593
- await this.client.createRecord('sys_db_object', tableData);
1594
- return { success: true };
1595
- } catch (error) {
1596
- return { success: false, error: error instanceof Error ? error.message : String(error) };
1597
- }
1598
- }
1599
-
1600
- /**
1601
- * Create missing field
1602
- */
1603
- private async createMissingField(resourceData: any): Promise<{success: boolean, error?: string}> {
1604
- try {
1605
- const fieldData = {
1606
- table: resourceData.table,
1607
- column_name: resourceData.field_name,
1608
- column_label: resourceData.label || resourceData.field_name,
1609
- internal_type: resourceData.type || 'string',
1610
- max_length: resourceData.max_length || 40
1611
- };
1612
-
1613
- await this.client.createRecord('sys_dictionary', fieldData);
1614
- return { success: true };
1615
- } catch (error) {
1616
- return { success: false, error: error instanceof Error ? error.message : String(error) };
1617
- }
1618
- }
1619
-
1620
- /**
1621
- * Create missing user
1622
- */
1623
- private async createMissingUser(resourceData: any): Promise<{success: boolean, error?: string}> {
1624
- try {
1625
- const userData = {
1626
- user_name: resourceData.user_name,
1627
- first_name: resourceData.first_name || 'Unknown',
1628
- last_name: resourceData.last_name || 'User',
1629
- email: resourceData.email,
1630
- active: resourceData.active !== false
1631
- };
1632
-
1633
- await this.client.createRecord('sys_user', userData);
1634
- return { success: true };
1635
- } catch (error) {
1636
- return { success: false, error: error instanceof Error ? error.message : String(error) };
1637
- }
1638
- }
1639
-
1640
- /**
1641
- * Create missing group
1642
- */
1643
- private async createMissingGroup(resourceData: any): Promise<{success: boolean, error?: string}> {
1644
- try {
1645
- const groupData = {
1646
- name: resourceData.group_name || resourceData.name,
1647
- description: resourceData.description || `Group ${resourceData.name}`,
1648
- active: resourceData.active !== false
1649
- };
1650
-
1651
- await this.client.createRecord('sys_user_group', groupData);
1652
- return { success: true };
1653
- } catch (error) {
1654
- return { success: false, error: error instanceof Error ? error.message : String(error) };
1655
- }
1656
- }
1657
-
1658
- /**
1659
- * Create generic resource
1660
- */
1661
- private async createGenericResource(resourceData: any): Promise<{success: boolean, error?: string}> {
1662
- try {
1663
- const table = resourceData.table || 'sys_metadata';
1664
- await this.client.createRecord(table, resourceData);
1665
- return { success: true };
1666
- } catch (error) {
1667
- return { success: false, error: error instanceof Error ? error.message : String(error) };
1668
- }
1669
- }
1670
-
1671
- // =====================================
1672
- // UTILITY METHODS
1673
- // =====================================
1674
-
1675
- /**
1676
- * Get ServiceNow table name from component type
1677
- */
1678
- private getTableFromComponentType(componentType?: string): string | null {
1679
- if (!componentType) return null;
1680
-
1681
- const tableMap: Record<string, string> = {
1682
- 'widget': 'sp_widget',
1683
- 'flow': 'sys_hub_flow',
1684
- 'application': 'sys_app',
1685
- 'script': 'sys_script_include',
1686
- 'business_rule': 'sys_script',
1687
- 'client_script': 'sys_script_client',
1688
- 'ui_policy': 'sys_ui_policy',
1689
- 'ui_action': 'sys_ui_action',
1690
- 'table': 'sys_db_object',
1691
- 'field': 'sys_dictionary',
1692
- 'user': 'sys_user',
1693
- 'group': 'sys_user_group'
1694
- };
1695
-
1696
- return tableMap[componentType.toLowerCase()] || null;
1697
- }
1698
-
1699
- /**
1700
- * Enhanced operation type extraction
1701
- */
1702
- private extractOperationType(operation: string): string {
1703
- const op = operation.toLowerCase();
1704
-
1705
- // More specific operation detection
1706
- if (op.includes('deploy') || op.includes('create_') || op.includes('snow_deploy')) return 'deploy';
1707
- if (op.includes('search') || op.includes('find') || op.includes('query') || op.includes('discover')) return 'search';
1708
- if (op.includes('create') || op.includes('insert') || op.includes('add')) return 'create';
1709
- if (op.includes('update') || op.includes('modify') || op.includes('edit') || op.includes('patch')) return 'update';
1710
- if (op.includes('delete') || op.includes('remove')) return 'delete';
1711
- if (op.includes('get') || op.includes('fetch') || op.includes('retrieve')) return 'read';
1712
- if (op.includes('authenticate') || op.includes('login') || op.includes('auth')) return 'auth';
1713
- if (op.includes('http') || op.includes('request') || op.includes('api')) return 'http';
1714
-
1715
- return 'generic';
1716
- }
1717
-
1718
- /**
1719
- * Check if error type is retryable
1720
- */
1721
- isRetryableError(errorType: ErrorType): boolean {
1722
- const retryableErrors = [
1723
- ErrorType.NETWORK_ERROR,
1724
- ErrorType.TIMEOUT_ERROR,
1725
- ErrorType.RATE_LIMIT_ERROR,
1726
- ErrorType.SERVICE_UNAVAILABLE,
1727
- ErrorType.API_ERROR,
1728
- ErrorType.CONFLICT_ERROR
1729
- ];
1730
-
1731
- return retryableErrors.includes(errorType);
1732
- }
1733
-
1734
- /**
1735
- * Get circuit breaker status for operation
1736
- */
1737
- getCircuitBreakerStatus(operation: string): {
1738
- isOpen: boolean;
1739
- failureCount: number;
1740
- lastFailureTime?: Date;
1741
- } {
1742
- // Simple circuit breaker implementation
1743
- const recentErrors = this.errorHistory
1744
- .filter(error =>
1745
- error.context.operation === operation &&
1746
- error.context.timestamp.getTime() > Date.now() - 300000 // Last 5 minutes
1747
- );
1748
-
1749
- const failureCount = recentErrors.length;
1750
- const isOpen = failureCount >= 5; // Open circuit after 5 failures
1751
-
1752
- return {
1753
- isOpen,
1754
- failureCount,
1755
- lastFailureTime: recentErrors.length > 0 ? recentErrors[recentErrors.length - 1].context.timestamp : undefined
1756
- };
1757
- }
1758
- }