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

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/README.md CHANGED
@@ -8,7 +8,7 @@ AI-powered log analysis MCP server with HTTP ingestion and SQLite storage.
8
8
 
9
9
  - **HTTP Server**: REST endpoint for log ingestion from any application
10
10
  - **MCP Server**: 7 AI-optimized tools for querying and analyzing logs
11
- - **SQLite Database**: Fast local storage with FTS5 full-text search
11
+ - **SQLite Database**: Fast local storage with FTS5 full-text search plus cached local embeddings
12
12
  - **Real-time Analysis**: Query logs, trace distributed requests, analyze error patterns
13
13
 
14
14
  Perfect for debugging, monitoring, and understanding application behavior through AI-powered analysis.
@@ -17,7 +17,7 @@ Perfect for debugging, monitoring, and understanding application behavior throug
17
17
 
18
18
  - ✅ **HTTP Log Ingestion**: POST logs from any application via REST API
19
19
  - ✅ **7 MCP Tools**: Query, search, trace, analyze, and manage logs
20
- - ✅ **Full-Text Search**: Fast FTS5-powered search across messages and errors
20
+ - ✅ **Hybrid Search**: FTS5-powered search plus local semantic retrieval across messages and errors
21
21
  - ✅ **Distributed Tracing**: Timeline view for trace IDs and span relationships
22
22
  - ✅ **Error Analysis**: Pattern detection and error categorization
23
23
  - ✅ **Structured Logging**: JSON metadata, trace IDs, span IDs
@@ -47,7 +47,8 @@ Perfect for debugging, monitoring, and understanding application behavior throug
47
47
 
48
48
 
49
49
  ┌─────────────────┐
50
- SQLite + FTS5
50
+ SQLite + FTS5 +
51
+ │ local vectors │
51
52
  │ ./logs/ │
52
53
  │ session.db │
53
54
  └─────────────────┘
@@ -257,11 +258,19 @@ Query all error logs from user-service
257
258
 
258
259
  ### 2. `search_logs`
259
260
 
260
- Full-text search across log messages and error messages.
261
+ Hybrid search across log messages and error messages. By default it combines:
262
+
263
+ - FTS5 exact / keyword matching
264
+ - Local semantic similarity from cached embeddings
261
265
 
262
266
  **Parameters:**
263
267
  - `searchQuery`: string (required) - Text to search for
264
- - `limit?`: number - Maximum results (default: 50)
268
+ - `mode?`: string - `hybrid` (default), `fts`, or `semantic`
269
+ - `service?`: string or string[] - Filter by service name
270
+ - `level?`: string or string[] - Filter by log level
271
+ - `startTime?`: string - ISO 8601 start time filter
272
+ - `endTime?`: string - ISO 8601 end time filter
273
+ - `limit?`: number - Maximum results (default: 100)
265
274
 
266
275
  **Example:**
267
276
  ```
@@ -526,6 +535,7 @@ bun run src/cli.ts logs <subcommand> [options]
526
535
  Subcommands:
527
536
  - `query`: Equivalent to `query_logs`
528
537
  - `search`: Equivalent to `search_logs`
538
+ - `search --mode hybrid`: Default hybrid search across FTS5 and semantic embeddings
529
539
  - `trace`: Equivalent to `get_trace_timeline`
530
540
  - `analyze-errors`: Equivalent to `analyze_errors`
531
541
  - `stats`: Equivalent to `get_log_stats`
@@ -0,0 +1 @@
1
+ var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return s}});
package/dist/cli.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const e=require(`./stdio-c5IGefy2.cjs`);let t=require(`node:fs`),n=require(`node:path`);n=e.f(n);let r=require(`node:url`),i=require(`commander`),a=require(`@agimon-ai/foundation-port-registry`),o=require(`@hono/node-server`),s=require(`node:fs/promises`);s=e.f(s);let c=require(`node:os`),l=require(`hono`),u=require(`hono/cors`),d=require(`hono-rate-limiter`),f=require(`zod`),p=require(`hono/html`),m=require(`hono/jsx/jsx-runtime`),h=function(e){return e.TRACE=`trace`,e.DEBUG=`debug`,e.INFO=`info`,e.WARN=`warn`,e.ERROR=`error`,e.FATAL=`fatal`,e}({});const g=f.z.object({service:f.z.string().optional(),level:f.z.string().optional(),traceId:f.z.string().optional(),search:f.z.string().optional(),limit:f.z.coerce.number().min(1).max(1e3).default(25),offset:f.z.coerce.number().min(0).default(0)});function _(t){let n=new l.Hono,r=t.get(e.d.LogQueryService),i=t.get(e.d.LogSearchService);return n.get(`/logs`,async e=>{try{let{service:t,level:n,traceId:a,search:o,limit:s,offset:c}=g.parse({service:e.req.query(`service`),level:e.req.query(`level`),traceId:e.req.query(`traceId`),search:e.req.query(`search`),limit:e.req.query(`limit`),offset:e.req.query(`offset`)}),l,u=0;if(o){let e={};t&&(e.service=t),n&&(e.level=n);let r=await i.searchLogsPaginated(o,{...e,offset:c},s);l=r.results,u=r.total}else{let e=n?n.split(`,`):[h.INFO,h.WARN,h.ERROR,h.FATAL],i=await r.queryLogs({level:e,service:t||void 0,traceId:a||void 0,limit:s,offset:c});l=i.logs,u=i.total}return e.json({logs:l,total:u,hasMore:c+s<u})}catch(t){return console.error(`Failed to query logs:`,t),e.json({error:`Failed to query logs`,message:t instanceof Error?t.message:String(t)},500)}}),n.get(`/services`,async e=>{try{let t=await r.getActiveServices();return e.json({services:t,total:t.length})}catch(t){return console.error(`Failed to get services:`,t),e.json({error:`Failed to get services`,message:t instanceof Error?t.message:String(t)},500)}}),n.get(`/stats`,async e=>{try{let t=await r.getStatistics(),n=t.reduce((e,t)=>e+t.count,0),i=t.filter(e=>e.level===`error`||e.level===`fatal`).reduce((e,t)=>e+t.count,0),a=new Set(t.map(e=>e.service)).size;return e.json({totalLogs:n,errors:i,services:a,breakdown:t})}catch(t){return console.error(`Failed to get statistics:`,t),e.json({error:`Failed to get statistics`,message:t instanceof Error?t.message:String(t)},500)}}),n}function v(e){return e.toLocaleString(`en-US`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1})}const ee=`dashboard-container`,te=`dashboard-header`,y=`controls-section`,ne=`input-field`,re=`select-field`,ie=`btn`,ae=`table-container`,b=`log-table`,x=`level-trace`,S=`level-debug`,C=`level-info`,w=`level-warn`,T=`level-error`,E=`level-fatal`,oe=`stats-container`,D=`stat-card`,O=`trace-link`,k=`timestamp-cell`,A=`service-cell`,se=`pagination-container`,j=`pagination-btn`,ce=`page-info`,M=`log-entry-group`,N=`metadata-row`,P=`message-row`,F=`message-full-cell`,I=`attributes-cell`,le=`
2
+ const e=require(`./chunk-DjWAcSYV.cjs`),t=require(`./stdio-DSsKUa5U.cjs`);let n=require(`node:fs`),r=require(`node:path`);r=e.t(r);let i=require(`node:url`),a=require(`commander`),o=require(`@agimon-ai/foundation-process-registry`),s=require(`@agimon-ai/foundation-port-registry`),c=require(`@hono/node-server`),l=require(`node:fs/promises`);l=e.t(l);let u=require(`node:os`),d=require(`hono`),f=require(`hono/cors`),p=require(`hono-rate-limiter`),m=require(`zod`),h=require(`hono/html`),g=require(`hono/jsx/jsx-runtime`),_=function(e){return e.TRACE=`trace`,e.DEBUG=`debug`,e.INFO=`info`,e.WARN=`warn`,e.ERROR=`error`,e.FATAL=`fatal`,e}({});const v=m.z.object({service:m.z.string().optional(),level:m.z.string().optional(),traceId:m.z.string().optional(),search:m.z.string().optional(),limit:m.z.coerce.number().min(1).max(1e3).default(25),offset:m.z.coerce.number().min(0).default(0)});function y(e){let n=new d.Hono,r=e.get(t.d.LogQueryService),i=e.get(t.d.LogSearchService);return n.get(`/logs`,async e=>{try{let{service:t,level:n,traceId:a,search:o,limit:s,offset:c}=v.parse({service:e.req.query(`service`),level:e.req.query(`level`),traceId:e.req.query(`traceId`),search:e.req.query(`search`),limit:e.req.query(`limit`),offset:e.req.query(`offset`)}),l,u=0;if(o){let e={};t&&(e.service=t),n&&(e.level=n);let r=await i.searchLogsPaginated(o,{...e,offset:c},s);l=r.results,u=r.total}else{let e=n?n.split(`,`):[_.INFO,_.WARN,_.ERROR,_.FATAL],i=await r.queryLogs({level:e,service:t||void 0,traceId:a||void 0,limit:s,offset:c});l=i.logs,u=i.total}return e.json({logs:l,total:u,hasMore:c+s<u})}catch(t){return console.error(`Failed to query logs:`,t),e.json({error:`Failed to query logs`,message:t instanceof Error?t.message:String(t)},500)}}),n.get(`/services`,async e=>{try{let t=await r.getActiveServices();return e.json({services:t,total:t.length})}catch(t){return console.error(`Failed to get services:`,t),e.json({error:`Failed to get services`,message:t instanceof Error?t.message:String(t)},500)}}),n.get(`/stats`,async e=>{try{let t=await r.getStatistics(),n=t.reduce((e,t)=>e+t.count,0),i=t.filter(e=>e.level===`error`||e.level===`fatal`).reduce((e,t)=>e+t.count,0),a=new Set(t.map(e=>e.service)).size;return e.json({totalLogs:n,errors:i,services:a,breakdown:t})}catch(t){return console.error(`Failed to get statistics:`,t),e.json({error:`Failed to get statistics`,message:t instanceof Error?t.message:String(t)},500)}}),n}function b(e){return e.toLocaleString(`en-US`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1})}const x=`dashboard-container`,ee=`dashboard-header`,te=`controls-section`,S=`input-field`,ne=`select-field`,re=`btn`,ie=`table-container`,C=`log-table`,w=`level-trace`,T=`level-debug`,E=`level-info`,D=`level-warn`,O=`level-error`,k=`level-fatal`,ae=`stats-container`,A=`stat-card`,j=`trace-link`,M=`timestamp-cell`,N=`service-cell`,oe=`pagination-container`,P=`pagination-btn`,se=`page-info`,F=`log-entry-group`,I=`metadata-row`,L=`message-row`,R=`message-full-cell`,z=`attributes-cell`,ce=`
3
3
  * {
4
4
  margin: 0;
5
5
  padding: 0;
@@ -314,7 +314,7 @@ const e=require(`./stdio-c5IGefy2.cjs`);let t=require(`node:fs`),n=require(`node
314
314
  color: #555;
315
315
  margin-top: 0.25rem;
316
316
  }
317
- `;function ue(e){let t=e.toLowerCase();return t===`trace`?x:t===`debug`?S:t===`info`?C:t===`warn`?w:t===`error`?T:t===`fatal`?E:``}function de(e){if(!e||typeof e!=`object`)return null;let t=e,n={};for(let[e,r]of Object.entries(t))e===`trace.id`||e===`span.id`||(n[e]=r);return Object.keys(n).length>0?n:null}function fe({logs:e}){return(0,m.jsx)(`div`,{class:`table-container`,children:(0,m.jsxs)(`table`,{class:b,children:[(0,m.jsx)(`thead`,{children:(0,m.jsxs)(`tr`,{children:[(0,m.jsx)(`th`,{children:`Timestamp`}),(0,m.jsx)(`th`,{children:`Level`}),(0,m.jsx)(`th`,{children:`Service`}),(0,m.jsx)(`th`,{children:`Trace ID`})]})}),e.map(e=>{let t=de(e.metadata);return(0,m.jsxs)(`tbody`,{class:M,children:[(0,m.jsxs)(`tr`,{class:N,children:[(0,m.jsx)(`td`,{class:k,children:v(e.timestamp)}),(0,m.jsx)(`td`,{class:ue(e.level),children:e.level.toUpperCase()}),(0,m.jsx)(`td`,{class:A,children:e.service}),(0,m.jsx)(`td`,{children:e.traceId?(0,m.jsxs)(`span`,{class:O,onclick:`dashboard.filterByTrace('${e.traceId}')`,children:[e.traceId.slice(0,8),`...`]}):(0,m.jsx)(`span`,{children:`-`})})]}),(0,m.jsx)(`tr`,{class:P,children:(0,m.jsxs)(`td`,{colspan:4,class:F,children:[(0,m.jsx)(`div`,{children:e.message}),t&&(0,m.jsx)(`pre`,{class:I,children:JSON.stringify(t,null,2)})]})})]},e.id)})]})})}function pe(){return(0,m.jsx)(`input`,{id:`search-input`,type:`text`,class:`input-field`,placeholder:`Search logs...`,oninput:`dashboard.handleSearchInput(event)`})}function me({services:e}){return(0,m.jsxs)(`select`,{id:`service-select`,class:`select-field`,onchange:`dashboard.handleServiceChange(event)`,children:[(0,m.jsx)(`option`,{value:``,children:`All Services`}),e.map(e=>(0,m.jsx)(`option`,{value:e,children:e},e))]})}function he({stats:e}){return(0,m.jsxs)(`div`,{class:`stats-container`,children:[(0,m.jsxs)(`div`,{class:D,children:[(0,m.jsx)(`h3`,{children:`Total Logs`}),(0,m.jsx)(`div`,{class:`value`,children:e.totalLogs.toLocaleString()})]}),(0,m.jsxs)(`div`,{class:D,children:[(0,m.jsx)(`h3`,{children:`Errors`}),(0,m.jsx)(`div`,{class:`value`,children:e.errors.toLocaleString()})]}),(0,m.jsxs)(`div`,{class:D,children:[(0,m.jsx)(`h3`,{children:`Services`}),(0,m.jsx)(`div`,{class:`value`,children:e.services})]})]})}function L({initialLogs:e,services:t,stats:n}){return(0,m.jsxs)(`div`,{class:`dashboard-container`,children:[(0,m.jsxs)(`div`,{class:`dashboard-header`,children:[(0,m.jsx)(`h1`,{children:`Log Dashboard`}),(0,m.jsx)(`p`,{children:`Real-time log streaming with auto-refresh (3 seconds)`})]}),(0,m.jsx)(he,{stats:n}),(0,m.jsxs)(`div`,{class:`controls-section`,children:[(0,m.jsx)(pe,{}),(0,m.jsx)(me,{services:t}),(0,m.jsx)(`button`,{id:`refresh-btn`,class:`btn`,onclick:`dashboard.manualRefresh()`,children:`Refresh Now`})]}),(0,m.jsx)(fe,{logs:e}),(0,m.jsxs)(`div`,{id:`pagination-controls`,class:`pagination-container`,children:[(0,m.jsx)(`button`,{id:`prev-btn`,class:j,onclick:`dashboard.prevPage()`,disabled:!0,children:`Previous`}),(0,m.jsx)(`span`,{id:`page-info`,class:`page-info`,children:`Page 1`}),(0,m.jsx)(`button`,{id:`next-btn`,class:j,onclick:`dashboard.nextPage()`,children:`Next`})]}),(0,m.jsx)(`script`,{children:(0,p.raw)(`
317
+ `;function le(e){let t=e.toLowerCase();return t===`trace`?w:t===`debug`?T:t===`info`?E:t===`warn`?D:t===`error`?O:t===`fatal`?k:``}function ue(e){if(!e||typeof e!=`object`)return null;let t=e,n={};for(let[e,r]of Object.entries(t))e===`trace.id`||e===`span.id`||(n[e]=r);return Object.keys(n).length>0?n:null}function de({logs:e}){return(0,g.jsx)(`div`,{class:`table-container`,children:(0,g.jsxs)(`table`,{class:C,children:[(0,g.jsx)(`thead`,{children:(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`th`,{children:`Timestamp`}),(0,g.jsx)(`th`,{children:`Level`}),(0,g.jsx)(`th`,{children:`Service`}),(0,g.jsx)(`th`,{children:`Trace ID`})]})}),e.map(e=>{let t=ue(e.metadata);return(0,g.jsxs)(`tbody`,{class:F,children:[(0,g.jsxs)(`tr`,{class:I,children:[(0,g.jsx)(`td`,{class:M,children:b(e.timestamp)}),(0,g.jsx)(`td`,{class:le(e.level),children:e.level.toUpperCase()}),(0,g.jsx)(`td`,{class:N,children:e.service}),(0,g.jsx)(`td`,{children:e.traceId?(0,g.jsxs)(`span`,{class:j,onclick:`dashboard.filterByTrace('${e.traceId}')`,children:[e.traceId.slice(0,8),`...`]}):(0,g.jsx)(`span`,{children:`-`})})]}),(0,g.jsx)(`tr`,{class:L,children:(0,g.jsxs)(`td`,{colspan:4,class:R,children:[(0,g.jsx)(`div`,{children:e.message}),t&&(0,g.jsx)(`pre`,{class:z,children:JSON.stringify(t,null,2)})]})})]},e.id)})]})})}function fe(){return(0,g.jsx)(`input`,{id:`search-input`,type:`text`,class:`input-field`,placeholder:`Search logs...`,oninput:`dashboard.handleSearchInput(event)`})}function pe({services:e}){return(0,g.jsxs)(`select`,{id:`service-select`,class:`select-field`,onchange:`dashboard.handleServiceChange(event)`,children:[(0,g.jsx)(`option`,{value:``,children:`All Services`}),e.map(e=>(0,g.jsx)(`option`,{value:e,children:e},e))]})}function me({stats:e}){return(0,g.jsxs)(`div`,{class:`stats-container`,children:[(0,g.jsxs)(`div`,{class:A,children:[(0,g.jsx)(`h3`,{children:`Total Logs`}),(0,g.jsx)(`div`,{class:`value`,children:e.totalLogs.toLocaleString()})]}),(0,g.jsxs)(`div`,{class:A,children:[(0,g.jsx)(`h3`,{children:`Errors`}),(0,g.jsx)(`div`,{class:`value`,children:e.errors.toLocaleString()})]}),(0,g.jsxs)(`div`,{class:A,children:[(0,g.jsx)(`h3`,{children:`Services`}),(0,g.jsx)(`div`,{class:`value`,children:e.services})]})]})}function he({initialLogs:e,services:t,stats:n}){return(0,g.jsxs)(`div`,{class:`dashboard-container`,children:[(0,g.jsxs)(`div`,{class:`dashboard-header`,children:[(0,g.jsx)(`h1`,{children:`Log Dashboard`}),(0,g.jsx)(`p`,{children:`Real-time log streaming with auto-refresh (3 seconds)`})]}),(0,g.jsx)(me,{stats:n}),(0,g.jsxs)(`div`,{class:`controls-section`,children:[(0,g.jsx)(fe,{}),(0,g.jsx)(pe,{services:t}),(0,g.jsx)(`button`,{id:`refresh-btn`,class:`btn`,onclick:`dashboard.manualRefresh()`,children:`Refresh Now`})]}),(0,g.jsx)(de,{logs:e}),(0,g.jsxs)(`div`,{id:`pagination-controls`,class:`pagination-container`,children:[(0,g.jsx)(`button`,{id:`prev-btn`,class:P,onclick:`dashboard.prevPage()`,disabled:!0,children:`Previous`}),(0,g.jsx)(`span`,{id:`page-info`,class:`page-info`,children:`Page 1`}),(0,g.jsx)(`button`,{id:`next-btn`,class:P,onclick:`dashboard.nextPage()`,children:`Next`})]}),(0,g.jsx)(`script`,{children:(0,h.raw)(`
318
318
  class DashboardManager {
319
319
  constructor() {
320
320
  this.filters = {
@@ -384,7 +384,7 @@ class DashboardManager {
384
384
  }
385
385
 
386
386
  updateLogTable(logs) {
387
- const table = document.querySelector('.${b}');
387
+ const table = document.querySelector('.${C}');
388
388
  if (!table) return;
389
389
 
390
390
  // Remove existing tbody elements (except thead)
@@ -396,26 +396,26 @@ class DashboardManager {
396
396
  const levelClass = this.getLevelClass(log.level);
397
397
  const timestamp = this.formatTimestamp(log.timestamp);
398
398
  const traceIdHtml = log.traceId
399
- ? \`<span class="${O}" onclick="dashboard.filterByTrace('\${log.traceId}')">\${log.traceId.slice(0, 8)}...</span>\`
399
+ ? \`<span class="${j}" onclick="dashboard.filterByTrace('\${log.traceId}')">\${log.traceId.slice(0, 8)}...</span>\`
400
400
  : '<span>-</span>';
401
401
 
402
402
  // Get display attributes (filter out trace/span IDs)
403
403
  const displayAttrs = this.getDisplayAttributes(log.metadata);
404
404
  const attrsHtml = displayAttrs
405
- ? \`<pre class="${I}">\${this.escapeHtml(JSON.stringify(displayAttrs, null, 2))}</pre>\`
405
+ ? \`<pre class="${z}">\${this.escapeHtml(JSON.stringify(displayAttrs, null, 2))}</pre>\`
406
406
  : '';
407
407
 
408
408
  const tbody = document.createElement('tbody');
409
- tbody.className = '${M}';
409
+ tbody.className = '${F}';
410
410
  tbody.innerHTML = \`
411
- <tr class="${N}">
412
- <td class="${k}">\${timestamp}</td>
411
+ <tr class="${I}">
412
+ <td class="${M}">\${timestamp}</td>
413
413
  <td class="\${levelClass}">\${log.level.toUpperCase()}</td>
414
- <td class="${A}">\${log.service}</td>
414
+ <td class="${N}">\${log.service}</td>
415
415
  <td>\${traceIdHtml}</td>
416
416
  </tr>
417
- <tr class="${P}">
418
- <td colspan="4" class="${F}">
417
+ <tr class="${L}">
418
+ <td colspan="4" class="${R}">
419
419
  <div>\${this.escapeHtml(log.message)}</div>
420
420
  \${attrsHtml}
421
421
  </td>
@@ -448,12 +448,12 @@ class DashboardManager {
448
448
 
449
449
  getLevelClass(level) {
450
450
  const classes = {
451
- trace: '${x}',
452
- debug: '${S}',
453
- info: '${C}',
454
- warn: '${w}',
455
- error: '${T}',
456
- fatal: '${E}',
451
+ trace: '${w}',
452
+ debug: '${T}',
453
+ info: '${E}',
454
+ warn: '${D}',
455
+ error: '${O}',
456
+ fatal: '${k}',
457
457
  };
458
458
  return classes[level.toLowerCase()] || '';
459
459
  }
@@ -556,7 +556,7 @@ const dashboard = new DashboardManager();
556
556
  window.addEventListener('beforeunload', () => {
557
557
  dashboard.stopAutoRefresh();
558
558
  });
559
- `)})]})}function R({title:e,children:t}){return(0,m.jsxs)(`html`,{lang:`en`,children:[(0,m.jsxs)(`head`,{children:[(0,m.jsx)(`meta`,{charset:`UTF-8`}),(0,m.jsx)(`meta`,{name:`viewport`,content:`width=device-width, initial-scale=1.0`}),(0,m.jsx)(`title`,{children:e}),(0,m.jsx)(`style`,{children:(0,p.raw)(`
559
+ `)})]})}function B({title:e,children:t}){return(0,g.jsxs)(`html`,{lang:`en`,children:[(0,g.jsxs)(`head`,{children:[(0,g.jsx)(`meta`,{charset:`UTF-8`}),(0,g.jsx)(`meta`,{name:`viewport`,content:`width=device-width, initial-scale=1.0`}),(0,g.jsx)(`title`,{children:e}),(0,g.jsx)(`style`,{children:(0,h.raw)(`
560
560
  * {
561
561
  margin: 0;
562
562
  padding: 0;
@@ -871,10 +871,10 @@ window.addEventListener('beforeunload', () => {
871
871
  color: #555;
872
872
  margin-top: 0.25rem;
873
873
  }
874
- `)})]}),(0,m.jsx)(`body`,{children:t})]})}function z(t){let n=new l.Hono,r=t.get(e.d.LogQueryService);return n.get(`/`,async e=>{try{let t=await r.getActiveServices(),n=await r.getStatistics(),i=await r.filterByLevel([h.INFO,h.WARN,h.ERROR,h.FATAL],100),a={totalLogs:n.reduce((e,t)=>e+t.count,0),errors:n.filter(e=>e.level===`error`||e.level===`fatal`).reduce((e,t)=>e+t.count,0),services:new Set(n.map(e=>e.service)).size};return e.html((0,m.jsx)(R,{title:`Log Dashboard`,children:(0,m.jsx)(L,{initialLogs:i,services:t,stats:a})}))}catch(t){return console.error(`Failed to render dashboard:`,t),e.html((0,m.jsx)(R,{title:`Log Dashboard - Error`,children:(0,m.jsxs)(`div`,{style:`padding: 2rem; text-align: center;`,children:[(0,m.jsx)(`h1`,{children:`Failed to load dashboard`}),(0,m.jsx)(`p`,{children:t instanceof Error?t.message:String(t)}),(0,m.jsx)(`a`,{href:`/`,style:`color: #2196f3; text-decoration: underline;`,children:`Retry`})]})}),500)}}),n}const B=5*1024*1024,ge=1e3,V=2e3,_e=200,H=8e3,ve=f.z.object({timestamp:f.z.string().datetime().optional(),level:f.z.enum([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]),message:f.z.string().min(1).max(H),traceId:f.z.string().regex(/^[0-9a-f]{32}$/i,`traceId must be 32 hex characters`).optional(),spanId:f.z.string().regex(/^[0-9a-f]{16}$/i,`spanId must be 16 hex characters`).optional(),parentSpanId:f.z.string().regex(/^[0-9a-f]{16}$/i,`parentSpanId must be 16 hex characters`).optional(),service:f.z.string().min(1).max(200),hostname:f.z.string().max(255).optional(),pid:f.z.number().int().nonnegative().optional(),metadata:f.z.record(f.z.string(),f.z.unknown()).optional(),errorType:f.z.string().max(255).optional(),errorMessage:f.z.string().max(8e3).optional(),errorStack:f.z.string().max(32e3).optional()}),ye=f.z.object({logs:f.z.array(ve).min(1).max(1e3)});function U(e){if(!e||!/^\d+$/.test(e))throw Error(`OTLP timestamp must be a numeric unix-nanoseconds string`);let t=Number(BigInt(e)/BigInt(1e6));return new Date(t)}function W(e,t){return typeof e==`string`&&RegExp(`^[0-9a-f]{${t}}$`,`i`).test(e)}function G(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<=0?null:t>B?`Request body exceeds ${B} bytes`:null}async function K(e){try{return await e.req.json()}catch(e){throw Error(`Invalid JSON body: ${e instanceof Error?e.message:String(e)}`)}}function be(e){if(!Array.isArray(e.resourceSpans)||e.resourceSpans.length===0)throw Error(`Invalid OTLP trace request: missing resourceSpans`);let t=0;for(let n of e.resourceSpans){if(!Array.isArray(n.scopeSpans))throw Error(`Invalid OTLP trace request: scopeSpans must be an array`);for(let e of n.scopeSpans){if(!Array.isArray(e.spans))throw Error(`Invalid OTLP trace request: spans must be an array`);for(let n of e.spans){if(t+=1,t>V)throw Error(`OTLP trace request exceeds ${V} spans`);if(!W(n.traceId,32))throw Error(`Invalid OTLP trace request: traceId must be 32 hex characters`);if(!W(n.spanId,16))throw Error(`Invalid OTLP trace request: spanId must be 16 hex characters`);if(n.parentSpanId&&!W(n.parentSpanId,16))throw Error(`Invalid OTLP trace request: parentSpanId must be 16 hex characters`);if(!n.name||n.name.length>H)throw Error(`Invalid OTLP trace request: span name is missing or too long`);U(n.startTimeUnixNano)}}}}function xe(e){if(!Array.isArray(e.resourceLogs)||e.resourceLogs.length===0)throw Error(`Invalid OTLP logs request: missing resourceLogs`);let t=0;for(let n of e.resourceLogs){if(!Array.isArray(n.scopeLogs))throw Error(`Invalid OTLP logs request: scopeLogs must be an array`);for(let e of n.scopeLogs){if(!Array.isArray(e.logRecords))throw Error(`Invalid OTLP logs request: logRecords must be an array`);for(let n of e.logRecords){if(t+=1,t>V)throw Error(`OTLP logs request exceeds ${V} log records`);if(n.traceId&&!W(n.traceId,32))throw Error(`Invalid OTLP logs request: traceId must be 32 hex characters`);if(n.spanId&&!W(n.spanId,16))throw Error(`Invalid OTLP logs request: spanId must be 16 hex characters`);if(n.severityText&&n.severityText.length>32)throw Error(`Invalid OTLP logs request: severityText is too long`);U(n.timeUnixNano)}}}}function Se(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function q(e,t){let n=e?.find(e=>e.key===t);if(!n)return;let{value:r}=n;if(r.stringValue!==void 0)return r.stringValue;if(r.intValue!==void 0)return r.intValue;if(r.doubleValue!==void 0)return String(r.doubleValue);if(r.boolValue!==void 0)return String(r.boolValue)}function Ce(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function we(e){return e===void 0?`info`:e<=4?`trace`:e<=8?`debug`:e<=12?`info`:e<=16?`warn`:e<=20?`error`:`fatal`}function Te(e,t){let n=U(e.timeUnixNano),r=e.severityText?.toLowerCase()||we(e.severityNumber),i=``;if(e.body?.stringValue)i=e.body.stringValue;else if(e.body?.kvlistValue){let t={};for(let n of e.body.kvlistValue.values){let e=n.value.stringValue??n.value.intValue??n.value.doubleValue??n.value.boolValue;t[n.key]=e}i=JSON.stringify(t)}let a={},o=null,s=null,c=null;if(e.attributes)for(let t of e.attributes){let e=t.value.stringValue??t.value.intValue??t.value.doubleValue??t.value.boolValue;t.key===`exception.type`?o=String(e):t.key===`exception.message`?s=String(e):t.key===`exception.stacktrace`?c=String(e):a[t.key]=e}return{timestamp:n,level:r,message:i||`[LOG] ${t}`,traceId:e.traceId??null,spanId:e.spanId??null,parentSpanId:null,service:t,hostname:null,pid:null,metadata:Object.keys(a).length>0?a:null,errorType:o,errorMessage:s,errorStack:c}}function Ee(e,t){let n=U(e.startTimeUnixNano),r=e.status?.code,i=`info`;r===2?i=`error`:r===1&&(i=`info`);let a=q(e.attributes,`exception.type`),o=q(e.attributes,`exception.message`),s=q(e.attributes,`exception.stacktrace`),c={};if(e.attributes)for(let t of e.attributes){let e=t.value.stringValue??t.value.intValue??t.value.doubleValue??t.value.boolValue;c[t.key]=e}return c.spanName=e.name,c.spanKind=e.kind,e.status?.message&&(c.statusMessage=e.status.message),{timestamp:n,level:i,message:`[SPAN] ${e.name}`,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId??null,service:t,hostname:null,pid:null,metadata:c,errorType:a??null,errorMessage:o??null,errorStack:s??null}}function De(t){let n=new l.Hono,r=t.get(e.d.LogStorageService);n.use(`*`,(0,u.cors)({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`,`Authorization`]})),n.get(`/health`,async e=>{try{return e.json({status:`healthy`,service:`log-sink-mcp-http`,serviceName:`log-sink-mcp-http`,timestamp:new Date().toISOString()})}catch(t){return e.json({status:`unhealthy`,error:t instanceof Error?t.message:String(t)},503)}});let i=(0,d.rateLimiter)({windowMs:60*1e3,limit:100,standardHeaders:`draft-6`,keyGenerator:e=>e.req.header(`x-forwarded-for`)||e.req.header(`x-real-ip`)||`unknown`});n.post(`/logs`,i,async e=>{try{let t=G(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await K(e),i=ye.safeParse(n);if(!i.success)return e.json({success:!1,error:`Validation failed`,details:i.error.issues},400);let{logs:a}=i.data,o=a.map(e=>({...e,timestamp:e.timestamp?new Date(e.timestamp):new Date})),s=await r.insertBatch(o);return e.json({success:!0,count:s.length,message:`Successfully inserted ${s.length} logs`},201)}catch(t){let n=t instanceof Error?t.message:String(t);return n.startsWith(`Invalid JSON body`)?e.json({success:!1,error:n},400):(console.error(`Failed to insert logs:`,t),e.json({success:!1,error:`Failed to insert logs`},500))}}),n.post(`/v1/traces`,i,async e=>{try{let t=G(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await K(e);be(n);let i=[];for(let e of n.resourceSpans){let t=Se(e);for(let n of e.scopeSpans)for(let e of n.spans){let n=Ee(e,t);i.push(n)}}let a=await r.insertBatch(i);return e.json({success:!0,count:a.length,message:`Successfully inserted ${a.length} trace spans as logs`},201)}catch(t){let n=t instanceof Error?t.message:String(t);return n.startsWith(`Invalid JSON body`)||n.startsWith(`Invalid OTLP`)||n.startsWith(`OTLP`)?e.json({success:!1,error:n},400):(console.error(`Failed to insert OTLP traces:`,t),e.json({success:!1,error:`Failed to insert OTLP traces`},500))}}),n.post(`/v1/logs`,i,async e=>{try{let t=G(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await K(e);xe(n);let i=[];for(let e of n.resourceLogs){let t=Ce(e);for(let n of e.scopeLogs)for(let e of n.logRecords){let n=Te(e,t);i.push(n)}}let a=await r.insertBatch(i);return e.json({success:!0,count:a.length,message:`Successfully inserted ${a.length} OTLP logs`},201)}catch(t){let n=t instanceof Error?t.message:String(t);return n.startsWith(`Invalid JSON body`)||n.startsWith(`Invalid OTLP`)||n.startsWith(`OTLP`)?e.json({success:!1,error:n},400):(console.error(`Failed to insert OTLP logs:`,t),e.json({success:!1,error:`Failed to insert OTLP logs`},500))}});let a=_(t);n.route(`/api`,a);let o=z(t);return n.route(`/`,o),n}const Oe=`3100`,ke=`./logs/session.db`,Ae=[`pnpm-workspace.yaml`,`nx.json`,`.git`];function je(e=process.cwd()){let r=n.default.resolve(e);for(;;){for(let e of Ae)if((0,t.existsSync)(n.default.join(r,e)))return r;let e=n.default.dirname(r);if(e===r)return process.cwd();r=e}}const Me=new i.Command(`http-serve`).description(`Start HTTP server for log ingestion with configurable port, database path, and in-memory mode`).option(`-p, --port <port>`,`Port to listen on`,`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`).action(async t=>{try{let n=parseInt(t.port,10),r={min:3e3,max:Math.max(4999,n)},i=process.env.NODE_ENV||`development`,s=je(process.cwd()),c=`log-sink-mcp-http`;t.registryPath&&(process.env.PORT_REGISTRY_PATH=t.registryPath);let l=new a.PortRegistryService(process.env.PORT_REGISTRY_PATH);console.log(`🚀 Starting HTTP server for log ingestion...`),console.log(` Port: ${n}`),console.log(` Database: ${t.inMemory?`In-Memory`:t.dbPath}`),console.log(` Registry path: ${process.env.PORT_REGISTRY_PATH??`${process.env.HOME}/.port-registry/ports.json`}`);let u=e.u(),d=u.get(e.d.LogStorageService);await d.initializeDatabase(t.dbPath,t.inMemory),console.log(`✓ Database initialized`);let f=De(u),p=await l.reservePort({repositoryPath:s,serviceName:c,serviceType:`tool`,environment:i,preferredPort:n,pid:process.pid,host:`127.0.0.1`,force:!0,portRange:r,metadata:{healthCheckUrl:`http://localhost:${n}/health`,dbPath:t.dbPath}});if(!p.success||!p.record)throw Error(p.error||`Failed to reserve service in global registry`);let m=p.record.port;if(m!==n){let e=await l.reservePort({repositoryPath:s,serviceName:c,serviceType:`tool`,environment:i,preferredPort:m,pid:process.pid,host:`127.0.0.1`,force:!0,portRange:r,metadata:{healthCheckUrl:`http://localhost:${m}/health`,dbPath:t.dbPath}});if(!e.success||!e.record)throw Error(e.error||`Failed to update service metadata in registry`);p=e}if(!p.record)throw Error(`Port reservation lost before startup`);let h=p.record,g=h.port,_=`http://localhost:${g}/health`,v;try{v=(0,o.serve)({fetch:f.fetch,port:g})}catch(e){try{await l.releasePort({repositoryPath:s,serviceName:c,serviceType:`tool`,environment:i,pid:process.pid})}catch{}throw e}console.log(`✓ HTTP server listening on http://localhost:${g}`),console.log(` Health check: ${_}`),console.log(` Log ingestion: POST http://localhost:${g}/logs`);let ee=h.port,te=h.serviceName||c;console.log(`✓ Service registered: ${te} on ${ee}`),console.log(`
875
- Press Ctrl+C to stop the server`);let y=async e=>{console.log(`\n\n${e} received. Shutting down gracefully...`);try{v.close(),console.log(`✓ Server closed`);try{await l.releasePort({repositoryPath:s,serviceName:c,serviceType:`tool`,environment:i,pid:process.pid}),console.log(`✓ Service deregistered`)}catch{}await d.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`,()=>y(`SIGINT`)),process.on(`SIGTERM`,()=>y(`SIGTERM`))}catch(e){console.error(`Error starting HTTP server:`,e),process.exit(1)}}),Ne=`./logs/session.db`,J=new Set(Object.values(h)),Pe=new Set([`level`,`service`,`both`]),Fe={stdout:e=>console.log(e),stderr:e=>console.error(e)};function Y(e){let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<=0)throw new i.InvalidArgumentError(`Expected a positive integer.`);return t}function Ie(e){if(!Pe.has(e))throw new i.InvalidArgumentError(`Expected one of: 'level', 'service', 'both'.`);return e}function X(e){if(!(!e||e.length===0))return e.map(e=>{if(!J.has(e))throw new i.InvalidArgumentError(`Invalid log level '${e}'. Expected one of: ${Array.from(J).join(`, `)}`);return e})}function Le(e){return e.content.map(e=>e.type===`text`?e.text:JSON.stringify(e)).join(`
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(`
874
+ `)})]}),(0,g.jsx)(`body`,{children:t})]})}function ge(e){let n=new d.Hono,r=e.get(t.d.LogQueryService);return n.get(`/`,async e=>{try{let t=await r.getActiveServices(),n=await r.getStatistics(),i=await r.filterByLevel([_.INFO,_.WARN,_.ERROR,_.FATAL],100),a={totalLogs:n.reduce((e,t)=>e+t.count,0),errors:n.filter(e=>e.level===`error`||e.level===`fatal`).reduce((e,t)=>e+t.count,0),services:new Set(n.map(e=>e.service)).size};return e.html((0,g.jsx)(B,{title:`Log Dashboard`,children:(0,g.jsx)(he,{initialLogs:i,services:t,stats:a})}))}catch(t){return console.error(`Failed to render dashboard:`,t),e.html((0,g.jsx)(B,{title:`Log Dashboard - Error`,children:(0,g.jsxs)(`div`,{style:`padding: 2rem; text-align: center;`,children:[(0,g.jsx)(`h1`,{children:`Failed to load dashboard`}),(0,g.jsx)(`p`,{children:t instanceof Error?t.message:String(t)}),(0,g.jsx)(`a`,{href:`/`,style:`color: #2196f3; text-decoration: underline;`,children:`Retry`})]})}),500)}}),n}const V=5*1024*1024,_e=1e3,H=2e3,ve=200,U=8e3,ye=m.z.object({timestamp:m.z.string().datetime().optional(),level:m.z.enum([`trace`,`debug`,`info`,`warn`,`error`,`fatal`]),message:m.z.string().min(1).max(U),traceId:m.z.string().regex(/^[0-9a-f]{32}$/i,`traceId must be 32 hex characters`).optional(),spanId:m.z.string().regex(/^[0-9a-f]{16}$/i,`spanId must be 16 hex characters`).optional(),parentSpanId:m.z.string().regex(/^[0-9a-f]{16}$/i,`parentSpanId must be 16 hex characters`).optional(),service:m.z.string().min(1).max(200),hostname:m.z.string().max(255).optional(),pid:m.z.number().int().nonnegative().optional(),metadata:m.z.record(m.z.string(),m.z.unknown()).optional(),errorType:m.z.string().max(255).optional(),errorMessage:m.z.string().max(8e3).optional(),errorStack:m.z.string().max(32e3).optional()}),be=m.z.object({logs:m.z.array(ye).min(1).max(1e3)});function W(e){if(!e||!/^\d+$/.test(e))throw Error(`OTLP timestamp must be a numeric unix-nanoseconds string`);let t=Number(BigInt(e)/BigInt(1e6));return new Date(t)}function G(e,t){return typeof e==`string`&&RegExp(`^[0-9a-f]{${t}}$`,`i`).test(e)}function K(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<=0?null:t>V?`Request body exceeds ${V} bytes`:null}async function q(e){try{return await e.req.json()}catch(e){throw Error(`Invalid JSON body: ${e instanceof Error?e.message:String(e)}`)}}function xe(e){if(!Array.isArray(e.resourceSpans)||e.resourceSpans.length===0)throw Error(`Invalid OTLP trace request: missing resourceSpans`);let t=0;for(let n of e.resourceSpans){if(!Array.isArray(n.scopeSpans))throw Error(`Invalid OTLP trace request: scopeSpans must be an array`);for(let e of n.scopeSpans){if(!Array.isArray(e.spans))throw Error(`Invalid OTLP trace request: spans must be an array`);for(let n of e.spans){if(t+=1,t>H)throw Error(`OTLP trace request exceeds ${H} spans`);if(!G(n.traceId,32))throw Error(`Invalid OTLP trace request: traceId must be 32 hex characters`);if(!G(n.spanId,16))throw Error(`Invalid OTLP trace request: spanId must be 16 hex characters`);if(n.parentSpanId&&!G(n.parentSpanId,16))throw Error(`Invalid OTLP trace request: parentSpanId must be 16 hex characters`);if(!n.name||n.name.length>U)throw Error(`Invalid OTLP trace request: span name is missing or too long`);W(n.startTimeUnixNano)}}}}function Se(e){if(!Array.isArray(e.resourceLogs)||e.resourceLogs.length===0)throw Error(`Invalid OTLP logs request: missing resourceLogs`);let t=0;for(let n of e.resourceLogs){if(!Array.isArray(n.scopeLogs))throw Error(`Invalid OTLP logs request: scopeLogs must be an array`);for(let e of n.scopeLogs){if(!Array.isArray(e.logRecords))throw Error(`Invalid OTLP logs request: logRecords must be an array`);for(let n of e.logRecords){if(t+=1,t>H)throw Error(`OTLP logs request exceeds ${H} log records`);if(n.traceId&&!G(n.traceId,32))throw Error(`Invalid OTLP logs request: traceId must be 32 hex characters`);if(n.spanId&&!G(n.spanId,16))throw Error(`Invalid OTLP logs request: spanId must be 16 hex characters`);if(n.severityText&&n.severityText.length>32)throw Error(`Invalid OTLP logs request: severityText is too long`);W(n.timeUnixNano)}}}}function Ce(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function J(e,t){let n=e?.find(e=>e.key===t);if(!n)return;let{value:r}=n;if(r.stringValue!==void 0)return r.stringValue;if(r.intValue!==void 0)return r.intValue;if(r.doubleValue!==void 0)return String(r.doubleValue);if(r.boolValue!==void 0)return String(r.boolValue)}function we(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function Te(e){return e===void 0?`info`:e<=4?`trace`:e<=8?`debug`:e<=12?`info`:e<=16?`warn`:e<=20?`error`:`fatal`}function Ee(e,t){let n=W(e.timeUnixNano),r=e.severityText?.toLowerCase()||Te(e.severityNumber),i=``;if(e.body?.stringValue)i=e.body.stringValue;else if(e.body?.kvlistValue){let t={};for(let n of e.body.kvlistValue.values){let e=n.value.stringValue??n.value.intValue??n.value.doubleValue??n.value.boolValue;t[n.key]=e}i=JSON.stringify(t)}let a={},o=null,s=null,c=null;if(e.attributes)for(let t of e.attributes){let e=t.value.stringValue??t.value.intValue??t.value.doubleValue??t.value.boolValue;t.key===`exception.type`?o=String(e):t.key===`exception.message`?s=String(e):t.key===`exception.stacktrace`?c=String(e):a[t.key]=e}return{timestamp:n,level:r,message:i||`[LOG] ${t}`,traceId:e.traceId??null,spanId:e.spanId??null,parentSpanId:null,service:t,hostname:null,pid:null,metadata:Object.keys(a).length>0?a:null,errorType:o,errorMessage:s,errorStack:c}}function De(e,t){let n=W(e.startTimeUnixNano),r=e.status?.code,i=`info`;r===2?i=`error`:r===1&&(i=`info`);let a=J(e.attributes,`exception.type`),o=J(e.attributes,`exception.message`),s=J(e.attributes,`exception.stacktrace`),c={};if(e.attributes)for(let t of e.attributes){let e=t.value.stringValue??t.value.intValue??t.value.doubleValue??t.value.boolValue;c[t.key]=e}return c.spanName=e.name,c.spanKind=e.kind,e.status?.message&&(c.statusMessage=e.status.message),{timestamp:n,level:i,message:`[SPAN] ${e.name}`,traceId:e.traceId,spanId:e.spanId,parentSpanId:e.parentSpanId??null,service:t,hostname:null,pid:null,metadata:c,errorType:a??null,errorMessage:o??null,errorStack:s??null}}function Oe(e){let n=new d.Hono,r=e.get(t.d.LogStorageService);n.use(`*`,(0,f.cors)({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`,`Authorization`]})),n.get(`/health`,async e=>{try{return e.json({status:`healthy`,service:`log-sink-mcp-http`,serviceName:`log-sink-mcp-http`,timestamp:new Date().toISOString()})}catch(t){return e.json({status:`unhealthy`,error:t instanceof Error?t.message:String(t)},503)}});let i=(0,p.rateLimiter)({windowMs:60*1e3,limit:100,standardHeaders:`draft-6`,keyGenerator:e=>e.req.header(`x-forwarded-for`)||e.req.header(`x-real-ip`)||`unknown`});n.post(`/logs`,i,async e=>{try{let t=K(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await q(e),i=be.safeParse(n);if(!i.success)return e.json({success:!1,error:`Validation failed`,details:i.error.issues},400);let{logs:a}=i.data,o=a.map(e=>({...e,timestamp:e.timestamp?new Date(e.timestamp):new Date})),s=await r.insertBatch(o);return e.json({success:!0,count:s.length,message:`Successfully inserted ${s.length} logs`},201)}catch(t){let n=t instanceof Error?t.message:String(t);return n.startsWith(`Invalid JSON body`)?e.json({success:!1,error:n},400):(console.error(`Failed to insert logs:`,t),e.json({success:!1,error:`Failed to insert logs`},500))}}),n.post(`/v1/traces`,i,async e=>{try{let t=K(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await q(e);xe(n);let i=[];for(let e of n.resourceSpans){let t=Ce(e);for(let n of e.scopeSpans)for(let e of n.spans){let n=De(e,t);i.push(n)}}let a=await r.insertBatch(i);return e.json({success:!0,count:a.length,message:`Successfully inserted ${a.length} trace spans as logs`},201)}catch(t){let n=t instanceof Error?t.message:String(t);return n.startsWith(`Invalid JSON body`)||n.startsWith(`Invalid OTLP`)||n.startsWith(`OTLP`)?e.json({success:!1,error:n},400):(console.error(`Failed to insert OTLP traces:`,t),e.json({success:!1,error:`Failed to insert OTLP traces`},500))}}),n.post(`/v1/logs`,i,async e=>{try{let t=K(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await q(e);Se(n);let i=[];for(let e of n.resourceLogs){let t=we(e);for(let n of e.scopeLogs)for(let e of n.logRecords){let n=Ee(e,t);i.push(n)}}let a=await r.insertBatch(i);return e.json({success:!0,count:a.length,message:`Successfully inserted ${a.length} OTLP logs`},201)}catch(t){let n=t instanceof Error?t.message:String(t);return n.startsWith(`Invalid JSON body`)||n.startsWith(`Invalid OTLP`)||n.startsWith(`OTLP`)?e.json({success:!1,error:n},400):(console.error(`Failed to insert OTLP logs:`,t),e.json({success:!1,error:`Failed to insert OTLP logs`},500))}});let a=y(e);n.route(`/api`,a);let o=ge(e);return n.route(`/`,o),n}const ke=256*1024*1024,Ae=.9,je=300*1e3,Me=1e3;function Y(e,t){if(e===void 0||e===``)return t;let n=Number.parseInt(e,10);if(!Number.isFinite(n)||n<=0)throw Error(`Invalid database retention value: ${e}`);return n}function X(e={}){let t=Y(e.dbMaxBytes??process.env.LOG_SINK_DB_MAX_BYTES,268435456),n=Y(e.dbTargetBytes??process.env.LOG_SINK_DB_TARGET_BYTES,Math.floor(t*.9));return{maxBytes:t,targetBytes:Math.min(n,t),intervalMs:Y(e.dbRetentionIntervalMs??process.env.LOG_SINK_DB_RETENTION_INTERVAL_MS,3e5),batchSize:Y(e.dbRetentionBatchSize??process.env.LOG_SINK_DB_RETENTION_BATCH_SIZE,1e3)}}const Ne=`3100`,Pe=`./logs/session.db`,Fe=[`pnpm-workspace.yaml`,`nx.json`,`.git`];function Ie(e=process.cwd()){let t=r.default.resolve(e);for(;;){for(let e of Fe)if((0,n.existsSync)(r.default.join(t,e)))return t;let e=r.default.dirname(t);if(e===t)return process.cwd();t=e}}function Le(e,t){if(!e)return;let n=r.default.resolve(e);return r.default.extname(n)===`.json`?r.default.join(r.default.dirname(n),t):r.default.join(n,t)}function Re(){return new o.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH)}async function ze(e,t,n,r,i){let a=Re(),o=await a.registerProcess({repositoryPath:e,serviceName:t,serviceType:`tool`,environment:n,pid:process.pid,port:r,host:`127.0.0.1`,command:process.argv[1],args:process.argv.slice(2),metadata:{dbPath:i,transport:`http`}});if(!o.success||!o.record)throw Error(o.error||`Failed to register process for ${t}`);let s=!1;return{release:async r=>{if(s)return;s=!0;let i=await a.releaseProcess({repositoryPath:e,serviceName:t,serviceType:`tool`,pid:process.pid,environment:n,kill:r?.kill??!1,releasePort:r?.releasePort??!1});if(!i.success&&i.error&&!i.error.includes(`No matching process entry`))throw Error(i.error||`Failed to release process for ${t}`)}}}const Be=new a.Command(`http-serve`).description(`Start HTTP server for log ingestion with configurable port, database path, and in-memory mode`).option(`-p, --port <port>`,`Port to listen on`,`3100`).option(`--db-path <path>`,`Path to SQLite database file`,`./logs/session.db`).option(`--in-memory`,`Use in-memory database (for testing)`,!1).option(`--db-max-bytes <bytes>`,`Maximum SQLite database size before trimming`).option(`--db-target-bytes <bytes>`,`Target SQLite database size after trimming`).option(`--db-retention-interval-ms <ms>`,`How often to check database size`,`300000`).option(`--db-retention-batch-size <count>`,`Number of oldest log rows to delete per trim batch`,`1000`).option(`--registry-path <path>`,`Custom registry path or directory for service discovery`).action(async e=>{try{let n=parseInt(e.port,10),r={min:3e3,max:Math.max(4999,n)},i=process.env.NODE_ENV||`development`,a=Ie(process.cwd()),o=`log-sink-mcp-http`;e.registryPath&&(process.env.PORT_REGISTRY_PATH=e.registryPath,process.env.PROCESS_REGISTRY_PATH=Le(e.registryPath,`processes.json`)??process.env.PROCESS_REGISTRY_PATH);let l=new s.PortRegistryService(process.env.PORT_REGISTRY_PATH);console.log(`🚀 Starting HTTP server for log ingestion...`),console.log(` Port: ${n}`),console.log(` Database: ${e.inMemory?`In-Memory`:e.dbPath}`),console.log(` Registry path: ${process.env.PORT_REGISTRY_PATH??`${process.env.HOME}/.port-registry/ports.json`}`);let u=t.u(),d=u.get(t.d.LogStorageService),f=u.get(t.d.LogRetentionService);await d.initializeDatabase(e.dbPath,e.inMemory);let p=f.startSizeRetentionMonitor(X({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize}));console.log(`✓ Database initialized`);let m=Oe(u),h=await l.reservePort({repositoryPath:a,serviceName:o,serviceType:`tool`,environment:i,preferredPort:n,pid:process.pid,host:`127.0.0.1`,force:!0,portRange:r,metadata:{healthCheckUrl:`http://localhost:${n}/health`,dbPath:e.dbPath}});if(!h.success||!h.record)throw Error(h.error||`Failed to reserve service in global registry`);let g=h.record.port;if(g!==n){let t=await l.reservePort({repositoryPath:a,serviceName:o,serviceType:`tool`,environment:i,preferredPort:g,pid:process.pid,host:`127.0.0.1`,force:!0,portRange:r,metadata:{healthCheckUrl:`http://localhost:${g}/health`,dbPath:e.dbPath}});if(!t.success||!t.record)throw Error(t.error||`Failed to update service metadata in registry`);h=t}if(!h.record)throw Error(`Port reservation lost before startup`);let _=h.record,v=_.port,y=await ze(a,o,i,v,e.dbPath),b=`http://localhost:${v}/health`,x;try{x=(0,c.serve)({fetch:m.fetch,port:v})}catch(e){try{await l.releasePort({repositoryPath:a,serviceName:o,serviceType:`tool`,environment:i,pid:process.pid})}catch{}try{await y.release({kill:!1,releasePort:!1})}catch{}throw e}console.log(`✓ HTTP server listening on http://localhost:${v}`),console.log(` Health check: ${b}`),console.log(` Log ingestion: POST http://localhost:${v}/logs`);let ee=_.port,te=_.serviceName||o;console.log(`✓ Service registered: ${te} on ${ee}`),console.log(`
875
+ Press Ctrl+C to stop the server`);let S=async e=>{console.log(`\n\n${e} received. Shutting down gracefully...`);try{x.close(),console.log(`✓ Server closed`);try{await l.releasePort({repositoryPath:a,serviceName:o,serviceType:`tool`,environment:i,pid:process.pid}),console.log(`✓ Service deregistered`)}catch{}try{await y.release({kill:!1,releasePort:!1}),console.log(`✓ Process deregistered`)}catch{}p(),await d.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`,()=>S(`SIGINT`)),process.on(`SIGTERM`,()=>S(`SIGTERM`))}catch(e){console.error(`Error starting HTTP server:`,e),process.exit(1)}}),Ve=`./logs/session.db`,He=new Set(Object.values(_)),Ue=new Set([`level`,`service`,`both`]),We={stdout:e=>console.log(e),stderr:e=>console.error(e)};function Z(e){let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<=0)throw new a.InvalidArgumentError(`Expected a positive integer.`);return t}function Ge(e){if(!Ue.has(e))throw new a.InvalidArgumentError(`Expected one of: 'level', 'service', 'both'.`);return e}function Ke(e){if(!(!e||e.length===0))return e.map(e=>{if(!He.has(e))throw new a.InvalidArgumentError(`Invalid log level '${e}'. Expected one of: ${Array.from(He).join(`, `)}`);return e})}function qe(e){return e.content.map(e=>e.type===`text`?e.text:JSON.stringify(e)).join(`
876
+ `)}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 Je(e,n){let r=t.u(),i=r.get(t.d.LogStorageService);await i.initializeDatabase(e.dbPath,e.inMemory);try{return await n(r)}finally{await i.disconnect()}}async function Ye(e,t,n,r=We){try{let i=await Je(e,async e=>t(e).execute(n)),a=qe(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 Ye(e,t,n);r!==0&&process.exit(r)}function Xe(e,t){if(!(!e&&!t)){if(!e||!t)throw new a.InvalidArgumentError(`Provide both --start-time and --end-time together.`);return{start:e,end:t}}}function Ze(){let e=new a.Command(`logs`).description(`Run log analysis commands that mirror MCP tool capabilities`),n=Q(new a.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`,Z).action(async e=>{await $(e,e=>new t.i(e),{level:Ke(e.level),traceId:e.traceId,service:e.service,startTime:e.startTime,endTime:e.endTime,limit:e.limit})})),r=Q(new a.Command(`search`).alias(`search-logs`).description(`Search log entries with full-text search and optional filters`).argument(`<search-query>`,`FTS5 search query`).option(`--mode <mode>`,`Search strategy: fts, semantic, or hybrid`,`hybrid`).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,n)=>{await $(n,e=>new t.r(e),{searchQuery:e,mode:n.mode,service:n.service,level:Ke(n.level),startTime:n.startTime,endTime:n.endTime,limit:n.limit})})),i=Q(new a.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(e,n)=>{await $(n,e=>new t.a(e),{traceId:e})})),o=Q(new a.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`,Z).action(async e=>{await $(e,e=>new t.l(e),{traceId:e.traceId,timeRange:Xe(e.startTime,e.endTime),limit:e.limit})})),s=Q(new a.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`,Ge).action(async e=>{await $(e,e=>new t.s(e),{startTime:e.startTime,endTime:e.endTime,groupBy:e.groupBy})})),c=Q(new a.Command(`services`).alias(`get-services`).description(`Get the list of unique services present in the log database`).action(async e=>{await $(e,e=>new t.o(e),{})})),l=Q(new a.Command(`clear`).alias(`clear-logs`).description(`Clear all logs from the database`).action(async e=>{await $(e,e=>new t.c(e),{})}));return e.addCommand(n).addCommand(r).addCommand(i).addCommand(o).addCommand(s).addCommand(c).addCommand(l)}const Qe=Ze();function $e(e,t){if(!e)return;let n=r.default.resolve(e);return r.default.extname(n)===`.json`?r.default.join(r.default.dirname(n),t):r.default.join(n,t)}function et(){return new o.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH)}async function tt(e){let t=et(),n=await t.registerProcess({repositoryPath:process.cwd(),serviceName:e,serviceType:`tool`,environment:process.env.NODE_ENV||`development`,pid:process.pid,host:`127.0.0.1`,command:process.argv[1],args:process.argv.slice(2),metadata:{transport:`stdio`,command:`mcp-serve`},force:!0});if(!n.success||!n.record)throw Error(n.error||`Failed to register process for ${e}`);let r=!1;return{release:async()=>{if(r)return;r=!0;let n=await t.releaseProcess({repositoryPath:process.cwd(),serviceName:e,serviceType:`tool`,environment:process.env.NODE_ENV||`development`,pid:process.pid,kill:!1,releasePort:!1});if(!n.success&&!n.error?.includes(`No matching process entry`))throw Error(n.error||`Failed to release process for ${e}`)}}}function nt(){return(0,r.join)((0,u.tmpdir)(),`log-sink-mcp`,`session.db`)}const rt=new a.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`,nt()).option(`--in-memory`,`Use in-memory database (for testing)`,!1).option(`--db-max-bytes <bytes>`,`Maximum SQLite database size before trimming`).option(`--db-target-bytes <bytes>`,`Target SQLite database size after trimming`).option(`--db-retention-interval-ms <ms>`,`How often to check database size`,`300000`).option(`--db-retention-batch-size <count>`,`Number of oldest log rows to delete per trim batch`,`1000`).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=>{let n;try{let r=e.registryPath||e.registryDir;r&&(process.env.PORT_REGISTRY_PATH=r,process.env.PROCESS_REGISTRY_PATH=$e(r,`processes.json`)??process.env.PROCESS_REGISTRY_PATH);let i=t.u(),a=i.get(t.d.LogStorageService),o=i.get(t.d.LogRetentionService),s=i.get(t.d.HttpServerManager);await a.initializeDatabase(e.dbPath,e.inMemory);let c=o.startSizeRetentionMonitor(X({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize})),l=await s.ensureRunning(3100,e.dbPath);l.running||console.error(`Warning: HTTP server failed to start: ${l.error}`);let u=new t.t(t.n(i));n=await tt(`log-sink-mcp-mcp-serve`);let d=async t=>{console.error(`\nReceived ${t}, shutting down gracefully...`);try{await u.stop(),e.cleanup&&l.running&&(await s.stop(),console.error(`HTTP server stopped`)),c(),await a.disconnect(),await n?.release(),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 u.start()}catch(e){await n?.release().catch(()=>void 0),console.error(`Failed to start MCP server:`,e),process.exit(1)}});function it(e,t){if(!e)return;let n=r.default.resolve(e);return r.default.extname(n)===`.json`?r.default.join(r.default.dirname(n),t):r.default.join(n,t)}function at(){return new o.ProcessRegistryService(process.env.PROCESS_REGISTRY_PATH)}async function ot(e){let t=at(),n=await t.registerProcess({repositoryPath:process.cwd(),serviceName:e,serviceType:`tool`,environment:process.env.NODE_ENV||`development`,pid:process.pid,host:`127.0.0.1`,command:process.argv[1],args:process.argv.slice(2),metadata:{transport:`stdio`,command:`start`},force:!0});if(!n.success||!n.record)throw Error(n.error||`Failed to register process for ${e}`);let r=!1;return{release:async()=>{if(r)return;r=!0;let n=await t.releaseProcess({repositoryPath:process.cwd(),serviceName:e,serviceType:`tool`,environment:process.env.NODE_ENV||`development`,pid:process.pid,kill:!1,releasePort:!1});if(!n.success&&!n.error?.includes(`No matching process entry`))throw Error(n.error||`Failed to release process for ${e}`)}}}const st=new a.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(`--db-max-bytes <bytes>`,`Maximum SQLite database size before trimming`).option(`--db-target-bytes <bytes>`,`Target SQLite database size after trimming`).option(`--db-retention-interval-ms <ms>`,`How often to check database size`,`300000`).option(`--db-retention-batch-size <count>`,`Number of oldest log rows to delete per trim batch`,`1000`).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=>{let n;try{let r=e.registryPath||e.registryDir;r&&(process.env.PORT_REGISTRY_PATH=r,process.env.PROCESS_REGISTRY_PATH=it(r,`processes.json`)??process.env.PROCESS_REGISTRY_PATH),e.dbMaxBytes&&(process.env.LOG_SINK_DB_MAX_BYTES=e.dbMaxBytes),e.dbTargetBytes&&(process.env.LOG_SINK_DB_TARGET_BYTES=e.dbTargetBytes),e.dbRetentionIntervalMs&&(process.env.LOG_SINK_DB_RETENTION_INTERVAL_MS=e.dbRetentionIntervalMs),e.dbRetentionBatchSize&&(process.env.LOG_SINK_DB_RETENTION_BATCH_SIZE=e.dbRetentionBatchSize);let i=t.u(),a=i.get(t.d.LogStorageService);await a.initializeDatabase(e.dbPath,e.inMemory);let o=parseInt(e.port,10),s=!e.mcpOnly,c=!e.httpOnly,l=i.get(t.d.LogRetentionService),u=c?l.startSizeRetentionMonitor(X({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize})):()=>void 0;if(console.log(`🚀 Starting log-sink-mcp services...`),console.log(` Database: ${e.inMemory?`In-Memory`:e.dbPath}`),s){let n=await i.get(t.d.HttpServerManager).ensureRunning(o,e.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}`),c||process.exit(1))}if(c){n=await ot(`log-sink-mcp-start`),console.log(`✓ MCP Server: Starting with stdio transport...`);let e=new t.t(t.n(i));await e.start(),console.log(`✓ MCP Server: Ready for connections`);let r=async r=>{console.log(`\n\n${r} received. Shutting down gracefully...`);try{await e.stop(),console.log(`✓ MCP server stopped`),s&&(await i.get(t.d.HttpServerManager).stop(),console.log(`✓ HTTP server stopped`)),u(),await a.disconnect(),console.log(`✓ Database disconnected`),await n?.release(),console.log(`✓ Process registry entry released`),console.log(`Goodbye! 👋`),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>r(`SIGINT`)),process.on(`SIGTERM`,()=>r(`SIGTERM`)),console.log(`
877
877
  Press Ctrl+C to stop the server`)}else console.log(`
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();
878
+ ✓ HTTP server started in background`),console.log(`Use "log-sink-mcp stop" to stop the HTTP server`),n&&await n.release().catch(()=>void 0),process.exit(0)}catch(e){n&&await n.release().catch(()=>void 0),console.error(`Error starting services:`,e),process.exit(1)}}),ct=new a.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 e=await t.u().get(t.d.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 l.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)}}),lt=new a.Command(`stop`).description(`Stop HTTP server and clean up registry/PID files`).action(async()=>{try{console.log(`🛑 Stopping log-sink-mcp services...`),await t.u().get(t.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)}}),ut=(0,r.dirname)((0,i.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),dt=JSON.parse((0,n.readFileSync)((0,r.join)(ut,`../package.json`),`utf-8`));async function ft(){let e=new a.Command;e.name(`log-sink-mcp`).description(`Log sink MCP server with HTTP ingestion and AI analysis`).version(dt.version),e.addCommand(st),e.addCommand(lt),e.addCommand(ct),e.addCommand(Be),e.addCommand(rt),e.addCommand(Qe),await e.parseAsync(process.argv)}ft();
880
880
  //# sourceMappingURL=cli.cjs.map