@agimon-ai/log-sink-mcp 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -876,4 +876,5 @@ Press Ctrl+C to stop the server`);let y=async e=>{console.log(`\n\n${e} received
876
876
  `)}function Z(e){return e.option(`--db-path <path>`,`Path to SQLite database file`,`./logs/session.db`).option(`--in-memory`,`Use in-memory database`,!1)}async function Re(t,n){let r=e.u(),i=r.get(e.d.LogStorageService);await i.initializeDatabase(t.dbPath,t.inMemory);try{return await n(r)}finally{await i.disconnect()}}async function ze(e,t,n,r=Fe){try{let i=await Re(e,async e=>t(e).execute(n)),a=Le(i);return i.isError?(r.stderr(a),1):(r.stdout(a),0)}catch(e){return r.stderr(`Error executing logs command: ${e instanceof Error?e.message:String(e)}`),1}}async function Q(e,t,n){let r=await ze(e,t,n);r!==0&&process.exit(r)}function Be(e,t){if(!(!e&&!t)){if(!e||!t)throw new i.InvalidArgumentError(`Provide both --start-time and --end-time together.`);return{start:e,end:t}}}function Ve(){let t=new i.Command(`logs`).description(`Run log analysis commands that mirror MCP tool capabilities`),n=Z(new i.Command(`query`).alias(`query-logs`).description(`Query and filter log entries by level, trace ID, service, or time range`).option(`--level <level...>`,`Log level(s) to filter by`).option(`--trace-id <traceId>`,`Trace ID to filter by`).option(`--service <service...>`,`Service name(s) to filter by`).option(`--start-time <iso8601>`,`Start time for log range (ISO 8601)`).option(`--end-time <iso8601>`,`End time for log range (ISO 8601)`).option(`--limit <number>`,`Maximum number of log entries to return`,Y).action(async t=>{await Q(t,t=>new e.i(t),{level:X(t.level),traceId:t.traceId,service:t.service,startTime:t.startTime,endTime:t.endTime,limit:t.limit})})),r=Z(new i.Command(`search`).alias(`search-logs`).description(`Search log entries with full-text search and optional filters`).argument(`<search-query>`,`FTS5 search query`).option(`--service <service...>`,`Service name(s) to filter by`).option(`--level <level...>`,`Log level(s) to filter by`).option(`--start-time <iso8601>`,`Start time for log range (ISO 8601)`).option(`--end-time <iso8601>`,`End time for log range (ISO 8601)`).option(`--limit <number>`,`Maximum number of search results to return`,Y).action(async(t,n)=>{await Q(n,t=>new e.r(t),{searchQuery:t,service:n.service,level:X(n.level),startTime:n.startTime,endTime:n.endTime,limit:n.limit})})),a=Z(new i.Command(`trace`).alias(`get-trace-timeline`).description(`Get the complete trace timeline for a trace ID`).argument(`<trace-id>`,`Trace ID to inspect`).action(async(t,n)=>{await Q(n,t=>new e.a(t),{traceId:t})})),o=Z(new i.Command(`analyze-errors`).alias(`errors`).description(`Analyze error patterns and group similar errors`).option(`--trace-id <traceId>`,`Optional trace ID to scope the analysis`).option(`--start-time <iso8601>`,`Start time for error analysis (ISO 8601)`).option(`--end-time <iso8601>`,`End time for error analysis (ISO 8601)`).option(`--limit <number>`,`Maximum number of error entries to analyze`,Y).action(async t=>{await Q(t,t=>new e.l(t),{traceId:t.traceId,timeRange:Be(t.startTime,t.endTime),limit:t.limit})})),s=Z(new i.Command(`stats`).alias(`get-log-stats`).description(`Get aggregated log statistics grouped by level, service, or both`).option(`--start-time <iso8601>`,`Start time for statistics (ISO 8601)`).option(`--end-time <iso8601>`,`End time for statistics (ISO 8601)`).option(`--group-by <groupBy>`,`Group statistics by level, service, or both`,Ie).action(async t=>{await Q(t,t=>new e.s(t),{startTime:t.startTime,endTime:t.endTime,groupBy:t.groupBy})})),c=Z(new i.Command(`services`).alias(`get-services`).description(`Get the list of unique services present in the log database`).action(async t=>{await Q(t,t=>new e.o(t),{})})),l=Z(new i.Command(`clear`).alias(`clear-logs`).description(`Clear all logs from the database`).action(async t=>{await Q(t,t=>new e.c(t),{})}));return t.addCommand(n).addCommand(r).addCommand(a).addCommand(o).addCommand(s).addCommand(c).addCommand(l)}const $=Ve();function He(){return(0,n.join)((0,c.tmpdir)(),`log-sink-mcp`,`session.db`)}const Ue=new i.Command(`mcp-serve`).description(`Start MCP server with stdio transport`).option(`--cleanup`,`Stop HTTP server on MCP server shutdown`,!1).option(`--db-path <path>`,`Path to SQLite database file`,He()).option(`--in-memory`,`Use in-memory database (for testing)`,!1).option(`--registry-path <path>`,`Custom registry path or directory for service discovery`).option(`--registry-dir <path>`,`Custom registry directory for service discovery`).action(async t=>{try{let n=t.registryPath||t.registryDir;n&&(process.env.PORT_REGISTRY_PATH=n);let r=e.u(),i=r.get(e.d.LogStorageService),a=r.get(e.d.HttpServerManager);await i.initializeDatabase(t.dbPath,t.inMemory);let o=await a.ensureRunning(3100,t.dbPath);o.running||console.error(`Warning: HTTP server failed to start: ${o.error}`);let s=new e.t(e.n(r)),c=async e=>{console.error(`\nReceived ${e}, shutting down gracefully...`);try{await s.stop(),t.cleanup&&o.running&&(await a.stop(),console.error(`HTTP server stopped`)),await i.disconnect(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>c(`SIGINT`)),process.on(`SIGTERM`,()=>c(`SIGTERM`)),await s.start()}catch(e){console.error(`Failed to start MCP server:`,e),process.exit(1)}}),We=new i.Command(`start`).description(`Start HTTP and/or MCP servers with singleton coordination`).option(`--mcp-only`,`Start only the MCP server (stdio transport)`,!1).option(`--http-only`,`Start only the HTTP server`,!1).option(`-p, --port <port>`,`Port for HTTP server`,`3100`).option(`--db-path <path>`,`Path to SQLite database file`,`./logs/session.db`).option(`--in-memory`,`Use in-memory database (for testing)`,!1).option(`--registry-path <path>`,`Custom registry path or directory for service discovery`).option(`--registry-dir <path>`,`Custom registry directory for service discovery`).action(async t=>{try{let n=t.registryPath||t.registryDir;n&&(process.env.PORT_REGISTRY_PATH=n);let r=e.u(),i=r.get(e.d.LogStorageService);await i.initializeDatabase(t.dbPath,t.inMemory);let a=parseInt(t.port,10),o=!t.mcpOnly,s=!t.httpOnly;if(console.log(`šŸš€ Starting log-sink-mcp services...`),console.log(` Database: ${t.inMemory?`In-Memory`:t.dbPath}`),o){let n=await r.get(e.d.HttpServerManager).ensureRunning(a,t.dbPath);n.running?(console.log(`āœ“ HTTP Server: Running on http://localhost:${n.port} (PID: ${n.pid})`),console.log(` Health check: http://localhost:${n.port}/health`),console.log(` Log ingestion: POST http://localhost:${n.port}/logs`)):(console.error(`āœ— HTTP Server failed to start: ${n.error}`),s||process.exit(1))}if(s){console.log(`āœ“ MCP Server: Starting with stdio transport...`);let t=new e.t(e.n(r));await t.start(),console.log(`āœ“ MCP Server: Ready for connections`);let n=async n=>{console.log(`\n\n${n} received. Shutting down gracefully...`);try{await t.stop(),console.log(`āœ“ MCP server stopped`),o&&(await r.get(e.d.HttpServerManager).stop(),console.log(`āœ“ HTTP server stopped`)),await i.disconnect(),console.log(`āœ“ Database disconnected`),console.log(`Goodbye! šŸ‘‹`),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>n(`SIGINT`)),process.on(`SIGTERM`,()=>n(`SIGTERM`)),console.log(`
877
877
  Press Ctrl+C to stop the server`)}else console.log(`
878
878
  āœ“ HTTP server started in background`),console.log(`Use "log-sink-mcp stop" to stop the HTTP server`),process.exit(0)}catch(e){console.error(`Error starting services:`,e),process.exit(1)}}),Ge=new i.Command(`status`).description(`Show status of HTTP server and diagnostics`).action(async()=>{try{console.log(`šŸ“Š log-sink-mcp Status
879
- `),console.log(`─`.repeat(50));let t=await e.u().get(e.d.HttpServerManager).getStatus();t.running?(console.log(`HTTP Server: 🟢 Running`),console.log(` PID: ${t.pid}`),console.log(` Port: ${t.port}`),console.log(` Health: http://localhost:${t.port}/health`)):(console.log(`HTTP Server: šŸ”“ Not Running`),t.error&&console.log(` Error: ${t.error}`)),console.log(``);try{let e=`./logs/session.db`,t=((await s.stat(e)).size/1024/1024).toFixed(2);console.log(`Database: ${e} (${t} MB)`)}catch{console.log(`Database: Not found or not accessible`)}console.log(`─`.repeat(50)),process.exit(0)}catch(e){console.error(`Error getting status:`,e),process.exit(1)}}),Ke=new i.Command(`stop`).description(`Stop HTTP server and clean up registry/PID files`).action(async()=>{try{console.log(`šŸ›‘ Stopping log-sink-mcp services...`),await e.u().get(e.d.HttpServerManager).stop()?(console.log(`āœ“ HTTP server stopped`),console.log(`āœ“ Registry and PID files cleaned up`)):console.log(`ℹ No HTTP server was running`),console.log(`Done! šŸ‘‹`),process.exit(0)}catch(e){console.error(`Error stopping services:`,e),process.exit(1)}}),qe=(0,n.dirname)((0,r.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),Je=JSON.parse((0,t.readFileSync)((0,n.join)(qe,`../package.json`),`utf-8`));async function Ye(){let e=new i.Command;e.name(`log-sink-mcp`).description(`Log sink MCP server with HTTP ingestion and AI analysis`).version(Je.version),e.addCommand(We),e.addCommand(Ke),e.addCommand(Ge),e.addCommand(Me),e.addCommand(Ue),e.addCommand($),await e.parseAsync(process.argv)}Ye();
879
+ `),console.log(`─`.repeat(50));let t=await e.u().get(e.d.HttpServerManager).getStatus();t.running?(console.log(`HTTP Server: 🟢 Running`),console.log(` PID: ${t.pid}`),console.log(` Port: ${t.port}`),console.log(` Health: http://localhost:${t.port}/health`)):(console.log(`HTTP Server: šŸ”“ Not Running`),t.error&&console.log(` Error: ${t.error}`)),console.log(``);try{let e=`./logs/session.db`,t=((await s.stat(e)).size/1024/1024).toFixed(2);console.log(`Database: ${e} (${t} MB)`)}catch{console.log(`Database: Not found or not accessible`)}console.log(`─`.repeat(50)),process.exit(0)}catch(e){console.error(`Error getting status:`,e),process.exit(1)}}),Ke=new i.Command(`stop`).description(`Stop HTTP server and clean up registry/PID files`).action(async()=>{try{console.log(`šŸ›‘ Stopping log-sink-mcp services...`),await e.u().get(e.d.HttpServerManager).stop()?(console.log(`āœ“ HTTP server stopped`),console.log(`āœ“ Registry and PID files cleaned up`)):console.log(`ℹ No HTTP server was running`),console.log(`Done! šŸ‘‹`),process.exit(0)}catch(e){console.error(`Error stopping services:`,e),process.exit(1)}}),qe=(0,n.dirname)((0,r.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),Je=JSON.parse((0,t.readFileSync)((0,n.join)(qe,`../package.json`),`utf-8`));async function Ye(){let e=new i.Command;e.name(`log-sink-mcp`).description(`Log sink MCP server with HTTP ingestion and AI analysis`).version(Je.version),e.addCommand(We),e.addCommand(Ke),e.addCommand(Ge),e.addCommand(Me),e.addCommand(Ue),e.addCommand($),await e.parseAsync(process.argv)}Ye();
880
+ //# sourceMappingURL=cli.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.cjs","names":["z","Hono","container","TYPES","filters: { service?: string; level?: LogLevel | LogLevel[] }","styles.levelTrace","styles.levelDebug","styles.levelInfo","styles.levelWarn","styles.levelError","styles.levelFatal","filtered: Record<string, unknown>","styles.tableContainer","styles.table","styles.logEntryGroup","styles.metadataRow","styles.timestampCell","styles.serviceCell","styles.traceLink","styles.messageRow","styles.messageFullCell","styles.attributesCell","styles.input","styles.select","styles.statsContainer","styles.statCard","styles.container","styles.header","styles.controlsSection","styles.button","styles.paginationContainer","styles.paginationButton","styles.pageInfo","styles.table","styles.traceLink","styles.attributesCell","styles.logEntryGroup","styles.metadataRow","styles.timestampCell","styles.serviceCell","styles.messageRow","styles.messageFullCell","styles.levelTrace","styles.levelDebug","styles.levelInfo","styles.levelWarn","styles.levelError","styles.levelFatal","Hono","container","TYPES","z","kvObj: Record<string, unknown>","metadata: Record<string, unknown>","errorType: string | null","errorMessage: string | null","errorStack: string | null","Hono","container","TYPES","logEntries: NewLogEntry[]","DEFAULT_DB_PATH","path","Command","PortRegistryService","container","createContainer","TYPES","defaultIO: CommandIO","InvalidArgumentError","container","createContainer","TYPES","input","logsCommand","Command","QueryLogsTool","SearchLogsTool","GetTraceTimelineTool","AnalyzeErrorsTool","GetLogStatsTool","GetServicesTool","ClearLogsTool","Command","container","createContainer","TYPES","StdioTransportHandler","createServer","Command","container","createContainer","TYPES","StdioTransportHandler","createServer","Command","createContainer","TYPES","fs","Command","createContainer","TYPES","__dirname","Command"],"sources":["../src/types/log.types.ts","../src/server/dashboard/routes/api.ts","../src/server/dashboard/utils/formatters.ts","../src/server/dashboard/components/styles.ts","../src/server/dashboard/components/LogTable.tsx","../src/server/dashboard/components/SearchBar.tsx","../src/server/dashboard/components/ServiceFilter.tsx","../src/server/dashboard/components/StatsHeader.tsx","../src/server/dashboard/components/Dashboard.tsx","../src/server/dashboard/components/Layout.tsx","../src/server/dashboard/routes/dashboard.tsx","../src/server/http.ts","../src/commands/http-serve.ts","../src/commands/logs.ts","../src/commands/mcp-serve.ts","../src/commands/start.ts","../src/commands/status.ts","../src/commands/stop.ts","../src/cli.ts"],"sourcesContent":["/**\n * Log Types\n *\n * DESIGN PATTERNS:\n * - Type-first development\n * - Align with Drizzle schema structure\n *\n * CODING STANDARDS:\n * - Export all log-related types\n * - Use descriptive names\n * - Leverage schema inference where possible\n */\n\nimport type { Log as DrizzleLog, NewLog as DrizzleNewLog } from '../models/schema.js';\n\n/**\n * Log entry from database (inferred from Drizzle schema)\n */\nexport type LogEntry = DrizzleLog;\n\n/**\n * New log entry for insertion (inferred from Drizzle schema)\n */\nexport type NewLogEntry = DrizzleNewLog;\n\n/**\n * Log level enum\n */\nexport enum LogLevel {\n TRACE = 'trace',\n DEBUG = 'debug',\n INFO = 'info',\n WARN = 'warn',\n ERROR = 'error',\n FATAL = 'fatal',\n}\n\n/**\n * Query filters for log retrieval\n */\nexport interface QueryFilters {\n level?: LogLevel | LogLevel[];\n service?: string | string[];\n traceId?: string;\n spanId?: string;\n errorType?: string;\n startTime?: Date;\n endTime?: Date;\n limit?: number;\n offset?: number;\n}\n\n/**\n * Search options for full-text search\n */\nexport interface SearchOptions {\n query: string;\n service?: string | string[];\n level?: LogLevel | LogLevel[];\n startTime?: Date;\n endTime?: Date;\n limit?: number;\n offset?: number;\n}\n\n/**\n * Log aggregation result\n */\nexport interface LogAggregation {\n service: string;\n level: LogLevel;\n count: number;\n}\n\n/**\n * Trace timeline entry\n */\nexport interface TraceTimelineEntry {\n timestamp: Date;\n service: string;\n spanId: string | null;\n parentSpanId?: string | null;\n level: LogLevel;\n message: string;\n duration?: number;\n}\n","import { Hono } from 'hono';\nimport type { Container } from 'inversify';\nimport { z } from 'zod';\nimport type { LogQueryService } from '../../../services/LogQueryService.js';\nimport type { LogSearchService } from '../../../services/LogSearchService.js';\nimport { TYPES } from '../../../types/container.types.js';\nimport { LogLevel } from '../../../types/log.types.js';\n\n/**\n * Query parameter schema for /api/logs endpoint\n */\nconst logsQuerySchema = z.object({\n service: z.string().optional(),\n level: z.string().optional(),\n traceId: z.string().optional(),\n search: z.string().optional(),\n limit: z.coerce.number().min(1).max(1000).default(25),\n offset: z.coerce.number().min(0).default(0),\n});\n\n/**\n * Create API router for log dashboard\n *\n * @param container - InversifyJS container for dependency injection\n * @returns Hono router with API endpoints\n */\nexport function createApiRouter(container: Container): Hono {\n const app = new Hono();\n\n // Get services from container\n const logQueryService = container.get<LogQueryService>(TYPES.LogQueryService);\n const logSearchService = container.get<LogSearchService>(TYPES.LogSearchService);\n\n /**\n * GET /api/logs - Query logs with filters\n *\n * Query priority: search > traceId > service > level > default\n */\n app.get('/logs', async (c) => {\n try {\n // Parse and validate query parameters\n const queryParams = logsQuerySchema.parse({\n service: c.req.query('service'),\n level: c.req.query('level'),\n traceId: c.req.query('traceId'),\n search: c.req.query('search'),\n limit: c.req.query('limit'),\n offset: c.req.query('offset'),\n });\n const { service, level, traceId, search, limit, offset } = queryParams;\n\n let logs;\n let total = 0;\n\n // Priority 1: Full-text search (still uses old method - search has its own counting)\n if (search) {\n const filters: { service?: string; level?: LogLevel | LogLevel[] } = {};\n if (service) filters.service = service;\n if (level) filters.level = level as LogLevel;\n const searchResult = await logSearchService.searchLogsPaginated(search, { ...filters, offset }, limit);\n logs = searchResult.results;\n total = searchResult.total;\n }\n // Priority 2-4 and default: Use queryLogs with proper pagination\n else {\n const levels = level\n ? (level.split(',') as LogLevel[])\n : [LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL];\n\n const result = await logQueryService.queryLogs({\n level: levels,\n service: service || undefined,\n traceId: traceId || undefined,\n limit,\n offset,\n });\n\n logs = result.logs;\n total = result.total;\n }\n\n return c.json({\n logs,\n total,\n hasMore: offset + limit < total,\n });\n } catch (error) {\n console.error('Failed to query logs:', error);\n return c.json(\n {\n error: 'Failed to query logs',\n message: error instanceof Error ? error.message : String(error),\n },\n 500,\n );\n }\n });\n\n /**\n * GET /api/services - List active services\n */\n app.get('/services', async (c) => {\n try {\n const services = await logQueryService.getActiveServices();\n\n return c.json({\n services,\n total: services.length,\n });\n } catch (error) {\n console.error('Failed to get services:', error);\n return c.json(\n {\n error: 'Failed to get services',\n message: error instanceof Error ? error.message : String(error),\n },\n 500,\n );\n }\n });\n\n /**\n * GET /api/stats - Get log statistics\n */\n app.get('/stats', async (c) => {\n try {\n const stats = await logQueryService.getStatistics();\n\n // Calculate totals\n const totalLogs = stats.reduce((sum, s) => sum + s.count, 0);\n const errors = stats\n .filter((s) => s.level === 'error' || s.level === 'fatal')\n .reduce((sum, s) => sum + s.count, 0);\n const services = new Set(stats.map((s) => s.service)).size;\n\n return c.json({\n totalLogs,\n errors,\n services,\n breakdown: stats,\n });\n } catch (error) {\n console.error('Failed to get statistics:', error);\n return c.json(\n {\n error: 'Failed to get statistics',\n message: error instanceof Error ? error.message : String(error),\n },\n 500,\n );\n }\n });\n\n return app;\n}\n","/**\n * Utility formatters for dashboard display\n */\n\n/**\n * Format a Date object to a human-readable string\n *\n * @param date - Date to format\n * @returns Formatted timestamp string\n */\nexport function formatTimestamp(date: Date): string {\n return date.toLocaleString('en-US', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n}\n\n/**\n * Truncate a message to a maximum length with ellipsis\n *\n * @param text - Text to truncate\n * @param maxLength - Maximum length before truncation\n * @returns Truncated text with ellipsis if needed\n */\nexport function truncateMessage(text: string, maxLength: number): string {\n if (text.length <= maxLength) {\n return text;\n }\n return text.slice(0, maxLength - 3) + '...';\n}\n\n/**\n * Get the color hex code for a log level\n *\n * @param level - Log level (trace, debug, info, warn, error, fatal)\n * @returns Hex color code\n */\nexport function getLevelColor(level: string): string {\n const colors: Record<string, string> = {\n trace: '#9e9e9e',\n debug: '#2196f3',\n info: '#4caf50',\n warn: '#ff9800',\n error: '#f44336',\n fatal: '#d32f2f',\n };\n\n return colors[level.toLowerCase()] || '#333';\n}\n","// Plain CSS class names for SSR compatibility\nexport const container = 'dashboard-container';\nexport const header = 'dashboard-header';\nexport const controlsSection = 'controls-section';\nexport const input = 'input-field';\nexport const select = 'select-field';\nexport const button = 'btn';\nexport const tableContainer = 'table-container';\nexport const table = 'log-table';\nexport const levelTrace = 'level-trace';\nexport const levelDebug = 'level-debug';\nexport const levelInfo = 'level-info';\nexport const levelWarn = 'level-warn';\nexport const levelError = 'level-error';\nexport const levelFatal = 'level-fatal';\nexport const statsContainer = 'stats-container';\nexport const statCard = 'stat-card';\nexport const traceLink = 'trace-link';\nexport const messageCell = 'message-cell';\nexport const timestampCell = 'timestamp-cell';\nexport const serviceCell = 'service-cell';\n\n// Pagination styles\nexport const paginationContainer = 'pagination-container';\nexport const paginationButton = 'pagination-btn';\nexport const pageInfo = 'page-info';\n\n// Log entry styles\nexport const logEntryGroup = 'log-entry-group';\nexport const metadataRow = 'metadata-row';\nexport const messageRow = 'message-row';\nexport const messageFullCell = 'message-full-cell';\nexport const attributesRow = 'attributes-row';\nexport const attributesCell = 'attributes-cell';\n\n// Global styles as plain CSS string for SSR\nexport const globalStyles = `\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n font-size: 14px;\n line-height: 1.5;\n color: #333;\n background-color: #f5f5f5;\n }\n\n h1, h2, h3 {\n margin-bottom: 1rem;\n }\n\n .dashboard-container {\n max-width: 1400px;\n margin: 0 auto;\n padding: 2rem;\n }\n\n .dashboard-header {\n background-color: #fff;\n padding: 1.5rem;\n margin-bottom: 1.5rem;\n border-radius: 8px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n }\n\n .controls-section {\n background-color: #fff;\n padding: 1rem;\n margin-bottom: 1rem;\n border-radius: 8px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n display: flex;\n gap: 1rem;\n align-items: center;\n flex-wrap: wrap;\n }\n\n .input-field {\n padding: 0.5rem 0.75rem;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n flex: 1;\n min-width: 200px;\n }\n\n .input-field:focus {\n outline: none;\n border-color: #2196f3;\n }\n\n .select-field {\n padding: 0.5rem 0.75rem;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n background-color: #fff;\n cursor: pointer;\n }\n\n .select-field:focus {\n outline: none;\n border-color: #2196f3;\n }\n\n .btn {\n padding: 0.5rem 1rem;\n border: none;\n border-radius: 4px;\n font-size: 14px;\n cursor: pointer;\n background-color: #2196f3;\n color: white;\n transition: background-color 0.2s;\n }\n\n .btn:hover {\n background-color: #1976d2;\n }\n\n .btn:disabled {\n background-color: #ccc;\n cursor: not-allowed;\n }\n\n .table-container {\n background-color: #fff;\n border-radius: 8px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n }\n\n .log-table {\n width: 100%;\n border-collapse: collapse;\n table-layout: fixed;\n }\n\n .log-table th {\n background-color: #f8f9fa;\n padding: 0.75rem;\n text-align: left;\n font-weight: 600;\n border-bottom: 2px solid #dee2e6;\n position: sticky;\n top: 0;\n }\n\n .log-table th:nth-child(1) {\n width: 18%;\n }\n\n .log-table th:nth-child(2) {\n width: 10%;\n }\n\n .log-table th:nth-child(3) {\n width: 22%;\n }\n\n .log-table th:nth-child(4) {\n width: 50%;\n }\n\n .log-table td {\n padding: 0.75rem;\n border-bottom: 1px solid #dee2e6;\n vertical-align: top;\n }\n\n .log-table tr:hover {\n background-color: #f8f9fa;\n }\n\n .level-trace {\n color: #9e9e9e;\n font-weight: 600;\n }\n\n .level-debug {\n color: #2196f3;\n font-weight: 600;\n }\n\n .level-info {\n color: #4caf50;\n font-weight: 600;\n }\n\n .level-warn {\n color: #ff9800;\n font-weight: 600;\n }\n\n .level-error {\n color: #f44336;\n font-weight: 600;\n }\n\n .level-fatal {\n color: #d32f2f;\n font-weight: 600;\n }\n\n .stats-container {\n display: flex;\n gap: 1.5rem;\n margin-bottom: 1rem;\n }\n\n .stat-card {\n background-color: #fff;\n padding: 1rem;\n border-radius: 8px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n flex: 1;\n }\n\n .stat-card h3 {\n font-size: 0.875rem;\n color: #666;\n margin-bottom: 0.5rem;\n }\n\n .stat-card .value {\n font-size: 1.5rem;\n font-weight: 700;\n color: #333;\n }\n\n .trace-link {\n color: #2196f3;\n text-decoration: underline;\n cursor: pointer;\n }\n\n .trace-link:hover {\n color: #1976d2;\n }\n\n .message-cell {\n max-width: 600px;\n word-wrap: break-word;\n white-space: pre-wrap;\n font-family: 'Courier New', monospace;\n font-size: 13px;\n }\n\n .timestamp-cell {\n white-space: nowrap;\n font-size: 12px;\n color: #666;\n }\n\n .service-cell {\n font-weight: 500;\n color: #555;\n }\n\n /* Pagination styles */\n .pagination-container {\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 1rem;\n padding: 1rem;\n background-color: #f8f9fa;\n border-top: 1px solid #dee2e6;\n }\n\n .pagination-btn {\n padding: 0.5rem 1rem;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n cursor: pointer;\n background-color: #fff;\n color: #333;\n transition: background-color 0.2s, border-color 0.2s;\n }\n\n .pagination-btn:hover:not(:disabled) {\n background-color: #e9ecef;\n border-color: #adb5bd;\n }\n\n .pagination-btn:disabled {\n background-color: #f8f9fa;\n color: #adb5bd;\n cursor: not-allowed;\n border-color: #dee2e6;\n }\n\n .page-info {\n font-size: 14px;\n color: #666;\n }\n\n /* Two-row log entry styles */\n .log-entry-group {\n border-bottom: 2px solid #dee2e6;\n }\n\n .log-entry-group:last-child {\n border-bottom: none;\n }\n\n .metadata-row td {\n padding: 0.5rem 0.75rem;\n padding-bottom: 0.25rem;\n border-bottom: none;\n }\n\n .message-row td {\n padding: 0.25rem 0.75rem;\n padding-bottom: 0.75rem;\n border-bottom: none;\n }\n\n .message-full-cell {\n font-family: 'Courier New', monospace;\n font-size: 13px;\n white-space: pre-wrap;\n word-wrap: break-word;\n background-color: #f8f9fa;\n padding: 0.5rem 0.75rem;\n border-radius: 4px;\n color: #333;\n }\n\n .attributes-row td {\n padding: 0.25rem 0.75rem;\n padding-bottom: 0.75rem;\n border-bottom: none;\n }\n\n .attributes-cell {\n font-family: 'Courier New', monospace;\n font-size: 12px;\n white-space: pre-wrap;\n word-wrap: break-word;\n background-color: #e8f4fc;\n padding: 0.5rem 0.75rem;\n border-radius: 4px;\n color: #555;\n margin-top: 0.25rem;\n }\n`;\n","import type { Log } from '../../../models/schema.js';\nimport { formatTimestamp } from '../utils/formatters.js';\nimport * as styles from './styles.js';\n\n/**\n * LogTable component props\n */\ninterface LogTableProps {\n logs: Log[];\n}\n\n/**\n * Get CSS class for log level\n */\nfunction getLevelClass(level: string) {\n const levelLower = level.toLowerCase();\n if (levelLower === 'trace') return styles.levelTrace;\n if (levelLower === 'debug') return styles.levelDebug;\n if (levelLower === 'info') return styles.levelInfo;\n if (levelLower === 'warn') return styles.levelWarn;\n if (levelLower === 'error') return styles.levelError;\n if (levelLower === 'fatal') return styles.levelFatal;\n return '';\n}\n\n/**\n * Filter out trace/span IDs from metadata for display\n */\nfunction getDisplayAttributes(metadata: unknown): Record<string, unknown> | null {\n if (!metadata || typeof metadata !== 'object') return null;\n const attrs = metadata as Record<string, unknown>;\n const filtered: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(attrs)) {\n // Skip trace context fields (already shown in table)\n if (key === 'trace.id' || key === 'span.id') continue;\n filtered[key] = value;\n }\n return Object.keys(filtered).length > 0 ? filtered : null;\n}\n\n/**\n * LogTable component\n *\n * Displays logs in a multi-row layout per entry:\n * - Row 1: Metadata (Timestamp, Level, Service, Trace ID)\n * - Row 2: Full message (spanning all columns)\n * - Row 3: Attributes as JSON (if present)\n */\nexport function LogTable({ logs }: LogTableProps) {\n return (\n <div class={styles.tableContainer}>\n <table class={styles.table}>\n <thead>\n <tr>\n <th>Timestamp</th>\n <th>Level</th>\n <th>Service</th>\n <th>Trace ID</th>\n </tr>\n </thead>\n {logs.map((log) => {\n const displayAttrs = getDisplayAttributes(log.metadata);\n return (\n <tbody key={log.id} class={styles.logEntryGroup}>\n <tr class={styles.metadataRow}>\n <td class={styles.timestampCell}>{formatTimestamp(log.timestamp)}</td>\n <td class={getLevelClass(log.level)}>{log.level.toUpperCase()}</td>\n <td class={styles.serviceCell}>{log.service}</td>\n <td>\n {log.traceId ? (\n <span class={styles.traceLink} onclick={`dashboard.filterByTrace('${log.traceId}')`}>\n {log.traceId.slice(0, 8)}...\n </span>\n ) : (\n <span>-</span>\n )}\n </td>\n </tr>\n <tr class={styles.messageRow}>\n <td colspan={4} class={styles.messageFullCell}>\n <div>{log.message}</div>\n {displayAttrs && <pre class={styles.attributesCell}>{JSON.stringify(displayAttrs, null, 2)}</pre>}\n </td>\n </tr>\n </tbody>\n );\n })}\n </table>\n </div>\n );\n}\n","import * as styles from './styles.js';\n\n/**\n * SearchBar component\n *\n * Provides a search input for full-text log search\n */\nexport function SearchBar() {\n return (\n <input\n id=\"search-input\"\n type=\"text\"\n class={styles.input}\n placeholder=\"Search logs...\"\n oninput=\"dashboard.handleSearchInput(event)\"\n />\n );\n}\n","import * as styles from './styles.js';\n\n/**\n * ServiceFilter component props\n */\ninterface ServiceFilterProps {\n services: string[];\n}\n\n/**\n * ServiceFilter component\n *\n * Provides a dropdown to filter logs by service\n */\nexport function ServiceFilter({ services }: ServiceFilterProps) {\n return (\n <select id=\"service-select\" class={styles.select} onchange=\"dashboard.handleServiceChange(event)\">\n <option value=\"\">All Services</option>\n {services.map((service) => (\n <option key={service} value={service}>\n {service}\n </option>\n ))}\n </select>\n );\n}\n","import * as styles from './styles.js';\n\n/**\n * Stats data structure\n */\ninterface Stats {\n totalLogs: number;\n errors: number;\n services: number;\n}\n\n/**\n * StatsHeader component props\n */\ninterface StatsHeaderProps {\n stats: Stats;\n}\n\n/**\n * StatsHeader component\n *\n * Displays log statistics (total logs, errors, services)\n */\nexport function StatsHeader({ stats }: StatsHeaderProps) {\n return (\n <div class={styles.statsContainer}>\n <div class={styles.statCard}>\n <h3>Total Logs</h3>\n <div class=\"value\">{stats.totalLogs.toLocaleString()}</div>\n </div>\n <div class={styles.statCard}>\n <h3>Errors</h3>\n <div class=\"value\">{stats.errors.toLocaleString()}</div>\n </div>\n <div class={styles.statCard}>\n <h3>Services</h3>\n <div class=\"value\">{stats.services}</div>\n </div>\n </div>\n );\n}\n","import { raw } from 'hono/html';\nimport type { Log } from '../../../models/schema.js';\nimport { LogTable } from './LogTable.js';\nimport { SearchBar } from './SearchBar.js';\nimport { ServiceFilter } from './ServiceFilter.js';\nimport { StatsHeader } from './StatsHeader.js';\nimport * as styles from './styles.js';\n\n/**\n * Stats data structure\n */\ninterface Stats {\n totalLogs: number;\n errors: number;\n services: number;\n}\n\n/**\n * Dashboard component props\n */\ninterface DashboardProps {\n initialLogs: Log[];\n services: string[];\n stats: Stats;\n}\n\n/**\n * Dashboard component\n *\n * Main dashboard UI with auto-refresh polling, search, and filtering\n */\nexport function Dashboard({ initialLogs, services, stats }: DashboardProps) {\n return (\n <div class={styles.container}>\n <div class={styles.header}>\n <h1>Log Dashboard</h1>\n <p>Real-time log streaming with auto-refresh (3 seconds)</p>\n </div>\n\n <StatsHeader stats={stats} />\n\n <div class={styles.controlsSection}>\n <SearchBar />\n <ServiceFilter services={services} />\n <button id=\"refresh-btn\" class={styles.button} onclick=\"dashboard.manualRefresh()\">\n Refresh Now\n </button>\n </div>\n\n <LogTable logs={initialLogs} />\n\n <div id=\"pagination-controls\" class={styles.paginationContainer}>\n <button id=\"prev-btn\" class={styles.paginationButton} onclick=\"dashboard.prevPage()\" disabled>\n Previous\n </button>\n <span id=\"page-info\" class={styles.pageInfo}>\n Page 1\n </span>\n <button id=\"next-btn\" class={styles.paginationButton} onclick=\"dashboard.nextPage()\">\n Next\n </button>\n </div>\n\n <script>\n {raw(`\nclass DashboardManager {\n constructor() {\n this.filters = {\n search: '',\n service: '',\n traceId: '',\n level: '',\n };\n this.searchDebounceTimer = null;\n this.autoRefreshInterval = null;\n this.isRefreshing = false;\n\n // Pagination state\n this.currentPage = 1;\n this.pageSize = 25;\n this.totalLogs = 0;\n this.hasMore = false;\n\n this.startAutoRefresh();\n }\n\n startAutoRefresh() {\n // Poll every 3 seconds\n this.autoRefreshInterval = setInterval(() => {\n this.fetchLogs();\n }, 3000);\n }\n\n stopAutoRefresh() {\n if (this.autoRefreshInterval) {\n clearInterval(this.autoRefreshInterval);\n this.autoRefreshInterval = null;\n }\n }\n\n async fetchLogs() {\n if (this.isRefreshing) return;\n\n this.isRefreshing = true;\n\n try {\n const params = new URLSearchParams();\n if (this.filters.search) params.set('search', this.filters.search);\n if (this.filters.service) params.set('service', this.filters.service);\n if (this.filters.traceId) params.set('traceId', this.filters.traceId);\n if (this.filters.level) params.set('level', this.filters.level);\n\n // Pagination parameters\n const offset = (this.currentPage - 1) * this.pageSize;\n params.set('limit', String(this.pageSize));\n params.set('offset', String(offset));\n\n const response = await fetch('/api/logs?' + params.toString());\n const data = await response.json();\n\n // Update pagination state\n this.totalLogs = data.total || 0;\n this.hasMore = data.hasMore || false;\n\n this.updateLogTable(data.logs);\n this.updatePaginationUI();\n } catch (error) {\n console.error('Failed to fetch logs:', error);\n } finally {\n this.isRefreshing = false;\n }\n }\n\n updateLogTable(logs) {\n const table = document.querySelector('.${styles.table}');\n if (!table) return;\n\n // Remove existing tbody elements (except thead)\n const existingTbodies = table.querySelectorAll('tbody');\n existingTbodies.forEach(tbody => tbody.remove());\n\n // Add new two-row layout for each log entry\n logs.forEach(log => {\n const levelClass = this.getLevelClass(log.level);\n const timestamp = this.formatTimestamp(log.timestamp);\n const traceIdHtml = log.traceId\n ? \\`<span class=\"${styles.traceLink}\" onclick=\"dashboard.filterByTrace('\\${log.traceId}')\">\\${log.traceId.slice(0, 8)}...</span>\\`\n : '<span>-</span>';\n\n // Get display attributes (filter out trace/span IDs)\n const displayAttrs = this.getDisplayAttributes(log.metadata);\n const attrsHtml = displayAttrs\n ? \\`<pre class=\"${styles.attributesCell}\">\\${this.escapeHtml(JSON.stringify(displayAttrs, null, 2))}</pre>\\`\n : '';\n\n const tbody = document.createElement('tbody');\n tbody.className = '${styles.logEntryGroup}';\n tbody.innerHTML = \\`\n <tr class=\"${styles.metadataRow}\">\n <td class=\"${styles.timestampCell}\">\\${timestamp}</td>\n <td class=\"\\${levelClass}\">\\${log.level.toUpperCase()}</td>\n <td class=\"${styles.serviceCell}\">\\${log.service}</td>\n <td>\\${traceIdHtml}</td>\n </tr>\n <tr class=\"${styles.messageRow}\">\n <td colspan=\"4\" class=\"${styles.messageFullCell}\">\n <div>\\${this.escapeHtml(log.message)}</div>\n \\${attrsHtml}\n </td>\n </tr>\n \\`;\n table.appendChild(tbody);\n });\n }\n\n getDisplayAttributes(metadata) {\n if (!metadata || typeof metadata !== 'object') return null;\n const filtered = {};\n for (const [key, value] of Object.entries(metadata)) {\n // Skip trace context fields (already shown in table)\n if (key === 'trace.id' || key === 'span.id') continue;\n filtered[key] = value;\n }\n return Object.keys(filtered).length > 0 ? filtered : null;\n }\n\n escapeHtml(str) {\n if (typeof str !== 'string') return str;\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#039;');\n }\n\n getLevelClass(level) {\n const classes = {\n trace: '${styles.levelTrace}',\n debug: '${styles.levelDebug}',\n info: '${styles.levelInfo}',\n warn: '${styles.levelWarn}',\n error: '${styles.levelError}',\n fatal: '${styles.levelFatal}',\n };\n return classes[level.toLowerCase()] || '';\n }\n\n formatTimestamp(timestamp) {\n const date = new Date(timestamp);\n return date.toLocaleString('en-US', {\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n });\n }\n\n handleSearchInput(event) {\n clearTimeout(this.searchDebounceTimer);\n\n this.searchDebounceTimer = setTimeout(() => {\n this.filters.search = event.target.value;\n this.filters.traceId = ''; // Clear traceId when searching\n this.currentPage = 1; // Reset to first page\n this.fetchLogs();\n }, 300);\n }\n\n handleServiceChange(event) {\n this.filters.service = event.target.value;\n this.filters.traceId = ''; // Clear traceId when changing service\n this.currentPage = 1; // Reset to first page\n this.fetchLogs();\n }\n\n filterByTrace(traceId) {\n this.filters.traceId = traceId;\n this.filters.search = ''; // Clear search when filtering by trace\n this.filters.service = ''; // Clear service when filtering by trace\n this.currentPage = 1; // Reset to first page\n\n // Update UI\n const searchInput = document.getElementById('search-input');\n const serviceSelect = document.getElementById('service-select');\n if (searchInput) searchInput.value = '';\n if (serviceSelect) serviceSelect.value = '';\n\n this.fetchLogs();\n }\n\n manualRefresh() {\n this.fetchLogs();\n }\n\n // Pagination methods\n goToPage(page) {\n if (page < 1) return;\n this.currentPage = page;\n this.fetchLogs();\n }\n\n nextPage() {\n if (this.hasMore) {\n this.currentPage++;\n this.fetchLogs();\n }\n }\n\n prevPage() {\n if (this.currentPage > 1) {\n this.currentPage--;\n this.fetchLogs();\n }\n }\n\n updatePaginationUI() {\n const prevBtn = document.getElementById('prev-btn');\n const nextBtn = document.getElementById('next-btn');\n const pageInfo = document.getElementById('page-info');\n\n if (prevBtn) {\n prevBtn.disabled = this.currentPage <= 1;\n }\n\n if (nextBtn) {\n nextBtn.disabled = !this.hasMore;\n }\n\n if (pageInfo) {\n const totalPages = Math.ceil(this.totalLogs / this.pageSize) || 1;\n pageInfo.textContent = \\`Page \\${this.currentPage} of \\${totalPages}\\`;\n }\n }\n}\n\n// Initialize dashboard\nconst dashboard = new DashboardManager();\n\n// Cleanup on page unload\nwindow.addEventListener('beforeunload', () => {\n dashboard.stopAutoRefresh();\n});\n `)}\n </script>\n </div>\n );\n}\n","import { raw } from 'hono/html';\nimport { globalStyles } from './styles.js';\n\n/**\n * Layout component props\n */\ninterface LayoutProps {\n title: string;\n children: any;\n}\n\n/**\n * Layout component\n *\n * Provides the HTML wrapper with styles and meta tags for SSR\n */\nexport function Layout({ title, children }: LayoutProps) {\n return (\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>{title}</title>\n <style>{raw(globalStyles)}</style>\n </head>\n <body>{children}</body>\n </html>\n );\n}\n","import { Hono } from 'hono';\nimport type { Container } from 'inversify';\nimport type { LogQueryService } from '../../../services/LogQueryService.js';\nimport { TYPES } from '../../../types/container.types.js';\nimport { LogLevel } from '../../../types/log.types.js';\nimport { Dashboard } from '../components/Dashboard.js';\nimport { Layout } from '../components/Layout.js';\n\n/**\n * Create dashboard router for server-side rendered UI\n *\n * @param container - InversifyJS container for dependency injection\n * @returns Hono router with dashboard routes\n */\nexport function createDashboardRouter(container: Container): Hono {\n const app = new Hono();\n\n // Get services from container\n const logQueryService = container.get<LogQueryService>(TYPES.LogQueryService);\n\n /**\n * GET / - Dashboard home page with SSR\n *\n * Renders the dashboard UI with initial server-side data\n */\n app.get('/', async (c) => {\n try {\n // Fetch initial data for dashboard\n const services = await logQueryService.getActiveServices();\n const statsData = await logQueryService.getStatistics();\n const recentLogs = await logQueryService.filterByLevel(\n [LogLevel.INFO, LogLevel.WARN, LogLevel.ERROR, LogLevel.FATAL],\n 100,\n );\n\n // Calculate stats\n const totalLogs = statsData.reduce((sum, s) => sum + s.count, 0);\n const errors = statsData\n .filter((s) => s.level === 'error' || s.level === 'fatal')\n .reduce((sum, s) => sum + s.count, 0);\n const servicesCount = new Set(statsData.map((s) => s.service)).size;\n\n const stats = {\n totalLogs,\n errors,\n services: servicesCount,\n };\n\n // Render dashboard with Layout wrapper\n return c.html(\n <Layout title=\"Log Dashboard\">\n <Dashboard initialLogs={recentLogs} services={services} stats={stats} />\n </Layout>,\n );\n } catch (error) {\n console.error('Failed to render dashboard:', error);\n\n return c.html(\n <Layout title=\"Log Dashboard - Error\">\n <div style=\"padding: 2rem; text-align: center;\">\n <h1>Failed to load dashboard</h1>\n <p>{error instanceof Error ? error.message : String(error)}</p>\n <a href=\"/\" style=\"color: #2196f3; text-decoration: underline;\">\n Retry\n </a>\n </div>\n </Layout>,\n 500,\n );\n }\n });\n\n return app;\n}\n","/**\n * HTTP Server for Log Ingestion\n *\n * DESIGN PATTERNS:\n * - Hono for HTTP routing and middleware\n * - Dependency injection with InversifyJS Container\n * - Service layer for business logic\n * - Zod validation for request payloads\n *\n * CODING STANDARDS:\n * - Use Hono for HTTP routing and middleware\n * - Use rate limiting for POST /logs endpoint\n * - Use CORS middleware for cross-origin requests\n * - Use Zod for request validation\n * - Keep route handlers thin, delegate to services\n *\n * AVOID:\n * - Business logic in route handlers\n * - Missing rate limiting on write endpoints\n * - Missing validation for request payloads\n */\n\nimport { Hono } from 'hono';\nimport type { Context } from 'hono';\nimport { cors } from 'hono/cors';\nimport { rateLimiter } from 'hono-rate-limiter';\nimport type { Container } from 'inversify';\nimport { z } from 'zod';\nimport type { LogStorageService } from '../services/LogStorageService.js';\nimport { TYPES } from '../types/container.types.js';\nimport type { NewLogEntry } from '../types/log.types.js';\nimport type {\n OtlpLogRecord,\n OtlpLogsRequest,\n OtlpResourceLogs,\n OtlpResourceSpans,\n OtlpSpan,\n OtlpTraceRequest,\n} from '../types/otlp.types.js';\nimport { createApiRouter } from './dashboard/routes/api.js';\nimport { createDashboardRouter } from './dashboard/routes/dashboard.js';\n\nconst MAX_REQUEST_BYTES = 5 * 1024 * 1024;\nconst MAX_LOG_BATCH_SIZE = 1000;\nconst MAX_OTLP_RECORDS = 2000;\nconst MAX_SERVICE_NAME_LENGTH = 200;\nconst MAX_MESSAGE_LENGTH = 8000;\nconst MAX_ERROR_MESSAGE_LENGTH = 8000;\nconst MAX_ERROR_STACK_LENGTH = 32000;\nconst LOG_LEVELS = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'] as const;\n\n/**\n * Zod schema for log entry validation\n */\nconst logEntrySchema = z.object({\n timestamp: z.string().datetime().optional(),\n level: z.enum(LOG_LEVELS),\n message: z.string().min(1).max(MAX_MESSAGE_LENGTH),\n traceId: z\n .string()\n .regex(/^[0-9a-f]{32}$/i, 'traceId must be 32 hex characters')\n .optional(),\n spanId: z\n .string()\n .regex(/^[0-9a-f]{16}$/i, 'spanId must be 16 hex characters')\n .optional(),\n parentSpanId: z\n .string()\n .regex(/^[0-9a-f]{16}$/i, 'parentSpanId must be 16 hex characters')\n .optional(),\n service: z.string().min(1).max(MAX_SERVICE_NAME_LENGTH),\n hostname: z.string().max(255).optional(),\n pid: z.number().int().nonnegative().optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n errorType: z.string().max(255).optional(),\n errorMessage: z.string().max(MAX_ERROR_MESSAGE_LENGTH).optional(),\n errorStack: z.string().max(MAX_ERROR_STACK_LENGTH).optional(),\n});\n\n/**\n * Zod schema for batch log ingestion\n */\nconst batchLogsSchema = z.object({\n logs: z.array(logEntrySchema).min(1).max(MAX_LOG_BATCH_SIZE),\n});\n\nfunction parseUnixNanoToDate(value: string | undefined): Date {\n if (!value || !/^\\d+$/.test(value)) {\n throw new Error('OTLP timestamp must be a numeric unix-nanoseconds string');\n }\n\n const timestampMs = Number(BigInt(value) / BigInt(1000000));\n return new Date(timestampMs);\n}\n\nfunction isValidHexId(value: string | undefined, expectedLength: number): boolean {\n return typeof value === 'string' && new RegExp(`^[0-9a-f]{${expectedLength}}$`, 'i').test(value);\n}\n\nfunction validateRequestSize(contentLengthHeader: string | undefined): string | null {\n if (!contentLengthHeader) {\n return null;\n }\n\n const contentLength = Number(contentLengthHeader);\n if (!Number.isFinite(contentLength) || contentLength <= 0) {\n return null;\n }\n\n if (contentLength > MAX_REQUEST_BYTES) {\n return `Request body exceeds ${MAX_REQUEST_BYTES} bytes`;\n }\n\n return null;\n}\n\nasync function parseJsonRequestBody(c: Context): Promise<unknown> {\n try {\n return await c.req.json();\n } catch (error) {\n throw new Error(`Invalid JSON body: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\nfunction validateOtlpTraceRequest(body: OtlpTraceRequest): void {\n if (!Array.isArray(body.resourceSpans) || body.resourceSpans.length === 0) {\n throw new Error('Invalid OTLP trace request: missing resourceSpans');\n }\n\n let spanCount = 0;\n for (const resourceSpan of body.resourceSpans) {\n if (!Array.isArray(resourceSpan.scopeSpans)) {\n throw new Error('Invalid OTLP trace request: scopeSpans must be an array');\n }\n\n for (const scopeSpan of resourceSpan.scopeSpans) {\n if (!Array.isArray(scopeSpan.spans)) {\n throw new Error('Invalid OTLP trace request: spans must be an array');\n }\n\n for (const span of scopeSpan.spans) {\n spanCount += 1;\n if (spanCount > MAX_OTLP_RECORDS) {\n throw new Error(`OTLP trace request exceeds ${MAX_OTLP_RECORDS} spans`);\n }\n\n if (!isValidHexId(span.traceId, 32)) {\n throw new Error('Invalid OTLP trace request: traceId must be 32 hex characters');\n }\n\n if (!isValidHexId(span.spanId, 16)) {\n throw new Error('Invalid OTLP trace request: spanId must be 16 hex characters');\n }\n\n if (span.parentSpanId && !isValidHexId(span.parentSpanId, 16)) {\n throw new Error('Invalid OTLP trace request: parentSpanId must be 16 hex characters');\n }\n\n if (!span.name || span.name.length > MAX_MESSAGE_LENGTH) {\n throw new Error('Invalid OTLP trace request: span name is missing or too long');\n }\n\n parseUnixNanoToDate(span.startTimeUnixNano);\n }\n }\n }\n}\n\nfunction validateOtlpLogsRequest(body: OtlpLogsRequest): void {\n if (!Array.isArray(body.resourceLogs) || body.resourceLogs.length === 0) {\n throw new Error('Invalid OTLP logs request: missing resourceLogs');\n }\n\n let recordCount = 0;\n for (const resourceLog of body.resourceLogs) {\n if (!Array.isArray(resourceLog.scopeLogs)) {\n throw new Error('Invalid OTLP logs request: scopeLogs must be an array');\n }\n\n for (const scopeLog of resourceLog.scopeLogs) {\n if (!Array.isArray(scopeLog.logRecords)) {\n throw new Error('Invalid OTLP logs request: logRecords must be an array');\n }\n\n for (const logRecord of scopeLog.logRecords) {\n recordCount += 1;\n if (recordCount > MAX_OTLP_RECORDS) {\n throw new Error(`OTLP logs request exceeds ${MAX_OTLP_RECORDS} log records`);\n }\n\n if (logRecord.traceId && !isValidHexId(logRecord.traceId, 32)) {\n throw new Error('Invalid OTLP logs request: traceId must be 32 hex characters');\n }\n\n if (logRecord.spanId && !isValidHexId(logRecord.spanId, 16)) {\n throw new Error('Invalid OTLP logs request: spanId must be 16 hex characters');\n }\n\n if (logRecord.severityText && logRecord.severityText.length > 32) {\n throw new Error('Invalid OTLP logs request: severityText is too long');\n }\n\n parseUnixNanoToDate(logRecord.timeUnixNano);\n }\n }\n }\n}\n\n/**\n * Extract service name from OTLP resource attributes\n */\nfunction extractServiceName(resourceSpans: OtlpResourceSpans): string {\n const serviceNameAttr = resourceSpans.resource?.attributes?.find((attr) => attr.key === 'service.name');\n return serviceNameAttr?.value?.stringValue || 'unknown-service';\n}\n\n/**\n * Extract attribute value as string\n */\nfunction getAttributeValue(\n attributes:\n | Array<{\n key: string;\n value: { stringValue?: string; intValue?: string; doubleValue?: number; boolValue?: boolean };\n }>\n | undefined,\n key: string,\n): string | undefined {\n const attr = attributes?.find((a) => a.key === key);\n if (!attr) return undefined;\n const { value } = attr;\n if (value.stringValue !== undefined) return value.stringValue;\n if (value.intValue !== undefined) return value.intValue;\n if (value.doubleValue !== undefined) return String(value.doubleValue);\n if (value.boolValue !== undefined) return String(value.boolValue);\n return undefined;\n}\n\n/**\n * Extract service name from OTLP resource logs attributes\n */\nfunction extractServiceNameFromResourceLogs(resourceLogs: OtlpResourceLogs): string {\n const serviceNameAttr = resourceLogs.resource?.attributes?.find((attr) => attr.key === 'service.name');\n return serviceNameAttr?.value?.stringValue || 'unknown-service';\n}\n\n/**\n * Convert OTLP severity number to log level string\n */\nfunction severityNumberToLevel(severityNumber: number | undefined): string {\n if (severityNumber === undefined) return 'info';\n // OTEL severity: 1-4 TRACE, 5-8 DEBUG, 9-12 INFO, 13-16 WARN, 17-20 ERROR, 21-24 FATAL\n if (severityNumber <= 4) return 'trace';\n if (severityNumber <= 8) return 'debug';\n if (severityNumber <= 12) return 'info';\n if (severityNumber <= 16) return 'warn';\n if (severityNumber <= 20) return 'error';\n return 'fatal';\n}\n\n/**\n * Convert OTLP log record to NewLogEntry\n */\nfunction convertOtlpLogRecordToLogEntry(logRecord: OtlpLogRecord, serviceName: string): NewLogEntry {\n const timestamp = parseUnixNanoToDate(logRecord.timeUnixNano);\n\n // Get log level from severity\n const level = logRecord.severityText?.toLowerCase() || severityNumberToLevel(logRecord.severityNumber);\n\n // Extract message from body\n let message = '';\n if (logRecord.body?.stringValue) {\n message = logRecord.body.stringValue;\n } else if (logRecord.body?.kvlistValue) {\n // Convert key-value list to JSON string\n const kvObj: Record<string, unknown> = {};\n for (const kv of logRecord.body.kvlistValue.values) {\n const val = kv.value.stringValue ?? kv.value.intValue ?? kv.value.doubleValue ?? kv.value.boolValue;\n kvObj[kv.key] = val;\n }\n message = JSON.stringify(kvObj);\n }\n\n // Build metadata from attributes\n const metadata: Record<string, unknown> = {};\n let errorType: string | null = null;\n let errorMessage: string | null = null;\n let errorStack: string | null = null;\n\n if (logRecord.attributes) {\n for (const attr of logRecord.attributes) {\n const value = attr.value.stringValue ?? attr.value.intValue ?? attr.value.doubleValue ?? attr.value.boolValue;\n // Extract error info from standard OTEL attributes\n if (attr.key === 'exception.type') {\n errorType = String(value);\n } else if (attr.key === 'exception.message') {\n errorMessage = String(value);\n } else if (attr.key === 'exception.stacktrace') {\n errorStack = String(value);\n } else {\n metadata[attr.key] = value;\n }\n }\n }\n\n return {\n timestamp,\n level,\n message: message || `[LOG] ${serviceName}`,\n traceId: logRecord.traceId ?? null,\n spanId: logRecord.spanId ?? null,\n parentSpanId: null,\n service: serviceName,\n hostname: null,\n pid: null,\n metadata: Object.keys(metadata).length > 0 ? metadata : null,\n errorType,\n errorMessage,\n errorStack,\n };\n}\n\n/**\n * Convert OTLP span to NewLogEntry\n */\nfunction convertOtlpSpanToLogEntry(span: OtlpSpan, serviceName: string): NewLogEntry {\n const timestamp = parseUnixNanoToDate(span.startTimeUnixNano);\n\n // Determine log level from span status\n const statusCode = span.status?.code;\n let level = 'info';\n if (statusCode === 2) {\n // ERROR status\n level = 'error';\n } else if (statusCode === 1) {\n // OK status\n level = 'info';\n }\n\n // Extract error information from attributes if present\n const errorType = getAttributeValue(span.attributes, 'exception.type');\n const errorMessage = getAttributeValue(span.attributes, 'exception.message');\n const errorStack = getAttributeValue(span.attributes, 'exception.stacktrace');\n\n // Build metadata from attributes\n const metadata: Record<string, unknown> = {};\n if (span.attributes) {\n for (const attr of span.attributes) {\n const value = attr.value.stringValue ?? attr.value.intValue ?? attr.value.doubleValue ?? attr.value.boolValue;\n metadata[attr.key] = value;\n }\n }\n\n // Add span metadata\n metadata.spanName = span.name;\n metadata.spanKind = span.kind;\n if (span.status?.message) {\n metadata.statusMessage = span.status.message;\n }\n\n return {\n timestamp,\n level,\n message: `[SPAN] ${span.name}`,\n traceId: span.traceId,\n spanId: span.spanId,\n parentSpanId: span.parentSpanId ?? null,\n service: serviceName,\n hostname: null,\n pid: null,\n metadata,\n errorType: errorType ?? null,\n errorMessage: errorMessage ?? null,\n errorStack: errorStack ?? null,\n };\n}\n\n/**\n * Create HTTP server for log ingestion\n *\n * @param container - InversifyJS container with services\n * @returns Configured Hono app\n */\nexport function createHttpServer(container: Container): Hono {\n const app = new Hono();\n const logStorageService = container.get<LogStorageService>(TYPES.LogStorageService);\n\n // CORS middleware - allow all origins for trace ingestion\n // When credentials are included, origin must be specific (not '*')\n app.use(\n '*',\n cors({\n origin: (origin) => origin ?? null,\n credentials: true,\n allowMethods: ['GET', 'POST', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Accept', 'Authorization'],\n }),\n );\n\n // Health check endpoint\n app.get('/health', async (c) => {\n try {\n return c.json({\n status: 'healthy',\n service: 'log-sink-mcp-http',\n serviceName: 'log-sink-mcp-http',\n timestamp: new Date().toISOString(),\n });\n } catch (error) {\n return c.json(\n {\n status: 'unhealthy',\n error: error instanceof Error ? error.message : String(error),\n },\n 503,\n );\n }\n });\n\n // Rate limiter for POST /logs\n const logsRateLimiter = rateLimiter({\n windowMs: 60 * 1000, // 1 minute\n limit: 100, // 100 requests per minute\n standardHeaders: 'draft-6',\n keyGenerator: (c) => {\n return c.req.header('x-forwarded-for') || c.req.header('x-real-ip') || 'unknown';\n },\n });\n\n // POST /logs endpoint with rate limiting and validation\n app.post('/logs', logsRateLimiter, async (c) => {\n try {\n const requestTooLarge = validateRequestSize(c.req.header('content-length'));\n if (requestTooLarge) {\n return c.json({ success: false, error: requestTooLarge }, 413);\n }\n\n const body = await parseJsonRequestBody(c);\n\n // Validate with Zod\n const validation = batchLogsSchema.safeParse(body);\n if (!validation.success) {\n return c.json(\n {\n success: false,\n error: 'Validation failed',\n details: validation.error.issues,\n },\n 400,\n );\n }\n\n const { logs } = validation.data;\n\n // Convert string timestamps to Date objects\n const logsToInsert = logs.map((log) => ({\n ...log,\n timestamp: log.timestamp ? new Date(log.timestamp) : new Date(),\n }));\n\n // Insert batch\n const insertedLogs = await logStorageService.insertBatch(logsToInsert);\n\n return c.json(\n {\n success: true,\n count: insertedLogs.length,\n message: `Successfully inserted ${insertedLogs.length} logs`,\n },\n 201,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.startsWith('Invalid JSON body')) {\n return c.json({ success: false, error: message }, 400);\n }\n\n console.error('Failed to insert logs:', error);\n return c.json(\n {\n success: false,\n error: 'Failed to insert logs',\n },\n 500,\n );\n }\n });\n\n // POST /v1/traces endpoint for OTLP trace ingestion\n app.post('/v1/traces', logsRateLimiter, async (c) => {\n try {\n const requestTooLarge = validateRequestSize(c.req.header('content-length'));\n if (requestTooLarge) {\n return c.json({ success: false, error: requestTooLarge }, 413);\n }\n\n const body = (await parseJsonRequestBody(c)) as OtlpTraceRequest;\n validateOtlpTraceRequest(body);\n\n const logEntries: NewLogEntry[] = [];\n\n // Process each resource span\n for (const resourceSpan of body.resourceSpans) {\n const serviceName = extractServiceName(resourceSpan);\n\n // Process each scope span\n for (const scopeSpan of resourceSpan.scopeSpans) {\n // Convert each span to a log entry\n for (const span of scopeSpan.spans) {\n const logEntry = convertOtlpSpanToLogEntry(span, serviceName);\n logEntries.push(logEntry);\n }\n }\n }\n\n // Insert all log entries\n const insertedLogs = await logStorageService.insertBatch(logEntries);\n\n return c.json(\n {\n success: true,\n count: insertedLogs.length,\n message: `Successfully inserted ${insertedLogs.length} trace spans as logs`,\n },\n 201,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.startsWith('Invalid JSON body') || message.startsWith('Invalid OTLP') || message.startsWith('OTLP')) {\n return c.json({ success: false, error: message }, 400);\n }\n\n console.error('Failed to insert OTLP traces:', error);\n return c.json(\n {\n success: false,\n error: 'Failed to insert OTLP traces',\n },\n 500,\n );\n }\n });\n\n // POST /v1/logs endpoint for OTLP log ingestion\n app.post('/v1/logs', logsRateLimiter, async (c) => {\n try {\n const requestTooLarge = validateRequestSize(c.req.header('content-length'));\n if (requestTooLarge) {\n return c.json({ success: false, error: requestTooLarge }, 413);\n }\n\n const body = (await parseJsonRequestBody(c)) as OtlpLogsRequest;\n validateOtlpLogsRequest(body);\n\n const logEntries: NewLogEntry[] = [];\n\n // Process each resource log\n for (const resourceLog of body.resourceLogs) {\n const serviceName = extractServiceNameFromResourceLogs(resourceLog);\n\n // Process each scope log\n for (const scopeLog of resourceLog.scopeLogs) {\n // Convert each log record to a log entry\n for (const logRecord of scopeLog.logRecords) {\n const logEntry = convertOtlpLogRecordToLogEntry(logRecord, serviceName);\n logEntries.push(logEntry);\n }\n }\n }\n\n // Insert all log entries\n const insertedLogs = await logStorageService.insertBatch(logEntries);\n\n return c.json(\n {\n success: true,\n count: insertedLogs.length,\n message: `Successfully inserted ${insertedLogs.length} OTLP logs`,\n },\n 201,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (message.startsWith('Invalid JSON body') || message.startsWith('Invalid OTLP') || message.startsWith('OTLP')) {\n return c.json({ success: false, error: message }, 400);\n }\n\n console.error('Failed to insert OTLP logs:', error);\n return c.json(\n {\n success: false,\n error: 'Failed to insert OTLP logs',\n },\n 500,\n );\n }\n });\n\n // Mount dashboard API routes\n const apiRouter = createApiRouter(container);\n app.route('/api', apiRouter);\n\n // Mount dashboard routes (should be last to not interfere with other routes)\n const dashboardRouter = createDashboardRouter(container);\n app.route('/', dashboardRouter);\n\n return app;\n}\n","/**\n * HTTP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Async/await pattern for asynchronous operations\n * - Error handling pattern with try-catch and proper exit codes\n * - Dependency injection with InversifyJS Container\n * - Graceful shutdown pattern for SIGINT/SIGTERM\n *\n * CODING STANDARDS:\n * - Use async action handlers for asynchronous operations\n * - Provide clear option descriptions and default values\n * - Handle errors gracefully with process.exit()\n * - Log progress and errors to console\n * - Use Commander's .option() for inputs\n * - Implement graceful shutdown for server cleanup\n *\n * AVOID:\n * - Synchronous blocking operations in action handlers\n * - Missing error handling (always use try-catch)\n * - Hardcoded values (use options or environment variables)\n * - Not exiting with appropriate exit codes on errors\n * - Missing signal handlers for shutdown\n */\n\nimport { existsSync } from 'node:fs';\nimport path from 'node:path';\nimport { PortRegistryService } from '@agimon-ai/foundation-port-registry';\nimport { serve } from '@hono/node-server';\nimport { Command } from 'commander';\nimport { createContainer } from '../container/index.js';\nimport { createHttpServer } from '../server/http.js';\nimport type { LogStorageService } from '../services/LogStorageService.js';\nimport { TYPES } from '../types/container.types.js';\n\ninterface HttpServeOptions {\n port: string;\n dbPath: string;\n inMemory: boolean;\n registryPath?: string;\n}\n\nconst DEFAULT_HTTP_PORT = '3100';\nconst DEFAULT_DB_PATH = './logs/session.db';\nconst WORKSPACE_MARKERS = ['pnpm-workspace.yaml', 'nx.json', '.git'];\n\nfunction resolveWorkspaceRoot(startPath = process.cwd()): string {\n let current = path.resolve(startPath);\n\n while (true) {\n for (const marker of WORKSPACE_MARKERS) {\n if (existsSync(path.join(current, marker))) {\n return current;\n }\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n return process.cwd();\n }\n\n current = parent;\n }\n}\n\n/**\n * Start HTTP server for log ingestion with configurable port, database path, and in-memory mode\n */\nexport const httpServeCommand = new Command('http-serve')\n .description('Start HTTP server for log ingestion with configurable port, database path, and in-memory mode')\n .option('-p, --port <port>', 'Port to listen on', DEFAULT_HTTP_PORT)\n .option('--db-path <path>', 'Path to SQLite database file', DEFAULT_DB_PATH)\n .option('--in-memory', 'Use in-memory database (for testing)', false)\n .option('--registry-path <path>', 'Custom registry path or directory for service discovery')\n .action(async (options: HttpServeOptions) => {\n try {\n const requestedPort = parseInt(options.port, 10);\n const requestedPortRange = {\n min: 3000,\n max: Math.max(4999, requestedPort),\n };\n const environment = process.env.NODE_ENV || 'development';\n const repositoryPath = resolveWorkspaceRoot(process.cwd());\n const serviceName = 'log-sink-mcp-http';\n if (options.registryPath) {\n process.env.PORT_REGISTRY_PATH = options.registryPath;\n }\n const portRegistry = new PortRegistryService(process.env.PORT_REGISTRY_PATH);\n\n console.log('šŸš€ Starting HTTP server for log ingestion...');\n console.log(` Port: ${requestedPort}`);\n console.log(` Database: ${options.inMemory ? 'In-Memory' : options.dbPath}`);\n console.log(\n ` Registry path: ${process.env.PORT_REGISTRY_PATH ?? `${process.env.HOME}/.port-registry/ports.json`}`,\n );\n\n // Create InversifyJS container\n const container = createContainer();\n\n // Initialize database\n const logStorageService = container.get<LogStorageService>(TYPES.LogStorageService);\n await logStorageService.initializeDatabase(options.dbPath, options.inMemory);\n console.log('āœ“ Database initialized');\n\n // Create HTTP server\n const app = createHttpServer(container);\n\n let reserved = await portRegistry.reservePort({\n repositoryPath,\n serviceName,\n serviceType: 'tool',\n environment,\n preferredPort: requestedPort,\n pid: process.pid,\n host: '127.0.0.1',\n force: true,\n portRange: requestedPortRange,\n metadata: {\n healthCheckUrl: `http://localhost:${requestedPort}/health`,\n dbPath: options.dbPath,\n },\n });\n\n if (!reserved.success || !reserved.record) {\n throw new Error(reserved.error || 'Failed to reserve service in global registry');\n }\n\n const reservedPort = reserved.record.port;\n if (reservedPort !== requestedPort) {\n const updated = await portRegistry.reservePort({\n repositoryPath,\n serviceName,\n serviceType: 'tool',\n environment,\n preferredPort: reservedPort,\n pid: process.pid,\n host: '127.0.0.1',\n force: true,\n portRange: requestedPortRange,\n metadata: {\n healthCheckUrl: `http://localhost:${reservedPort}/health`,\n dbPath: options.dbPath,\n },\n });\n\n if (!updated.success || !updated.record) {\n throw new Error(updated.error || 'Failed to update service metadata in registry');\n }\n\n reserved = updated;\n }\n\n if (!reserved.record) {\n throw new Error('Port reservation lost before startup');\n }\n const reservedRecord = reserved.record;\n const port = reservedRecord.port;\n const healthCheckUrl = `http://localhost:${port}/health`;\n\n // Start server\n let server;\n try {\n server = serve({\n fetch: app.fetch,\n port,\n });\n } catch (error) {\n try {\n await portRegistry.releasePort({\n repositoryPath,\n serviceName,\n serviceType: 'tool',\n environment,\n pid: process.pid,\n });\n } catch {\n // Ignore best-effort cleanup errors\n }\n\n throw error;\n }\n\n console.log(`āœ“ HTTP server listening on http://localhost:${port}`);\n console.log(` Health check: ${healthCheckUrl}`);\n console.log(` Log ingestion: POST http://localhost:${port}/logs`);\n\n const registeredPort = reservedRecord.port;\n const registrationServiceName = reservedRecord.serviceName || serviceName;\n console.log(`āœ“ Service registered: ${registrationServiceName} on ${registeredPort}`);\n\n console.log('\\nPress Ctrl+C to stop the server');\n\n // Graceful shutdown handling\n const shutdown = async (signal: string) => {\n console.log(`\\n\\n${signal} received. Shutting down gracefully...`);\n\n try {\n // Close server\n server.close();\n console.log('āœ“ Server closed');\n\n // Deregister service\n try {\n await portRegistry.releasePort({\n repositoryPath,\n serviceName,\n serviceType: 'tool',\n environment,\n pid: process.pid,\n });\n console.log('āœ“ Service deregistered');\n } catch {\n // Ignore errors during cleanup\n }\n\n // Disconnect from database\n await logStorageService.disconnect();\n console.log('āœ“ Database disconnected');\n\n console.log('Goodbye! šŸ‘‹');\n process.exit(0);\n } catch (error) {\n console.error('Error during shutdown:', error);\n process.exit(1);\n }\n };\n\n // Register signal handlers\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n } catch (error) {\n console.error('Error starting HTTP server:', error);\n process.exit(1);\n }\n });\n","/**\n * Logs Command\n *\n * CLI wrappers for the MCP analysis tools so local shell workflows can use the\n * same capabilities without going through the MCP transport.\n */\n\nimport { Command, InvalidArgumentError } from 'commander';\nimport type { Container } from 'inversify';\nimport { createContainer } from '../container/index.js';\nimport type { LogStorageService } from '../services/LogStorageService.js';\nimport { AnalyzeErrorsTool } from '../tools/AnalyzeErrorsTool.js';\nimport { ClearLogsTool } from '../tools/ClearLogsTool.js';\nimport { GetLogStatsTool } from '../tools/GetLogStatsTool.js';\nimport { GetServicesTool } from '../tools/GetServicesTool.js';\nimport { GetTraceTimelineTool } from '../tools/GetTraceTimelineTool.js';\nimport { QueryLogsTool } from '../tools/QueryLogsTool.js';\nimport { SearchLogsTool } from '../tools/SearchLogsTool.js';\nimport { TYPES } from '../types/container.types.js';\nimport type { Tool } from '../types/index.js';\nimport { LogLevel } from '../types/log.types.js';\n\nconst DEFAULT_DB_PATH = './logs/session.db';\nconst VALID_LOG_LEVELS = new Set<LogLevel>(Object.values(LogLevel));\nconst VALID_STATS_GROUP_BY = new Set(['level', 'service', 'both'] as const);\n\ninterface CommonLogsCommandOptions {\n dbPath: string;\n inMemory: boolean;\n}\n\ninterface QueryCommandOptions extends CommonLogsCommandOptions {\n level?: string[];\n traceId?: string;\n service?: string[];\n startTime?: string;\n endTime?: string;\n limit?: number;\n}\n\ninterface SearchCommandOptions extends CommonLogsCommandOptions {\n level?: string[];\n service?: string[];\n startTime?: string;\n endTime?: string;\n limit?: number;\n}\n\ninterface AnalyzeErrorsCommandOptions extends CommonLogsCommandOptions {\n traceId?: string;\n startTime?: string;\n endTime?: string;\n limit?: number;\n}\n\ninterface StatsCommandOptions extends CommonLogsCommandOptions {\n startTime?: string;\n endTime?: string;\n groupBy?: 'level' | 'service' | 'both';\n}\n\nexport interface CommandIO {\n stdout(message: string): void;\n stderr(message: string): void;\n}\n\nconst defaultIO: CommandIO = {\n stdout: (message) => console.log(message),\n stderr: (message) => console.error(message),\n};\n\nfunction parsePositiveInteger(value: string): number {\n const parsed = Number.parseInt(value, 10);\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n\n return parsed;\n}\n\nfunction parseGroupBy(value: string): 'level' | 'service' | 'both' {\n if (!VALID_STATS_GROUP_BY.has(value as 'level' | 'service' | 'both')) {\n throw new InvalidArgumentError(\"Expected one of: 'level', 'service', 'both'.\");\n }\n\n return value as 'level' | 'service' | 'both';\n}\n\nfunction parseLogLevels(levels: string[] | undefined): LogLevel[] | undefined {\n if (!levels || levels.length === 0) {\n return undefined;\n }\n\n return levels.map((level) => {\n if (!VALID_LOG_LEVELS.has(level as LogLevel)) {\n throw new InvalidArgumentError(\n `Invalid log level '${level}'. Expected one of: ${Array.from(VALID_LOG_LEVELS).join(', ')}`,\n );\n }\n\n return level as LogLevel;\n });\n}\n\nfunction getCommandTextContent(result: Awaited<ReturnType<Tool<unknown>['execute']>>): string {\n return result.content\n .map((content) => {\n if (content.type === 'text') {\n return content.text;\n }\n\n return JSON.stringify(content);\n })\n .join('\\n');\n}\n\nfunction addDatabaseOptions<T extends Command>(command: T): T {\n return command\n .option('--db-path <path>', 'Path to SQLite database file', DEFAULT_DB_PATH)\n .option('--in-memory', 'Use in-memory database', false);\n}\n\nasync function withInitializedContainer<T>(\n options: CommonLogsCommandOptions,\n callback: (container: Container) => Promise<T>,\n): Promise<T> {\n const container = createContainer();\n const logStorageService = container.get<LogStorageService>(TYPES.LogStorageService);\n await logStorageService.initializeDatabase(options.dbPath, options.inMemory);\n\n try {\n return await callback(container);\n } finally {\n await logStorageService.disconnect();\n }\n}\n\nexport async function executeToolCommand<TInput>(\n options: CommonLogsCommandOptions,\n createTool: (container: Container) => Tool<TInput>,\n input: TInput,\n io: CommandIO = defaultIO,\n): Promise<number> {\n try {\n const result = await withInitializedContainer(options, async (container) => createTool(container).execute(input));\n const output = getCommandTextContent(result);\n\n if (result.isError) {\n io.stderr(output);\n return 1;\n }\n\n io.stdout(output);\n return 0;\n } catch (error) {\n io.stderr(`Error executing logs command: ${error instanceof Error ? error.message : String(error)}`);\n return 1;\n }\n}\n\nasync function runCommand<TInput>(\n options: CommonLogsCommandOptions,\n createTool: (container: Container) => Tool<TInput>,\n input: TInput,\n): Promise<void> {\n const exitCode = await executeToolCommand(options, createTool, input);\n if (exitCode !== 0) {\n process.exit(exitCode);\n }\n}\n\nfunction buildTimeRange(startTime?: string, endTime?: string): { start: string; end: string } | undefined {\n if (!startTime && !endTime) {\n return undefined;\n }\n\n if (!startTime || !endTime) {\n throw new InvalidArgumentError('Provide both --start-time and --end-time together.');\n }\n\n return {\n start: startTime,\n end: endTime,\n };\n}\n\nexport function createLogsCommand(): Command {\n const logsCommand = new Command('logs').description('Run log analysis commands that mirror MCP tool capabilities');\n\n const queryCommand = addDatabaseOptions(\n new Command('query')\n .alias('query-logs')\n .description('Query and filter log entries by level, trace ID, service, or time range')\n .option('--level <level...>', 'Log level(s) to filter by')\n .option('--trace-id <traceId>', 'Trace ID to filter by')\n .option('--service <service...>', 'Service name(s) to filter by')\n .option('--start-time <iso8601>', 'Start time for log range (ISO 8601)')\n .option('--end-time <iso8601>', 'End time for log range (ISO 8601)')\n .option('--limit <number>', 'Maximum number of log entries to return', parsePositiveInteger)\n .action(async (options: QueryCommandOptions) => {\n await runCommand(options, (container) => new QueryLogsTool(container), {\n level: parseLogLevels(options.level),\n traceId: options.traceId,\n service: options.service,\n startTime: options.startTime,\n endTime: options.endTime,\n limit: options.limit,\n });\n }),\n );\n\n const searchCommand = addDatabaseOptions(\n new Command('search')\n .alias('search-logs')\n .description('Search log entries with full-text search and optional filters')\n .argument('<search-query>', 'FTS5 search query')\n .option('--service <service...>', 'Service name(s) to filter by')\n .option('--level <level...>', 'Log level(s) to filter by')\n .option('--start-time <iso8601>', 'Start time for log range (ISO 8601)')\n .option('--end-time <iso8601>', 'End time for log range (ISO 8601)')\n .option('--limit <number>', 'Maximum number of search results to return', parsePositiveInteger)\n .action(async (searchQuery: string, options: SearchCommandOptions) => {\n await runCommand(options, (container) => new SearchLogsTool(container), {\n searchQuery,\n service: options.service,\n level: parseLogLevels(options.level),\n startTime: options.startTime,\n endTime: options.endTime,\n limit: options.limit,\n });\n }),\n );\n\n const traceCommand = addDatabaseOptions(\n new Command('trace')\n .alias('get-trace-timeline')\n .description('Get the complete trace timeline for a trace ID')\n .argument('<trace-id>', 'Trace ID to inspect')\n .action(async (traceId: string, options: CommonLogsCommandOptions) => {\n await runCommand(options, (container) => new GetTraceTimelineTool(container), { traceId });\n }),\n );\n\n const analyzeErrorsCommand = addDatabaseOptions(\n new Command('analyze-errors')\n .alias('errors')\n .description('Analyze error patterns and group similar errors')\n .option('--trace-id <traceId>', 'Optional trace ID to scope the analysis')\n .option('--start-time <iso8601>', 'Start time for error analysis (ISO 8601)')\n .option('--end-time <iso8601>', 'End time for error analysis (ISO 8601)')\n .option('--limit <number>', 'Maximum number of error entries to analyze', parsePositiveInteger)\n .action(async (options: AnalyzeErrorsCommandOptions) => {\n await runCommand(options, (container) => new AnalyzeErrorsTool(container), {\n traceId: options.traceId,\n timeRange: buildTimeRange(options.startTime, options.endTime),\n limit: options.limit,\n });\n }),\n );\n\n const statsCommand = addDatabaseOptions(\n new Command('stats')\n .alias('get-log-stats')\n .description('Get aggregated log statistics grouped by level, service, or both')\n .option('--start-time <iso8601>', 'Start time for statistics (ISO 8601)')\n .option('--end-time <iso8601>', 'End time for statistics (ISO 8601)')\n .option('--group-by <groupBy>', 'Group statistics by level, service, or both', parseGroupBy)\n .action(async (options: StatsCommandOptions) => {\n await runCommand(options, (container) => new GetLogStatsTool(container), {\n startTime: options.startTime,\n endTime: options.endTime,\n groupBy: options.groupBy,\n });\n }),\n );\n\n const servicesCommand = addDatabaseOptions(\n new Command('services')\n .alias('get-services')\n .description('Get the list of unique services present in the log database')\n .action(async (options: CommonLogsCommandOptions) => {\n await runCommand(options, (container) => new GetServicesTool(container), {});\n }),\n );\n\n const clearCommand = addDatabaseOptions(\n new Command('clear')\n .alias('clear-logs')\n .description('Clear all logs from the database')\n .action(async (options: CommonLogsCommandOptions) => {\n await runCommand(options, (container) => new ClearLogsTool(container), {});\n }),\n );\n\n return logsCommand\n .addCommand(queryCommand)\n .addCommand(searchCommand)\n .addCommand(traceCommand)\n .addCommand(analyzeErrorsCommand)\n .addCommand(statsCommand)\n .addCommand(servicesCommand)\n .addCommand(clearCommand);\n}\n\nexport const logsCommand = createLogsCommand();\n","/**\n * MCP Serve Command\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Implement proper error handling with try-catch blocks\n * - Handle process signals for graceful shutdown\n * - Provide clear CLI options and help messages\n *\n * AVOID:\n * - Hardcoded configuration values (use CLI options or environment variables)\n * - Missing error handling for transport startup\n * - Not cleaning up resources on shutdown\n */\n\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { Command } from 'commander';\nimport { createContainer } from '../container/index.js';\nimport { createServer } from '../server';\nimport type { HttpServerManager } from '../services/HttpServerManager.js';\nimport type { LogStorageService } from '../services/LogStorageService.js';\nimport { StdioTransportHandler } from '../transports/stdio';\nimport { TYPES } from '../types/container.types.js';\n\n/**\n * Get default database path in OS temp directory\n */\nfunction getDefaultDbPath(): string {\n return join(tmpdir(), 'log-sink-mcp', 'session.db');\n}\n\n/**\n * MCP Serve command - starts MCP server with stdio transport\n */\nexport const mcpServeCommand = new Command('mcp-serve')\n .description('Start MCP server with stdio transport')\n .option('--cleanup', 'Stop HTTP server on MCP server shutdown', false)\n .option('--db-path <path>', 'Path to SQLite database file', getDefaultDbPath())\n .option('--in-memory', 'Use in-memory database (for testing)', false)\n .option('--registry-path <path>', 'Custom registry path or directory for service discovery')\n .option('--registry-dir <path>', 'Custom registry directory for service discovery')\n .action(async (options) => {\n try {\n const registryPath = options.registryPath || options.registryDir;\n if (registryPath) {\n process.env.PORT_REGISTRY_PATH = registryPath;\n }\n\n // Create container for HTTP server coordination\n const container = createContainer();\n const logStorageService = container.get<LogStorageService>(TYPES.LogStorageService);\n const httpServerManager = container.get<HttpServerManager>(TYPES.HttpServerManager);\n\n // Initialize database\n await logStorageService.initializeDatabase(options.dbPath, options.inMemory);\n\n // Ensure HTTP server is running for log ingestion\n const httpStatus = await httpServerManager.ensureRunning(3100, options.dbPath);\n\n if (!httpStatus.running) {\n console.error(`Warning: HTTP server failed to start: ${httpStatus.error}`);\n }\n\n const server = createServer(container);\n const handler = new StdioTransportHandler(server);\n\n // Enhanced shutdown for stdio transport\n const shutdown = async (signal: string) => {\n console.error(`\\nReceived ${signal}, shutting down gracefully...`);\n try {\n await handler.stop();\n\n // Stop HTTP server if --cleanup flag is set\n if (options.cleanup && httpStatus.running) {\n await httpServerManager.stop();\n console.error('HTTP server stopped');\n }\n\n await logStorageService.disconnect();\n process.exit(0);\n } catch (error) {\n console.error('Error during shutdown:', error);\n process.exit(1);\n }\n };\n\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n\n await handler.start();\n } catch (error) {\n console.error('Failed to start MCP server:', error);\n process.exit(1);\n }\n });\n","/**\n * Start Command\n *\n * Unified command to start HTTP and/or MCP servers with singleton coordination.\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Async/await pattern for asynchronous operations\n * - Error handling pattern with try-catch and proper exit codes\n * - Graceful shutdown pattern with signal handling\n *\n * CODING STANDARDS:\n * - Use async action handlers for asynchronous operations\n * - Provide clear option descriptions and default values\n * - Handle errors gracefully with process.exit()\n * - Log progress and errors to console\n * - Use Commander's .option() for inputs\n *\n * AVOID:\n * - Synchronous blocking operations in action handlers\n * - Missing error handling (always use try-catch)\n * - Hardcoded values (use options or environment variables)\n * - Not exiting with appropriate exit codes on errors\n */\n\nimport { Command } from 'commander';\nimport { createContainer } from '../container/index.js';\nimport { createServer } from '../server/index.js';\nimport type { HttpServerManager } from '../services/HttpServerManager.js';\nimport type { LogStorageService } from '../services/LogStorageService.js';\nimport { StdioTransportHandler } from '../transports/stdio.js';\nimport { TYPES } from '../types/container.types.js';\n\ninterface StartOptions {\n mcpOnly: boolean;\n httpOnly: boolean;\n port: string;\n dbPath: string;\n inMemory: boolean;\n registryPath?: string;\n registryDir?: string;\n}\n\n/**\n * Start HTTP and/or MCP servers\n */\nexport const startCommand = new Command('start')\n .description('Start HTTP and/or MCP servers with singleton coordination')\n .option('--mcp-only', 'Start only the MCP server (stdio transport)', false)\n .option('--http-only', 'Start only the HTTP server', false)\n .option('-p, --port <port>', 'Port for HTTP server', '3100')\n .option('--db-path <path>', 'Path to SQLite database file', './logs/session.db')\n .option('--in-memory', 'Use in-memory database (for testing)', false)\n .option('--registry-path <path>', 'Custom registry path or directory for service discovery')\n .option('--registry-dir <path>', 'Custom registry directory for service discovery')\n .action(async (options: StartOptions) => {\n try {\n const registryPath = options.registryPath || options.registryDir;\n if (registryPath) {\n process.env.PORT_REGISTRY_PATH = registryPath;\n }\n\n // Create InversifyJS container\n const container = createContainer();\n\n // Initialize database\n const logStorageService = container.get<LogStorageService>(TYPES.LogStorageService);\n await logStorageService.initializeDatabase(options.dbPath, options.inMemory);\n\n const port = parseInt(options.port, 10);\n const startHttp = !options.mcpOnly;\n const startMcp = !options.httpOnly;\n\n console.log('šŸš€ Starting log-sink-mcp services...');\n console.log(` Database: ${options.inMemory ? 'In-Memory' : options.dbPath}`);\n\n // Start HTTP server if requested\n if (startHttp) {\n const httpServerManager = container.get<HttpServerManager>(TYPES.HttpServerManager);\n const httpStatus = await httpServerManager.ensureRunning(port, options.dbPath);\n\n if (httpStatus.running) {\n console.log(`āœ“ HTTP Server: Running on http://localhost:${httpStatus.port} (PID: ${httpStatus.pid})`);\n console.log(` Health check: http://localhost:${httpStatus.port}/health`);\n console.log(` Log ingestion: POST http://localhost:${httpStatus.port}/logs`);\n } else {\n console.error(`āœ— HTTP Server failed to start: ${httpStatus.error}`);\n if (!startMcp) {\n process.exit(1);\n }\n }\n }\n\n // Start MCP server if requested\n if (startMcp) {\n console.log('āœ“ MCP Server: Starting with stdio transport...');\n\n const mcpServer = createServer(container);\n const handler = new StdioTransportHandler(mcpServer);\n await handler.start();\n\n console.log('āœ“ MCP Server: Ready for connections');\n\n // Handle graceful shutdown\n const shutdown = async (signal: string) => {\n console.log(`\\n\\n${signal} received. Shutting down gracefully...`);\n\n try {\n // Stop MCP server\n await handler.stop();\n console.log('āœ“ MCP server stopped');\n\n // Stop HTTP server if it was started\n if (startHttp) {\n const httpServerManager = container.get<HttpServerManager>(TYPES.HttpServerManager);\n await httpServerManager.stop();\n console.log('āœ“ HTTP server stopped');\n }\n\n // Disconnect from database\n await logStorageService.disconnect();\n console.log('āœ“ Database disconnected');\n\n console.log('Goodbye! šŸ‘‹');\n process.exit(0);\n } catch (error) {\n console.error('Error during shutdown:', error);\n process.exit(1);\n }\n };\n\n // Register signal handlers\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n\n console.log('\\nPress Ctrl+C to stop the server');\n } else {\n // HTTP-only mode - no need to keep process running\n console.log('\\nāœ“ HTTP server started in background');\n console.log('Use \"log-sink-mcp stop\" to stop the HTTP server');\n process.exit(0);\n }\n } catch (error) {\n console.error('Error starting services:', error);\n process.exit(1);\n }\n });\n","/**\n * Status Command\n *\n * Shows status of HTTP server and other diagnostics.\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Async/await pattern for asynchronous operations\n * - Error handling pattern with try-catch and proper exit codes\n *\n * CODING STANDARDS:\n * - Use async action handlers for asynchronous operations\n * - Provide clear option descriptions and default values\n * - Handle errors gracefully with process.exit()\n * - Log progress and errors to console\n * - Use Commander's .option() for inputs\n *\n * AVOID:\n * - Synchronous blocking operations in action handlers\n * - Missing error handling (always use try-catch)\n * - Hardcoded values (use options or environment variables)\n * - Not exiting with appropriate exit codes on errors\n */\n\nimport * as fs from 'node:fs/promises';\nimport { Command } from 'commander';\nimport { createContainer } from '../container/index.js';\nimport type { HttpServerManager } from '../services/HttpServerManager.js';\nimport { TYPES } from '../types/container.types.js';\n\n/**\n * Show status of log-sink-mcp services\n */\nexport const statusCommand = new Command('status')\n .description('Show status of HTTP server and diagnostics')\n .action(async () => {\n try {\n console.log('šŸ“Š log-sink-mcp Status\\n');\n console.log('─'.repeat(50));\n\n // Create container to get HttpServerManager\n const container = createContainer();\n const httpServerManager = container.get<HttpServerManager>(TYPES.HttpServerManager);\n\n // Get HTTP server status\n const httpStatus = await httpServerManager.getStatus();\n\n if (httpStatus.running) {\n console.log(`HTTP Server: 🟢 Running`);\n console.log(` PID: ${httpStatus.pid}`);\n console.log(` Port: ${httpStatus.port}`);\n console.log(` Health: http://localhost:${httpStatus.port}/health`);\n } else {\n console.log(`HTTP Server: šŸ”“ Not Running`);\n if (httpStatus.error) {\n console.log(` Error: ${httpStatus.error}`);\n }\n }\n\n console.log('');\n\n // Try to get database size\n try {\n const dbPath = './logs/session.db';\n const stats = await fs.stat(dbPath);\n const sizeInMB = (stats.size / 1024 / 1024).toFixed(2);\n console.log(`Database: ${dbPath} (${sizeInMB} MB)`);\n } catch {\n console.log('Database: Not found or not accessible');\n }\n\n console.log('─'.repeat(50));\n process.exit(0);\n } catch (error) {\n console.error('Error getting status:', error);\n process.exit(1);\n }\n });\n","/**\n * Stop Command\n *\n * Stops HTTP server and cleans up registry/PID files.\n *\n * DESIGN PATTERNS:\n * - Command pattern with Commander for CLI argument parsing\n * - Async/await pattern for asynchronous operations\n * - Error handling pattern with try-catch and proper exit codes\n *\n * CODING STANDARDS:\n * - Use async action handlers for asynchronous operations\n * - Provide clear option descriptions and default values\n * - Handle errors gracefully with process.exit()\n * - Log progress and errors to console\n * - Use Commander's .option() for inputs\n *\n * AVOID:\n * - Synchronous blocking operations in action handlers\n * - Missing error handling (always use try-catch)\n * - Hardcoded values (use options or environment variables)\n * - Not exiting with appropriate exit codes on errors\n */\n\nimport { Command } from 'commander';\nimport { createContainer } from '../container/index.js';\nimport type { HttpServerManager } from '../services/HttpServerManager.js';\nimport { TYPES } from '../types/container.types.js';\n\n/**\n * Stop HTTP server\n */\nexport const stopCommand = new Command('stop')\n .description('Stop HTTP server and clean up registry/PID files')\n .action(async () => {\n try {\n console.log('šŸ›‘ Stopping log-sink-mcp services...');\n\n // Create container to get HttpServerManager\n const container = createContainer();\n const httpServerManager = container.get<HttpServerManager>(TYPES.HttpServerManager);\n\n // Stop HTTP server\n const stopped = await httpServerManager.stop();\n\n if (stopped) {\n console.log('āœ“ HTTP server stopped');\n console.log('āœ“ Registry and PID files cleaned up');\n } else {\n console.log('ℹ No HTTP server was running');\n }\n\n console.log('Done! šŸ‘‹');\n process.exit(0);\n } catch (error) {\n console.error('Error stopping services:', error);\n process.exit(1);\n }\n });\n","#!/usr/bin/env node\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n/**\n * MCP Server Entry Point\n *\n * DESIGN PATTERNS:\n * - CLI pattern with Commander for argument parsing\n * - Command pattern for organizing CLI commands\n * - Transport abstraction for multiple communication methods\n *\n * CODING STANDARDS:\n * - Use async/await for asynchronous operations\n * - Handle errors gracefully with try-catch\n * - Log important events for debugging\n * - Register all commands in main entry point\n *\n * AVOID:\n * - Hardcoding command logic in index.ts (use separate command files)\n * - Missing error handling for command execution\n */\nimport { Command } from 'commander';\nimport { httpServeCommand } from './commands/http-serve.js';\nimport { logsCommand } from './commands/logs.js';\nimport { mcpServeCommand } from './commands/mcp-serve.js';\nimport { startCommand } from './commands/start.js';\nimport { statusCommand } from './commands/status.js';\nimport { stopCommand } from './commands/stop.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf-8'));\n\n/**\n * Main entry point\n */\nasync function main() {\n const program = new Command();\n\n program\n .name('log-sink-mcp')\n .description('Log sink MCP server with HTTP ingestion and AI analysis')\n .version(packageJson.version);\n\n // Add all commands\n program.addCommand(startCommand);\n program.addCommand(stopCommand);\n program.addCommand(statusCommand);\n program.addCommand(httpServeCommand);\n program.addCommand(mcpServeCommand);\n program.addCommand(logsCommand);\n\n // Parse arguments\n await program.parseAsync(process.argv);\n}\n\nmain();\n"],"mappings":"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"}
package/dist/cli.mjs CHANGED
@@ -561,4 +561,5 @@ Press Ctrl+C to stop the server`);let b=async e=>{console.log(`\n\n${e} received
561
561
  `)}function Q(e){return e.option(`--db-path <path>`,`Path to SQLite database file`,`./logs/session.db`).option(`--in-memory`,`Use in-memory database`,!1)}async function Ie(e,t){let r=u(),i=r.get(n.LogStorageService);await i.initializeDatabase(e.dbPath,e.inMemory);try{return await t(r)}finally{await i.disconnect()}}async function Le(e,t,n,r=Me){try{let i=await Ie(e,async e=>t(e).execute(n)),a=Fe(i);return i.isError?(r.stderr(a),1):(r.stdout(a),0)}catch(e){return r.stderr(`Error executing logs command: ${e instanceof Error?e.message:String(e)}`),1}}async function $(e,t,n){let r=await Le(e,t,n);r!==0&&process.exit(r)}function Re(e,t){if(!(!e&&!t)){if(!e||!t)throw new v(`Provide both --start-time and --end-time together.`);return{start:e,end:t}}}function ze(){let n=new _(`logs`).description(`Run log analysis commands that mirror MCP tool capabilities`),a=Q(new _(`query`).alias(`query-logs`).description(`Query and filter log entries by level, trace ID, service, or time range`).option(`--level <level...>`,`Log level(s) to filter by`).option(`--trace-id <traceId>`,`Trace ID to filter by`).option(`--service <service...>`,`Service name(s) to filter by`).option(`--start-time <iso8601>`,`Start time for log range (ISO 8601)`).option(`--end-time <iso8601>`,`End time for log range (ISO 8601)`).option(`--limit <number>`,`Maximum number of log entries to return`,Z).action(async e=>{await $(e,e=>new r(e),{level:Pe(e.level),traceId:e.traceId,service:e.service,startTime:e.startTime,endTime:e.endTime,limit:e.limit})})),l=Q(new _(`search`).alias(`search-logs`).description(`Search log entries with full-text search and optional filters`).argument(`<search-query>`,`FTS5 search query`).option(`--service <service...>`,`Service name(s) to filter by`).option(`--level <level...>`,`Log level(s) to filter by`).option(`--start-time <iso8601>`,`Start time for log range (ISO 8601)`).option(`--end-time <iso8601>`,`End time for log range (ISO 8601)`).option(`--limit <number>`,`Maximum number of search results to return`,Z).action(async(e,t)=>{await $(t,e=>new s(e),{searchQuery:e,service:t.service,level:Pe(t.level),startTime:t.startTime,endTime:t.endTime,limit:t.limit})})),u=Q(new _(`trace`).alias(`get-trace-timeline`).description(`Get the complete trace timeline for a trace ID`).argument(`<trace-id>`,`Trace ID to inspect`).action(async(t,n)=>{await $(n,t=>new e(t),{traceId:t})})),d=Q(new _(`analyze-errors`).alias(`errors`).description(`Analyze error patterns and group similar errors`).option(`--trace-id <traceId>`,`Optional trace ID to scope the analysis`).option(`--start-time <iso8601>`,`Start time for error analysis (ISO 8601)`).option(`--end-time <iso8601>`,`End time for error analysis (ISO 8601)`).option(`--limit <number>`,`Maximum number of error entries to analyze`,Z).action(async e=>{await $(e,e=>new i(e),{traceId:e.traceId,timeRange:Re(e.startTime,e.endTime),limit:e.limit})})),f=Q(new _(`stats`).alias(`get-log-stats`).description(`Get aggregated log statistics grouped by level, service, or both`).option(`--start-time <iso8601>`,`Start time for statistics (ISO 8601)`).option(`--end-time <iso8601>`,`End time for statistics (ISO 8601)`).option(`--group-by <groupBy>`,`Group statistics by level, service, or both`,Ne).action(async e=>{await $(e,e=>new c(e),{startTime:e.startTime,endTime:e.endTime,groupBy:e.groupBy})})),p=Q(new _(`services`).alias(`get-services`).description(`Get the list of unique services present in the log database`).action(async e=>{await $(e,e=>new o(e),{})})),m=Q(new _(`clear`).alias(`clear-logs`).description(`Clear all logs from the database`).action(async e=>{await $(e,e=>new t(e),{})}));return n.addCommand(a).addCommand(l).addCommand(u).addCommand(d).addCommand(f).addCommand(p).addCommand(m)}const Be=ze();function Ve(){return h(b(),`log-sink-mcp`,`session.db`)}const He=new _(`mcp-serve`).description(`Start MCP server with stdio transport`).option(`--cleanup`,`Stop HTTP server on MCP server shutdown`,!1).option(`--db-path <path>`,`Path to SQLite database file`,Ve()).option(`--in-memory`,`Use in-memory database (for testing)`,!1).option(`--registry-path <path>`,`Custom registry path or directory for service discovery`).option(`--registry-dir <path>`,`Custom registry directory for service discovery`).action(async e=>{try{let t=e.registryPath||e.registryDir;t&&(process.env.PORT_REGISTRY_PATH=t);let r=u(),i=r.get(n.LogStorageService),o=r.get(n.HttpServerManager);await i.initializeDatabase(e.dbPath,e.inMemory);let s=await o.ensureRunning(3100,e.dbPath);s.running||console.error(`Warning: HTTP server failed to start: ${s.error}`);let c=new l(a(r)),d=async t=>{console.error(`\nReceived ${t}, shutting down gracefully...`);try{await c.stop(),e.cleanup&&s.running&&(await o.stop(),console.error(`HTTP server stopped`)),await i.disconnect(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>d(`SIGINT`)),process.on(`SIGTERM`,()=>d(`SIGTERM`)),await c.start()}catch(e){console.error(`Failed to start MCP server:`,e),process.exit(1)}}),Ue=new _(`start`).description(`Start HTTP and/or MCP servers with singleton coordination`).option(`--mcp-only`,`Start only the MCP server (stdio transport)`,!1).option(`--http-only`,`Start only the HTTP server`,!1).option(`-p, --port <port>`,`Port for HTTP server`,`3100`).option(`--db-path <path>`,`Path to SQLite database file`,`./logs/session.db`).option(`--in-memory`,`Use in-memory database (for testing)`,!1).option(`--registry-path <path>`,`Custom registry path or directory for service discovery`).option(`--registry-dir <path>`,`Custom registry directory for service discovery`).action(async e=>{try{let t=e.registryPath||e.registryDir;t&&(process.env.PORT_REGISTRY_PATH=t);let r=u(),i=r.get(n.LogStorageService);await i.initializeDatabase(e.dbPath,e.inMemory);let o=parseInt(e.port,10),s=!e.mcpOnly,c=!e.httpOnly;if(console.log(`šŸš€ Starting log-sink-mcp services...`),console.log(` Database: ${e.inMemory?`In-Memory`:e.dbPath}`),s){let t=await r.get(n.HttpServerManager).ensureRunning(o,e.dbPath);t.running?(console.log(`āœ“ HTTP Server: Running on http://localhost:${t.port} (PID: ${t.pid})`),console.log(` Health check: http://localhost:${t.port}/health`),console.log(` Log ingestion: POST http://localhost:${t.port}/logs`)):(console.error(`āœ— HTTP Server failed to start: ${t.error}`),c||process.exit(1))}if(c){console.log(`āœ“ MCP Server: Starting with stdio transport...`);let e=new l(a(r));await e.start(),console.log(`āœ“ MCP Server: Ready for connections`);let t=async t=>{console.log(`\n\n${t} received. Shutting down gracefully...`);try{await e.stop(),console.log(`āœ“ MCP server stopped`),s&&(await r.get(n.HttpServerManager).stop(),console.log(`āœ“ HTTP server stopped`)),await i.disconnect(),console.log(`āœ“ Database disconnected`),console.log(`Goodbye! šŸ‘‹`),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>t(`SIGINT`)),process.on(`SIGTERM`,()=>t(`SIGTERM`)),console.log(`
562
562
  Press Ctrl+C to stop the server`)}else console.log(`
563
563
  āœ“ HTTP server started in background`),console.log(`Use "log-sink-mcp stop" to stop the HTTP server`),process.exit(0)}catch(e){console.error(`Error starting services:`,e),process.exit(1)}}),We=new _(`status`).description(`Show status of HTTP server and diagnostics`).action(async()=>{try{console.log(`šŸ“Š log-sink-mcp Status
564
- `),console.log(`─`.repeat(50));let e=await u().get(n.HttpServerManager).getStatus();e.running?(console.log(`HTTP Server: 🟢 Running`),console.log(` PID: ${e.pid}`),console.log(` Port: ${e.port}`),console.log(` Health: http://localhost:${e.port}/health`)):(console.log(`HTTP Server: šŸ”“ Not Running`),e.error&&console.log(` Error: ${e.error}`)),console.log(``);try{let e=`./logs/session.db`,t=((await y.stat(e)).size/1024/1024).toFixed(2);console.log(`Database: ${e} (${t} MB)`)}catch{console.log(`Database: Not found or not accessible`)}console.log(`─`.repeat(50)),process.exit(0)}catch(e){console.error(`Error getting status:`,e),process.exit(1)}}),Ge=new _(`stop`).description(`Stop HTTP server and clean up registry/PID files`).action(async()=>{try{console.log(`šŸ›‘ Stopping log-sink-mcp services...`),await u().get(n.HttpServerManager).stop()?(console.log(`āœ“ HTTP server stopped`),console.log(`āœ“ Registry and PID files cleaned up`)):console.log(`ℹ No HTTP server was running`),console.log(`Done! šŸ‘‹`),process.exit(0)}catch(e){console.error(`Error stopping services:`,e),process.exit(1)}}),Ke=m(g(import.meta.url)),qe=JSON.parse(f(h(Ke,`../package.json`),`utf-8`));async function Je(){let e=new _;e.name(`log-sink-mcp`).description(`Log sink MCP server with HTTP ingestion and AI analysis`).version(qe.version),e.addCommand(Ue),e.addCommand(Ge),e.addCommand(We),e.addCommand(ke),e.addCommand(He),e.addCommand(Be),await e.parseAsync(process.argv)}Je();export{};
564
+ `),console.log(`─`.repeat(50));let e=await u().get(n.HttpServerManager).getStatus();e.running?(console.log(`HTTP Server: 🟢 Running`),console.log(` PID: ${e.pid}`),console.log(` Port: ${e.port}`),console.log(` Health: http://localhost:${e.port}/health`)):(console.log(`HTTP Server: šŸ”“ Not Running`),e.error&&console.log(` Error: ${e.error}`)),console.log(``);try{let e=`./logs/session.db`,t=((await y.stat(e)).size/1024/1024).toFixed(2);console.log(`Database: ${e} (${t} MB)`)}catch{console.log(`Database: Not found or not accessible`)}console.log(`─`.repeat(50)),process.exit(0)}catch(e){console.error(`Error getting status:`,e),process.exit(1)}}),Ge=new _(`stop`).description(`Stop HTTP server and clean up registry/PID files`).action(async()=>{try{console.log(`šŸ›‘ Stopping log-sink-mcp services...`),await u().get(n.HttpServerManager).stop()?(console.log(`āœ“ HTTP server stopped`),console.log(`āœ“ Registry and PID files cleaned up`)):console.log(`ℹ No HTTP server was running`),console.log(`Done! šŸ‘‹`),process.exit(0)}catch(e){console.error(`Error stopping services:`,e),process.exit(1)}}),Ke=m(g(import.meta.url)),qe=JSON.parse(f(h(Ke,`../package.json`),`utf-8`));async function Je(){let e=new _;e.name(`log-sink-mcp`).description(`Log sink MCP server with HTTP ingestion and AI analysis`).version(qe.version),e.addCommand(Ue),e.addCommand(Ge),e.addCommand(We),e.addCommand(ke),e.addCommand(He),e.addCommand(Be),await e.parseAsync(process.argv)}Je();export{};
565
+ //# sourceMappingURL=cli.mjs.map