@vfarcic/dot-ai 1.2.4 → 1.3.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.
@@ -45,6 +45,7 @@ const openapi_generator_1 = require("./openapi-generator");
45
45
  const rest_route_registry_1 = require("./rest-route-registry");
46
46
  const routes_1 = require("./routes");
47
47
  const resource_sync_handler_1 = require("./resource-sync-handler");
48
+ const embedding_migration_handler_1 = require("./embedding-migration-handler");
48
49
  const prompts_1 = require("../tools/prompts");
49
50
  const generic_session_manager_1 = require("../core/generic-session-manager");
50
51
  const shared_prompt_loader_1 = require("../core/shared-prompt-loader");
@@ -90,7 +91,7 @@ class RestApiRouter {
90
91
  version: 'v1',
91
92
  enableCors: true,
92
93
  requestTimeout: 1800000, // 30 minutes for long-running operations (capability scan with slower AI providers)
93
- ...config
94
+ ...config,
94
95
  };
95
96
  // Initialize route registry and register all routes (PRD #354)
96
97
  this.routeRegistry = new rest_route_registry_1.RestRouteRegistry(logger);
@@ -118,7 +119,7 @@ class RestApiRouter {
118
119
  requestId,
119
120
  method: req.method,
120
121
  url: req.url,
121
- hasBody: !!body
122
+ hasBody: !!body,
122
123
  });
123
124
  // Handle CORS preflight
124
125
  if (this.config.enableCors) {
@@ -162,7 +163,7 @@ class RestApiRouter {
162
163
  catch (error) {
163
164
  this.logger.error('REST API request failed', error instanceof Error ? error : new Error(String(error)), {
164
165
  requestId,
165
- errorMessage: error instanceof Error ? error.message : String(error)
166
+ errorMessage: error instanceof Error ? error.message : String(error),
166
167
  });
167
168
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'INTERNAL_ERROR', 'An internal server error occurred');
168
169
  }
@@ -193,13 +194,17 @@ class RestApiRouter {
193
194
  'GET:/api/v1/sessions/:sessionId': () => this.handleSessionRetrieval(req, res, requestId, params.sessionId),
194
195
  'DELETE:/api/v1/knowledge/source/:sourceIdentifier': () => this.handleDeleteKnowledgeSource(req, res, requestId, params.sourceIdentifier),
195
196
  'POST:/api/v1/knowledge/ask': () => this.handleKnowledgeAsk(req, res, requestId, body),
197
+ 'POST:/api/v1/embeddings/migrate': () => this.handleEmbeddingMigrationRequest(req, res, requestId, body),
196
198
  };
197
199
  const handler = handlers[routeKey];
198
200
  if (handler) {
199
201
  await handler();
200
202
  }
201
203
  else {
202
- this.logger.warn('Route matched but no handler found', { requestId, routeKey });
204
+ this.logger.warn('Route matched but no handler found', {
205
+ requestId,
206
+ routeKey,
207
+ });
203
208
  await this.sendErrorResponse(res, requestId, HttpStatus.NOT_FOUND, 'NOT_FOUND', 'Handler not found for route');
204
209
  }
205
210
  }
@@ -220,19 +225,19 @@ class RestApiRouter {
220
225
  tools,
221
226
  total: tools.length,
222
227
  categories,
223
- tags
228
+ tags,
224
229
  },
225
230
  meta: {
226
231
  timestamp: new Date().toISOString(),
227
232
  requestId,
228
- version: this.config.version
229
- }
233
+ version: this.config.version,
234
+ },
230
235
  };
231
236
  await this.sendJsonResponse(res, HttpStatus.OK, response);
232
237
  this.logger.info('Tool discovery request completed', {
233
238
  requestId,
234
239
  totalTools: tools.length,
235
- filters: { category, tag, search }
240
+ filters: { category, tag, search },
236
241
  });
237
242
  }
238
243
  catch {
@@ -258,7 +263,7 @@ class RestApiRouter {
258
263
  this.logger.info('Executing tool via REST API', {
259
264
  requestId,
260
265
  toolName,
261
- parameters: Object.keys(body)
266
+ parameters: Object.keys(body),
262
267
  });
263
268
  // Execute the tool handler with timeout
264
269
  // Note: Tool handlers expect the same format as MCP calls
@@ -268,7 +273,7 @@ class RestApiRouter {
268
273
  const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Request timeout exceeded')), timeoutMs));
269
274
  // Prevent unhandled rejection if toolPromise resolves after timeout
270
275
  toolPromise.catch(() => { });
271
- const mcpResult = await Promise.race([toolPromise, timeoutPromise]);
276
+ const mcpResult = (await Promise.race([toolPromise, timeoutPromise]));
272
277
  // Transform MCP format to proper REST JSON
273
278
  // All MCP tools return JSON.stringify() in content[0].text, so parse it back to proper JSON
274
279
  let transformedResult;
@@ -280,7 +285,9 @@ class RestApiRouter {
280
285
  this.logger.warn('Failed to parse MCP tool result as JSON, returning as text', {
281
286
  requestId,
282
287
  toolName,
283
- error: parseError instanceof Error ? parseError.message : String(parseError)
288
+ error: parseError instanceof Error
289
+ ? parseError.message
290
+ : String(parseError),
284
291
  });
285
292
  transformedResult = mcpResult.content[0].text;
286
293
  }
@@ -295,20 +302,20 @@ class RestApiRouter {
295
302
  data: {
296
303
  result: transformedResult,
297
304
  tool: toolName,
298
- executionTime
305
+ executionTime,
299
306
  },
300
307
  meta: {
301
308
  timestamp: new Date().toISOString(),
302
309
  requestId,
303
- version: this.config.version
304
- }
310
+ version: this.config.version,
311
+ },
305
312
  };
306
313
  await this.sendJsonResponse(res, HttpStatus.OK, response);
307
314
  this.logger.info('Tool execution completed', {
308
315
  requestId,
309
316
  toolName,
310
317
  executionTime,
311
- success: true
318
+ success: true,
312
319
  });
313
320
  }
314
321
  catch (error) {
@@ -316,7 +323,7 @@ class RestApiRouter {
316
323
  this.logger.error('Tool execution failed', error instanceof Error ? error : new Error(String(error)), {
317
324
  requestId,
318
325
  toolName,
319
- errorMessage
326
+ errorMessage,
320
327
  });
321
328
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'EXECUTION_ERROR', errorMessage);
322
329
  }
@@ -332,13 +339,13 @@ class RestApiRouter {
332
339
  this.logger.info('OpenAPI specification served successfully', {
333
340
  requestId,
334
341
  pathCount: Object.keys(spec.paths).length,
335
- componentCount: Object.keys(spec.components?.schemas || {}).length
342
+ componentCount: Object.keys(spec.components?.schemas || {}).length,
336
343
  });
337
344
  }
338
345
  catch (error) {
339
346
  this.logger.error('Failed to generate OpenAPI specification', error instanceof Error ? error : new Error(String(error)), {
340
347
  requestId,
341
- errorMessage: error instanceof Error ? error.message : String(error)
348
+ errorMessage: error instanceof Error ? error.message : String(error),
342
349
  });
343
350
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'OPENAPI_ERROR', 'Failed to generate OpenAPI specification');
344
351
  }
@@ -360,10 +367,13 @@ class RestApiRouter {
360
367
  let httpStatus = HttpStatus.OK;
361
368
  if (!response.success) {
362
369
  const errorCode = response.error?.code;
363
- if (errorCode === 'VECTOR_DB_UNAVAILABLE' || errorCode === 'HEALTH_CHECK_FAILED') {
370
+ if (errorCode === 'VECTOR_DB_UNAVAILABLE' ||
371
+ errorCode === 'HEALTH_CHECK_FAILED') {
364
372
  httpStatus = HttpStatus.SERVICE_UNAVAILABLE;
365
373
  }
366
- else if (errorCode === 'SERVICE_INIT_FAILED' || errorCode === 'COLLECTION_INIT_FAILED' || errorCode === 'RESYNC_FAILED') {
374
+ else if (errorCode === 'SERVICE_INIT_FAILED' ||
375
+ errorCode === 'COLLECTION_INIT_FAILED' ||
376
+ errorCode === 'RESYNC_FAILED') {
367
377
  httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
368
378
  }
369
379
  else {
@@ -375,14 +385,14 @@ class RestApiRouter {
375
385
  requestId,
376
386
  success: response.success,
377
387
  upserted: response.data?.upserted,
378
- deleted: response.data?.deleted
388
+ deleted: response.data?.deleted,
379
389
  });
380
390
  }
381
391
  catch (error) {
382
392
  const errorMessage = error instanceof Error ? error.message : String(error);
383
393
  this.logger.error('Resource sync request failed', error instanceof Error ? error : new Error(String(error)), {
384
394
  requestId,
385
- errorMessage
395
+ errorMessage,
386
396
  });
387
397
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'SYNC_ERROR', 'Resource sync failed', { error: errorMessage });
388
398
  }
@@ -395,30 +405,33 @@ class RestApiRouter {
395
405
  async handleGetResourceKinds(req, res, requestId, searchParams) {
396
406
  try {
397
407
  const namespace = searchParams.get('namespace') || undefined;
398
- this.logger.info('Processing get resource kinds request', { requestId, namespace });
408
+ this.logger.info('Processing get resource kinds request', {
409
+ requestId,
410
+ namespace,
411
+ });
399
412
  const kinds = await (0, resource_tools_1.getResourceKinds)(namespace);
400
413
  const response = {
401
414
  success: true,
402
415
  data: {
403
- kinds
416
+ kinds,
404
417
  },
405
418
  meta: {
406
419
  timestamp: new Date().toISOString(),
407
420
  requestId,
408
- version: this.config.version
409
- }
421
+ version: this.config.version,
422
+ },
410
423
  };
411
424
  await this.sendJsonResponse(res, HttpStatus.OK, response);
412
425
  this.logger.info('Get resource kinds request completed', {
413
426
  requestId,
414
- kindCount: kinds.length
427
+ kindCount: kinds.length,
415
428
  });
416
429
  }
417
430
  catch (error) {
418
431
  const errorMessage = error instanceof Error ? error.message : String(error);
419
432
  this.logger.error('Get resource kinds request failed', error instanceof Error ? error : new Error(String(error)), {
420
433
  requestId,
421
- errorMessage
434
+ errorMessage,
422
435
  });
423
436
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'RESOURCE_KINDS_ERROR', 'Failed to retrieve resource kinds', { error: errorMessage });
424
437
  }
@@ -454,7 +467,8 @@ class RestApiRouter {
454
467
  await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'INVALID_PARAMETER', 'The "offset" parameter must be a non-negative integer');
455
468
  return;
456
469
  }
457
- if (minScoreParam && (isNaN(minScore) || minScore < 0 || minScore > 1)) {
470
+ if (minScoreParam &&
471
+ (isNaN(minScore) || minScore < 0 || minScore > 1)) {
458
472
  await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'INVALID_PARAMETER', 'The "minScore" parameter must be a number between 0 and 1');
459
473
  return;
460
474
  }
@@ -466,7 +480,7 @@ class RestApiRouter {
466
480
  apiVersion,
467
481
  limit,
468
482
  offset,
469
- minScore
483
+ minScore,
470
484
  });
471
485
  // Build filters
472
486
  const filters = {};
@@ -492,7 +506,7 @@ class RestApiRouter {
492
506
  apiVersion: r.resource.apiVersion,
493
507
  labels: r.resource.labels || {},
494
508
  createdAt: r.resource.createdAt,
495
- score: r.score
509
+ score: r.score,
496
510
  }));
497
511
  const response = {
498
512
  success: true,
@@ -500,27 +514,27 @@ class RestApiRouter {
500
514
  resources,
501
515
  total: results.length,
502
516
  limit,
503
- offset
517
+ offset,
504
518
  },
505
519
  meta: {
506
520
  timestamp: new Date().toISOString(),
507
521
  requestId,
508
- version: this.config.version
509
- }
522
+ version: this.config.version,
523
+ },
510
524
  };
511
525
  await this.sendJsonResponse(res, HttpStatus.OK, response);
512
526
  this.logger.info('Search resources request completed', {
513
527
  requestId,
514
528
  query: q,
515
529
  resultCount: resources.length,
516
- totalMatches: results.length
530
+ totalMatches: results.length,
517
531
  });
518
532
  }
519
533
  catch (error) {
520
534
  const errorMessage = error instanceof Error ? error.message : String(error);
521
535
  this.logger.error('Search resources request failed', error instanceof Error ? error : new Error(String(error)), {
522
536
  requestId,
523
- errorMessage
537
+ errorMessage,
524
538
  });
525
539
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'SEARCH_ERROR', 'Failed to search resources', { error: errorMessage });
526
540
  }
@@ -567,14 +581,22 @@ class RestApiRouter {
567
581
  namespace,
568
582
  includeStatus,
569
583
  limit,
570
- offset
584
+ offset,
571
585
  });
572
586
  // PRD #343: Never pass includeStatus to listResources (it uses direct kubectl)
573
587
  // Fetch status via plugin separately if requested
574
- const result = await (0, resource_tools_1.listResources)({ kind, apiVersion, namespace, limit, offset });
588
+ const result = await (0, resource_tools_1.listResources)({
589
+ kind,
590
+ apiVersion,
591
+ namespace,
592
+ limit,
593
+ offset,
594
+ });
575
595
  // Enrich with live status via plugin if requested
576
596
  // PRD #359: Use unified plugin registry
577
- if (includeStatus && result.resources.length > 0 && (0, plugin_registry_1.isPluginInitialized)()) {
597
+ if (includeStatus &&
598
+ result.resources.length > 0 &&
599
+ (0, plugin_registry_1.isPluginInitialized)()) {
578
600
  const statusPromises = result.resources.map(async (resource) => {
579
601
  const resourceType = resource.apiGroup
580
602
  ? `${resource.kind.toLowerCase()}.${resource.apiGroup}`
@@ -583,7 +605,7 @@ class RestApiRouter {
583
605
  const pluginResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_get_resource_json', {
584
606
  resource: resourceId,
585
607
  namespace: resource.namespace,
586
- field: 'status'
608
+ field: 'status',
587
609
  });
588
610
  if (pluginResponse.success && pluginResponse.result) {
589
611
  const pluginResult = pluginResponse.result;
@@ -606,22 +628,22 @@ class RestApiRouter {
606
628
  meta: {
607
629
  timestamp: new Date().toISOString(),
608
630
  requestId,
609
- version: this.config.version
610
- }
631
+ version: this.config.version,
632
+ },
611
633
  };
612
634
  await this.sendJsonResponse(res, HttpStatus.OK, response);
613
635
  this.logger.info('List resources request completed', {
614
636
  requestId,
615
637
  resourceCount: result.resources.length,
616
638
  total: result.total,
617
- includeStatus
639
+ includeStatus,
618
640
  });
619
641
  }
620
642
  catch (error) {
621
643
  const errorMessage = error instanceof Error ? error.message : String(error);
622
644
  this.logger.error('List resources request failed', error instanceof Error ? error : new Error(String(error)), {
623
645
  requestId,
624
- errorMessage
646
+ errorMessage,
625
647
  });
626
648
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'LIST_RESOURCES_ERROR', 'Failed to list resources', { error: errorMessage });
627
649
  }
@@ -637,25 +659,25 @@ class RestApiRouter {
637
659
  const response = {
638
660
  success: true,
639
661
  data: {
640
- namespaces
662
+ namespaces,
641
663
  },
642
664
  meta: {
643
665
  timestamp: new Date().toISOString(),
644
666
  requestId,
645
- version: this.config.version
646
- }
667
+ version: this.config.version,
668
+ },
647
669
  };
648
670
  await this.sendJsonResponse(res, HttpStatus.OK, response);
649
671
  this.logger.info('Get namespaces request completed', {
650
672
  requestId,
651
- namespaceCount: namespaces.length
673
+ namespaceCount: namespaces.length,
652
674
  });
653
675
  }
654
676
  catch (error) {
655
677
  const errorMessage = error instanceof Error ? error.message : String(error);
656
678
  this.logger.error('Get namespaces request failed', error instanceof Error ? error : new Error(String(error)), {
657
679
  requestId,
658
- errorMessage
680
+ errorMessage,
659
681
  });
660
682
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'NAMESPACES_ERROR', 'Failed to retrieve namespaces', { error: errorMessage });
661
683
  }
@@ -683,7 +705,13 @@ class RestApiRouter {
683
705
  await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'BAD_REQUEST', 'name query parameter is required');
684
706
  return;
685
707
  }
686
- this.logger.info('Processing get resource request', { requestId, kind, apiVersion, name, namespace });
708
+ this.logger.info('Processing get resource request', {
709
+ requestId,
710
+ kind,
711
+ apiVersion,
712
+ name,
713
+ namespace,
714
+ });
687
715
  // Extract apiGroup from apiVersion (e.g., "apps/v1" -> "apps", "v1" -> "")
688
716
  const apiGroup = apiVersion.includes('/') ? apiVersion.split('/')[0] : '';
689
717
  // PRD #359: Use unified plugin registry
@@ -692,12 +720,14 @@ class RestApiRouter {
692
720
  return;
693
721
  }
694
722
  // Build resource identifier (kind.group/name or kind/name for core resources)
695
- const resourceType = apiGroup ? `${kind.toLowerCase()}.${apiGroup}` : kind.toLowerCase();
723
+ const resourceType = apiGroup
724
+ ? `${kind.toLowerCase()}.${apiGroup}`
725
+ : kind.toLowerCase();
696
726
  const resourceId = `${resourceType}/${name}`;
697
727
  // PRD #359: Use unified plugin registry for kubectl operations
698
728
  const pluginResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_get_resource_json', {
699
729
  resource: resourceId,
700
- namespace: namespace
730
+ namespace: namespace,
701
731
  });
702
732
  // Check for plugin-level failures first
703
733
  if (!pluginResponse.success) {
@@ -734,27 +764,27 @@ class RestApiRouter {
734
764
  const response = {
735
765
  success: true,
736
766
  data: {
737
- resource
767
+ resource,
738
768
  },
739
769
  meta: {
740
770
  timestamp: new Date().toISOString(),
741
771
  requestId,
742
- version: this.config.version
743
- }
772
+ version: this.config.version,
773
+ },
744
774
  };
745
775
  await this.sendJsonResponse(res, HttpStatus.OK, response);
746
776
  this.logger.info('Get resource request completed', {
747
777
  requestId,
748
778
  kind,
749
779
  name,
750
- namespace
780
+ namespace,
751
781
  });
752
782
  }
753
783
  catch (error) {
754
784
  const errorMessage = error instanceof Error ? error.message : String(error);
755
785
  this.logger.error('Get resource request failed', error instanceof Error ? error : new Error(String(error)), {
756
786
  requestId,
757
- errorMessage
787
+ errorMessage,
758
788
  });
759
789
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'RESOURCE_ERROR', 'Failed to retrieve resource', { error: errorMessage });
760
790
  }
@@ -778,7 +808,13 @@ class RestApiRouter {
778
808
  await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'BAD_REQUEST', 'kind query parameter is required');
779
809
  return;
780
810
  }
781
- this.logger.info('Processing get events request', { requestId, name, kind, namespace, uid });
811
+ this.logger.info('Processing get events request', {
812
+ requestId,
813
+ name,
814
+ kind,
815
+ namespace,
816
+ uid,
817
+ });
782
818
  // PRD #359: Use unified plugin registry
783
819
  if (!(0, plugin_registry_1.isPluginInitialized)()) {
784
820
  await this.sendErrorResponse(res, requestId, HttpStatus.SERVICE_UNAVAILABLE, 'PLUGIN_UNAVAILABLE', 'Plugin system not initialized');
@@ -787,7 +823,7 @@ class RestApiRouter {
787
823
  // Build field selector for involvedObject filtering
788
824
  const fieldSelectors = [
789
825
  `involvedObject.name=${name}`,
790
- `involvedObject.kind=${kind}`
826
+ `involvedObject.kind=${kind}`,
791
827
  ];
792
828
  if (uid) {
793
829
  fieldSelectors.push(`involvedObject.uid=${uid}`);
@@ -795,7 +831,7 @@ class RestApiRouter {
795
831
  // PRD #359: Use unified plugin registry for kubectl operations
796
832
  const pluginResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_events', {
797
833
  namespace: namespace,
798
- args: [`--field-selector=${fieldSelectors.join(',')}`]
834
+ args: [`--field-selector=${fieldSelectors.join(',')}`],
799
835
  });
800
836
  const events = [];
801
837
  if (pluginResponse.success && pluginResponse.result) {
@@ -803,7 +839,9 @@ class RestApiRouter {
803
839
  if (pluginResult.success && pluginResult.data) {
804
840
  // Parse the table output or handle JSON if available
805
841
  // Events output is typically table format, so we need to parse it
806
- const lines = pluginResult.data.split('\n').filter(line => line.trim());
842
+ const lines = pluginResult.data
843
+ .split('\n')
844
+ .filter(line => line.trim());
807
845
  if (lines.length > 1) {
808
846
  // Skip header line, parse remaining lines
809
847
  for (let i = 1; i < lines.length; i++) {
@@ -814,7 +852,7 @@ class RestApiRouter {
814
852
  type: parts[1],
815
853
  reason: parts[2],
816
854
  involvedObject: { kind, name },
817
- message: parts.slice(4).join(' ')
855
+ message: parts.slice(4).join(' '),
818
856
  });
819
857
  }
820
858
  }
@@ -825,13 +863,13 @@ class RestApiRouter {
825
863
  success: true,
826
864
  data: {
827
865
  events,
828
- count: events.length
866
+ count: events.length,
829
867
  },
830
868
  meta: {
831
869
  timestamp: new Date().toISOString(),
832
870
  requestId,
833
- version: this.config.version
834
- }
871
+ version: this.config.version,
872
+ },
835
873
  };
836
874
  await this.sendJsonResponse(res, HttpStatus.OK, response);
837
875
  this.logger.info('Get events request completed', {
@@ -839,14 +877,14 @@ class RestApiRouter {
839
877
  name,
840
878
  kind,
841
879
  namespace,
842
- eventCount: events.length
880
+ eventCount: events.length,
843
881
  });
844
882
  }
845
883
  catch (error) {
846
884
  const errorMessage = error instanceof Error ? error.message : String(error);
847
885
  this.logger.error('Get events request failed', error instanceof Error ? error : new Error(String(error)), {
848
886
  requestId,
849
- errorMessage
887
+ errorMessage,
850
888
  });
851
889
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'EVENTS_ERROR', 'Failed to retrieve events', { error: errorMessage });
852
890
  }
@@ -879,7 +917,13 @@ class RestApiRouter {
879
917
  return;
880
918
  }
881
919
  }
882
- this.logger.info('Processing get logs request', { requestId, name, namespace, container, tailLines });
920
+ this.logger.info('Processing get logs request', {
921
+ requestId,
922
+ name,
923
+ namespace,
924
+ container,
925
+ tailLines,
926
+ });
883
927
  // PRD #359: Use unified plugin registry
884
928
  if (!(0, plugin_registry_1.isPluginInitialized)()) {
885
929
  await this.sendErrorResponse(res, requestId, HttpStatus.SERVICE_UNAVAILABLE, 'PLUGIN_UNAVAILABLE', 'Plugin system not initialized');
@@ -897,7 +941,7 @@ class RestApiRouter {
897
941
  const pluginResponse = await (0, plugin_registry_1.invokePluginTool)('agentic-tools', 'kubectl_logs', {
898
942
  resource: name,
899
943
  namespace: namespace,
900
- args: args.length > 0 ? args : undefined
944
+ args: args.length > 0 ? args : undefined,
901
945
  });
902
946
  if (!pluginResponse.success) {
903
947
  const errorMsg = pluginResponse.error?.message || 'Failed to retrieve logs';
@@ -914,13 +958,13 @@ class RestApiRouter {
914
958
  data: {
915
959
  logs: result.data,
916
960
  container: container || 'default',
917
- containerCount: 1
961
+ containerCount: 1,
918
962
  },
919
963
  meta: {
920
964
  timestamp: new Date().toISOString(),
921
965
  requestId,
922
- version: this.config.version
923
- }
966
+ version: this.config.version,
967
+ },
924
968
  };
925
969
  await this.sendJsonResponse(res, HttpStatus.OK, response);
926
970
  this.logger.info('Get logs request completed', {
@@ -928,14 +972,14 @@ class RestApiRouter {
928
972
  name,
929
973
  namespace,
930
974
  container: container || 'default',
931
- logLength: result.data.length
975
+ logLength: result.data.length,
932
976
  });
933
977
  }
934
978
  catch (error) {
935
979
  const errorMessage = error instanceof Error ? error.message : String(error);
936
980
  this.logger.error('Get logs request failed', error instanceof Error ? error : new Error(String(error)), {
937
981
  requestId,
938
- errorMessage
982
+ errorMessage,
939
983
  });
940
984
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'LOGS_ERROR', 'Failed to retrieve logs', { error: errorMessage });
941
985
  }
@@ -953,18 +997,18 @@ class RestApiRouter {
953
997
  meta: {
954
998
  timestamp: new Date().toISOString(),
955
999
  requestId,
956
- version: this.config.version
957
- }
1000
+ version: this.config.version,
1001
+ },
958
1002
  };
959
1003
  await this.sendJsonResponse(res, HttpStatus.OK, response);
960
1004
  this.logger.info('Prompts list request completed', {
961
1005
  requestId,
962
- promptCount: result.prompts?.length || 0
1006
+ promptCount: result.prompts?.length || 0,
963
1007
  });
964
1008
  }
965
1009
  catch (error) {
966
1010
  this.logger.error('Prompts list request failed', error instanceof Error ? error : new Error(String(error)), {
967
- requestId
1011
+ requestId,
968
1012
  });
969
1013
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'PROMPTS_LIST_ERROR', 'Failed to list prompts');
970
1014
  }
@@ -974,7 +1018,10 @@ class RestApiRouter {
974
1018
  */
975
1019
  async handlePromptsGetRequest(req, res, requestId, promptName, body) {
976
1020
  try {
977
- this.logger.info('Processing prompt get request', { requestId, promptName });
1021
+ this.logger.info('Processing prompt get request', {
1022
+ requestId,
1023
+ promptName,
1024
+ });
978
1025
  const bodyObj = body;
979
1026
  const result = await (0, prompts_1.handlePromptsGetRequest)({ name: promptName, arguments: bodyObj?.arguments }, this.logger, requestId);
980
1027
  const response = {
@@ -983,25 +1030,27 @@ class RestApiRouter {
983
1030
  meta: {
984
1031
  timestamp: new Date().toISOString(),
985
1032
  requestId,
986
- version: this.config.version
987
- }
1033
+ version: this.config.version,
1034
+ },
988
1035
  };
989
1036
  await this.sendJsonResponse(res, HttpStatus.OK, response);
990
1037
  this.logger.info('Prompt get request completed', {
991
1038
  requestId,
992
- promptName
1039
+ promptName,
993
1040
  });
994
1041
  }
995
1042
  catch (error) {
996
1043
  const errorMessage = error instanceof Error ? error.message : String(error);
997
1044
  this.logger.error('Prompt get request failed', error instanceof Error ? error : new Error(String(error)), {
998
1045
  requestId,
999
- promptName
1046
+ promptName,
1000
1047
  });
1001
1048
  // Check if it's a validation error (missing required arguments or prompt not found)
1002
1049
  const isValidationError = errorMessage.includes('Missing required arguments') ||
1003
1050
  errorMessage.includes('Prompt not found');
1004
- await this.sendErrorResponse(res, requestId, isValidationError ? HttpStatus.BAD_REQUEST : HttpStatus.INTERNAL_SERVER_ERROR, isValidationError ? 'VALIDATION_ERROR' : 'PROMPT_GET_ERROR', errorMessage);
1051
+ await this.sendErrorResponse(res, requestId, isValidationError
1052
+ ? HttpStatus.BAD_REQUEST
1053
+ : HttpStatus.INTERNAL_SERVER_ERROR, isValidationError ? 'VALIDATION_ERROR' : 'PROMPT_GET_ERROR', errorMessage);
1005
1054
  }
1006
1055
  }
1007
1056
  /**
@@ -1020,7 +1069,7 @@ class RestApiRouter {
1020
1069
  requestId,
1021
1070
  sessionIds,
1022
1071
  isMultiSession,
1023
- reload
1072
+ reload,
1024
1073
  });
1025
1074
  // Fetch all sessions
1026
1075
  const sessions = [];
@@ -1037,11 +1086,13 @@ class RestApiRouter {
1037
1086
  // For single session, check cache (multi-session doesn't use cache yet)
1038
1087
  // PRD #320: Skip cache if reload=true to regenerate from current session data
1039
1088
  const primarySession = sessions[0];
1040
- if (!isMultiSession && !reload && primarySession.data.cachedVisualization) {
1089
+ if (!isMultiSession &&
1090
+ !reload &&
1091
+ primarySession.data.cachedVisualization) {
1041
1092
  this.logger.info('Returning cached visualization', {
1042
1093
  requestId,
1043
1094
  sessionId: sessionIds[0],
1044
- generatedAt: primarySession.data.cachedVisualization.generatedAt
1095
+ generatedAt: primarySession.data.cachedVisualization.generatedAt,
1045
1096
  });
1046
1097
  const cachedResponse = {
1047
1098
  success: true,
@@ -1049,13 +1100,13 @@ class RestApiRouter {
1049
1100
  title: primarySession.data.cachedVisualization.title,
1050
1101
  visualizations: primarySession.data.cachedVisualization.visualizations,
1051
1102
  insights: primarySession.data.cachedVisualization.insights,
1052
- toolsUsed: primarySession.data.cachedVisualization.toolsUsed // PRD #320
1103
+ toolsUsed: primarySession.data.cachedVisualization.toolsUsed, // PRD #320
1053
1104
  },
1054
1105
  meta: {
1055
1106
  timestamp: new Date().toISOString(),
1056
1107
  requestId,
1057
- version: this.config.version
1058
- }
1108
+ version: this.config.version,
1109
+ },
1059
1110
  };
1060
1111
  await this.sendJsonResponse(res, HttpStatus.OK, cachedResponse);
1061
1112
  return;
@@ -1069,7 +1120,12 @@ class RestApiRouter {
1069
1120
  // PRD #320: Select prompt based on tool name (defaults to 'query' for backwards compatibility)
1070
1121
  const toolName = (primarySession.data.toolName || 'query');
1071
1122
  const promptName = (0, visualization_1.getPromptForTool)(toolName);
1072
- this.logger.info('Loading visualization prompt', { requestId, sessionIds, toolName, promptName });
1123
+ this.logger.info('Loading visualization prompt', {
1124
+ requestId,
1125
+ sessionIds,
1126
+ toolName,
1127
+ promptName,
1128
+ });
1073
1129
  // Load system prompt with session context
1074
1130
  // PRD #320: Unified visualization prompt with tool-aware data selection
1075
1131
  let intent;
@@ -1079,7 +1135,9 @@ class RestApiRouter {
1079
1135
  switch (toolName) {
1080
1136
  case 'recommend':
1081
1137
  intent = sessionData.intent || '';
1082
- data = isMultiSession ? sessions.map(s => s.data) : primarySession.data;
1138
+ data = isMultiSession
1139
+ ? sessions.map(s => s.data)
1140
+ : primarySession.data;
1083
1141
  break;
1084
1142
  case 'remediate':
1085
1143
  intent = sessionData.issue || '';
@@ -1102,15 +1160,17 @@ class RestApiRouter {
1102
1160
  const promptData = {
1103
1161
  intent,
1104
1162
  data: JSON.stringify(data, null, 2),
1105
- visualizationOutput: (0, shared_prompt_loader_1.loadPrompt)('partials/visualization-output')
1163
+ visualizationOutput: (0, shared_prompt_loader_1.loadPrompt)('partials/visualization-output'),
1106
1164
  };
1107
1165
  const systemPrompt = (0, shared_prompt_loader_1.loadPrompt)(promptName, promptData);
1108
1166
  // PRD #343: Local executor for non-plugin tools (capability, resource, mermaid)
1109
1167
  const localToolExecutor = async (toolName, input) => {
1110
- if (toolName.startsWith('search_capabilities') || toolName.startsWith('query_capabilities')) {
1168
+ if (toolName.startsWith('search_capabilities') ||
1169
+ toolName.startsWith('query_capabilities')) {
1111
1170
  return (0, capability_tools_1.executeCapabilityTools)(toolName, input);
1112
1171
  }
1113
- if (toolName.startsWith('search_resources') || toolName.startsWith('query_resources')) {
1172
+ if (toolName.startsWith('search_resources') ||
1173
+ toolName.startsWith('query_resources')) {
1114
1174
  return (0, resource_tools_1.executeResourceTools)(toolName, input);
1115
1175
  }
1116
1176
  // PRD #320: Mermaid validation tools
@@ -1120,7 +1180,7 @@ class RestApiRouter {
1120
1180
  return {
1121
1181
  success: false,
1122
1182
  error: `Unknown tool: ${toolName}`,
1123
- message: `Tool '${toolName}' is not implemented in visualization`
1183
+ message: `Tool '${toolName}' is not implemented in visualization`,
1124
1184
  };
1125
1185
  };
1126
1186
  // PRD #343: Use plugin executor for kubectl tools, local for others
@@ -1134,42 +1194,57 @@ class RestApiRouter {
1134
1194
  'kubectl_describe',
1135
1195
  'kubectl_logs',
1136
1196
  'kubectl_events',
1137
- 'kubectl_get_crd_schema'
1197
+ 'kubectl_get_crd_schema',
1138
1198
  ];
1139
1199
  const pluginKubectlTools = this.pluginManager
1140
- ? this.pluginManager.getDiscoveredTools().filter(t => KUBECTL_READONLY_TOOL_NAMES.includes(t.name))
1200
+ ? this.pluginManager
1201
+ .getDiscoveredTools()
1202
+ .filter(t => KUBECTL_READONLY_TOOL_NAMES.includes(t.name))
1141
1203
  : [];
1142
- this.logger.info('Starting AI visualization generation with tools', { requestId, sessionIds, toolName });
1204
+ this.logger.info('Starting AI visualization generation with tools', {
1205
+ requestId,
1206
+ sessionIds,
1207
+ toolName,
1208
+ });
1143
1209
  // Execute tool loop - AI can gather additional data if needed
1144
1210
  const result = await aiProvider.toolLoop({
1145
1211
  systemPrompt,
1146
1212
  userMessage: 'Generate visualizations based on the query results provided. Use tools if you need additional information about any resources.',
1147
1213
  // PRD #320: Include MERMAID_TOOLS for diagram validation
1148
1214
  // PRD #343: kubectl tools from plugin
1149
- tools: [...capability_tools_1.CAPABILITY_TOOLS, ...resource_tools_1.RESOURCE_TOOLS, ...pluginKubectlTools, ...mermaid_tools_1.MERMAID_TOOLS],
1215
+ tools: [
1216
+ ...capability_tools_1.CAPABILITY_TOOLS,
1217
+ ...resource_tools_1.RESOURCE_TOOLS,
1218
+ ...pluginKubectlTools,
1219
+ ...mermaid_tools_1.MERMAID_TOOLS,
1220
+ ],
1150
1221
  toolExecutor: executeVisualizationTools,
1151
1222
  maxIterations: 10, // Allow enough iterations for tool calls + JSON generation
1152
- operation: `visualize-${toolName}` // PRD #320: Include tool name for debugging
1223
+ operation: `visualize-${toolName}`, // PRD #320: Include tool name for debugging
1153
1224
  });
1154
1225
  this.logger.info('AI visualization generation completed', {
1155
1226
  requestId,
1156
1227
  sessionIds,
1157
1228
  toolName,
1158
1229
  iterations: result.iterations,
1159
- toolsUsed: [...new Set(result.toolCallsExecuted.map(tc => tc.tool))]
1230
+ toolsUsed: [...new Set(result.toolCallsExecuted.map(tc => tc.tool))],
1160
1231
  });
1161
1232
  // Parse AI response as JSON using shared function
1162
1233
  let visualizationResponse;
1163
1234
  let isFallbackResponse = false;
1164
1235
  try {
1165
- const toolsUsed = [...new Set(result.toolCallsExecuted.map(tc => tc.tool))];
1236
+ const toolsUsed = [
1237
+ ...new Set(result.toolCallsExecuted.map(tc => tc.tool)),
1238
+ ];
1166
1239
  visualizationResponse = (0, visualization_1.parseVisualizationResponse)(result.finalMessage, toolsUsed);
1167
1240
  }
1168
1241
  catch (parseError) {
1169
- this.logger.error('Failed to parse AI visualization response', parseError instanceof Error ? parseError : new Error(String(parseError)), {
1242
+ this.logger.error('Failed to parse AI visualization response', parseError instanceof Error
1243
+ ? parseError
1244
+ : new Error(String(parseError)), {
1170
1245
  requestId,
1171
1246
  sessionIds,
1172
- rawResponse: result.finalMessage.substring(0, 500)
1247
+ rawResponse: result.finalMessage.substring(0, 500),
1173
1248
  });
1174
1249
  // Fallback to basic visualization on parse error
1175
1250
  // NOTE: isFallbackResponse flag prevents caching this response
@@ -1183,13 +1258,13 @@ class RestApiRouter {
1183
1258
  type: 'code',
1184
1259
  content: {
1185
1260
  language: 'json',
1186
- code: JSON.stringify(isMultiSession ? sessions.map(s => s.data) : primarySession.data, null, 2)
1187
- }
1188
- }
1261
+ code: JSON.stringify(isMultiSession
1262
+ ? sessions.map(s => s.data)
1263
+ : primarySession.data, null, 2),
1264
+ },
1265
+ },
1189
1266
  ],
1190
- insights: [
1191
- 'AI visualization generation failed - showing raw data'
1192
- ]
1267
+ insights: ['AI visualization generation failed - showing raw data'],
1193
1268
  };
1194
1269
  }
1195
1270
  // Cache the visualization in the session for subsequent requests (single session only)
@@ -1203,13 +1278,19 @@ class RestApiRouter {
1203
1278
  visualizations: visualizationResponse.visualizations,
1204
1279
  insights: visualizationResponse.insights,
1205
1280
  toolsUsed: visualizationResponse.toolsUsed, // PRD #320: Cache toolsUsed
1206
- generatedAt: new Date().toISOString()
1207
- }
1281
+ generatedAt: new Date().toISOString(),
1282
+ },
1283
+ });
1284
+ this.logger.info('Visualization cached in session', {
1285
+ requestId,
1286
+ sessionId: sessionIds[0],
1208
1287
  });
1209
- this.logger.info('Visualization cached in session', { requestId, sessionId: sessionIds[0] });
1210
1288
  }
1211
1289
  else if (isFallbackResponse) {
1212
- this.logger.warn('Skipping cache for fallback visualization response', { requestId, sessionId: sessionIds[0] });
1290
+ this.logger.warn('Skipping cache for fallback visualization response', {
1291
+ requestId,
1292
+ sessionId: sessionIds[0],
1293
+ });
1213
1294
  }
1214
1295
  const response = {
1215
1296
  success: true,
@@ -1217,8 +1298,8 @@ class RestApiRouter {
1217
1298
  meta: {
1218
1299
  timestamp: new Date().toISOString(),
1219
1300
  requestId,
1220
- version: this.config.version
1221
- }
1301
+ version: this.config.version,
1302
+ },
1222
1303
  };
1223
1304
  await this.sendJsonResponse(res, HttpStatus.OK, response);
1224
1305
  this.logger.info('Visualization request completed', {
@@ -1226,14 +1307,14 @@ class RestApiRouter {
1226
1307
  sessionIds,
1227
1308
  visualizationCount: visualizationResponse.visualizations.length,
1228
1309
  cached: !isMultiSession && !isFallbackResponse,
1229
- isFallback: isFallbackResponse
1310
+ isFallback: isFallbackResponse,
1230
1311
  });
1231
1312
  }
1232
1313
  catch (error) {
1233
1314
  const errorMessage = error instanceof Error ? error.message : String(error);
1234
1315
  this.logger.error('Visualization request failed', error instanceof Error ? error : new Error(String(error)), {
1235
1316
  requestId,
1236
- sessionIdParam
1317
+ sessionIdParam,
1237
1318
  });
1238
1319
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'VISUALIZATION_ERROR', 'Failed to generate visualization', { error: errorMessage });
1239
1320
  }
@@ -1249,7 +1330,7 @@ class RestApiRouter {
1249
1330
  this.logger.info('Processing session retrieval', {
1250
1331
  requestId,
1251
1332
  sessionId,
1252
- sessionPrefix
1333
+ sessionPrefix,
1253
1334
  });
1254
1335
  const sessionManager = new generic_session_manager_1.GenericSessionManager(sessionPrefix);
1255
1336
  const session = sessionManager.getSession(sessionId);
@@ -1263,21 +1344,21 @@ class RestApiRouter {
1263
1344
  meta: {
1264
1345
  timestamp: new Date().toISOString(),
1265
1346
  requestId,
1266
- version: this.config.version
1267
- }
1347
+ version: this.config.version,
1348
+ },
1268
1349
  };
1269
1350
  await this.sendJsonResponse(res, HttpStatus.OK, response);
1270
1351
  this.logger.info('Session retrieved successfully', {
1271
1352
  requestId,
1272
1353
  sessionId,
1273
- toolName: session.data?.toolName || 'unknown'
1354
+ toolName: session.data?.toolName || 'unknown',
1274
1355
  });
1275
1356
  }
1276
1357
  catch (error) {
1277
1358
  const errorMessage = error instanceof Error ? error.message : String(error);
1278
1359
  this.logger.error('Session retrieval failed', error instanceof Error ? error : new Error(String(error)), {
1279
1360
  requestId,
1280
- sessionId
1361
+ sessionId,
1281
1362
  });
1282
1363
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'SESSION_RETRIEVAL_ERROR', 'Failed to retrieve session', { error: errorMessage });
1283
1364
  }
@@ -1306,7 +1387,12 @@ class RestApiRouter {
1306
1387
  const queryResponse = await (0, plugin_registry_1.invokePluginTool)(PLUGIN_NAME, 'vector_query', {
1307
1388
  collection: KNOWLEDGE_COLLECTION,
1308
1389
  filter: {
1309
- must: [{ key: 'metadata.sourceIdentifier', match: { value: decodedSourceIdentifier } }],
1390
+ must: [
1391
+ {
1392
+ key: 'metadata.sourceIdentifier',
1393
+ match: { value: decodedSourceIdentifier },
1394
+ },
1395
+ ],
1310
1396
  },
1311
1397
  limit: 10000, // High limit to get all chunks for a source
1312
1398
  });
@@ -1314,7 +1400,8 @@ class RestApiRouter {
1314
1400
  const error = queryResponse.error;
1315
1401
  const errorMessage = error?.message || error?.error || 'Query failed';
1316
1402
  // If collection doesn't exist (Not Found), return success with 0 deleted
1317
- if (errorMessage.includes('Not Found') || errorMessage.includes('not found')) {
1403
+ if (errorMessage.includes('Not Found') ||
1404
+ errorMessage.includes('not found')) {
1318
1405
  this.logger.info('Collection not found - returning success with 0 deleted', {
1319
1406
  requestId,
1320
1407
  sourceIdentifier: decodedSourceIdentifier,
@@ -1346,7 +1433,8 @@ class RestApiRouter {
1346
1433
  if (!queryResult.success) {
1347
1434
  const errorMessage = queryResult.error || queryResult.message;
1348
1435
  // If collection doesn't exist, return success with 0 deleted
1349
- if (errorMessage.includes('Not Found') || errorMessage.includes('not found')) {
1436
+ if (errorMessage.includes('Not Found') ||
1437
+ errorMessage.includes('not found')) {
1350
1438
  this.logger.info('Collection not found - returning success with 0 deleted', {
1351
1439
  requestId,
1352
1440
  sourceIdentifier: decodedSourceIdentifier,
@@ -1407,7 +1495,11 @@ class RestApiRouter {
1407
1495
  chunkId: chunk.id,
1408
1496
  deletedSoFar: deletedCount,
1409
1497
  });
1410
- await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'DELETE_SOURCE_ERROR', 'Failed to delete chunk', { chunkId: chunk.id, error: errorMessage, chunksDeletedBeforeFailure: deletedCount });
1498
+ await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'DELETE_SOURCE_ERROR', 'Failed to delete chunk', {
1499
+ chunkId: chunk.id,
1500
+ error: errorMessage,
1501
+ chunksDeletedBeforeFailure: deletedCount,
1502
+ });
1411
1503
  return;
1412
1504
  }
1413
1505
  deletedCount++;
@@ -1452,7 +1544,7 @@ class RestApiRouter {
1452
1544
  await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'BAD_REQUEST', 'Request body must be a JSON object');
1453
1545
  return;
1454
1546
  }
1455
- const { query, limit = 20, uriFilter } = body;
1547
+ const { query, limit = 20, uriFilter, } = body;
1456
1548
  // Validate limit parameter (must be positive integer)
1457
1549
  if (typeof limit !== 'number' || !Number.isInteger(limit) || limit < 1) {
1458
1550
  await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'INVALID_PARAMETER', 'The "limit" parameter must be a positive integer');
@@ -1607,6 +1699,41 @@ class RestApiRouter {
1607
1699
  await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'SYNTHESIS_ERROR', 'Failed to process knowledge ask request', { error: errorMessage });
1608
1700
  }
1609
1701
  }
1702
+ /**
1703
+ * Handle embedding migration request (PRD #384)
1704
+ */
1705
+ async handleEmbeddingMigrationRequest(req, res, requestId, body) {
1706
+ try {
1707
+ this.logger.info('Processing embedding migration request', { requestId });
1708
+ // Delegate to the embedding migration handler
1709
+ const response = await (0, embedding_migration_handler_1.handleEmbeddingMigration)(body, this.logger, requestId);
1710
+ // Determine HTTP status based on response
1711
+ let httpStatus = HttpStatus.OK;
1712
+ if (!response.success) {
1713
+ const errorCode = response.error?.code;
1714
+ if (errorCode === 'PLUGIN_UNAVAILABLE' ||
1715
+ errorCode === 'EMBEDDING_SERVICE_UNAVAILABLE') {
1716
+ httpStatus = HttpStatus.SERVICE_UNAVAILABLE;
1717
+ }
1718
+ else if (errorCode === 'MIGRATION_ERROR') {
1719
+ httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
1720
+ }
1721
+ else {
1722
+ httpStatus = HttpStatus.BAD_REQUEST;
1723
+ }
1724
+ }
1725
+ await this.sendJsonResponse(res, httpStatus, response);
1726
+ this.logger.info('Embedding migration request completed', {
1727
+ requestId,
1728
+ success: response.success,
1729
+ });
1730
+ }
1731
+ catch (error) {
1732
+ const errorMessage = error instanceof Error ? error.message : String(error);
1733
+ this.logger.error('Embedding migration request failed', error instanceof Error ? error : new Error(String(error)), { requestId });
1734
+ await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'MIGRATION_ERROR', 'Failed to process embedding migration request', { error: errorMessage });
1735
+ }
1736
+ }
1610
1737
  /**
1611
1738
  * Set CORS headers
1612
1739
  */
@@ -1632,13 +1759,13 @@ class RestApiRouter {
1632
1759
  error: {
1633
1760
  code,
1634
1761
  message,
1635
- details
1762
+ details,
1636
1763
  },
1637
1764
  meta: {
1638
1765
  timestamp: new Date().toISOString(),
1639
1766
  requestId,
1640
- version: this.config.version
1641
- }
1767
+ version: this.config.version,
1768
+ },
1642
1769
  };
1643
1770
  await this.sendJsonResponse(res, status, response);
1644
1771
  }