@agimon-ai/log-sink-mcp 0.21.1 → 0.21.2
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 +12 -0
- package/dist/cli.cjs +132 -22
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +134 -24
- package/dist/cli.mjs.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +93 -1
- package/dist/index.d.mts +93 -1
- package/dist/index.mjs +1 -1
- package/dist/stdio-Bj5GoSEh.mjs +321 -0
- package/dist/stdio-Bj5GoSEh.mjs.map +1 -0
- package/dist/{stdio-CKbZ8sJq.cjs → stdio-D2vdWaab.cjs} +17 -8
- package/dist/stdio-D2vdWaab.cjs.map +1 -0
- package/package.json +4 -4
- package/dist/stdio-C-t8NeMl.mjs +0 -312
- package/dist/stdio-C-t8NeMl.mjs.map +0 -1
- package/dist/stdio-CKbZ8sJq.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -561,6 +561,7 @@ Subcommands:
|
|
|
561
561
|
- `trace`: Equivalent to `get_trace_timeline`
|
|
562
562
|
- `analyze-errors`: CLI-only grouped error analysis
|
|
563
563
|
- `stats`: Equivalent to `get_log_stats`
|
|
564
|
+
- `metrics`: Workflow, token, failure, and per-tool consumption metrics
|
|
564
565
|
- `services`: Equivalent to `get_services`
|
|
565
566
|
- `clear`: CLI-only command to delete all logs from the selected database
|
|
566
567
|
|
|
@@ -585,6 +586,17 @@ bun run src/cli.ts logs analyze-errors \
|
|
|
585
586
|
--start-time 2026-01-15T10:00:00Z \
|
|
586
587
|
--end-time 2026-01-15T11:00:00Z
|
|
587
588
|
|
|
589
|
+
# Inspect workflow and per-tool token metrics, narrowed to one provider/model
|
|
590
|
+
bun run src/cli.ts logs metrics \
|
|
591
|
+
--provider openai \
|
|
592
|
+
--model gpt-5.3-codex \
|
|
593
|
+
--sort total-tokens \
|
|
594
|
+
--tool-sort p90-total-tokens
|
|
595
|
+
|
|
596
|
+
# Compare workflow metrics by model or provider
|
|
597
|
+
bun run src/cli.ts logs metrics --group-by model
|
|
598
|
+
bun run src/cli.ts logs metrics --group-by provider
|
|
599
|
+
|
|
588
600
|
# View service names discovered in the log database
|
|
589
601
|
bun run src/cli.ts logs services
|
|
590
602
|
```
|
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=require(`./node-CFha1yzq.cjs`),t=require(`./stdio-
|
|
2
|
+
const e=require(`./node-CFha1yzq.cjs`),t=require(`./stdio-D2vdWaab.cjs`);let n=require(`zod`),r=require(`node:fs`),i=require(`node:fs/promises`);i=e.u(i,1);let a=require(`node:path`),o=require(`node:url`),s=require(`@agimon-ai/foundation-port-registry`),c=require(`@agimon-ai/foundation-process-registry`),l=require(`commander`),u=require(`@hono/node-server`),d=require(`node:crypto`),f=require(`hono`),p=require(`hono/cors`),m=require(`hono-rate-limiter`),h=require(`hono/html`),g=require(`hono/jsx/jsx-runtime`);function _(e){return e.option(`--config <path>`,`Path to .logsink.yaml`).option(`--global`,`Use the global log-sink instance`).option(`--instance <scope>`,`Force log-sink instance: local or global`)}function v(e){let t=e.instance?.trim();if(e.global&&t&&t.toLowerCase()!==`global`)throw Error(`Use either --global or --instance local, not both`);return e.global?`global`:t}function y(t,n=e.s){t.config&&(process.env.LOG_SINK_CONFIG=t.config);let r=v(t);return r&&(process.env.LOG_SINK_INSTANCE=r),e.c({configPath:t.config,packageName:n})}const b=[`error`,`fatal`];function x(e){let t=e.trim();if(!t)return null;try{return JSON.parse(t)}catch{return null}}function S(e){if(e==null)return`unknown`;if(typeof e==`string`)return e;try{return JSON.stringify(e,null,2)}catch{return String(e)}}function C(e){return e.hook_event_name===`PostToolUseFailure`?`Current tool failure:\n- Tool: ${e.tool_name??`unknown tool`}\n- Error: ${S(e.error)}`:null}function w(e,t,n){let r=new Map;for(let t of e){let e=`${t.service}::${t.level}::${t.error_type??``}::${t.error_message??t.message}`,i=new Date(Number(t.created_at)).toISOString();r.has(e)||r.set(e,{key:e,count:0,service:t.service,level:t.level,errorType:t.error_type,errorMessage:t.error_message,message:t.message,firstSeen:i,lastSeen:i,traceIds:new Set,examples:[]});let a=r.get(e);a.count+=1,a.lastSeen=i,t.trace_id&&a.traceIds.add(t.trace_id),a.examples.length<n&&a.examples.push({id:t.id,timestamp:i,traceId:t.trace_id})}return Array.from(r.values()).sort((e,t)=>t.count-e.count||t.lastSeen.localeCompare(e.lastSeen)).slice(0,t)}function ee(e,t,n,r){let i=w(e,n,r),a=[];a.push(`Recent log errors (${e.length} new):`),t.tool_name&&a.push(`- Triggered after tool: ${t.tool_name}`);for(let[e,t]of i.entries()){a.push(`${e+1}. ${t.service} [${t.level}] x${t.count} ${t.errorType?`(${t.errorType}) `:``}${t.errorMessage??t.message}`),a.push(` First seen: ${t.firstSeen}`),a.push(` Last seen: ${t.lastSeen}`),t.traceIds.size>0&&a.push(` Trace IDs: ${Array.from(t.traceIds).slice(0,3).join(`, `)}`);for(let e of t.examples)a.push(` Example: ${e.timestamp} id=${e.id}${e.traceId?` trace=${e.traceId}`:``}`)}if(t.hook_event_name===`PostToolUseFailure`){let e=C(t);e&&a.push(``,e)}return a.join(`
|
|
3
3
|
`)}function te(t){if(t.session_id?.trim())return(0,s.normalizeRepositoryPath)(e.l(t.cwd?.trim()||process.cwd()))}async function ne(t,n){let i=v(n);if(n.dbPath){let e=i===`global`?`global`:`local`;return(0,r.existsSync)(n.dbPath)?{dbPath:n.dbPath,scope:e}:void 0}let a={...process.env};n.config&&(a.LOG_SINK_CONFIG=n.config),i&&(a.LOG_SINK_INSTANCE=i);let o=e.c({configPath:n.config,cwd:t.cwd??process.cwd(),env:a,packageName:e.s}),c=new s.PortRegistryService(a.PORT_REGISTRY_PATH??s.DEFAULT_REGISTRY_PATH);try{let e=await c.getPort({repositoryPath:o.repositoryPath,serviceName:`log-sink-mcp-http`,serviceType:`tool`,environment:a.NODE_ENV||`development`}),t=e.record?.metadata?.dbPath;if(e.success&&typeof t==`string`&&t&&(0,r.existsSync)(t))return{dbPath:t,scope:o.scope}}catch{}return(0,r.existsSync)(o.dbPath)?{dbPath:o.dbPath,scope:o.scope}:void 0}async function re(e,t,n,r,i,a){let o=e.getSqliteClient(),s=Date.now()-n*60*1e3,c=``;if(t){let e=o.prepare(`
|
|
4
4
|
SELECT id, created_at
|
|
5
5
|
FROM logs
|
|
@@ -26,7 +26,7 @@ const e=require(`./node-CFha1yzq.cjs`),t=require(`./stdio-CKbZ8sJq.cjs`);let n=r
|
|
|
26
26
|
${l}
|
|
27
27
|
ORDER BY created_at ASC, id ASC
|
|
28
28
|
LIMIT ?
|
|
29
|
-
`).all(...u);return d.length===0?{rows:[]}:{rows:d,nextCheckpoint:d[d.length-1]}}async function ie(e,n={}){let r=te(e),i=e.session_id?.trim();if(!r||!i)return null;let a=await ne(e,n);if(!a)return null;let o=new t.u;try{await o.initializeDatabase(a.dbPath);let{rows:t,nextCheckpoint:s}=await re(o,await o.getHookCheckpoint(i,r),n.lookbackMinutes??10,n.maxGroups??5,n.maxExamplesPerGroup??3,a.scope===`global`?i:void 0),c=C(e);if(t.length===0)return c?{hookSpecificOutput:{hookEventName:e.hook_event_name??`PostToolUse`,additionalContext:c}}:null;let l=ee(t,e,n.maxGroups??5,n.maxExamplesPerGroup??3);return await o.setHookCheckpoint(i,r,s?.id??t[t.length-1].id),{hookSpecificOutput:{hookEventName:e.hook_event_name??`PostToolUse`,additionalContext:c?`${l}\n\n${c}`:l}}}finally{await o.disconnect().catch(()=>void 0)}}const ae=_(new l.Command(`claude-hook`)).alias(`hook`).description(`Emit additional Claude Code context from recent log errors`).option(`--db-path <path>`,`Path to the SQLite database file`).option(`--lookback-minutes <minutes>`,`Initial monitoring window in minutes`,`10`).option(`--max-groups <count>`,`Maximum number of grouped errors to include`,`5`).option(`--max-examples-per-group <count>`,`Maximum number of example errors to include per group`,`3`).action(async e=>{try{let t=x((0,r.readFileSync)(0,`utf-8`));t||process.exit(0);let n=await ie(t,{dbPath:e.dbPath,config:e.config,global:e.global,instance:e.instance,lookbackMinutes:e.lookbackMinutes?Number.parseInt(e.lookbackMinutes,10):10,maxGroups:e.maxGroups?Number.parseInt(e.maxGroups,10):5,maxExamplesPerGroup:e.maxExamplesPerGroup?Number.parseInt(e.maxExamplesPerGroup,10):3});n||process.exit(0),process.stdout.write(`${JSON.stringify(n)}\n`),process.exit(0)}catch(e){process.stderr.write(`Error running Claude hook: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}),oe=n.z.object({service:n.z.string().optional(),level:n.z.string().optional(),traceId:n.z.string().optional(),search:n.z.string().optional(),limit:n.z.coerce.number().min(1).max(1e3).default(25),offset:n.z.coerce.number().min(0).default(0)});function se(e){let n=new f.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}=oe.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 ce(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 T=`log-table`,le=`level-trace`,ue=`level-debug`,E=`level-info`,D=`level-warn`,O=`level-error`,k=`level-fatal`,A=`stat-card`,j=`trace-link`,M=`timestamp-cell`,de=`service-cell`,fe=`pagination-btn`,pe=`log-entry-group`,me=`metadata-row`,he=`message-row`,ge=`message-full-cell`;function _e(e){let t=e.toLowerCase();return t===`trace`?le:t===`debug`?ue:t===`info`?E:t===`warn`?D:t===`error`?O:t===`fatal`?k:``}function ve(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 ye({logs:e}){return(0,g.jsx)(`div`,{class:`table-container`,children:(0,g.jsxs)(`table`,{class:T,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=ve(e.metadata);return(0,g.jsxs)(`tbody`,{class:pe,children:[(0,g.jsxs)(`tr`,{class:me,children:[(0,g.jsx)(`td`,{class:M,children:ce(e.timestamp)}),(0,g.jsx)(`td`,{class:_e(e.level),children:e.level.toUpperCase()}),(0,g.jsx)(`td`,{class:de,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:he,children:(0,g.jsxs)(`td`,{colspan:4,class:ge,children:[(0,g.jsx)(`div`,{children:e.message}),t&&(0,g.jsx)(`pre`,{class:`attributes-cell`,children:JSON.stringify(t,null,2)})]})})]},e.id)})]})})}function be(){return(0,g.jsx)(`input`,{id:`search-input`,type:`text`,class:`input-field`,placeholder:`Search logs...`,oninput:`dashboard.handleSearchInput(event)`})}function xe({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 Se({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 Ce({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)(Se,{stats:n}),(0,g.jsxs)(`div`,{class:`controls-section`,children:[(0,g.jsx)(be,{}),(0,g.jsx)(xe,{services:t}),(0,g.jsx)(`button`,{type:`button`,id:`refresh-btn`,class:`btn`,onclick:`dashboard.manualRefresh()`,children:`Refresh Now`})]}),(0,g.jsx)(ye,{logs:e}),(0,g.jsxs)(`div`,{id:`pagination-controls`,class:`pagination-container`,children:[(0,g.jsx)(`button`,{type:`button`,id:`prev-btn`,class:fe,onclick:`dashboard.prevPage()`,disabled:!0,children:`Previous`}),(0,g.jsx)(`span`,{id:`page-info`,class:`page-info`,children:`Page 1`}),(0,g.jsx)(`button`,{type:`button`,id:`next-btn`,class:fe,onclick:`dashboard.nextPage()`,children:`Next`})]}),(0,g.jsx)(`script`,{children:(0,h.raw)(`
|
|
29
|
+
`).all(...u);return d.length===0?{rows:[]}:{rows:d,nextCheckpoint:d[d.length-1]}}async function ie(e,n={}){let r=te(e),i=e.session_id?.trim();if(!r||!i)return null;let a=await ne(e,n);if(!a)return null;let o=new t.u;try{await o.initializeDatabase(a.dbPath);let{rows:t,nextCheckpoint:s}=await re(o,await o.getHookCheckpoint(i,r),n.lookbackMinutes??10,n.maxGroups??5,n.maxExamplesPerGroup??3,a.scope===`global`?i:void 0),c=C(e);if(t.length===0)return c?{hookSpecificOutput:{hookEventName:e.hook_event_name??`PostToolUse`,additionalContext:c}}:null;let l=ee(t,e,n.maxGroups??5,n.maxExamplesPerGroup??3);return await o.setHookCheckpoint(i,r,s?.id??t[t.length-1].id),{hookSpecificOutput:{hookEventName:e.hook_event_name??`PostToolUse`,additionalContext:c?`${l}\n\n${c}`:l}}}finally{await o.disconnect().catch(()=>void 0)}}const ae=_(new l.Command(`claude-hook`)).alias(`hook`).description(`Emit additional Claude Code context from recent log errors`).option(`--db-path <path>`,`Path to the SQLite database file`).option(`--lookback-minutes <minutes>`,`Initial monitoring window in minutes`,`10`).option(`--max-groups <count>`,`Maximum number of grouped errors to include`,`5`).option(`--max-examples-per-group <count>`,`Maximum number of example errors to include per group`,`3`).action(async e=>{try{let t=x((0,r.readFileSync)(0,`utf-8`));t||process.exit(0);let n=await ie(t,{dbPath:e.dbPath,config:e.config,global:e.global,instance:e.instance,lookbackMinutes:e.lookbackMinutes?Number.parseInt(e.lookbackMinutes,10):10,maxGroups:e.maxGroups?Number.parseInt(e.maxGroups,10):5,maxExamplesPerGroup:e.maxExamplesPerGroup?Number.parseInt(e.maxExamplesPerGroup,10):3});n||process.exit(0),process.stdout.write(`${JSON.stringify(n)}\n`),process.exit(0)}catch(e){process.stderr.write(`Error running Claude hook: ${e instanceof Error?e.message:String(e)}\n`),process.exit(1)}}),oe=n.z.object({service:n.z.string().optional(),level:n.z.string().optional(),traceId:n.z.string().optional(),search:n.z.string().optional(),limit:n.z.coerce.number().min(1).max(1e3).default(25),offset:n.z.coerce.number().min(0).default(0)}),se=n.z.object({groupBy:n.z.enum([`workflow-run`,`workflow-name`,`job`,`agent`,`model`,`provider`]).optional(),startTime:n.z.string().optional(),endTime:n.z.string().optional(),workflowRunId:n.z.string().optional(),workflowName:n.z.string().optional(),jobName:n.z.string().optional(),agentName:n.z.string().optional(),service:n.z.array(n.z.string()).optional(),model:n.z.array(n.z.string()).optional(),provider:n.z.array(n.z.string()).optional(),limit:n.z.coerce.number().min(1).max(500).optional(),sort:n.z.enum([`last-seen`,`total-tokens`,`issues`]).optional(),toolLimit:n.z.coerce.number().min(1).max(500).optional(),toolSort:n.z.enum([`p90-total-tokens`,`avg-total-tokens`,`invocations`]).optional()});function T(e,t){let n=e.req.queries(t)??[],r=e.req.query(t),i=(n.length>0?n:r?[r]:[]).flatMap(e=>e.split(`,`)).map(e=>e.trim()).filter(e=>e.length>0);return i.length>0?i:void 0}function ce(e){let n=new f.Hono,r=e.get(t.p.LogQueryService),i=e.get(t.p.LogSearchService),a=e.get(t.p.LogMetricsService);return n.get(`/logs`,async e=>{try{let{service:t,level:n,traceId:a,search:o,limit:s,offset:c}=oe.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.get(`/metrics`,async e=>{try{let t=se.parse({groupBy:e.req.query(`groupBy`),startTime:e.req.query(`startTime`),endTime:e.req.query(`endTime`),workflowRunId:e.req.query(`workflowRunId`),workflowName:e.req.query(`workflowName`),jobName:e.req.query(`jobName`),agentName:e.req.query(`agentName`),service:T(e,`service`),model:T(e,`model`),provider:T(e,`provider`),limit:e.req.query(`limit`),sort:e.req.query(`sort`),toolLimit:e.req.query(`toolLimit`),toolSort:e.req.query(`toolSort`)}),n=await a.getMetrics({workflowRunId:t.workflowRunId,workflowName:t.workflowName,jobName:t.jobName,agentName:t.agentName,service:t.service,model:t.model,provider:t.provider,startTime:t.startTime?new Date(t.startTime):void 0,endTime:t.endTime?new Date(t.endTime):void 0},{groupBy:t.groupBy,sort:t.sort,toolSort:t.toolSort,limit:t.limit,toolLimit:t.toolLimit});return e.json(n)}catch(t){return console.error(`Failed to get metrics:`,t),e.json({error:`Failed to get metrics`,message:t instanceof Error?t.message:String(t)},500)}}),n}function le(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 ue=`controls-section`,E=`input-field`,D=`table-container`,de=`log-table`,fe=`compact-table`,pe=`level-trace`,me=`level-debug`,O=`level-info`,k=`level-warn`,he=`level-error`,ge=`level-fatal`,_e=`stats-container`,A=`stat-card`,ve=`trace-link`,ye=`timestamp-cell`,be=`service-cell`,xe=`pagination-btn`,Se=`log-entry-group`,Ce=`metadata-row`,we=`message-row`,Te=`message-full-cell`;function Ee(e){let t=e.toLowerCase();return t===`trace`?pe:t===`debug`?me:t===`info`?O:t===`warn`?k:t===`error`?he:t===`fatal`?ge:``}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 Oe({logs:e}){return(0,g.jsx)(`div`,{class:D,children:(0,g.jsxs)(`table`,{class:de,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=De(e.metadata);return(0,g.jsxs)(`tbody`,{class:Se,children:[(0,g.jsxs)(`tr`,{class:Ce,children:[(0,g.jsx)(`td`,{class:ye,children:le(e.timestamp)}),(0,g.jsx)(`td`,{class:Ee(e.level),children:e.level.toUpperCase()}),(0,g.jsx)(`td`,{class:be,children:e.service}),(0,g.jsx)(`td`,{children:e.traceId?(0,g.jsxs)(`span`,{class:ve,onclick:`dashboard.filterByTrace('${e.traceId}')`,children:[e.traceId.slice(0,8),`...`]}):(0,g.jsx)(`span`,{children:`-`})})]}),(0,g.jsx)(`tr`,{class:we,children:(0,g.jsxs)(`td`,{colspan:4,class:Te,children:[(0,g.jsx)(`div`,{children:e.message}),t&&(0,g.jsx)(`pre`,{class:`attributes-cell`,children:JSON.stringify(t,null,2)})]})})]},e.id)})]})})}function j(e){return Math.round(e).toLocaleString()}function ke(e){return`${e.toFixed(1)}%`}function Ae(e){return e?e.toISOString():`-`}function je({metrics:e}){return(0,g.jsxs)(`section`,{class:`metrics-section`,children:[(0,g.jsx)(`div`,{class:`section-header`,children:(0,g.jsx)(`h2`,{children:`Workflow Metrics`})}),(0,g.jsxs)(`div`,{class:ue,children:[(0,g.jsx)(`input`,{id:`metrics-model-filter`,class:E,type:`text`,placeholder:`Model`}),(0,g.jsx)(`input`,{id:`metrics-provider-filter`,class:E,type:`text`,placeholder:`Provider`}),(0,g.jsx)(`button`,{type:`button`,class:`btn`,onclick:`dashboard.fetchMetrics()`,children:`Apply Metrics Filters`})]}),(0,g.jsxs)(`div`,{id:`metrics-summary`,class:_e,children:[(0,g.jsxs)(`div`,{class:A,children:[(0,g.jsx)(`h3`,{children:`Workflow Runs`}),(0,g.jsx)(`div`,{id:`metrics-group-count`,class:`value`,children:e.totals.groupCount.toLocaleString()})]}),(0,g.jsxs)(`div`,{class:A,children:[(0,g.jsx)(`h3`,{children:`Failed Runs`}),(0,g.jsx)(`div`,{id:`metrics-failed-groups`,class:`value`,children:e.totals.failedGroups.toLocaleString()})]}),(0,g.jsxs)(`div`,{class:A,children:[(0,g.jsx)(`h3`,{children:`Total Tokens`}),(0,g.jsx)(`div`,{id:`metrics-total-tokens`,class:`value`,children:j(e.totals.totalTokens)})]}),(0,g.jsxs)(`div`,{class:A,children:[(0,g.jsx)(`h3`,{children:`Token Coverage`}),(0,g.jsx)(`div`,{id:`metrics-token-coverage`,class:`value`,children:ke(e.totals.tokenCoveragePercent)})]})]}),(0,g.jsxs)(`div`,{class:`metrics-grid`,children:[(0,g.jsxs)(`div`,{class:D,children:[(0,g.jsx)(`h3`,{children:`Top Workflow Runs`}),(0,g.jsxs)(`table`,{class:fe,children:[(0,g.jsx)(`thead`,{children:(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`th`,{children:`Workflow`}),(0,g.jsx)(`th`,{children:`Last Seen`}),(0,g.jsx)(`th`,{children:`Tokens`}),(0,g.jsx)(`th`,{children:`Issues`})]})}),(0,g.jsx)(`tbody`,{id:`workflow-metrics-body`,children:e.groups.slice(0,10).map(e=>(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`td`,{children:e.key}),(0,g.jsx)(`td`,{children:Ae(e.lastSeen)}),(0,g.jsx)(`td`,{children:j(e.totalTokens)}),(0,g.jsx)(`td`,{children:e.issueCount.toLocaleString()})]}))})]})]}),(0,g.jsxs)(`div`,{class:D,children:[(0,g.jsx)(`h3`,{children:`Top Tools By Token Use`}),(0,g.jsxs)(`table`,{class:fe,children:[(0,g.jsx)(`thead`,{children:(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`th`,{children:`Tool`}),(0,g.jsx)(`th`,{children:`Invocations`}),(0,g.jsx)(`th`,{children:`Call p90`}),(0,g.jsx)(`th`,{children:`Output p90`})]})}),(0,g.jsx)(`tbody`,{id:`tool-metrics-body`,children:e.tools.rows.slice(0,10).map(e=>(0,g.jsxs)(`tr`,{children:[(0,g.jsx)(`td`,{children:e.toolName}),(0,g.jsx)(`td`,{children:e.invocationCount.toLocaleString()}),(0,g.jsx)(`td`,{children:j(e.toolCallTurn.p90TotalTokens)}),(0,g.jsx)(`td`,{children:j(e.toolOutputImpact.p90TotalTokens)})]}))})]})]})]})]})}function Me(){return(0,g.jsx)(`input`,{id:`search-input`,type:`text`,class:E,placeholder:`Search logs...`,oninput:`dashboard.handleSearchInput(event)`})}function Ne({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 Pe({stats:e}){return(0,g.jsxs)(`div`,{class:_e,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 Fe({initialLogs:e,services:t,stats:n,metrics:r}){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)(Pe,{stats:n}),(0,g.jsx)(je,{metrics:r}),(0,g.jsxs)(`div`,{class:ue,children:[(0,g.jsx)(Me,{}),(0,g.jsx)(Ne,{services:t}),(0,g.jsx)(`button`,{type:`button`,id:`refresh-btn`,class:`btn`,onclick:`dashboard.manualRefresh()`,children:`Refresh Now`})]}),(0,g.jsx)(Oe,{logs:e}),(0,g.jsxs)(`div`,{id:`pagination-controls`,class:`pagination-container`,children:[(0,g.jsx)(`button`,{type:`button`,id:`prev-btn`,class:xe,onclick:`dashboard.prevPage()`,disabled:!0,children:`Previous`}),(0,g.jsx)(`span`,{id:`page-info`,class:`page-info`,children:`Page 1`}),(0,g.jsx)(`button`,{type:`button`,id:`next-btn`,class:xe,onclick:`dashboard.nextPage()`,children:`Next`})]}),(0,g.jsx)(`script`,{children:(0,h.raw)(`
|
|
30
30
|
class DashboardManager {
|
|
31
31
|
constructor() {
|
|
32
32
|
this.filters = {
|
|
@@ -95,8 +95,71 @@ class DashboardManager {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
async fetchMetrics() {
|
|
99
|
+
try {
|
|
100
|
+
const params = new URLSearchParams();
|
|
101
|
+
const model = document.getElementById('metrics-model-filter')?.value?.trim();
|
|
102
|
+
const provider = document.getElementById('metrics-provider-filter')?.value?.trim();
|
|
103
|
+
if (model) params.set('model', model);
|
|
104
|
+
if (provider) params.set('provider', provider);
|
|
105
|
+
params.set('limit', '10');
|
|
106
|
+
params.set('toolLimit', '10');
|
|
107
|
+
|
|
108
|
+
const response = await fetch('/api/metrics?' + params.toString());
|
|
109
|
+
const data = await response.json();
|
|
110
|
+
this.updateMetricsPanel(data);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.error('Failed to fetch metrics:', error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
updateMetricsPanel(metrics) {
|
|
117
|
+
const setText = (id, value) => {
|
|
118
|
+
const element = document.getElementById(id);
|
|
119
|
+
if (element) element.textContent = value;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const number = (value) => Math.round(Number(value || 0)).toLocaleString();
|
|
123
|
+
const percent = (value) => Number(value || 0).toFixed(1) + '%';
|
|
124
|
+
|
|
125
|
+
setText('metrics-group-count', number(metrics.totals?.groupCount));
|
|
126
|
+
setText('metrics-failed-groups', number(metrics.totals?.failedGroups));
|
|
127
|
+
setText('metrics-total-tokens', number(metrics.totals?.totalTokens));
|
|
128
|
+
setText('metrics-token-coverage', percent(metrics.totals?.tokenCoveragePercent));
|
|
129
|
+
|
|
130
|
+
const workflowBody = document.getElementById('workflow-metrics-body');
|
|
131
|
+
if (workflowBody) {
|
|
132
|
+
workflowBody.innerHTML = '';
|
|
133
|
+
for (const row of metrics.groups || []) {
|
|
134
|
+
const tr = document.createElement('tr');
|
|
135
|
+
tr.innerHTML = \`
|
|
136
|
+
<td>\${this.escapeHtml(row.key || 'unknown')}</td>
|
|
137
|
+
<td>\${this.escapeHtml(row.lastSeen || '-')}</td>
|
|
138
|
+
<td>\${number(row.totalTokens)}</td>
|
|
139
|
+
<td>\${number(row.issueCount)}</td>
|
|
140
|
+
\`;
|
|
141
|
+
workflowBody.appendChild(tr);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const toolBody = document.getElementById('tool-metrics-body');
|
|
146
|
+
if (toolBody) {
|
|
147
|
+
toolBody.innerHTML = '';
|
|
148
|
+
for (const row of metrics.tools?.rows || []) {
|
|
149
|
+
const tr = document.createElement('tr');
|
|
150
|
+
tr.innerHTML = \`
|
|
151
|
+
<td>\${this.escapeHtml(row.toolName || 'unknown')}</td>
|
|
152
|
+
<td>\${number(row.invocationCount)}</td>
|
|
153
|
+
<td>\${number(row.toolCallTurn?.p90TotalTokens)}</td>
|
|
154
|
+
<td>\${number(row.toolOutputImpact?.p90TotalTokens)}</td>
|
|
155
|
+
\`;
|
|
156
|
+
toolBody.appendChild(tr);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
98
161
|
updateLogTable(logs) {
|
|
99
|
-
const table = document.querySelector('.${
|
|
162
|
+
const table = document.querySelector('.${de}');
|
|
100
163
|
if (!table) return;
|
|
101
164
|
|
|
102
165
|
// Remove existing tbody elements (except thead)
|
|
@@ -108,7 +171,7 @@ class DashboardManager {
|
|
|
108
171
|
const levelClass = this.getLevelClass(log.level);
|
|
109
172
|
const timestamp = this.formatTimestamp(log.timestamp);
|
|
110
173
|
const traceIdHtml = log.traceId
|
|
111
|
-
? \`<span class="${
|
|
174
|
+
? \`<span class="${ve}" onclick="dashboard.filterByTrace('\${log.traceId}')">\${log.traceId.slice(0, 8)}...</span>\`
|
|
112
175
|
: '<span>-</span>';
|
|
113
176
|
|
|
114
177
|
// Get display attributes (filter out trace/span IDs)
|
|
@@ -118,16 +181,16 @@ class DashboardManager {
|
|
|
118
181
|
: '';
|
|
119
182
|
|
|
120
183
|
const tbody = document.createElement('tbody');
|
|
121
|
-
tbody.className = '${
|
|
184
|
+
tbody.className = '${Se}';
|
|
122
185
|
tbody.innerHTML = \`
|
|
123
|
-
<tr class="${
|
|
124
|
-
<td class="${
|
|
186
|
+
<tr class="${Ce}">
|
|
187
|
+
<td class="${ye}">\${timestamp}</td>
|
|
125
188
|
<td class="\${levelClass}">\${log.level.toUpperCase()}</td>
|
|
126
|
-
<td class="${
|
|
189
|
+
<td class="${be}">\${log.service}</td>
|
|
127
190
|
<td>\${traceIdHtml}</td>
|
|
128
191
|
</tr>
|
|
129
|
-
<tr class="${
|
|
130
|
-
<td colspan="4" class="${
|
|
192
|
+
<tr class="${we}">
|
|
193
|
+
<td colspan="4" class="${Te}">
|
|
131
194
|
<div>\${this.escapeHtml(log.message)}</div>
|
|
132
195
|
\${attrsHtml}
|
|
133
196
|
</td>
|
|
@@ -160,12 +223,12 @@ class DashboardManager {
|
|
|
160
223
|
|
|
161
224
|
getLevelClass(level) {
|
|
162
225
|
const classes = {
|
|
163
|
-
trace: '${
|
|
164
|
-
debug: '${
|
|
165
|
-
info: '${
|
|
166
|
-
warn: '${
|
|
167
|
-
error: '${
|
|
168
|
-
fatal: '${
|
|
226
|
+
trace: '${pe}',
|
|
227
|
+
debug: '${me}',
|
|
228
|
+
info: '${O}',
|
|
229
|
+
warn: '${k}',
|
|
230
|
+
error: '${he}',
|
|
231
|
+
fatal: '${ge}',
|
|
169
232
|
};
|
|
170
233
|
return classes[level.toLowerCase()] || '';
|
|
171
234
|
}
|
|
@@ -268,7 +331,7 @@ const dashboard = new DashboardManager();
|
|
|
268
331
|
window.addEventListener('beforeunload', () => {
|
|
269
332
|
dashboard.stopAutoRefresh();
|
|
270
333
|
});
|
|
271
|
-
`)})]})}function
|
|
334
|
+
`)})]})}function M({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)(`
|
|
272
335
|
* {
|
|
273
336
|
margin: 0;
|
|
274
337
|
padding: 0;
|
|
@@ -446,6 +509,45 @@ window.addEventListener('beforeunload', () => {
|
|
|
446
509
|
margin-bottom: 1rem;
|
|
447
510
|
}
|
|
448
511
|
|
|
512
|
+
.metrics-section {
|
|
513
|
+
background-color: #fff;
|
|
514
|
+
padding: 1rem;
|
|
515
|
+
border-radius: 8px;
|
|
516
|
+
margin-bottom: 1.5rem;
|
|
517
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.section-header h2 {
|
|
521
|
+
font-size: 1rem;
|
|
522
|
+
margin-bottom: 1rem;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.metrics-grid {
|
|
526
|
+
display: grid;
|
|
527
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
528
|
+
gap: 1rem;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.compact-table {
|
|
532
|
+
width: 100%;
|
|
533
|
+
border-collapse: collapse;
|
|
534
|
+
font-size: 13px;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.compact-table th,
|
|
538
|
+
.compact-table td {
|
|
539
|
+
text-align: left;
|
|
540
|
+
padding: 0.5rem;
|
|
541
|
+
border-bottom: 1px solid #e5e5e5;
|
|
542
|
+
vertical-align: top;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.compact-table th {
|
|
546
|
+
color: #666;
|
|
547
|
+
font-size: 12px;
|
|
548
|
+
font-weight: 600;
|
|
549
|
+
}
|
|
550
|
+
|
|
449
551
|
.stat-card {
|
|
450
552
|
background-color: #fff;
|
|
451
553
|
padding: 1rem;
|
|
@@ -466,6 +568,14 @@ window.addEventListener('beforeunload', () => {
|
|
|
466
568
|
color: #333;
|
|
467
569
|
}
|
|
468
570
|
|
|
571
|
+
@media (max-width: 900px) {
|
|
572
|
+
.stats-container,
|
|
573
|
+
.metrics-grid {
|
|
574
|
+
grid-template-columns: 1fr;
|
|
575
|
+
flex-direction: column;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
469
579
|
.trace-link {
|
|
470
580
|
color: #2196f3;
|
|
471
581
|
text-decoration: underline;
|
|
@@ -583,10 +693,10 @@ window.addEventListener('beforeunload', () => {
|
|
|
583
693
|
color: #555;
|
|
584
694
|
margin-top: 0.25rem;
|
|
585
695
|
}
|
|
586
|
-
`)})]}),(0,g.jsx)(`body`,{children:t})]})}function Te(e){let n=new f.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)(we,{title:`Log Dashboard`,children:(0,g.jsx)(Ce,{initialLogs:i,services:t,stats:a})}))}catch(t){return console.error(`Failed to render dashboard:`,t),e.html((0,g.jsx)(we,{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 Ee=5*1024*1024,N=2e3,P=8e3,De=8e3,Oe=32e3,ke=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],Ae=[`log`,`span`],je=/(authorization|cookie|email|account|password|secret|token|api[_-]?key|clientsecrethash)/i,Me=/(prompt|tool[_-]?input|tool[_-]?output|request\.body|response\.body|command|argv|stdout|stderr|transcript)/i,Ne=n.z.object({timestamp:n.z.string().datetime().optional(),level:n.z.enum(ke),message:n.z.string().min(1).max(P),traceId:n.z.string().regex(/^[0-9a-f]{32}$/i,`traceId must be 32 hex characters`).optional(),spanId:n.z.string().regex(/^[0-9a-f]{16}$/i,`spanId must be 16 hex characters`).optional(),parentSpanId:n.z.string().regex(/^[0-9a-f]{16}$/i,`parentSpanId must be 16 hex characters`).optional(),service:n.z.string().min(1).max(200),hostname:n.z.string().max(255).optional(),pid:n.z.number().int().nonnegative().optional(),metadata:n.z.record(n.z.string(),n.z.unknown()).optional(),errorType:n.z.string().max(255).optional(),errorMessage:n.z.string().max(De).optional(),errorStack:n.z.string().max(Oe).optional(),sessionId:n.z.string().max(200).optional(),workflowRunId:n.z.string().max(200).optional(),workflowName:n.z.string().max(200).optional(),jobName:n.z.string().max(200).optional(),jobId:n.z.string().max(200).optional(),agentName:n.z.string().max(200).optional(),parentSessionId:n.z.string().max(200).optional(),workflowRunKey:n.z.string().max(200).optional(),workflowId:n.z.string().max(200).optional(),workflowWorkspace:n.z.string().max(200).optional(),stepName:n.z.string().max(200).optional(),phase:n.z.string().max(200).optional(),signalType:n.z.enum(Ae).optional(),durationMs:n.z.number().nonnegative().optional()}),Pe=n.z.object({logs:n.z.array(Ne).min(1).max(1e3)});function F(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 I(e,t){return typeof e==`string`&&RegExp(`^[0-9a-f]{${t}}$`,`i`).test(e)}function L(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<=0?null:t>Ee?`Request body exceeds ${Ee} bytes`:null}function R(e){return e.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,`[REDACTED_EMAIL]`).replace(/(bearer\s+)[A-Za-z0-9._~+/=-]+/gi,`$1[REDACTED]`).replace(/((?:token|secret|password|api[_-]?key|clientsecrethash)\s*[:=]\s*)[^\s,;]+/gi,`$1[REDACTED]`)}function Fe(e){let t=typeof e==`string`?e:JSON.stringify(e);return{length:t.length,sha256:(0,d.createHash)(`sha256`).update(t).digest(`hex`)}}function z(e,t,n){return je.test(e)?`[REDACTED]`:Me.test(e)&&!n?Fe(t):typeof t==`string`?R(t).slice(0,P):Array.isArray(t)?t.map(t=>z(e,t,n)):t&&typeof t==`object`?Object.fromEntries(Object.entries(t).map(([e,t])=>[e,z(e,t,n)])):t}function B(e){let t=[`1`,`true`,`yes`,`on`].includes(String(process.env.LOG_SINK_ALLOW_SENSITIVE_TELEMETRY??``).toLowerCase()),n=e.metadata?z(`metadata`,e.metadata,t):null;return n?.[`console.args`]===e.message&&delete n[`console.args`],{...e,message:R(e.message).slice(0,P),metadata:n,errorMessage:e.errorMessage?R(e.errorMessage).slice(0,De):null,errorStack:e.errorStack?R(e.errorStack).slice(0,Oe):null}}function V(e){let t=(...t)=>{for(let n of t){let t=e[n];if(typeof t==`string`&&t.length>0)return t.slice(0,200)}return null};return{agentName:t(`agent.name`,`agent`),sessionId:t(`agent.session.id`,`session.id`),parentSessionId:t(`agent.parent_session.id`,`parent.session.id`),workflowRunId:t(`workflow.run.id`),workflowRunKey:t(`workflow.run.key`),workflowId:t(`workflow.id`),workflowName:t(`workflow.name`),workflowWorkspace:t(`workflow.workspace`),jobId:t(`workflow.job.id`),jobName:t(`workflow.job.name`),stepName:t(`workflow.step.name`),phase:t(`workflow.phase`)}}async function H(e){try{return await e.req.json()}catch(e){throw Error(`Invalid JSON body: ${e instanceof Error?e.message:String(e)}`)}}function Ie(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>N)throw Error(`OTLP trace request exceeds ${N} spans`);if(!I(n.traceId,32))throw Error(`Invalid OTLP trace request: traceId must be 32 hex characters`);if(!I(n.spanId,16))throw Error(`Invalid OTLP trace request: spanId must be 16 hex characters`);if(n.parentSpanId&&!I(n.parentSpanId,16))throw Error(`Invalid OTLP trace request: parentSpanId must be 16 hex characters`);if(!n.name||n.name.length>P)throw Error(`Invalid OTLP trace request: span name is missing or too long`);F(n.startTimeUnixNano),F(n.endTimeUnixNano)}}}}function Le(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>N)throw Error(`OTLP logs request exceeds ${N} log records`);if(n.traceId&&!I(n.traceId,32))throw Error(`Invalid OTLP logs request: traceId must be 32 hex characters`);if(n.spanId&&!I(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`);F(n.timeUnixNano)}}}}function Re(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function U(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 ze(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function Be(e){return e===void 0?`info`:e<=4?`trace`:e<=8?`debug`:e<=12?`info`:e<=16?`warn`:e<=20?`error`:`fatal`}function W(e){let t=t=>{let n=e.req.header(t);return n&&n.length>0?n.slice(0,200):null};return{sessionId:t(`x-agent-session-id`),workflowRunId:t(`x-workflow-run-id`),workflowName:t(`x-workflow-name`),jobName:t(`x-workflow-job-name`),jobId:t(`x-workflow-job-id`),agent:t(`x-agent`),parentSessionId:t(`x-agent-parent-session-id`),workflowRunKey:t(`x-workflow-run-key`),workflowId:t(`x-workflow-id`),workflowWorkspace:t(`x-workflow-workspace`),stepName:t(`x-workflow-step-name`),phase:t(`x-workflow-phase`)}}function G(e,t){return{...e,service:e.service===`unknown-service`&&t.agent?t.agent:e.service,sessionId:e.sessionId??t.sessionId,workflowRunId:e.workflowRunId??t.workflowRunId,workflowName:e.workflowName??t.workflowName,jobName:e.jobName??t.jobName,jobId:e.jobId??t.jobId,agentName:e.agentName??t.agent,parentSessionId:e.parentSessionId??t.parentSessionId,workflowRunKey:e.workflowRunKey??t.workflowRunKey,workflowId:e.workflowId??t.workflowId,workflowWorkspace:e.workflowWorkspace??t.workflowWorkspace,stepName:e.stepName??t.stepName,phase:e.phase??t.phase}}function Ve(e,t){let n=F(e.timeUnixNano),r=e.severityText?.toLowerCase()||Be(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,l=t;if(e.attributes)for(let n of e.attributes){let e=n.value.stringValue??n.value.intValue??n.value.doubleValue??n.value.boolValue;n.key===`exception.type`?o=String(e):n.key===`exception.message`?s=String(e):n.key===`exception.stacktrace`?c=String(e):n.key===`service.name`&&e?(l=String(e).slice(0,200),a[`resource.service.name`]=t):a[n.key]=e}return{timestamp:n,level:r,message:i||`[LOG] ${t}`,traceId:e.traceId??null,spanId:e.spanId??null,parentSpanId:null,service:l,hostname:null,pid:null,metadata:Object.keys(a).length>0?a:null,errorType:o,errorMessage:s,errorStack:c,signalType:`log`,...V(a)}}function He(e,t){let n=F(e.startTimeUnixNano),r=e.status?.code,i=`info`;r===2?i=`error`:r===1&&(i=`info`);let a=U(e.attributes,`exception.type`),o=U(e.attributes,`exception.message`),s=U(e.attributes,`exception.stacktrace`),c={},l=t;if(e.attributes)for(let n of e.attributes){let e=n.value.stringValue??n.value.intValue??n.value.doubleValue??n.value.boolValue;c[n.key]=e,n.key===`service.name`&&e&&(l=String(e).slice(0,200),c[`resource.service.name`]=t)}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:l,hostname:null,pid:null,metadata:c,errorType:a??null,errorMessage:o??null,errorStack:s??null,signalType:`span`,durationMs:Math.max(0,Number((BigInt(e.endTimeUnixNano)-BigInt(e.startTimeUnixNano))/BigInt(1e6))),...V(c)}}function Ue(e){let n=new f.Hono,r=e.get(t.d.LogStorageService);n.use(`*`,(0,p.cors)({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`,`Authorization`,`x-agent`,`x-agent-session-id`,`x-agent-parent-session-id`,`x-workflow-run-id`,`x-workflow-run-key`,`x-workflow-id`,`x-workflow-name`,`x-workflow-workspace`,`x-workflow-job-id`,`x-workflow-job-name`,`x-workflow-step-name`,`x-workflow-phase`]})),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,m.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=L(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await H(e),i=Pe.safeParse(n);if(!i.success)return e.json({success:!1,error:`Validation failed`,details:i.error.issues},400);let{logs:a}=i.data,o=W(e),s=a.map(e=>B(G({...e,timestamp:e.timestamp?new Date(e.timestamp):new Date},o))),c=await r.insertBatch(s);return e.json({success:!0,count:c.length,message:`Successfully inserted ${c.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=L(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await H(e);Ie(n);let i=W(e),a=[];for(let e of n.resourceSpans){let t=Re(e);for(let n of e.scopeSpans)for(let e of n.spans){let n=B(G(He(e,t),i));a.push(n)}}let o=await r.insertBatch(a);return e.json({success:!0,count:o.length,message:`Successfully inserted ${o.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=L(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await H(e);Le(n);let i=W(e),a=[];for(let e of n.resourceLogs){let t=ze(e);for(let n of e.scopeLogs)for(let e of n.logRecords){let n=B(G(Ve(e,t),i));a.push(n)}}let o=await r.insertBatch(a);return e.json({success:!0,count:o.length,message:`Successfully inserted ${o.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=se(e);n.route(`/api`,a);let o=Te(e);return n.route(`/`,o),n}function K(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 q(e={}){let t=K(e.dbMaxBytes??process.env.LOG_SINK_DB_MAX_BYTES,268435456),n=K(e.dbTargetBytes??process.env.LOG_SINK_DB_TARGET_BYTES,Math.floor(t*.9));return{maxBytes:t,targetBytes:Math.min(n,t),intervalMs:K(e.dbRetentionIntervalMs??process.env.LOG_SINK_DB_RETENTION_INTERVAL_MS,3e5),batchSize:K(e.dbRetentionBatchSize??process.env.LOG_SINK_DB_RETENTION_BATCH_SIZE,1e3)}}const We=_(new l.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`).option(`--db-path <path>`,`Path to SQLite database file`).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=>{let n;try{let r=y(e),i=e.dbPath??r.dbPath,a=e.port?Number.parseInt(e.port,10):r.port,o={min:s.DEFAULT_PORT_RANGE.min,max:a===void 0?s.DEFAULT_PORT_RANGE.max??4999:Math.max(s.DEFAULT_PORT_RANGE.max??4999,a)},l=process.env.NODE_ENV||`development`,d=r.repositoryPath,f=`log-sink-mcp-http`;e.registryPath&&(process.env.PORT_REGISTRY_PATH=e.registryPath,process.env.PROCESS_REGISTRY_PATH=(0,c.resolveSiblingRegistryPath)(e.registryPath,`processes.json`)??process.env.PROCESS_REGISTRY_PATH);let p=new s.PortRegistryService(process.env.PORT_REGISTRY_PATH),m=t.l(),h=m.get(t.d.LogStorageService),g=m.get(t.d.LogRetentionService);await h.initializeDatabase(i,e.inMemory);let _=g.startSizeRetentionMonitor(q({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize})),v=Ue(m),b=await p.reservePort({repositoryPath:d,serviceName:f,serviceType:`tool`,environment:l,...a===void 0?{}:{preferredPort:a},pid:process.pid,host:`127.0.0.1`,force:!0,portRange:o,metadata:{dbPath:i,scope:r.scope}});if(!b.success||!b.record)throw Error(b.error||`Failed to reserve service in global registry`);let x=b.record.port;if(x!==a||!b.record.metadata?.healthCheckUrl){let e=await p.reservePort({repositoryPath:d,serviceName:f,serviceType:`tool`,environment:l,preferredPort:x,pid:process.pid,host:`127.0.0.1`,force:!0,portRange:o,metadata:{healthCheckUrl:`http://localhost:${x}/health`,dbPath:i,scope:r.scope}});if(!e.success||!e.record)throw Error(e.error||`Failed to update service metadata in registry`);b=e}if(!b.record)throw Error(`Port reservation lost before startup`);let S=b.record.port;n=await(0,c.createProcessLease)({repositoryPath:d,serviceName:f,serviceType:`tool`,environment:l,pid:process.pid,port:S,host:`127.0.0.1`,command:process.argv[1],args:process.argv.slice(2),metadata:{dbPath:i,scope:r.scope,transport:`http`}});let C;try{C=(0,u.serve)({fetch:v.fetch,port:S})}catch(e){try{await n.release({kill:!1})}catch{}throw e}let w=async()=>{try{C.close();try{await n?.release({kill:!1})}catch{}_(),await h.disconnect(),process.exit(0)}catch{process.exit(1)}};process.on(`SIGINT`,()=>w()),process.on(`SIGTERM`,()=>w())}catch{try{await n?.release({kill:!1})}catch{}process.exit(1)}}),J=n.z.object({timeRange:n.z.object({start:n.z.string().describe(`Start time for error analysis (ISO 8601 format)`),end:n.z.string().describe(`End time for error analysis (ISO 8601 format)`)}).optional().describe(`Optional time range to filter errors`),traceId:n.z.string().optional().describe(`Optional trace ID to analyze errors within a specific trace`),limit:n.z.number().min(1).max(1e3).optional().default(100).describe(`Maximum number of error entries to analyze`)});var Ge=class e{static TOOL_NAME=`analyze_errors`;queryService;constructor(e){this.queryService=e.get(t.d.LogQueryService)}getInputSchema(){return J}getDefinition(){return{name:e.TOOL_NAME,description:`Analyze error patterns and group similar errors by type and message. Calculates error frequencies, suggests root causes, and provides debugging insights.`,inputSchema:n.z.toJSONSchema(J,{reused:`inline`})}}async execute(e){try{let t=J.parse(e),n=t.limit,r;if(t.traceId)r=(await this.queryService.filterByTrace(t.traceId)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,n);else if(t.timeRange){let e=new Date(t.timeRange.start),i=new Date(t.timeRange.end);r=(await this.queryService.filterByTimeRange(e,i)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,n)}else r=await this.queryService.filterByLevel([`error`,`fatal`],n);let i=new Map;for(let e of r){let t=`${e.errorType}::${e.errorMessage}`;i.has(t)||i.set(t,{errorType:e.errorType,errorMessage:e.errorMessage,frequency:0,services:new Set,examples:[]});let n=i.get(t);n.frequency+=1,n.services.add(e.service),n.examples.length<3&&n.examples.push({id:e.id,timestamp:e.timestamp.toISOString(),service:e.service})}let a=Array.from(i.values()).sort((e,t)=>t.frequency-e.frequency).map(e=>({errorType:e.errorType,errorMessage:e.errorMessage,frequency:e.frequency,affectedServices:Array.from(e.services),examples:e.examples}));return{content:[{type:`text`,text:JSON.stringify({totalErrorsAnalyzed:r.length,uniqueErrorPatterns:a.length,analysis:a},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error analyzing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const Ke=n.z.object({});var qe=class e{static TOOL_NAME=`clear_logs`;retentionService;constructor(e){this.retentionService=e.get(t.d.LogRetentionService)}getInputSchema(){return Ke}getDefinition(){return{name:e.TOOL_NAME,description:`Clear all stored log entries from the database. Useful for resetting state in development.`,inputSchema:n.z.toJSONSchema(Ke,{reused:`inline`})}}async execute(e){try{let e=await this.retentionService.clearAllLogs();return{content:[{type:`text`,text:JSON.stringify({success:!0,message:`All logs have been cleared`,deletedEntries:e},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error clearing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const Je=new Set(Object.values(t.i)),Ye=new Set([`level`,`service`,`both`]),Xe={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 l.InvalidArgumentError(`Expected a positive integer.`);return t}function Ze(e){if(!Ye.has(e))throw new l.InvalidArgumentError(`Expected one of: 'level', 'service', 'both'.`);return e}function X(e,t=[]){if(!Je.has(e))throw new l.InvalidArgumentError(`Invalid log level '${e}'. Expected one of: ${Array.from(Je).join(`, `)}`);return[...t,e]}function Qe(e){return e.content.map(e=>e.type===`text`?e.text:JSON.stringify(e)).join(`
|
|
587
|
-
`)}function Z(e){return _(e).option(`--db-path <path>`,`Path to SQLite database file`).option(`--in-memory`,`Use in-memory database`,!1)}async function $e(e,n){let r=y(e),i=e.dbPath??r.dbPath,a=t.l(),o=a.get(t.d.LogStorageService);await o.initializeDatabase(i,e.inMemory);try{return await n(a)}finally{await o.disconnect()}}async function et(e,t,n,r=Xe){try{let i=await $e(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 Q(e,t,n){let r=await et(e,t,n);r!==0&&process.exit(r)}function tt(e,t){if(!(!e&&!t)){if(!e||!t)throw new l.InvalidArgumentError(`Provide both --start-time and --end-time together.`);return{start:e,end:t}}}function nt(){let e=new l.Command(`logs`).description(`Run log analysis commands that mirror MCP tool capabilities`),n=Z(new l.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`,X).option(`--trace-id <traceId>`,`Trace ID to filter by`).option(`--span-id <spanId>`,`Span ID to filter by`).option(`--error-type <errorType>`,`Error type to filter by`).option(`--service <service...>`,`Service name(s) to filter by`).option(`--session-id <sessionId>`,`Agent session id to filter by`).option(`--workflow-run-id <workflowRunId>`,`Workflow run id to filter by`).option(`--workflow-name <workflowName>`,`Workflow name to filter by (all runs of a workflow type)`).option(`--job-name <jobName>`,`Workflow job name 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 e=>{await Q(e,e=>new t.r(e),{level:e.level,traceId:e.traceId,spanId:e.spanId,errorType:e.errorType,service:e.service,sessionId:e.sessionId,workflowRunId:e.workflowRunId,workflowName:e.workflowName,jobName:e.jobName,startTime:e.startTime,endTime:e.endTime,limit:e.limit})})),r=Z(new l.Command(`search`).alias(`search-logs`).description(`Search or filter log entries with optional full-text search`).argument(`[search-query]`,`Optional 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`,X).option(`--trace-id <traceId>`,`Trace ID to filter by`).option(`--span-id <spanId>`,`Span ID to filter by`).option(`--error-type <errorType>`,`Error type to filter by`).option(`--session-id <sessionId>`,`Agent session id to filter by`).option(`--workflow-run-id <workflowRunId>`,`Workflow run id to filter by`).option(`--workflow-name <workflowName>`,`Workflow name to filter by (all runs of a workflow type)`).option(`--job-name <jobName>`,`Workflow job name 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(e,n)=>{await Q(n,e=>new t.r(e),{searchQuery:e,mode:n.mode,service:n.service,level:n.level,traceId:n.traceId,spanId:n.spanId,errorType:n.errorType,sessionId:n.sessionId,workflowRunId:n.workflowRunId,workflowName:n.workflowName,jobName:n.jobName,startTime:n.startTime,endTime:n.endTime,limit:n.limit})})),i=Z(new l.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 Q(n,e=>new t.a(e),{traceId:e})})),a=Z(new l.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 function(e){let t;try{t=tt(e.startTime,e.endTime)}catch(e){this.error(e instanceof Error?e.message:String(e))}await Q(e,e=>new Ge(e),{traceId:e.traceId,timeRange:t,limit:e.limit})})),o=Z(new l.Command(`agent-issues`).alias(`agent-problems`).description(`Detect and aggregate agent-run problems (tool failures, API errors/refusals, retries, rejections)`).option(`--session-id <sessionId>`,`Agent session id to scope by`).option(`--workflow-run-id <workflowRunId>`,`Workflow run id to scope by`).option(`--workflow-name <workflowName>`,`Workflow name to scope by`).option(`--job-name <jobName>`,`Workflow job name to scope by`).option(`--service <service...>`,`Agent/service name(s) to scope by`).option(`--start-time <iso8601>`,`Start time for the analysis window (ISO 8601)`).option(`--end-time <iso8601>`,`End time for the analysis window (ISO 8601)`).option(`--limit <number>`,`Maximum number of issue samples to return`,Y).action(async e=>{await Q(e,e=>new t.c(e),{sessionId:e.sessionId,workflowRunId:e.workflowRunId,workflowName:e.workflowName,jobName:e.jobName,service:e.service,startTime:e.startTime,endTime:e.endTime,limit:e.limit})})),s=Z(new l.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`,Ze).action(async e=>{await Q(e,e=>new t.s(e),{startTime:e.startTime,endTime:e.endTime,groupBy:e.groupBy})})),c=Z(new l.Command(`services`).alias(`get-services`).description(`Get the list of unique services present in the log database`).action(async e=>{await Q(e,e=>new t.o(e),{})})),u=Z(new l.Command(`clear`).alias(`clear-logs`).description(`Clear all logs from the database`).action(async e=>{await Q(e,e=>new qe(e),{})}));return e.addCommand(n).addCommand(r).addCommand(i).addCommand(a).addCommand(o).addCommand(s).addCommand(c).addCommand(u)}const rt=nt(),it=_(new l.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`).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=(0,c.resolveSiblingRegistryPath)(r,`processes.json`)??process.env.PROCESS_REGISTRY_PATH);let i=y(e),a=e.dbPath??i.dbPath,o=t.l(),s=o.get(t.d.LogStorageService),l=o.get(t.d.LogRetentionService),u=o.get(t.d.HttpServerManager);await s.initializeDatabase(a,e.inMemory);let d=l.startSizeRetentionMonitor(q({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize})),f=new t.t(t.n(o));n=await(0,c.createProcessLease)({repositoryPath:i.repositoryPath,serviceName:`log-sink-mcp-mcp-serve`,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`,scope:i.scope}});let p=u.ensureRunning(void 0,a),m;p.then(e=>{e.running&&(m=u.startHealthMonitor(a))}).catch(()=>void 0);let h=async t=>{console.error(`\nReceived ${t}, shutting down gracefully...`);try{m?.(),await f.stop(),e.cleanup&&(await p.catch(()=>({running:!1}))).running&&(await u.stop(),console.error(`HTTP server stopped`)),d(),await s.disconnect(),await n?.release(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>h(`SIGINT`)),process.on(`SIGTERM`,()=>h(`SIGTERM`)),await f.start()}catch(e){await n?.release().catch(()=>void 0),console.error(`Failed to start MCP server:`,e),process.exit(1)}}),at={stdout:e=>console.log(e),stderr:e=>console.error(e)};function ot(e,t){let n={...process.env};e.config&&(n.LOG_SINK_CONFIG=e.config);let r=t??v(e);return r&&(n.LOG_SINK_INSTANCE=r),n}async function $(t,n){let r=ot(t,n),i=e.c({configPath:t.config,env:r,packageName:e.s}),a=await e.o({configPath:t.config,env:r,packageName:e.s,healthCheck:t.healthCheck});return{scope:i.scope,registered:!!a,port:a?.port,host:a?.host,endpoint:a?.endpoint,configPath:i.configPath,dbPath:i.dbPath,repositoryPath:i.repositoryPath,registeredName:i.registeredName}}function st(e,t){return e.registered?`${e.scope}: ${e.endpoint} (port ${e.port})`:`${e.scope}: not registered${t===!1?``:` or unhealthy`}`}async function ct(e,t=at){try{let n=e.all?await Promise.all([$(e,`local`),$(e,`global`)]):[await $(e)];return e.json?t.stdout(JSON.stringify(e.all?n:n[0],null,2)):t.stdout(n.map(t=>st(t,e.healthCheck)).join(`
|
|
588
|
-
`)),+!n.some(e=>e.registered)}catch(e){return t.stderr(`Error resolving log-sink port: ${e instanceof Error?e.message:String(e)}`),1}}const
|
|
696
|
+
`)})]}),(0,g.jsx)(`body`,{children:t})]})}function Ie(e){let n=new f.Hono,r=e.get(t.p.LogQueryService),i=e.get(t.p.LogMetricsService);return n.get(`/`,async e=>{try{let t=await r.getActiveServices(),n=await r.getStatistics(),a=await i.getMetrics({},{limit:10,toolLimit:10}),o=await r.filterByLevel([`info`,`warn`,`error`,`fatal`],100),s={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)(M,{title:`Log Dashboard`,children:(0,g.jsx)(Fe,{initialLogs:o,services:t,stats:s,metrics:a})}))}catch(t){return console.error(`Failed to render dashboard:`,t),e.html((0,g.jsx)(M,{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 N=5*1024*1024,P=2e3,F=8e3,Le=8e3,Re=32e3,ze=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],Be=[`log`,`span`],Ve=/(authorization|cookie|email|account|password|secret|token|api[_-]?key|clientsecrethash)/i,He=/(prompt|tool[_-]?input|tool[_-]?output|request\.body|response\.body|command|argv|stdout|stderr|transcript)/i,Ue=n.z.object({timestamp:n.z.string().datetime().optional(),level:n.z.enum(ze),message:n.z.string().min(1).max(F),traceId:n.z.string().regex(/^[0-9a-f]{32}$/i,`traceId must be 32 hex characters`).optional(),spanId:n.z.string().regex(/^[0-9a-f]{16}$/i,`spanId must be 16 hex characters`).optional(),parentSpanId:n.z.string().regex(/^[0-9a-f]{16}$/i,`parentSpanId must be 16 hex characters`).optional(),service:n.z.string().min(1).max(200),hostname:n.z.string().max(255).optional(),pid:n.z.number().int().nonnegative().optional(),metadata:n.z.record(n.z.string(),n.z.unknown()).optional(),errorType:n.z.string().max(255).optional(),errorMessage:n.z.string().max(Le).optional(),errorStack:n.z.string().max(Re).optional(),sessionId:n.z.string().max(200).optional(),workflowRunId:n.z.string().max(200).optional(),workflowName:n.z.string().max(200).optional(),jobName:n.z.string().max(200).optional(),jobId:n.z.string().max(200).optional(),agentName:n.z.string().max(200).optional(),parentSessionId:n.z.string().max(200).optional(),workflowRunKey:n.z.string().max(200).optional(),workflowId:n.z.string().max(200).optional(),workflowWorkspace:n.z.string().max(200).optional(),stepName:n.z.string().max(200).optional(),phase:n.z.string().max(200).optional(),signalType:n.z.enum(Be).optional(),durationMs:n.z.number().nonnegative().optional()}),We=n.z.object({logs:n.z.array(Ue).min(1).max(1e3)});function I(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 L(e,t){return typeof e==`string`&&RegExp(`^[0-9a-f]{${t}}$`,`i`).test(e)}function R(e){if(!e)return null;let t=Number(e);return!Number.isFinite(t)||t<=0?null:t>N?`Request body exceeds ${N} bytes`:null}function z(e){return e.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi,`[REDACTED_EMAIL]`).replace(/(bearer\s+)[A-Za-z0-9._~+/=-]+/gi,`$1[REDACTED]`).replace(/((?:token|secret|password|api[_-]?key|clientsecrethash)\s*[:=]\s*)[^\s,;]+/gi,`$1[REDACTED]`)}function Ge(e){let t=typeof e==`string`?e:JSON.stringify(e);return{length:t.length,sha256:(0,d.createHash)(`sha256`).update(t).digest(`hex`)}}function B(e,n,r){if(Ve.test(e)&&t.d(e)){if(n&&typeof n==`object`)return Array.isArray(n)?n.map(t=>B(e,t,r)):Object.fromEntries(Object.entries(n).map(([e,t])=>[e,B(e,t,r)]));if(t.f(n))return n}return Ve.test(e)?`[REDACTED]`:He.test(e)&&!r?Ge(n):typeof n==`string`?z(n).slice(0,F):Array.isArray(n)?n.map(t=>B(e,t,r)):n&&typeof n==`object`?Object.fromEntries(Object.entries(n).map(([e,t])=>[e,B(e,t,r)])):n}function V(e){let t=[`1`,`true`,`yes`,`on`].includes(String(process.env.LOG_SINK_ALLOW_SENSITIVE_TELEMETRY??``).toLowerCase()),n=e.metadata?B(`metadata`,e.metadata,t):null;return n?.[`console.args`]===e.message&&delete n[`console.args`],{...e,message:z(e.message).slice(0,F),metadata:n,errorMessage:e.errorMessage?z(e.errorMessage).slice(0,Le):null,errorStack:e.errorStack?z(e.errorStack).slice(0,Re):null}}function Ke(e){let t=(...t)=>{for(let n of t){let t=e[n];if(typeof t==`string`&&t.length>0)return t.slice(0,200)}return null};return{agentName:t(`agent.name`,`agent`),sessionId:t(`agent.session.id`,`session.id`),parentSessionId:t(`agent.parent_session.id`,`parent.session.id`),workflowRunId:t(`workflow.run.id`),workflowRunKey:t(`workflow.run.key`),workflowId:t(`workflow.id`),workflowName:t(`workflow.name`),workflowWorkspace:t(`workflow.workspace`),jobId:t(`workflow.job.id`),jobName:t(`workflow.job.name`),stepName:t(`workflow.step.name`),phase:t(`workflow.phase`)}}async function H(e){try{return await e.req.json()}catch(e){throw Error(`Invalid JSON body: ${e instanceof Error?e.message:String(e)}`)}}function qe(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>P)throw Error(`OTLP trace request exceeds ${P} spans`);if(!L(n.traceId,32))throw Error(`Invalid OTLP trace request: traceId must be 32 hex characters`);if(!L(n.spanId,16))throw Error(`Invalid OTLP trace request: spanId must be 16 hex characters`);if(n.parentSpanId&&!L(n.parentSpanId,16))throw Error(`Invalid OTLP trace request: parentSpanId must be 16 hex characters`);if(!n.name||n.name.length>F)throw Error(`Invalid OTLP trace request: span name is missing or too long`);I(n.startTimeUnixNano),I(n.endTimeUnixNano)}}}}function Je(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>P)throw Error(`OTLP logs request exceeds ${P} log records`);if(n.traceId&&!L(n.traceId,32))throw Error(`Invalid OTLP logs request: traceId must be 32 hex characters`);if(n.spanId&&!L(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`);I(n.timeUnixNano)}}}}function Ye(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function U(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 Xe(e){return(e.resource?.attributes?.find(e=>e.key===`service.name`))?.value?.stringValue||`unknown-service`}function Ze(e){return e===void 0?`info`:e<=4?`trace`:e<=8?`debug`:e<=12?`info`:e<=16?`warn`:e<=20?`error`:`fatal`}function W(e){let t=t=>{let n=e.req.header(t);return n&&n.length>0?n.slice(0,200):null};return{sessionId:t(`x-agent-session-id`),workflowRunId:t(`x-workflow-run-id`),workflowName:t(`x-workflow-name`),jobName:t(`x-workflow-job-name`),jobId:t(`x-workflow-job-id`),agent:t(`x-agent`),parentSessionId:t(`x-agent-parent-session-id`),workflowRunKey:t(`x-workflow-run-key`),workflowId:t(`x-workflow-id`),workflowWorkspace:t(`x-workflow-workspace`),stepName:t(`x-workflow-step-name`),phase:t(`x-workflow-phase`)}}function G(e,t){return{...e,service:e.service===`unknown-service`&&t.agent?t.agent:e.service,sessionId:e.sessionId??t.sessionId,workflowRunId:e.workflowRunId??t.workflowRunId,workflowName:e.workflowName??t.workflowName,jobName:e.jobName??t.jobName,jobId:e.jobId??t.jobId,agentName:e.agentName??t.agent,parentSessionId:e.parentSessionId??t.parentSessionId,workflowRunKey:e.workflowRunKey??t.workflowRunKey,workflowId:e.workflowId??t.workflowId,workflowWorkspace:e.workflowWorkspace??t.workflowWorkspace,stepName:e.stepName??t.stepName,phase:e.phase??t.phase}}function Qe(e,t){let n=I(e.timeUnixNano),r=e.severityText?.toLowerCase()||Ze(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,l=t;if(e.attributes)for(let n of e.attributes){let e=n.value.stringValue??n.value.intValue??n.value.doubleValue??n.value.boolValue;n.key===`exception.type`?o=String(e):n.key===`exception.message`?s=String(e):n.key===`exception.stacktrace`?c=String(e):n.key===`service.name`&&e?(l=String(e).slice(0,200),a[`resource.service.name`]=t):a[n.key]=e}return{timestamp:n,level:r,message:i||`[LOG] ${t}`,traceId:e.traceId??null,spanId:e.spanId??null,parentSpanId:null,service:l,hostname:null,pid:null,metadata:Object.keys(a).length>0?a:null,errorType:o,errorMessage:s,errorStack:c,signalType:`log`,...Ke(a)}}function $e(e,t){let n=I(e.startTimeUnixNano),r=e.status?.code,i=`info`;r===2?i=`error`:r===1&&(i=`info`);let a=U(e.attributes,`exception.type`),o=U(e.attributes,`exception.message`),s=U(e.attributes,`exception.stacktrace`),c={},l=t;if(e.attributes)for(let n of e.attributes){let e=n.value.stringValue??n.value.intValue??n.value.doubleValue??n.value.boolValue;c[n.key]=e,n.key===`service.name`&&e&&(l=String(e).slice(0,200),c[`resource.service.name`]=t)}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:l,hostname:null,pid:null,metadata:c,errorType:a??null,errorMessage:o??null,errorStack:s??null,signalType:`span`,durationMs:Math.max(0,Number((BigInt(e.endTimeUnixNano)-BigInt(e.startTimeUnixNano))/BigInt(1e6))),...Ke(c)}}function et(e){let n=new f.Hono,r=e.get(t.p.LogStorageService);n.use(`*`,(0,p.cors)({origin:e=>e??null,credentials:!0,allowMethods:[`GET`,`POST`,`OPTIONS`],allowHeaders:[`Content-Type`,`Accept`,`Authorization`,`x-agent`,`x-agent-session-id`,`x-agent-parent-session-id`,`x-workflow-run-id`,`x-workflow-run-key`,`x-workflow-id`,`x-workflow-name`,`x-workflow-workspace`,`x-workflow-job-id`,`x-workflow-job-name`,`x-workflow-step-name`,`x-workflow-phase`]})),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,m.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=R(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await H(e),i=We.safeParse(n);if(!i.success)return e.json({success:!1,error:`Validation failed`,details:i.error.issues},400);let{logs:a}=i.data,o=W(e),s=a.map(e=>V(G({...e,timestamp:e.timestamp?new Date(e.timestamp):new Date},o))),c=await r.insertBatch(s);return e.json({success:!0,count:c.length,message:`Successfully inserted ${c.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=R(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await H(e);qe(n);let i=W(e),a=[];for(let e of n.resourceSpans){let t=Ye(e);for(let n of e.scopeSpans)for(let e of n.spans){let n=V(G($e(e,t),i));a.push(n)}}let o=await r.insertBatch(a);return e.json({success:!0,count:o.length,message:`Successfully inserted ${o.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=R(e.req.header(`content-length`));if(t)return e.json({success:!1,error:t},413);let n=await H(e);Je(n);let i=W(e),a=[];for(let e of n.resourceLogs){let t=Xe(e);for(let n of e.scopeLogs)for(let e of n.logRecords){let n=V(G(Qe(e,t),i));a.push(n)}}let o=await r.insertBatch(a);return e.json({success:!0,count:o.length,message:`Successfully inserted ${o.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=ce(e);n.route(`/api`,a);let o=Ie(e);return n.route(`/`,o),n}function K(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 q(e={}){let t=K(e.dbMaxBytes??process.env.LOG_SINK_DB_MAX_BYTES,268435456),n=K(e.dbTargetBytes??process.env.LOG_SINK_DB_TARGET_BYTES,Math.floor(t*.9));return{maxBytes:t,targetBytes:Math.min(n,t),intervalMs:K(e.dbRetentionIntervalMs??process.env.LOG_SINK_DB_RETENTION_INTERVAL_MS,3e5),batchSize:K(e.dbRetentionBatchSize??process.env.LOG_SINK_DB_RETENTION_BATCH_SIZE,1e3)}}const tt=_(new l.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`).option(`--db-path <path>`,`Path to SQLite database file`).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=>{let n;try{let r=y(e),i=e.dbPath??r.dbPath,a=e.port?Number.parseInt(e.port,10):r.port,o={min:s.DEFAULT_PORT_RANGE.min,max:a===void 0?s.DEFAULT_PORT_RANGE.max??4999:Math.max(s.DEFAULT_PORT_RANGE.max??4999,a)},l=process.env.NODE_ENV||`development`,d=r.repositoryPath,f=`log-sink-mcp-http`;e.registryPath&&(process.env.PORT_REGISTRY_PATH=e.registryPath,process.env.PROCESS_REGISTRY_PATH=(0,c.resolveSiblingRegistryPath)(e.registryPath,`processes.json`)??process.env.PROCESS_REGISTRY_PATH);let p=new s.PortRegistryService(process.env.PORT_REGISTRY_PATH),m=t.l(),h=m.get(t.p.LogStorageService),g=m.get(t.p.LogRetentionService);await h.initializeDatabase(i,e.inMemory);let _=g.startSizeRetentionMonitor(q({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize})),v=et(m),b=await p.reservePort({repositoryPath:d,serviceName:f,serviceType:`tool`,environment:l,...a===void 0?{}:{preferredPort:a},pid:process.pid,host:`127.0.0.1`,force:!0,portRange:o,metadata:{dbPath:i,scope:r.scope}});if(!b.success||!b.record)throw Error(b.error||`Failed to reserve service in global registry`);let x=b.record.port;if(x!==a||!b.record.metadata?.healthCheckUrl){let e=await p.reservePort({repositoryPath:d,serviceName:f,serviceType:`tool`,environment:l,preferredPort:x,pid:process.pid,host:`127.0.0.1`,force:!0,portRange:o,metadata:{healthCheckUrl:`http://localhost:${x}/health`,dbPath:i,scope:r.scope}});if(!e.success||!e.record)throw Error(e.error||`Failed to update service metadata in registry`);b=e}if(!b.record)throw Error(`Port reservation lost before startup`);let S=b.record.port;n=await(0,c.createProcessLease)({repositoryPath:d,serviceName:f,serviceType:`tool`,environment:l,pid:process.pid,port:S,host:`127.0.0.1`,command:process.argv[1],args:process.argv.slice(2),metadata:{dbPath:i,scope:r.scope,transport:`http`}});let C;try{C=(0,u.serve)({fetch:v.fetch,port:S})}catch(e){try{await n.release({kill:!1})}catch{}throw e}let w=async()=>{try{C.close();try{await n?.release({kill:!1})}catch{}_(),await h.disconnect(),process.exit(0)}catch{process.exit(1)}};process.on(`SIGINT`,()=>w()),process.on(`SIGTERM`,()=>w())}catch{try{await n?.release({kill:!1})}catch{}process.exit(1)}}),J=n.z.object({timeRange:n.z.object({start:n.z.string().describe(`Start time for error analysis (ISO 8601 format)`),end:n.z.string().describe(`End time for error analysis (ISO 8601 format)`)}).optional().describe(`Optional time range to filter errors`),traceId:n.z.string().optional().describe(`Optional trace ID to analyze errors within a specific trace`),limit:n.z.number().min(1).max(1e3).optional().default(100).describe(`Maximum number of error entries to analyze`)});var nt=class e{static TOOL_NAME=`analyze_errors`;queryService;constructor(e){this.queryService=e.get(t.p.LogQueryService)}getInputSchema(){return J}getDefinition(){return{name:e.TOOL_NAME,description:`Analyze error patterns and group similar errors by type and message. Calculates error frequencies, suggests root causes, and provides debugging insights.`,inputSchema:n.z.toJSONSchema(J,{reused:`inline`})}}async execute(e){try{let t=J.parse(e),n=t.limit,r;if(t.traceId)r=(await this.queryService.filterByTrace(t.traceId)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,n);else if(t.timeRange){let e=new Date(t.timeRange.start),i=new Date(t.timeRange.end);r=(await this.queryService.filterByTimeRange(e,i)).filter(e=>e.level===`error`||e.level===`fatal`).slice(0,n)}else r=await this.queryService.filterByLevel([`error`,`fatal`],n);let i=new Map;for(let e of r){let t=`${e.errorType}::${e.errorMessage}`;i.has(t)||i.set(t,{errorType:e.errorType,errorMessage:e.errorMessage,frequency:0,services:new Set,examples:[]});let n=i.get(t);n.frequency+=1,n.services.add(e.service),n.examples.length<3&&n.examples.push({id:e.id,timestamp:e.timestamp.toISOString(),service:e.service})}let a=Array.from(i.values()).sort((e,t)=>t.frequency-e.frequency).map(e=>({errorType:e.errorType,errorMessage:e.errorMessage,frequency:e.frequency,affectedServices:Array.from(e.services),examples:e.examples}));return{content:[{type:`text`,text:JSON.stringify({totalErrorsAnalyzed:r.length,uniqueErrorPatterns:a.length,analysis:a},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error analyzing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const rt=n.z.object({});var it=class e{static TOOL_NAME=`clear_logs`;retentionService;constructor(e){this.retentionService=e.get(t.p.LogRetentionService)}getInputSchema(){return rt}getDefinition(){return{name:e.TOOL_NAME,description:`Clear all stored log entries from the database. Useful for resetting state in development.`,inputSchema:n.z.toJSONSchema(rt,{reused:`inline`})}}async execute(e){try{let e=await this.retentionService.clearAllLogs();return{content:[{type:`text`,text:JSON.stringify({success:!0,message:`All logs have been cleared`,deletedEntries:e},null,2)}]}}catch(e){return{content:[{type:`text`,text:`Error clearing logs: ${e instanceof Error?e.message:`Unknown error`}`}],isError:!0}}}};const at=new Set(Object.values(t.i)),ot=new Set([`level`,`service`,`both`,`agent`,`workflow`,`job`,`signal`]),st=new Set([`workflow-run`,`workflow-name`,`job`,`agent`,`model`,`provider`]),ct=new Set([`last-seen`,`total-tokens`,`issues`]),lt=new Set([`p90-total-tokens`,`avg-total-tokens`,`invocations`]),ut=new Set([`log`,`span`,`all`]),Y={stdout:e=>console.log(e),stderr:e=>console.error(e)};function X(e){let t=Number.parseInt(e,10);if(!Number.isInteger(t)||t<=0)throw new l.InvalidArgumentError(`Expected a positive integer.`);return t}function dt(e){if(!ot.has(e))throw new l.InvalidArgumentError(`Expected one of: 'level', 'service', 'both', 'agent', 'workflow', 'job', 'signal'.`);return e}function ft(e){if(!st.has(e))throw new l.InvalidArgumentError(`Expected one of: 'workflow-run', 'workflow-name', 'job', 'agent', 'model', 'provider'.`);return e}function pt(e){if(!ct.has(e))throw new l.InvalidArgumentError(`Expected one of: 'last-seen', 'total-tokens', 'issues'.`);return e}function mt(e){if(!lt.has(e))throw new l.InvalidArgumentError(`Expected one of: 'p90-total-tokens', 'avg-total-tokens', 'invocations'.`);return e}function ht(e){if(!ut.has(e))throw new l.InvalidArgumentError(`Expected one of: 'log', 'span', 'all'.`);return e}function gt(e,t=[]){if(!at.has(e))throw new l.InvalidArgumentError(`Invalid log level '${e}'. Expected one of: ${Array.from(at).join(`, `)}`);return[...t,e]}function _t(e){return e.content.map(e=>e.type===`text`?e.text:JSON.stringify(e)).join(`
|
|
697
|
+
`)}function Z(e){return _(e).option(`--db-path <path>`,`Path to SQLite database file`).option(`--in-memory`,`Use in-memory database`,!1)}async function vt(e,n){let r=y(e),i=e.dbPath??r.dbPath,a=t.l(),o=a.get(t.p.LogStorageService);await o.initializeDatabase(i,e.inMemory);try{return await n(a)}finally{await o.disconnect()}}async function yt(e,t,n,r=Y){try{let i=await vt(e,async e=>t(e).execute(n)),a=_t(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 yt(e,t,n);r!==0&&process.exit(r)}async function bt(e){try{let n=await vt(e,async n=>n.get(t.p.LogMetricsService).getMetrics({workflowRunId:e.workflowRunId,workflowName:e.workflowName,jobName:e.jobName,agentName:e.agentName,service:e.service,model:e.model,provider:e.provider,startTime:e.startTime?new Date(e.startTime):void 0,endTime:e.endTime?new Date(e.endTime):void 0},{groupBy:e.groupBy,sort:e.sort,toolSort:e.toolSort,limit:e.limit,toolLimit:e.toolLimit}));Y.stdout(JSON.stringify(n,null,2))}catch(e){Y.stderr(`Error executing metrics command: ${e instanceof Error?e.message:String(e)}`),process.exit(1)}}function xt(e,t){if(!(!e&&!t)){if(!e||!t)throw new l.InvalidArgumentError(`Provide both --start-time and --end-time together.`);return{start:e,end:t}}}function St(){let e=new l.Command(`logs`).description(`Run log analysis commands that mirror MCP tool capabilities`),n=Z(new l.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`,gt).option(`--trace-id <traceId>`,`Trace ID to filter by`).option(`--span-id <spanId>`,`Span ID to filter by`).option(`--error-type <errorType>`,`Error type to filter by`).option(`--service <service...>`,`Service name(s) to filter by`).option(`--session-id <sessionId>`,`Agent session id to filter by`).option(`--workflow-run-id <workflowRunId>`,`Workflow run id to filter by`).option(`--workflow-name <workflowName>`,`Workflow name to filter by (all runs of a workflow type)`).option(`--job-name <jobName>`,`Workflow job name 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`,X).action(async e=>{await Q(e,e=>new t.r(e),{level:e.level,traceId:e.traceId,spanId:e.spanId,errorType:e.errorType,service:e.service,sessionId:e.sessionId,workflowRunId:e.workflowRunId,workflowName:e.workflowName,jobName:e.jobName,startTime:e.startTime,endTime:e.endTime,limit:e.limit})})),r=Z(new l.Command(`search`).alias(`search-logs`).description(`Search or filter log entries with optional full-text search`).argument(`[search-query]`,`Optional 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`,gt).option(`--trace-id <traceId>`,`Trace ID to filter by`).option(`--span-id <spanId>`,`Span ID to filter by`).option(`--error-type <errorType>`,`Error type to filter by`).option(`--session-id <sessionId>`,`Agent session id to filter by`).option(`--workflow-run-id <workflowRunId>`,`Workflow run id to filter by`).option(`--workflow-name <workflowName>`,`Workflow name to filter by (all runs of a workflow type)`).option(`--job-name <jobName>`,`Workflow job name 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`,X).action(async(e,n)=>{await Q(n,e=>new t.r(e),{searchQuery:e,mode:n.mode,service:n.service,level:n.level,traceId:n.traceId,spanId:n.spanId,errorType:n.errorType,sessionId:n.sessionId,workflowRunId:n.workflowRunId,workflowName:n.workflowName,jobName:n.jobName,startTime:n.startTime,endTime:n.endTime,limit:n.limit})})),i=Z(new l.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 Q(n,e=>new t.a(e),{traceId:e})})),a=Z(new l.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`,X).action(async function(e){let t;try{t=xt(e.startTime,e.endTime)}catch(e){this.error(e instanceof Error?e.message:String(e))}await Q(e,e=>new nt(e),{traceId:e.traceId,timeRange:t,limit:e.limit})})),o=Z(new l.Command(`agent-issues`).alias(`agent-problems`).description(`Detect and aggregate agent-run problems (tool failures, API errors/refusals, retries, rejections)`).option(`--session-id <sessionId>`,`Agent session id to scope by`).option(`--workflow-run-id <workflowRunId>`,`Workflow run id to scope by`).option(`--workflow-name <workflowName>`,`Workflow name to scope by`).option(`--job-name <jobName>`,`Workflow job name to scope by`).option(`--service <service...>`,`Agent/service name(s) to scope by`).option(`--start-time <iso8601>`,`Start time for the analysis window (ISO 8601)`).option(`--end-time <iso8601>`,`End time for the analysis window (ISO 8601)`).option(`--limit <number>`,`Maximum number of issue samples to return`,X).action(async e=>{await Q(e,e=>new t.c(e),{sessionId:e.sessionId,workflowRunId:e.workflowRunId,workflowName:e.workflowName,jobName:e.jobName,service:e.service,startTime:e.startTime,endTime:e.endTime,limit:e.limit})})),s=Z(new l.Command(`stats`).alias(`get-log-stats`).description(`Get aggregated log statistics grouped by level, service, agent, workflow, job, or signal`).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, both, agent, workflow, job, or signal`,dt).option(`--signal-type <signalType>`,`Count log records, span records, or all signals`,ht).action(async e=>{await Q(e,e=>new t.s(e),{startTime:e.startTime,endTime:e.endTime,groupBy:e.groupBy,signalType:e.signalType})})),c=Z(new l.Command(`metrics`).description(`Get workflow, token, failure, and per-tool consumption metrics`).option(`--group-by <groupBy>`,`Group workflow metrics by workflow-run, workflow-name, job, agent, model, or provider`,ft).option(`--start-time <iso8601>`,`Start time for metrics (ISO 8601)`).option(`--end-time <iso8601>`,`End time for metrics (ISO 8601)`).option(`--workflow-run-id <workflowRunId>`,`Workflow run id to scope by`).option(`--workflow-name <workflowName>`,`Workflow name to scope by`).option(`--job-name <jobName>`,`Workflow job name to scope by`).option(`--agent-name <agentName>`,`Agent name to scope by`).option(`--service <service...>`,`Service name(s) to scope by`).option(`--model <model...>`,`Model name(s) to scope by`).option(`--provider <provider...>`,`Provider name(s) to scope by`).option(`--limit <number>`,`Maximum number of workflow metric rows to return`,X).option(`--sort <sort>`,`Sort workflow rows by last-seen, total-tokens, or issues`,pt).option(`--tool-limit <number>`,`Maximum number of tool metric rows to return`,X).option(`--tool-sort <sort>`,`Sort tool rows by p90-total-tokens, avg-total-tokens, or invocations`,mt).action(async e=>{await bt(e)})),u=Z(new l.Command(`services`).alias(`get-services`).description(`Get the list of unique services present in the log database`).action(async e=>{await Q(e,e=>new t.o(e),{})})),d=Z(new l.Command(`clear`).alias(`clear-logs`).description(`Clear all logs from the database`).action(async e=>{await Q(e,e=>new it(e),{})}));return e.addCommand(n).addCommand(r).addCommand(i).addCommand(a).addCommand(o).addCommand(s).addCommand(c).addCommand(u).addCommand(d)}const Ct=St(),wt=_(new l.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`).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=(0,c.resolveSiblingRegistryPath)(r,`processes.json`)??process.env.PROCESS_REGISTRY_PATH);let i=y(e),a=e.dbPath??i.dbPath,o=t.l(),s=o.get(t.p.LogStorageService),l=o.get(t.p.LogRetentionService),u=o.get(t.p.HttpServerManager);await s.initializeDatabase(a,e.inMemory);let d=l.startSizeRetentionMonitor(q({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize})),f=new t.t(t.n(o));n=await(0,c.createProcessLease)({repositoryPath:i.repositoryPath,serviceName:`log-sink-mcp-mcp-serve`,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`,scope:i.scope}});let p=u.ensureRunning(void 0,a),m;p.then(e=>{e.running&&(m=u.startHealthMonitor(a))}).catch(()=>void 0);let h=async t=>{console.error(`\nReceived ${t}, shutting down gracefully...`);try{m?.(),await f.stop(),e.cleanup&&(await p.catch(()=>({running:!1}))).running&&(await u.stop(),console.error(`HTTP server stopped`)),d(),await s.disconnect(),await n?.release(),process.exit(0)}catch(e){console.error(`Error during shutdown:`,e),process.exit(1)}};process.on(`SIGINT`,()=>h(`SIGINT`)),process.on(`SIGTERM`,()=>h(`SIGTERM`)),await f.start()}catch(e){await n?.release().catch(()=>void 0),console.error(`Failed to start MCP server:`,e),process.exit(1)}}),Tt={stdout:e=>console.log(e),stderr:e=>console.error(e)};function Et(e,t){let n={...process.env};e.config&&(n.LOG_SINK_CONFIG=e.config);let r=t??v(e);return r&&(n.LOG_SINK_INSTANCE=r),n}async function $(t,n){let r=Et(t,n),i=e.c({configPath:t.config,env:r,packageName:e.s}),a=await e.o({configPath:t.config,env:r,packageName:e.s,healthCheck:t.healthCheck});return{scope:i.scope,registered:!!a,port:a?.port,host:a?.host,endpoint:a?.endpoint,configPath:i.configPath,dbPath:i.dbPath,repositoryPath:i.repositoryPath,registeredName:i.registeredName}}function Dt(e,t){return e.registered?`${e.scope}: ${e.endpoint} (port ${e.port})`:`${e.scope}: not registered${t===!1?``:` or unhealthy`}`}async function Ot(e,t=Tt){try{let n=e.all?await Promise.all([$(e,`local`),$(e,`global`)]):[await $(e)];return e.json?t.stdout(JSON.stringify(e.all?n:n[0],null,2)):t.stdout(n.map(t=>Dt(t,e.healthCheck)).join(`
|
|
698
|
+
`)),+!n.some(e=>e.registered)}catch(e){return t.stderr(`Error resolving log-sink port: ${e instanceof Error?e.message:String(e)}`),1}}const kt=_(new l.Command(`port`)).description(`Show the registered log-sink HTTP port for the selected instance`).option(`--all`,`Show both local and global log-sink ports`,!1).option(`--json`,`Print machine-readable JSON output`,!1).option(`--no-health-check`,`Read the registry without requiring a healthy HTTP server`).action(async e=>{let t=await Ot(e);t!==0&&process.exit(t)}),At=_(new l.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`).option(`--db-path <path>`,`Path to SQLite database file`).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=(0,c.resolveSiblingRegistryPath)(r,`processes.json`)??process.env.PROCESS_REGISTRY_PATH);let i=y(e),a=e.dbPath??i.dbPath;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 o=t.l(),s=o.get(t.p.LogStorageService);await s.initializeDatabase(a,e.inMemory);let l=e.port?Number.parseInt(e.port,10):i.port,u=!e.mcpOnly,d=!e.httpOnly,f=o.get(t.p.LogRetentionService),p=d?f.startSizeRetentionMonitor(q({dbMaxBytes:e.dbMaxBytes,dbTargetBytes:e.dbTargetBytes,dbRetentionIntervalMs:e.dbRetentionIntervalMs,dbRetentionBatchSize:e.dbRetentionBatchSize})):()=>void 0;if(console.log(`🚀 Starting log-sink-mcp services...`),console.log(` Instance: ${i.scope}`),console.log(` Database: ${e.inMemory?`In-Memory`:a}`),u){let e=await o.get(t.p.HttpServerManager).ensureRunning(l,a);e.running?(console.log(`✓ HTTP Server: Running on http://localhost:${e.port} (PID: ${e.pid})`),console.log(` Health check: http://localhost:${e.port}/health`),console.log(` Log ingestion: POST http://localhost:${e.port}/logs`)):(console.error(`✗ HTTP Server failed to start: ${e.error}`),d||process.exit(1))}if(d){n=await(0,c.createProcessLease)({repositoryPath:i.repositoryPath,serviceName:`log-sink-mcp-start`,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`,scope:i.scope}}),console.log(`✓ MCP Server: Starting with stdio transport...`);let e=new t.t(t.n(o));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`),u&&(await o.get(t.p.HttpServerManager).stop(),console.log(`✓ HTTP server stopped`)),p(),await s.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(`
|
|
589
699
|
Press Ctrl+C to stop the server`)}else console.log(`
|
|
590
|
-
✓ 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)}}),
|
|
591
|
-
`),console.log(`─`.repeat(50));let n=y(e);console.log(`Instance: ${n.scope}`);let r=await t.l().get(t.
|
|
700
|
+
✓ 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)}}),jt=_(new l.Command(`status`)).description(`Show status of HTTP server and diagnostics`).action(async e=>{try{console.log(`📊 log-sink-mcp Status
|
|
701
|
+
`),console.log(`─`.repeat(50));let n=y(e);console.log(`Instance: ${n.scope}`);let r=await t.l().get(t.p.HttpServerManager).getStatus();r.running?(console.log(`HTTP Server: 🟢 Running`),console.log(` PID: ${r.pid}`),console.log(` Port: ${r.port}`),console.log(` Health: http://localhost:${r.port}/health`)):(console.log(`HTTP Server: 🔴 Not Running`),r.error&&console.log(` Error: ${r.error}`)),console.log(``);try{let e=n.dbPath,t=((await i.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)}}),Mt=_(new l.Command(`stop`)).description(`Stop HTTP server and clean up registry/PID files`).action(async e=>{try{console.log(`🛑 Stopping log-sink-mcp services...`);let n=y(e);console.log(`Instance: ${n.scope}`),await t.l().get(t.p.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)}}),Nt=(0,a.dirname)((0,o.fileURLToPath)(require(`url`).pathToFileURL(__filename).href)),Pt=JSON.parse((0,r.readFileSync)((0,a.join)(Nt,`../package.json`),`utf-8`));async function Ft(){let e=new l.Command;e.name(`log-sink-mcp`).description(`Log sink MCP server with HTTP ingestion and AI analysis`).version(Pt.version),e.addCommand(ae),e.addCommand(At),e.addCommand(Mt),e.addCommand(jt),e.addCommand(kt),e.addCommand(tt),e.addCommand(wt),e.addCommand(Ct),await e.parseAsync(process.argv)}Ft();
|
|
592
702
|
//# sourceMappingURL=cli.cjs.map
|