@vfarcic/dot-ai 0.124.0 → 0.126.0

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 (79) hide show
  1. package/assets/project-setup/templates/.github/CODEOWNERS.hbs +25 -0
  2. package/assets/project-setup/templates/.github/FUNDING.yml.hbs +35 -0
  3. package/assets/project-setup/templates/.github/ISSUE_TEMPLATE/bug_report.yml.hbs +175 -0
  4. package/assets/project-setup/templates/.github/ISSUE_TEMPLATE/config.yml.hbs +32 -0
  5. package/assets/project-setup/templates/.github/ISSUE_TEMPLATE/feature_request.yml.hbs +134 -0
  6. package/assets/project-setup/templates/.github/PULL_REQUEST_TEMPLATE.md.hbs +172 -0
  7. package/assets/project-setup/templates/.github/labeler.yml.hbs +123 -0
  8. package/assets/project-setup/templates/.github/release.yml.hbs +51 -0
  9. package/assets/project-setup/templates/.github/workflows/labeler.yml.hbs +21 -0
  10. package/assets/project-setup/templates/.github/workflows/scorecard.yml.hbs +71 -0
  11. package/assets/project-setup/templates/.github/workflows/stale.yml.hbs +57 -0
  12. package/dist/core/ai-provider-factory.d.ts.map +1 -1
  13. package/dist/core/ai-provider-factory.js +22 -5
  14. package/dist/core/ai-provider.interface.d.ts +16 -0
  15. package/dist/core/ai-provider.interface.d.ts.map +1 -1
  16. package/dist/core/capability-scan-workflow.d.ts +4 -9
  17. package/dist/core/capability-scan-workflow.d.ts.map +1 -1
  18. package/dist/core/capability-scan-workflow.js +203 -456
  19. package/dist/core/discovery.d.ts +6 -0
  20. package/dist/core/discovery.d.ts.map +1 -1
  21. package/dist/core/discovery.js +39 -3
  22. package/dist/core/embedding-service.d.ts +9 -36
  23. package/dist/core/embedding-service.d.ts.map +1 -1
  24. package/dist/core/embedding-service.js +137 -246
  25. package/dist/core/index.d.ts +1 -1
  26. package/dist/core/index.js +2 -2
  27. package/dist/core/kubernetes-utils.d.ts +1 -0
  28. package/dist/core/kubernetes-utils.d.ts.map +1 -1
  29. package/dist/core/kubernetes-utils.js +53 -48
  30. package/dist/core/model-config.d.ts +2 -0
  31. package/dist/core/model-config.d.ts.map +1 -1
  32. package/dist/core/model-config.js +3 -1
  33. package/dist/core/providers/anthropic-provider.d.ts.map +1 -1
  34. package/dist/core/providers/anthropic-provider.js +352 -282
  35. package/dist/core/providers/provider-debug-utils.d.ts +4 -0
  36. package/dist/core/providers/provider-debug-utils.d.ts.map +1 -1
  37. package/dist/core/providers/provider-debug-utils.js +25 -3
  38. package/dist/core/providers/vercel-provider.d.ts +1 -0
  39. package/dist/core/providers/vercel-provider.d.ts.map +1 -1
  40. package/dist/core/providers/vercel-provider.js +412 -345
  41. package/dist/core/tracing/ai-tracing.d.ts +80 -0
  42. package/dist/core/tracing/ai-tracing.d.ts.map +1 -0
  43. package/dist/core/tracing/ai-tracing.js +122 -0
  44. package/dist/core/tracing/config.d.ts +15 -0
  45. package/dist/core/tracing/config.d.ts.map +1 -0
  46. package/dist/core/tracing/config.js +133 -0
  47. package/dist/core/tracing/http-tracing.d.ts +28 -0
  48. package/dist/core/tracing/http-tracing.d.ts.map +1 -0
  49. package/dist/core/tracing/http-tracing.js +119 -0
  50. package/dist/core/tracing/index.d.ts +14 -0
  51. package/dist/core/tracing/index.d.ts.map +1 -0
  52. package/dist/core/tracing/index.js +40 -0
  53. package/dist/core/tracing/k8s-tracing.d.ts +57 -0
  54. package/dist/core/tracing/k8s-tracing.d.ts.map +1 -0
  55. package/dist/core/tracing/k8s-tracing.js +155 -0
  56. package/dist/core/tracing/qdrant-tracing.d.ts +68 -0
  57. package/dist/core/tracing/qdrant-tracing.d.ts.map +1 -0
  58. package/dist/core/tracing/qdrant-tracing.js +102 -0
  59. package/dist/core/tracing/tool-tracing.d.ts +31 -0
  60. package/dist/core/tracing/tool-tracing.d.ts.map +1 -0
  61. package/dist/core/tracing/tool-tracing.js +76 -0
  62. package/dist/core/tracing/tracer.d.ts +21 -0
  63. package/dist/core/tracing/tracer.d.ts.map +1 -0
  64. package/dist/core/tracing/tracer.js +215 -0
  65. package/dist/core/tracing/types.d.ts +86 -0
  66. package/dist/core/tracing/types.d.ts.map +1 -0
  67. package/dist/core/tracing/types.js +41 -0
  68. package/dist/core/unified-creation-session.js +1 -1
  69. package/dist/core/vector-db-service.d.ts.map +1 -1
  70. package/dist/core/vector-db-service.js +238 -163
  71. package/dist/interfaces/mcp.d.ts.map +1 -1
  72. package/dist/interfaces/mcp.js +71 -43
  73. package/dist/mcp/server.js +12 -2
  74. package/dist/tools/organizational-data.d.ts.map +1 -1
  75. package/dist/tools/organizational-data.js +2 -4
  76. package/dist/tools/version.d.ts +12 -1
  77. package/dist/tools/version.d.ts.map +1 -1
  78. package/dist/tools/version.js +24 -4
  79. package/package.json +11 -1
@@ -8,7 +8,6 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.handleResourceSelection = handleResourceSelection;
10
10
  exports.handleResourceSpecification = handleResourceSpecification;
11
- exports.handleProcessingMode = handleProcessingMode;
12
11
  exports.handleScanning = handleScanning;
13
12
  const discovery_1 = require("./discovery");
14
13
  const capabilities_1 = require("./capabilities");
@@ -36,7 +35,7 @@ function createResourceDefinitionErrorMessage(resourceName, error) {
36
35
  /**
37
36
  * Handle resource selection step
38
37
  */
39
- async function handleResourceSelection(session, args, _logger, _requestId, parseNumericResponse, transitionCapabilitySession) {
38
+ async function handleResourceSelection(session, args, logger, requestId, capabilityService, parseNumericResponse, transitionCapabilitySession, cleanupCapabilitySession, createCapabilityScanCompletionResponse, handleScanningFn) {
40
39
  if (!args.response) {
41
40
  // Show initial resource selection prompt
42
41
  return {
@@ -83,49 +82,13 @@ async function handleResourceSelection(session, args, _logger, _requestId, parse
83
82
  // Process user response
84
83
  const normalizedResponse = parseNumericResponse(args.response, ['all', 'specific']);
85
84
  if (normalizedResponse === 'all') {
86
- // Transition to processing mode for all resources
87
- transitionCapabilitySession(session, 'processing-mode', { selectedResources: 'all' }, args);
88
- return {
89
- success: true,
90
- operation: 'scan',
91
- dataType: 'capabilities',
92
- // CRITICAL: Put required parameters at top level for maximum visibility
93
- REQUIRED_NEXT_CALL: {
94
- tool: 'dot-ai:manageOrgData',
95
- parameters: {
96
- dataType: 'capabilities',
97
- operation: 'scan',
98
- sessionId: session.sessionId,
99
- step: 'processing-mode', // MANDATORY PARAMETER
100
- response: 'user_choice_here' // Replace with actual user choice
101
- },
102
- note: 'The step parameter is MANDATORY when sessionId is provided'
103
- },
104
- workflow: {
105
- step: 'processing-mode',
106
- question: 'Processing mode: auto (batch process) or manual (review each)?',
107
- options: [
108
- { number: 1, value: 'auto', display: '1. auto - Batch process automatically' },
109
- { number: 2, value: 'manual', display: '2. manual - Review each step' }
110
- ],
111
- sessionId: session.sessionId,
112
- selectedResources: 'all',
113
- instruction: 'IMPORTANT: You MUST ask the user to make a choice. Do NOT automatically select a processing mode.',
114
- userPrompt: 'How would you like to process the resources?',
115
- clientInstructions: {
116
- behavior: 'interactive',
117
- requirement: 'Ask user to choose processing mode',
118
- prohibit: 'Do not auto-select processing mode',
119
- nextStep: `Call with step='processing-mode', sessionId='${session.sessionId}', and response parameter containing the semantic value (auto or manual)`,
120
- responseFormat: 'Convert user input to semantic values: 1→auto, 2→manual, or pass through semantic words directly',
121
- requiredParameters: {
122
- step: 'processing-mode',
123
- sessionId: session.sessionId,
124
- response: 'user choice (auto or manual)'
125
- }
126
- }
127
- }
128
- };
85
+ // Transition directly to scanning (auto mode only - manual mode removed)
86
+ transitionCapabilitySession(session, 'scanning', {
87
+ selectedResources: 'all',
88
+ currentResourceIndex: 0 // Start with first resource
89
+ }, args);
90
+ // Begin actual capability scanning and return completion summary
91
+ return await handleScanningFn(session, { ...args, response: undefined }, logger, requestId, capabilityService, parseNumericResponse, transitionCapabilitySession, cleanupCapabilitySession, createCapabilityScanCompletionResponse);
129
92
  }
130
93
  if (normalizedResponse === 'specific') {
131
94
  // Transition to resource specification
@@ -190,7 +153,7 @@ async function handleResourceSelection(session, args, _logger, _requestId, parse
190
153
  /**
191
154
  * Handle resource specification step
192
155
  */
193
- async function handleResourceSpecification(session, args, _logger, _requestId, transitionCapabilitySession) {
156
+ async function handleResourceSpecification(session, args, logger, requestId, capabilityService, parseNumericResponse, transitionCapabilitySession, cleanupCapabilitySession, createCapabilityScanCompletionResponse, handleScanningFn) {
194
157
  if (!args.resourceList) {
195
158
  return {
196
159
  success: false,
@@ -218,176 +181,49 @@ async function handleResourceSpecification(session, args, _logger, _requestId, t
218
181
  }
219
182
  };
220
183
  }
221
- // Transition to processing mode for specific resources
222
- transitionCapabilitySession(session, 'processing-mode', {
223
- selectedResources: resources,
224
- resourceList: args.resourceList
225
- }, args);
226
- return {
227
- success: true,
228
- operation: 'scan',
229
- dataType: 'capabilities',
230
- // CRITICAL: Put required parameters at top level for maximum visibility
231
- REQUIRED_NEXT_CALL: {
232
- tool: 'dot-ai:manageOrgData',
233
- parameters: {
234
- dataType: 'capabilities',
235
- operation: 'scan',
236
- sessionId: session.sessionId,
237
- step: 'processing-mode', // MANDATORY PARAMETER
238
- response: 'user_choice_here' // Replace with actual user choice
239
- },
240
- note: 'The step parameter is MANDATORY when sessionId is provided'
241
- },
242
- workflow: {
243
- step: 'processing-mode',
244
- question: `Processing mode for ${resources.length} selected resources: auto (batch process) or manual (review each)?`,
245
- options: [
246
- { number: 1, value: 'auto', display: '1. auto - Batch process automatically' },
247
- { number: 2, value: 'manual', display: '2. manual - Review each step' }
248
- ],
249
- sessionId: session.sessionId,
250
- selectedResources: resources,
251
- instruction: 'IMPORTANT: You MUST ask the user to choose processing mode for the specified resources.',
252
- userPrompt: `How would you like to process these ${resources.length} resources?`,
253
- clientInstructions: {
254
- behavior: 'interactive',
255
- requirement: 'Ask user to choose processing mode for specific resources',
256
- context: `Processing ${resources.length} user-specified resources: ${resources.join(', ')}`,
257
- prohibit: 'Do not auto-select processing mode',
258
- nextStep: `Call with step='processing-mode', sessionId='${session.sessionId}', and response parameter containing the semantic value (auto or manual)`,
259
- responseFormat: 'Convert user input to semantic values: 1→auto, 2→manual, or pass through semantic words directly',
260
- requiredParameters: {
261
- step: 'processing-mode',
262
- sessionId: session.sessionId,
263
- response: 'user choice (auto or manual)'
264
- }
265
- }
266
- }
267
- };
268
- }
269
- /**
270
- * Handle processing mode step
271
- */
272
- async function handleProcessingMode(session, args, logger, requestId, capabilityService, parseNumericResponse, transitionCapabilitySession, cleanupCapabilitySession, createCapabilityScanCompletionResponse, handleScanningFn) {
273
- if (!args.response) {
274
- return {
275
- success: false,
276
- operation: 'scan',
277
- dataType: 'capabilities',
278
- error: {
279
- message: 'Missing processing mode response',
280
- details: 'Expected response parameter with processing mode choice',
281
- currentStep: session.currentStep,
282
- expectedCall: `Call with step='processing-mode' and response parameter (auto or manual)`
283
- }
284
- };
285
- }
286
- // Process user response
287
- const processingModeResponse = parseNumericResponse(args.response, ['auto', 'manual']);
288
- if (processingModeResponse !== 'auto' && processingModeResponse !== 'manual') {
289
- return {
290
- success: false,
291
- operation: 'scan',
292
- dataType: 'capabilities',
293
- error: {
294
- message: 'Invalid processing mode response',
295
- details: `Expected 'auto' or 'manual', got: ${args.response}`,
296
- currentStep: session.currentStep
297
- }
298
- };
299
- }
300
- // Transition to scanning with processing mode and initialize resource tracking
184
+ // Transition directly to scanning (auto mode only - manual mode removed)
301
185
  transitionCapabilitySession(session, 'scanning', {
302
- processingMode: processingModeResponse,
186
+ selectedResources: resources,
187
+ resourceList: args.resourceList,
303
188
  currentResourceIndex: 0 // Start with first resource
304
189
  }, args);
305
- // Begin actual capability scanning - clear response from previous step
190
+ // Begin actual capability scanning and return completion summary
306
191
  return await handleScanningFn(session, { ...args, response: undefined }, logger, requestId, capabilityService, parseNumericResponse, transitionCapabilitySession, cleanupCapabilitySession, createCapabilityScanCompletionResponse);
307
192
  }
308
193
  /**
309
- * Handle scanning step (actual capability analysis)
194
+ * Handle scanning step (actual capability analysis - auto mode only)
310
195
  */
311
196
  async function handleScanning(session, args, logger, requestId, capabilityService, parseNumericResponse, transitionCapabilitySession, cleanupCapabilitySession, createCapabilityScanCompletionResponse) {
312
197
  try {
313
- // If this is a response to a manual mode preview, handle it first
314
- if (session.processingMode === 'manual' && args.response) {
315
- const userResponse = parseNumericResponse(args.response, ['yes', 'no', 'stop']);
316
- if (userResponse === 'stop') {
317
- // User wants to stop scanning
318
- transitionCapabilitySession(session, 'complete', {}, args);
319
- cleanupCapabilitySession(session, args, logger, requestId);
320
- const currentIndex = session.currentResourceIndex || 0;
321
- const totalResources = Array.isArray(session.selectedResources) ? session.selectedResources.length : 1;
322
- return createCapabilityScanCompletionResponse(session.sessionId, totalResources, currentIndex, // Resources processed so far
323
- 0, 'stopped interactively', 'manual', true // stopped = true
324
- );
325
- }
326
- if (userResponse === 'yes' || userResponse === 'no') {
327
- // TODO: If 'yes', store the capability in Vector DB (Milestone 2)
328
- // For now, just log the decision
329
- logger.info(`User ${userResponse === 'yes' ? 'accepted' : 'skipped'} capability for resource`, {
330
- requestId,
331
- sessionId: session.sessionId,
332
- resourceIndex: session.currentResourceIndex,
333
- decision: userResponse
334
- });
335
- // Move to the next resource
336
- const nextIndex = (session.currentResourceIndex || 0) + 1;
337
- transitionCapabilitySession(session, 'scanning', { currentResourceIndex: nextIndex }, args);
338
- // Continue processing (will handle the next resource or complete if done)
339
- return await handleScanning(session, { ...args, response: undefined }, logger, requestId, capabilityService, parseNumericResponse, transitionCapabilitySession, cleanupCapabilitySession, createCapabilityScanCompletionResponse);
340
- }
341
- // Invalid response
342
- return {
343
- success: false,
344
- operation: 'scan',
345
- dataType: 'capabilities',
346
- error: {
347
- message: 'Invalid response to capability preview',
348
- details: `Expected 'yes', 'no', or 'stop', got: ${args.response}`,
349
- currentStep: session.currentStep
350
- }
351
- };
352
- }
353
- // Import capability engine
354
- // Already imported at top of file
355
- // Validate AI provider - skip in test environment
356
- const isTestEnvironment = process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID;
357
- if (!isTestEnvironment) {
358
- try {
359
- const aiProvider = (0, ai_provider_factory_1.createAIProvider)();
360
- if (!aiProvider.isInitialized()) {
361
- return {
362
- success: false,
363
- operation: 'scan',
364
- dataType: 'capabilities',
365
- error: {
366
- message: 'AI provider API key required for capability inference',
367
- details: 'Configure AI provider credentials to enable AI-powered capability analysis'
368
- }
369
- };
370
- }
371
- }
372
- catch (error) {
198
+ // Validate and initialize AI provider
199
+ let aiProvider;
200
+ try {
201
+ aiProvider = (0, ai_provider_factory_1.createAIProvider)();
202
+ if (!aiProvider.isInitialized()) {
373
203
  return {
374
204
  success: false,
375
205
  operation: 'scan',
376
206
  dataType: 'capabilities',
377
207
  error: {
378
- message: 'AI provider initialization failed',
379
- details: error instanceof Error ? error.message : 'Unknown error'
208
+ message: 'AI provider API key required for capability inference',
209
+ details: 'Configure AI provider credentials to enable AI-powered capability analysis'
380
210
  }
381
211
  };
382
212
  }
383
213
  }
214
+ catch (error) {
215
+ return {
216
+ success: false,
217
+ operation: 'scan',
218
+ dataType: 'capabilities',
219
+ error: {
220
+ message: 'AI provider initialization failed',
221
+ details: error instanceof Error ? error.message : 'Unknown error'
222
+ }
223
+ };
224
+ }
384
225
  // Initialize capability engine
385
- const aiProvider = (0, ai_provider_factory_1.createAIProvider)();
386
226
  const engine = new capabilities_1.CapabilityInferenceEngine(aiProvider, logger);
387
- // Get the resource to analyze
388
- let resourceName;
389
- let currentIndex;
390
- let totalResources;
391
227
  if (session.selectedResources === 'all') {
392
228
  // For 'all' mode, discover actual cluster resources first
393
229
  try {
@@ -435,13 +271,9 @@ async function handleScanning(session, args, logger, requestId, capabilityServic
435
271
  // Update session with discovered resources and start batch processing
436
272
  transitionCapabilitySession(session, 'scanning', {
437
273
  selectedResources: discoveredResourceNames,
438
- processingMode: session.processingMode,
439
274
  currentResourceIndex: 0
440
275
  }, args);
441
- // Continue to batch processing with discovered resources
442
- resourceName = discoveredResourceNames[0]; // First resource for manual mode
443
- currentIndex = 0;
444
- totalResources = discoveredResourceNames.length;
276
+ // Fall through to batch processing with discovered resources
445
277
  }
446
278
  catch (error) {
447
279
  logger.error('Failed to discover cluster resources', error, {
@@ -465,286 +297,201 @@ async function handleScanning(session, args, logger, requestId, capabilityServic
465
297
  };
466
298
  }
467
299
  }
468
- else if (Array.isArray(session.selectedResources)) {
469
- // Get the current resource based on currentResourceIndex
470
- currentIndex = session.currentResourceIndex || 0;
471
- totalResources = session.selectedResources.length;
472
- if (currentIndex >= totalResources) {
473
- // All resources processed - mark as complete
474
- transitionCapabilitySession(session, 'complete', {}, args);
475
- cleanupCapabilitySession(session, args, logger, requestId);
476
- return createCapabilityScanCompletionResponse(session.sessionId, totalResources, totalResources, // Assume all successful for manual mode
477
- 0, 'completed interactively', 'manual');
478
- }
479
- resourceName = session.selectedResources[currentIndex];
480
- if (!resourceName) {
481
- throw new Error(`No resource found at index ${currentIndex}`);
482
- }
483
- }
484
- else {
485
- throw new Error('Invalid selectedResources in session state');
300
+ // Auto mode: Process ALL resources in batch without user interaction
301
+ // At this point, selectedResources should always be an array (either discovered or specified)
302
+ if (!Array.isArray(session.selectedResources)) {
303
+ throw new Error(`Invalid selectedResources state: expected array, got ${typeof session.selectedResources}. This indicates a bug in resource discovery.`);
486
304
  }
487
- // Get complete resource definition for comprehensive analysis
488
- let resourceDefinition;
489
- try {
490
- // Import and connect to discovery engine for kubectl access
491
- const discovery = new discovery_1.KubernetesDiscovery();
492
- await discovery.connect();
493
- // Get complete CRD definition if it's a custom resource
494
- if (resourceName.includes('.')) {
495
- const crdOutput = await discovery.executeKubectl(['get', 'crd', resourceName, '-o', 'yaml']);
496
- resourceDefinition = crdOutput;
497
- logger.info('Found complete CRD definition for capability analysis', {
498
- requestId,
499
- sessionId: session.sessionId,
500
- resource: resourceName,
501
- hasDefinition: !!resourceDefinition,
502
- definitionSize: resourceDefinition?.length || 0
503
- });
504
- }
505
- else {
506
- // For core resources, use kubectl explain to get schema information
507
- const explainOutput = await discovery.explainResource(resourceName);
508
- resourceDefinition = explainOutput;
509
- logger.info('Found core resource explanation for capability analysis', {
510
- requestId,
511
- sessionId: session.sessionId,
512
- resource: resourceName,
513
- hasDefinition: !!resourceDefinition
514
- });
515
- }
516
- }
517
- catch (error) {
518
- logger.error('Failed to retrieve resource definition for capability analysis', error, {
519
- requestId,
520
- sessionId: session.sessionId,
521
- resource: resourceName
522
- });
523
- throw new Error(createResourceDefinitionErrorMessage(resourceName, error));
524
- }
525
- logger.info('Analyzing resource for capability inference', {
305
+ const resources = session.selectedResources;
306
+ const totalResources = resources.length;
307
+ const processedResults = [];
308
+ const errors = [];
309
+ logger.info('Starting auto batch processing', {
526
310
  requestId,
527
311
  sessionId: session.sessionId,
528
- resource: resourceName,
529
- mode: session.processingMode
312
+ totalResources,
313
+ resources: resources
530
314
  });
531
- if (session.processingMode === 'manual') {
532
- // Manual mode: Show capability data for user review
533
- const capability = await engine.inferCapabilities(resourceName, resourceDefinition, args.interaction_id);
534
- const capabilityId = capabilities_1.CapabilityInferenceEngine.generateCapabilityId(resourceName);
535
- return {
536
- success: true,
537
- operation: 'scan',
538
- dataType: 'capabilities',
539
- mode: 'manual',
540
- step: 'scanning',
541
- sessionId: session.sessionId,
542
- preview: {
543
- resource: resourceName,
544
- resourceIndex: `${currentIndex + 1}/${totalResources}`,
545
- id: capabilityId,
546
- data: capability,
547
- question: 'Continue storing this capability?',
548
- options: [
549
- { number: 1, value: 'yes', display: '1. yes - Store this capability' },
550
- { number: 2, value: 'no', display: '2. no - Skip this resource' },
551
- { number: 3, value: 'stop', display: '3. stop - End scanning process' }
552
- ],
553
- instruction: 'Review the capability analysis results before storing',
554
- clientInstructions: {
555
- behavior: 'interactive',
556
- requirement: 'Ask user to review capability data and decide on storage',
557
- nextStep: `Call with step='scanning' and response parameter containing their choice (yes/no/stop)`,
558
- responseFormat: 'Convert user input to semantic values: 1→yes, 2→no, 3→stop'
559
- }
560
- }
315
+ // Initialize progress tracking
316
+ const startTime = Date.now();
317
+ const updateProgress = (current, currentResource, successful, failed, recentErrors) => {
318
+ const elapsed = Date.now() - startTime;
319
+ const percentage = Math.round((current / totalResources) * 100);
320
+ // Calculate estimated time remaining
321
+ let estimatedTimeRemaining;
322
+ if (current > 0) {
323
+ const avgTimePerResource = elapsed / current;
324
+ const remainingResources = totalResources - current;
325
+ const estimatedRemainingMs = remainingResources * avgTimePerResource;
326
+ const estimatedMinutes = Math.round(estimatedRemainingMs / 60000 * 10) / 10;
327
+ estimatedTimeRemaining = estimatedMinutes > 1 ?
328
+ `${estimatedMinutes} minutes` :
329
+ `${Math.round(estimatedRemainingMs / 1000)} seconds`;
330
+ }
331
+ const progressData = {
332
+ status: 'processing',
333
+ current: current,
334
+ total: totalResources,
335
+ percentage: percentage,
336
+ currentResource: currentResource,
337
+ startedAt: new Date(startTime).toISOString(),
338
+ lastUpdated: new Date().toISOString(),
339
+ estimatedTimeRemaining,
340
+ successfulResources: successful,
341
+ failedResources: failed,
342
+ errors: recentErrors.slice(-5) // Keep last 5 errors for troubleshooting
561
343
  };
344
+ // Update session file with progress
345
+ transitionCapabilitySession(session, 'scanning', {
346
+ selectedResources: session.selectedResources,
347
+ currentResourceIndex: current - 1,
348
+ progress: progressData
349
+ }, args);
350
+ };
351
+ // Setup kubectl access for getting complete resource definitions
352
+ let discovery;
353
+ try {
354
+ discovery = new discovery_1.KubernetesDiscovery();
355
+ await discovery.connect();
356
+ logger.info('Connected to Kubernetes for batch resource definition retrieval', {
357
+ requestId,
358
+ sessionId: session.sessionId
359
+ });
562
360
  }
563
- else {
564
- // Auto mode: Process ALL resources in batch without user interaction
565
- // At this point, selectedResources should always be an array (either discovered or specified)
566
- if (!Array.isArray(session.selectedResources)) {
567
- throw new Error(`Invalid selectedResources state: expected array, got ${typeof session.selectedResources}. This indicates a bug in resource discovery.`);
568
- }
569
- const resources = session.selectedResources;
570
- const totalResources = resources.length;
571
- const processedResults = [];
572
- const errors = [];
573
- logger.info('Starting auto batch processing', {
361
+ catch (error) {
362
+ logger.warn('Could not connect to Kubernetes for batch processing, falling back to name-based inference', {
574
363
  requestId,
575
364
  sessionId: session.sessionId,
576
- totalResources,
577
- resources: resources
365
+ error: error instanceof Error ? error.message : String(error)
578
366
  });
579
- // Initialize progress tracking
580
- const startTime = Date.now();
581
- const updateProgress = (current, currentResource, successful, failed, recentErrors) => {
582
- const elapsed = Date.now() - startTime;
583
- const percentage = Math.round((current / totalResources) * 100);
584
- // Calculate estimated time remaining
585
- let estimatedTimeRemaining;
586
- if (current > 0) {
587
- const avgTimePerResource = elapsed / current;
588
- const remainingResources = totalResources - current;
589
- const estimatedRemainingMs = remainingResources * avgTimePerResource;
590
- const estimatedMinutes = Math.round(estimatedRemainingMs / 60000 * 10) / 10;
591
- estimatedTimeRemaining = estimatedMinutes > 1 ?
592
- `${estimatedMinutes} minutes` :
593
- `${Math.round(estimatedRemainingMs / 1000)} seconds`;
594
- }
595
- const progressData = {
596
- status: 'processing',
597
- current: current,
598
- total: totalResources,
599
- percentage: percentage,
600
- currentResource: currentResource,
601
- startedAt: new Date(startTime).toISOString(),
602
- lastUpdated: new Date().toISOString(),
603
- estimatedTimeRemaining,
604
- successfulResources: successful,
605
- failedResources: failed,
606
- errors: recentErrors.slice(-5) // Keep last 5 errors for troubleshooting
607
- };
608
- // Update session file with progress
609
- transitionCapabilitySession(session, 'scanning', {
610
- processingMode: session.processingMode,
611
- selectedResources: session.selectedResources,
612
- currentResourceIndex: current - 1,
613
- progress: progressData
614
- }, args);
615
- };
616
- // Setup kubectl access for getting complete resource definitions
617
- let discovery;
618
- try {
619
- discovery = new discovery_1.KubernetesDiscovery();
620
- await discovery.connect();
621
- logger.info('Connected to Kubernetes for batch resource definition retrieval', {
622
- requestId,
623
- sessionId: session.sessionId
624
- });
625
- }
626
- catch (error) {
627
- logger.warn('Could not connect to Kubernetes for batch processing, falling back to name-based inference', {
628
- requestId,
629
- sessionId: session.sessionId,
630
- error: error instanceof Error ? error.message : String(error)
631
- });
632
- }
633
- // Process each resource in the batch with progress tracking
634
- for (let i = 0; i < resources.length; i++) {
635
- const currentResource = resources[i];
636
- // Get complete resource definition for this resource
637
- let currentResourceDefinition;
638
- if (discovery) {
367
+ }
368
+ // Process each resource in the batch with progress tracking
369
+ for (let i = 0; i < resources.length; i++) {
370
+ const currentResource = resources[i];
371
+ // Get complete resource definition for this resource
372
+ let currentResourceDefinition;
373
+ if (discovery) {
374
+ try {
375
+ // Try kubectl explain with full name first (works for CRDs and core resources)
639
376
  try {
377
+ currentResourceDefinition = await discovery.explainResource(currentResource);
378
+ }
379
+ catch (explainError) {
380
+ // If explain fails and resource has a dot (like Deployment.apps), try with just the Kind
640
381
  if (currentResource.includes('.')) {
641
- // Get complete CRD definition
642
- currentResourceDefinition = await discovery.executeKubectl(['get', 'crd', currentResource, '-o', 'yaml']);
382
+ const resourceKind = currentResource.split('.')[0];
383
+ logger.info(`kubectl explain failed for ${currentResource}, attempting with Kind only: ${resourceKind}`, {
384
+ requestId,
385
+ sessionId: session.sessionId,
386
+ resource: currentResource,
387
+ resourceKind
388
+ });
389
+ currentResourceDefinition = await discovery.explainResource(resourceKind);
643
390
  }
644
391
  else {
645
- // Get core resource explanation
646
- currentResourceDefinition = await discovery.explainResource(currentResource);
392
+ // Re-throw explain error for resources without dots
393
+ throw explainError;
647
394
  }
648
395
  }
649
- catch (error) {
650
- logger.error(`Failed to get resource definition for ${currentResource}`, error, {
651
- requestId,
652
- sessionId: session.sessionId,
653
- resource: currentResource
654
- });
655
- // Add to errors array and skip processing this resource
656
- errors.push({
657
- resource: currentResource,
658
- error: createResourceDefinitionErrorMessage(currentResource, error),
659
- timestamp: new Date().toISOString()
660
- });
661
- // Skip processing this resource
662
- continue;
663
- }
664
396
  }
665
- // Update progress before processing
666
- updateProgress(i + 1, currentResource, processedResults.length, errors.length, errors);
667
- try {
668
- logger.info(`Processing resource ${i + 1}/${totalResources}`, {
669
- requestId,
670
- sessionId: session.sessionId,
671
- resource: currentResource,
672
- percentage: Math.round(((i + 1) / totalResources) * 100)
673
- });
674
- const capability = await engine.inferCapabilities(currentResource, currentResourceDefinition, args.interaction_id);
675
- const capabilityId = capabilities_1.CapabilityInferenceEngine.generateCapabilityId(currentResource);
676
- // Store capability in Vector DB
677
- await capabilityService.storeCapability(capability);
678
- processedResults.push({
679
- resource: currentResource,
680
- id: capabilityId,
681
- capabilities: capability.capabilities,
682
- providers: capability.providers,
683
- complexity: capability.complexity,
684
- confidence: capability.confidence
685
- });
686
- logger.info(`Successfully processed resource ${i + 1}/${totalResources}`, {
397
+ catch (error) {
398
+ logger.error(`Failed to get resource definition for ${currentResource}`, error, {
687
399
  requestId,
688
400
  sessionId: session.sessionId,
689
- resource: currentResource,
690
- capabilitiesFound: capability.capabilities.length,
691
- percentage: Math.round(((i + 1) / totalResources) * 100)
401
+ resource: currentResource
692
402
  });
693
- }
694
- catch (error) {
695
- const errorMessage = error instanceof Error ? error.message : String(error);
696
- const errorDetail = {
403
+ // Add to errors array and skip processing this resource
404
+ errors.push({
697
405
  resource: currentResource,
698
- error: errorMessage,
699
- index: i + 1,
406
+ error: createResourceDefinitionErrorMessage(currentResource, error),
700
407
  timestamp: new Date().toISOString()
701
- };
702
- logger.error(`Failed to process resource ${i + 1}/${totalResources}`, error, {
703
- requestId,
704
- sessionId: session.sessionId,
705
- resource: currentResource,
706
- percentage: Math.round(((i + 1) / totalResources) * 100)
707
408
  });
708
- errors.push(errorDetail);
409
+ // Skip processing this resource
410
+ continue;
709
411
  }
710
412
  }
711
- // Final progress update - mark as completed
712
- const finalElapsed = Date.now() - startTime;
713
- const finalMinutes = Math.round(finalElapsed / 60000 * 10) / 10;
714
- const successful = processedResults.length;
715
- const failed = errors.length;
716
- const completionData = {
717
- status: 'completed',
718
- current: totalResources,
719
- total: totalResources,
720
- percentage: 100,
721
- currentResource: 'Processing complete',
722
- startedAt: new Date(startTime).toISOString(),
723
- lastUpdated: new Date().toISOString(),
724
- completedAt: new Date().toISOString(),
725
- totalProcessingTime: finalMinutes > 1 ? `${finalMinutes} minutes` : `${Math.round(finalElapsed / 1000)} seconds`,
726
- successfulResources: successful,
727
- failedResources: failed,
728
- errors: errors.slice(-5)
729
- };
730
- // Update session with completion status
731
- transitionCapabilitySession(session, 'complete', {
732
- progress: completionData
733
- }, args);
734
- logger.info('Auto batch processing completed', {
735
- requestId,
736
- sessionId: session.sessionId,
737
- processed: totalResources,
738
- successful,
739
- failed,
740
- processingTime: completionData.totalProcessingTime
741
- });
742
- // Clean up session file after a brief delay to allow progress viewing
743
- setTimeout(() => {
744
- cleanupCapabilitySession(session, args, logger, requestId);
745
- }, 30000); // Keep for 30 seconds after completion
746
- return createCapabilityScanCompletionResponse(session.sessionId, totalResources, successful, failed, completionData.totalProcessingTime || 'completed', 'auto');
413
+ // Update progress before processing
414
+ updateProgress(i + 1, currentResource, processedResults.length, errors.length, errors);
415
+ try {
416
+ logger.info(`Processing resource ${i + 1}/${totalResources}`, {
417
+ requestId,
418
+ sessionId: session.sessionId,
419
+ resource: currentResource,
420
+ percentage: Math.round(((i + 1) / totalResources) * 100)
421
+ });
422
+ const capability = await engine.inferCapabilities(currentResource, currentResourceDefinition, args.interaction_id);
423
+ const capabilityId = capabilities_1.CapabilityInferenceEngine.generateCapabilityId(currentResource);
424
+ // Store capability in Vector DB
425
+ await capabilityService.storeCapability(capability);
426
+ processedResults.push({
427
+ resource: currentResource,
428
+ id: capabilityId,
429
+ capabilities: capability.capabilities,
430
+ providers: capability.providers,
431
+ complexity: capability.complexity,
432
+ confidence: capability.confidence
433
+ });
434
+ logger.info(`Successfully processed resource ${i + 1}/${totalResources}`, {
435
+ requestId,
436
+ sessionId: session.sessionId,
437
+ resource: currentResource,
438
+ capabilitiesFound: capability.capabilities.length,
439
+ percentage: Math.round(((i + 1) / totalResources) * 100)
440
+ });
441
+ }
442
+ catch (error) {
443
+ const errorMessage = error instanceof Error ? error.message : String(error);
444
+ const errorDetail = {
445
+ resource: currentResource,
446
+ error: errorMessage,
447
+ index: i + 1,
448
+ timestamp: new Date().toISOString()
449
+ };
450
+ logger.error(`Failed to process resource ${i + 1}/${totalResources}`, error, {
451
+ requestId,
452
+ sessionId: session.sessionId,
453
+ resource: currentResource,
454
+ percentage: Math.round(((i + 1) / totalResources) * 100)
455
+ });
456
+ errors.push(errorDetail);
457
+ }
747
458
  }
459
+ // Final progress update - mark as completed
460
+ const finalElapsed = Date.now() - startTime;
461
+ const finalMinutes = Math.round(finalElapsed / 60000 * 10) / 10;
462
+ const successful = processedResults.length;
463
+ const failed = errors.length;
464
+ const completionData = {
465
+ status: 'completed',
466
+ current: totalResources,
467
+ total: totalResources,
468
+ percentage: 100,
469
+ currentResource: 'Processing complete',
470
+ startedAt: new Date(startTime).toISOString(),
471
+ lastUpdated: new Date().toISOString(),
472
+ completedAt: new Date().toISOString(),
473
+ totalProcessingTime: finalMinutes > 1 ? `${finalMinutes} minutes` : `${Math.round(finalElapsed / 1000)} seconds`,
474
+ successfulResources: successful,
475
+ failedResources: failed,
476
+ errors: errors.slice(-5)
477
+ };
478
+ // Update session with completion status
479
+ transitionCapabilitySession(session, 'complete', {
480
+ progress: completionData
481
+ }, args);
482
+ logger.info('Auto batch processing completed', {
483
+ requestId,
484
+ sessionId: session.sessionId,
485
+ processed: totalResources,
486
+ successful,
487
+ failed,
488
+ processingTime: completionData.totalProcessingTime
489
+ });
490
+ // Clean up session file after a brief delay to allow progress viewing
491
+ setTimeout(() => {
492
+ cleanupCapabilitySession(session, args, logger, requestId);
493
+ }, 30000); // Keep for 30 seconds after completion
494
+ return createCapabilityScanCompletionResponse(session.sessionId, totalResources, successful, failed, completionData.totalProcessingTime || 'completed', 'auto');
748
495
  }
749
496
  catch (error) {
750
497
  logger.error('Capability scanning failed', error, {