Refactor search functionality to utilize SearchManager
- Introduced SearchManager to handle search operations directly instead of proxying to MCP server. - Updated WorkerService to initialize SearchManager after database setup. - Modified SearchRoutes to call SearchManager methods for search operations. - Adjusted SearchManager to manage timeline and formatting services. - Enhanced error handling and logging for search operations.
This commit is contained in:
@@ -21,6 +21,9 @@ import { SSEBroadcaster } from './worker/SSEBroadcaster.js';
|
||||
import { SDKAgent } from './worker/SDKAgent.js';
|
||||
import { PaginationHelper } from './worker/PaginationHelper.js';
|
||||
import { SettingsManager } from './worker/SettingsManager.js';
|
||||
import { SearchManager } from './worker/SearchManager.js';
|
||||
import { FormattingService } from './worker/FormattingService.js';
|
||||
import { TimelineService } from './worker/TimelineService.js';
|
||||
|
||||
// Import HTTP layer
|
||||
import { createMiddleware, summarizeRequestBody as summarizeBody } from './worker/http/middleware.js';
|
||||
@@ -73,11 +76,12 @@ export class WorkerService {
|
||||
version: '1.0.0'
|
||||
}, { capabilities: {} });
|
||||
|
||||
// Initialize route handlers
|
||||
// Initialize route handlers (SearchRoutes will use MCP client initially, then switch to SearchManager after DB init)
|
||||
this.viewerRoutes = new ViewerRoutes(this.sseBroadcaster, this.dbManager, this.sessionManager);
|
||||
this.sessionRoutes = new SessionRoutes(this.sessionManager, this.dbManager, this.sdkAgent, this.sseBroadcaster, this);
|
||||
this.dataRoutes = new DataRoutes(this.paginationHelper, this.dbManager, this.sessionManager, this.sseBroadcaster, this, this.startTime);
|
||||
this.searchRoutes = new SearchRoutes(this.mcpClient);
|
||||
// SearchRoutes needs SearchManager which requires initialized DB - will be created in initializeBackground()
|
||||
this.searchRoutes = null as any; // Temporary - will be set in initializeBackground()
|
||||
this.settingsRoutes = new SettingsRoutes(this.settingsManager);
|
||||
|
||||
this.setupMiddleware();
|
||||
@@ -104,7 +108,10 @@ export class WorkerService {
|
||||
this.viewerRoutes.setupRoutes(this.app);
|
||||
this.sessionRoutes.setupRoutes(this.app);
|
||||
this.dataRoutes.setupRoutes(this.app);
|
||||
this.searchRoutes.setupRoutes(this.app);
|
||||
// searchRoutes is set up after database initialization in initializeBackground()
|
||||
if (this.searchRoutes) {
|
||||
this.searchRoutes.setupRoutes(this.app);
|
||||
}
|
||||
this.settingsRoutes.setupRoutes(this.app);
|
||||
}
|
||||
|
||||
@@ -169,6 +176,20 @@ export class WorkerService {
|
||||
// Initialize database (once, stays open)
|
||||
await this.dbManager.initialize();
|
||||
|
||||
// Initialize search services (requires initialized database)
|
||||
const formattingService = new FormattingService();
|
||||
const timelineService = new TimelineService();
|
||||
const searchManager = new SearchManager(
|
||||
this.dbManager.getSessionSearch(),
|
||||
this.dbManager.getSessionStore(),
|
||||
this.dbManager.getChromaSync(),
|
||||
formattingService,
|
||||
timelineService
|
||||
);
|
||||
this.searchRoutes = new SearchRoutes(searchManager);
|
||||
this.searchRoutes.setupRoutes(this.app); // Setup search routes now that SearchManager is ready
|
||||
logger.info('WORKER', 'SearchManager initialized and search routes registered');
|
||||
|
||||
// Connect to MCP server
|
||||
const mcpServerPath = path.join(__dirname, '..', '..', 'plugin', 'scripts', 'mcp-server.cjs');
|
||||
const transport = new StdioClientTransport({
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* The MCP server now acts as a thin HTTP wrapper that calls these methods via HTTP.
|
||||
*/
|
||||
|
||||
import { basename } from 'path';
|
||||
import { SessionSearch } from '../sqlite/SessionSearch.js';
|
||||
import { SessionStore } from '../sqlite/SessionStore.js';
|
||||
import { ChromaSync } from '../sync/ChromaSync.js';
|
||||
@@ -22,7 +23,7 @@ export class SearchManager {
|
||||
private sessionStore: SessionStore,
|
||||
private chromaSync: ChromaSync,
|
||||
private formatter: FormattingService,
|
||||
private timeline: TimelineService
|
||||
private timelineService: TimelineService
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -302,7 +303,7 @@ export class SearchManager {
|
||||
|
||||
let anchorId: string | number;
|
||||
let anchorEpoch: number;
|
||||
let timeline: any;
|
||||
let timelineData: any;
|
||||
|
||||
// MODE 1: Query-based timeline
|
||||
if (query) {
|
||||
@@ -345,7 +346,7 @@ export class SearchManager {
|
||||
anchorId = topResult.id;
|
||||
anchorEpoch = topResult.created_at_epoch;
|
||||
silentDebug(`[search-server] Query mode: Using observation #${topResult.id} as timeline anchor`);
|
||||
timeline = this.sessionStore.getTimelineAroundObservation(topResult.id, topResult.created_at_epoch, depth_before, depth_after, project);
|
||||
timelineData = this.sessionStore.getTimelineAroundObservation(topResult.id, topResult.created_at_epoch, depth_before, depth_after, project);
|
||||
}
|
||||
// MODE 2: Anchor-based timeline
|
||||
else if (typeof anchor === 'number') {
|
||||
@@ -362,7 +363,7 @@ export class SearchManager {
|
||||
}
|
||||
anchorId = anchor;
|
||||
anchorEpoch = obs.created_at_epoch;
|
||||
timeline = this.sessionStore.getTimelineAroundObservation(anchor, anchorEpoch, depth_before, depth_after, project);
|
||||
timelineData = this.sessionStore.getTimelineAroundObservation(anchor, anchorEpoch, depth_before, depth_after, project);
|
||||
} else if (typeof anchor === 'string') {
|
||||
// Session ID or ISO timestamp
|
||||
if (anchor.startsWith('S') || anchor.startsWith('#S')) {
|
||||
@@ -380,7 +381,7 @@ export class SearchManager {
|
||||
}
|
||||
anchorEpoch = sessions[0].created_at_epoch;
|
||||
anchorId = `S${sessionNum}`;
|
||||
timeline = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
timelineData = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
} else {
|
||||
// ISO timestamp
|
||||
const date = new Date(anchor);
|
||||
@@ -395,7 +396,7 @@ export class SearchManager {
|
||||
}
|
||||
anchorEpoch = date.getTime();
|
||||
anchorId = anchor;
|
||||
timeline = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
timelineData = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
@@ -409,12 +410,12 @@ export class SearchManager {
|
||||
|
||||
// Combine, sort, and filter timeline items
|
||||
const items: TimelineItem[] = [
|
||||
...timeline.observations.map((obs: any) => ({ type: 'observation' as const, data: obs, epoch: obs.created_at_epoch })),
|
||||
...timeline.sessions.map((sess: any) => ({ type: 'session' as const, data: sess, epoch: sess.created_at_epoch })),
|
||||
...timeline.prompts.map((prompt: any) => ({ type: 'prompt' as const, data: prompt, epoch: prompt.created_at_epoch }))
|
||||
...timelineData.observations.map((obs: any) => ({ type: 'observation' as const, data: obs, epoch: obs.created_at_epoch })),
|
||||
...timelineData.sessions.map((sess: any) => ({ type: 'session' as const, data: sess, epoch: sess.created_at_epoch })),
|
||||
...timelineData.prompts.map((prompt: any) => ({ type: 'prompt' as const, data: prompt, epoch: prompt.created_at_epoch }))
|
||||
];
|
||||
items.sort((a, b) => a.epoch - b.epoch);
|
||||
const filteredItems = this.timeline.filterByDepth(items, anchorId, anchorEpoch, depth_before, depth_after);
|
||||
const filteredItems = this.timelineService.filterByDepth(items, anchorId, anchorEpoch, depth_before, depth_after);
|
||||
|
||||
if (filteredItems.length === 0) {
|
||||
return {
|
||||
@@ -428,25 +429,25 @@ export class SearchManager {
|
||||
}
|
||||
|
||||
// Format timeline (helper functions)
|
||||
function formatDate(epochMs: number): string {
|
||||
const formatDate = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function formatTime(epochMs: number): string {
|
||||
const formatTime = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function formatDateTime(epochMs: number): string {
|
||||
const formatDateTime = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
@@ -455,12 +456,12 @@ export class SearchManager {
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function estimateTokens(text: string | null): number {
|
||||
const estimateTokens = (text: string | null): number => {
|
||||
if (!text) return 0;
|
||||
return Math.ceil(text.length / 4);
|
||||
}
|
||||
};
|
||||
|
||||
// Format results
|
||||
const lines: string[] = [];
|
||||
@@ -468,7 +469,7 @@ export class SearchManager {
|
||||
// Header
|
||||
if (query) {
|
||||
const anchorObs = filteredItems.find(item => item.type === 'observation' && item.data.id === anchorId);
|
||||
const anchorTitle = anchorObs ? (anchorObs.data.title || 'Untitled') : 'Unknown';
|
||||
const anchorTitle = anchorObs && anchorObs.type === 'observation' ? ((anchorObs.data as ObservationSearchResult).title || 'Untitled') : 'Unknown';
|
||||
lines.push(`# Timeline for query: "${query}"`);
|
||||
lines.push(`**Anchor:** Observation #${anchorId} - ${anchorTitle}`);
|
||||
} else {
|
||||
@@ -522,7 +523,7 @@ export class SearchManager {
|
||||
lastTime = '';
|
||||
}
|
||||
|
||||
const sess = item.data;
|
||||
const sess = item.data as SessionSummarySearchResult;
|
||||
const title = sess.request || 'Session summary';
|
||||
const link = `claude-mem://session-summary/${sess.id}`;
|
||||
const marker = isAnchor ? ' ← **ANCHOR**' : '';
|
||||
@@ -537,14 +538,14 @@ export class SearchManager {
|
||||
lastTime = '';
|
||||
}
|
||||
|
||||
const prompt = item.data;
|
||||
const truncated = prompt.prompt.length > 100 ? prompt.prompt.substring(0, 100) + '...' : prompt.prompt;
|
||||
const prompt = item.data as UserPromptSearchResult;
|
||||
const truncated = prompt.prompt_text.length > 100 ? prompt.prompt_text.substring(0, 100) + '...' : prompt.prompt_text;
|
||||
|
||||
lines.push(`**💬 User Prompt #${prompt.prompt_number}** (${formatDateTime(item.epoch)})`);
|
||||
lines.push(`> ${truncated}`);
|
||||
lines.push('');
|
||||
} else if (item.type === 'observation') {
|
||||
const obs = item.data;
|
||||
const obs = item.data as ObservationSearchResult;
|
||||
const file = 'General';
|
||||
|
||||
if (file !== currentFile) {
|
||||
@@ -1543,7 +1544,7 @@ export class SearchManager {
|
||||
let anchorId: string | number = anchor;
|
||||
|
||||
// Resolve anchor and get timeline data
|
||||
let timeline;
|
||||
let timelineData;
|
||||
if (typeof anchor === 'number') {
|
||||
// Observation ID - use ID-based boundary detection
|
||||
const obs = this.sessionStore.getObservationById(anchor);
|
||||
@@ -1557,7 +1558,7 @@ export class SearchManager {
|
||||
};
|
||||
}
|
||||
anchorEpoch = obs.created_at_epoch;
|
||||
timeline = this.sessionStore.getTimelineAroundObservation(anchor, anchorEpoch, depth_before, depth_after, project);
|
||||
timelineData = this.sessionStore.getTimelineAroundObservation(anchor, anchorEpoch, depth_before, depth_after, project);
|
||||
} else if (typeof anchor === 'string') {
|
||||
// Session ID or ISO timestamp
|
||||
if (anchor.startsWith('S') || anchor.startsWith('#S')) {
|
||||
@@ -1575,7 +1576,7 @@ export class SearchManager {
|
||||
}
|
||||
anchorEpoch = sessions[0].created_at_epoch;
|
||||
anchorId = `S${sessionNum}`;
|
||||
timeline = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
timelineData = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
} else {
|
||||
// ISO timestamp
|
||||
const date = new Date(anchor);
|
||||
@@ -1589,7 +1590,7 @@ export class SearchManager {
|
||||
};
|
||||
}
|
||||
anchorEpoch = date.getTime(); // Keep as milliseconds
|
||||
timeline = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
timelineData = this.sessionStore.getTimelineAroundTimestamp(anchorEpoch, depth_before, depth_after, project);
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
@@ -1603,12 +1604,12 @@ export class SearchManager {
|
||||
|
||||
// Combine, sort, and filter timeline items
|
||||
const items: TimelineItem[] = [
|
||||
...timeline.observations.map(obs => ({ type: 'observation' as const, data: obs, epoch: obs.created_at_epoch })),
|
||||
...timeline.sessions.map(sess => ({ type: 'session' as const, data: sess, epoch: sess.created_at_epoch })),
|
||||
...timeline.prompts.map(prompt => ({ type: 'prompt' as const, data: prompt, epoch: prompt.created_at_epoch }))
|
||||
...timelineData.observations.map(obs => ({ type: 'observation' as const, data: obs, epoch: obs.created_at_epoch })),
|
||||
...timelineData.sessions.map(sess => ({ type: 'session' as const, data: sess, epoch: sess.created_at_epoch })),
|
||||
...timelineData.prompts.map(prompt => ({ type: 'prompt' as const, data: prompt, epoch: prompt.created_at_epoch }))
|
||||
];
|
||||
items.sort((a, b) => a.epoch - b.epoch);
|
||||
const filteredItems = this.timeline.filterByDepth(items, anchorId, anchorEpoch, depth_before, depth_after);
|
||||
const filteredItems = this.timelineService.filterByDepth(items, anchorId, anchorEpoch, depth_before, depth_after);
|
||||
|
||||
if (filteredItems.length === 0) {
|
||||
const anchorDate = new Date(anchorEpoch).toLocaleString();
|
||||
@@ -1621,25 +1622,25 @@ export class SearchManager {
|
||||
}
|
||||
|
||||
// Helper functions matching context-hook.ts
|
||||
function formatDate(epochMs: number): string {
|
||||
const formatDate = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function formatTime(epochMs: number): string {
|
||||
const formatTime = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function formatDateTime(epochMs: number): string {
|
||||
const formatDateTime = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
@@ -1648,12 +1649,12 @@ export class SearchManager {
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function estimateTokens(text: string | null): number {
|
||||
const estimateTokens = (text: string | null): number => {
|
||||
if (!text) return 0;
|
||||
return Math.ceil(text.length / 4);
|
||||
}
|
||||
};
|
||||
|
||||
// Format results matching context-hook.ts exactly
|
||||
const lines: string[] = [];
|
||||
@@ -1709,7 +1710,7 @@ export class SearchManager {
|
||||
}
|
||||
|
||||
// Render session
|
||||
const sess = item.data;
|
||||
const sess = item.data as SessionSummarySearchResult;
|
||||
const title = sess.request || 'Session summary';
|
||||
const link = `claude-mem://session-summary/${sess.id}`;
|
||||
const marker = isAnchor ? ' ← **ANCHOR**' : '';
|
||||
@@ -1726,15 +1727,15 @@ export class SearchManager {
|
||||
}
|
||||
|
||||
// Render prompt
|
||||
const prompt = item.data;
|
||||
const truncated = prompt.prompt.length > 100 ? prompt.prompt.substring(0, 100) + '...' : prompt.prompt;
|
||||
const prompt = item.data as UserPromptSearchResult;
|
||||
const truncated = prompt.prompt_text.length > 100 ? prompt.prompt_text.substring(0, 100) + '...' : prompt.prompt_text;
|
||||
|
||||
lines.push(`**💬 User Prompt #${prompt.prompt_number}** (${formatDateTime(item.epoch)})`);
|
||||
lines.push(`> ${truncated}`);
|
||||
lines.push('');
|
||||
} else if (item.type === 'observation') {
|
||||
// Render observation in table
|
||||
const obs = item.data;
|
||||
const obs = item.data as ObservationSearchResult;
|
||||
const file = 'General'; // Simplified for timeline view
|
||||
|
||||
// Check if we need a new file section
|
||||
@@ -1888,7 +1889,7 @@ export class SearchManager {
|
||||
silentDebug(`[search-server] Auto mode: Using observation #${topResult.id} as timeline anchor`);
|
||||
|
||||
// Get timeline around this observation
|
||||
const timeline = this.sessionStore.getTimelineAroundObservation(
|
||||
const timelineData = this.sessionStore.getTimelineAroundObservation(
|
||||
topResult.id,
|
||||
topResult.created_at_epoch,
|
||||
depth_before,
|
||||
@@ -1898,12 +1899,12 @@ export class SearchManager {
|
||||
|
||||
// Combine, sort, and filter timeline items
|
||||
const items: TimelineItem[] = [
|
||||
...timeline.observations.map(obs => ({ type: 'observation' as const, data: obs, epoch: obs.created_at_epoch })),
|
||||
...timeline.sessions.map(sess => ({ type: 'session' as const, data: sess, epoch: sess.created_at_epoch })),
|
||||
...timeline.prompts.map(prompt => ({ type: 'prompt' as const, data: prompt, epoch: prompt.created_at_epoch }))
|
||||
...timelineData.observations.map(obs => ({ type: 'observation' as const, data: obs, epoch: obs.created_at_epoch })),
|
||||
...timelineData.sessions.map(sess => ({ type: 'session' as const, data: sess, epoch: sess.created_at_epoch })),
|
||||
...timelineData.prompts.map(prompt => ({ type: 'prompt' as const, data: prompt, epoch: prompt.created_at_epoch }))
|
||||
];
|
||||
items.sort((a, b) => a.epoch - b.epoch);
|
||||
const filteredItems = this.timeline.filterByDepth(items, topResult.id, 0, depth_before, depth_after);
|
||||
const filteredItems = this.timelineService.filterByDepth(items, topResult.id, 0, depth_before, depth_after);
|
||||
|
||||
if (filteredItems.length === 0) {
|
||||
return {
|
||||
@@ -1915,25 +1916,25 @@ export class SearchManager {
|
||||
}
|
||||
|
||||
// Helper functions (reused from get_context_timeline)
|
||||
function formatDate(epochMs: number): string {
|
||||
const formatDate = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function formatTime(epochMs: number): string {
|
||||
const formatTime = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function formatDateTime(epochMs: number): string {
|
||||
const formatDateTime = (epochMs: number): string => {
|
||||
const date = new Date(epochMs);
|
||||
return date.toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
@@ -1942,12 +1943,12 @@ export class SearchManager {
|
||||
minute: '2-digit',
|
||||
hour12: true
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function estimateTokens(text: string | null): number {
|
||||
const estimateTokens = (text: string | null): number => {
|
||||
if (!text) return 0;
|
||||
return Math.ceil(text.length / 4);
|
||||
}
|
||||
};
|
||||
|
||||
// Format timeline (reused from get_context_timeline)
|
||||
const lines: string[] = [];
|
||||
@@ -2001,7 +2002,7 @@ export class SearchManager {
|
||||
}
|
||||
|
||||
// Render session
|
||||
const sess = item.data;
|
||||
const sess = item.data as SessionSummarySearchResult;
|
||||
const title = sess.request || 'Session summary';
|
||||
const link = `claude-mem://session-summary/${sess.id}`;
|
||||
|
||||
@@ -2017,15 +2018,15 @@ export class SearchManager {
|
||||
}
|
||||
|
||||
// Render prompt
|
||||
const prompt = item.data;
|
||||
const truncated = prompt.prompt.length > 100 ? prompt.prompt.substring(0, 100) + '...' : prompt.prompt;
|
||||
const prompt = item.data as UserPromptSearchResult;
|
||||
const truncated = prompt.prompt_text.length > 100 ? prompt.prompt_text.substring(0, 100) + '...' : prompt.prompt_text;
|
||||
|
||||
lines.push(`**💬 User Prompt #${prompt.prompt_number}** (${formatDateTime(item.epoch)})`);
|
||||
lines.push(`> ${truncated}`);
|
||||
lines.push('');
|
||||
} else if (item.type === 'observation') {
|
||||
// Render observation in table
|
||||
const obs = item.data;
|
||||
const obs = item.data as ObservationSearchResult;
|
||||
const file = 'General'; // Simplified for timeline view
|
||||
|
||||
// Check if we need a new file section
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* Search Routes
|
||||
*
|
||||
* Handles all search operations by proxying to the MCP search server.
|
||||
* All endpoints call MCP tools via the client connection.
|
||||
* Handles all search operations via SearchManager.
|
||||
* All endpoints call SearchManager methods directly.
|
||||
*/
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { logger } from '../../../../utils/logger.js';
|
||||
import { SearchManager } from '../../SearchManager.js';
|
||||
|
||||
export class SearchRoutes {
|
||||
constructor(
|
||||
private mcpClient: Client
|
||||
private searchManager: SearchManager
|
||||
) {}
|
||||
|
||||
setupRoutes(app: express.Application): void {
|
||||
@@ -47,10 +47,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleUnifiedSearch(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'search',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.search(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Unified search failed', {}, error as Error);
|
||||
@@ -64,10 +61,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleUnifiedTimeline(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'timeline',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.timeline(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Unified timeline failed', {}, error as Error);
|
||||
@@ -81,10 +75,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleDecisions(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'decisions',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.decisions(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Decisions search failed', {}, error as Error);
|
||||
@@ -98,10 +89,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleChanges(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'changes',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.changes(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Changes search failed', {}, error as Error);
|
||||
@@ -115,10 +103,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleHowItWorks(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'how_it_works',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.howItWorks(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'How it works search failed', {}, error as Error);
|
||||
@@ -132,10 +117,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleSearchObservations(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'search_observations',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.searchObservations(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -149,10 +131,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleSearchSessions(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'search_sessions',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.searchSessions(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -166,10 +145,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleSearchPrompts(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'search_user_prompts',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.searchUserPrompts(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -183,10 +159,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleSearchByConcept(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'find_by_concept',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.findByConcept(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -200,10 +173,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleSearchByFile(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'find_by_file',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.findByFile(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -217,10 +187,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleSearchByType(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'find_by_type',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.findByType(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -234,10 +201,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleGetRecentContext(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'get_recent_context',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.getRecentContext(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -251,10 +215,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleGetContextTimeline(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'get_context_timeline',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.getContextTimeline(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
@@ -352,10 +313,7 @@ export class SearchRoutes {
|
||||
*/
|
||||
private async handleGetTimelineByQuery(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
const result = await this.mcpClient.callTool({
|
||||
name: 'get_timeline_by_query',
|
||||
arguments: req.query
|
||||
});
|
||||
const result = await this.searchManager.getTimelineByQuery(req.query);
|
||||
res.json(result.content);
|
||||
} catch (error) {
|
||||
logger.failure('WORKER', 'Search failed', {}, error as Error);
|
||||
|
||||
Reference in New Issue
Block a user