@lensmcp/node-instrumentation 1.25.32 → 1.25.33

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.
@@ -21,7 +21,8 @@
21
21
  *
22
22
  * The live context also carries its END signal when whoever opened it gives
23
23
  * one (`CONTEXT_END` below): the Nest interceptor for a request, and
24
- * `runInFlowContext` for a queue job.
24
+ * `runInFlowContext` for a queue job. A request the gateway routed with no
25
+ * flow also carries a START signal (`FLOW_START`), for the work that gives it one.
25
26
  */
26
27
  import type { FlowCtx } from './emit';
27
28
  interface LiveCtx {
@@ -109,6 +110,21 @@ export interface ContextEndSignal {
109
110
  export declare function attachContextEnd(ctx: object): () => void;
110
111
  /** The end signal a live context carries — undefined when whoever opened it gives none. */
111
112
  export declare function contextEndOf(live: object | undefined): ContextEndSignal | undefined;
113
+ /**
114
+ * A context's START signal — the protocol `@lensmcp/nest-instrumentation` defines in `flow-start.ts`, under the same
115
+ * kind of registered symbol as `CONTEXT_END`.
116
+ *
117
+ * The Nest interceptor leaves a request the gateway routed with no flow without one (a service's background chatter),
118
+ * and attaches a starter to its context. A tap that sees the request do work that matters — an AI provider's call
119
+ * (tap-egress, tap-ai) — calls it, and the request gets the flow a request with no flow gets: `api:<request id>`.
120
+ */
121
+ export declare const FLOW_START: unique symbol;
122
+ /**
123
+ * The live context's flow, started now if it has none and whoever opened it can start one; undefined otherwise (no
124
+ * context, a context nobody may start — headless flows off, a liveness path — or a queue job's, which has its own).
125
+ * Call it BEFORE `flowContext()` snapshots the call.
126
+ */
127
+ export declare function startLiveFlow(): string | undefined;
112
128
  /**
113
129
  * Run `fn` inside a fresh flow context (queue workers, timers — async
114
130
  * entry points that aren't HTTP requests). Inner taps then inherit the
@@ -1 +1 @@
1
- "use strict";var d=Object.defineProperty;var o=(e,t)=>d(e,"name",{value:t,configurable:!0});var l=Object.defineProperty,n=o((e,t)=>l(e,"name",{value:t,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.CONTEXT_END=void 0,exports.flowContextStatus=flowContextStatus,exports.resetFlowContextForTests=resetFlowContextForTests,exports.isRequireEsmError=isRequireEsmError,exports.isModuleNotFoundError=isModuleNotFoundError,exports.primeFlowContext=primeFlowContext,exports.attachContextEnd=attachContextEnd,exports.contextEndOf=contextEndOf,exports.runInFlowContext=runInFlowContext,exports.liveFlowContext=liveFlowContext,exports.flowContext=flowContext;let getCtx=null,runWith=null,status={state:"not-primed"},warned=!1;function flowContextStatus(){return status}o(flowContextStatus,"flowContextStatus"),n(flowContextStatus,"flowContextStatus");function resetFlowContextForTests(){getCtx=null,runWith=null,status={state:"not-primed"},warned=!1}o(resetFlowContextForTests,"resetFlowContextForTests"),n(resetFlowContextForTests,"resetFlowContextForTests");function isRequireEsmError(e){const t=e?.code;return t==="ERR_REQUIRE_ESM"||t==="ERR_REQUIRE_ASYNC_MODULE"}o(isRequireEsmError,"isRequireEsmError"),n(isRequireEsmError,"isRequireEsmError");function isModuleNotFoundError(e){const t=e;return t?.code!=="MODULE_NOT_FOUND"&&t?.code!=="ERR_MODULE_NOT_FOUND"?!1:String(t?.message??"").includes("@lensmcp/nest-instrumentation")}o(isModuleNotFoundError,"isModuleNotFoundError"),n(isModuleNotFoundError,"isModuleNotFoundError");function requireNestInstrumentation(){return require("@lensmcp/nest-instrumentation")}o(requireNestInstrumentation,"requireNestInstrumentation"),n(requireNestInstrumentation,"requireNestInstrumentation");function primeFlowContext(e=requireNestInstrumentation){try{const t=e();getCtx=t.currentLensmcpContext,runWith=t.runInLensmcpContext,status={state:"ready"}}catch(t){if(getCtx=null,runWith=null,isModuleNotFoundError(t)){status={state:"absent"};return}const r=t instanceof Error?t.message:String(t);if(status=isRequireEsmError(t)?{state:"runtime-too-old",detail:r}:{state:"failed",detail:r},!warned){warned=!0;const s=status.state==="runtime-too-old"?`this Node (${process.version}) cannot require an ESM package; @lensmcp/node-instrumentation needs >=20.19`:r;console.warn(`[lensmcp] flow correlation is OFF \u2014 could not load @lensmcp/nest-instrumentation: ${s}`)}}}o(primeFlowContext,"primeFlowContext"),n(primeFlowContext,"primeFlowContext"),exports.CONTEXT_END=Symbol.for("lensmcp.contextEnd");function attachContextEnd(e){let t=!1;const r=[],s={get ended(){return t},onEnd(u){t?runEndListener(u):r.push(u)}};return Object.defineProperty(e,exports.CONTEXT_END,{value:s}),()=>{if(!t){t=!0;for(const u of r.splice(0))runEndListener(u)}}}o(attachContextEnd,"attachContextEnd"),n(attachContextEnd,"attachContextEnd");function runEndListener(e){try{e()}catch{}}o(runEndListener,"runEndListener"),n(runEndListener,"runEndListener");function contextEndOf(e){const t=e?.[exports.CONTEXT_END];return t&&typeof t.onEnd=="function"?t:void 0}o(contextEndOf,"contextEndOf"),n(contextEndOf,"contextEndOf");function runInFlowContext(e,t){if(!runWith)return t();const r={sessionId:"pending",requestId:e.requestId,...e.flowId?{flowId:e.flowId}:{}},s=attachContextEnd(r);let u;try{u=runWith(r,t)}catch{return s(),t()}const i=u;return i&&typeof i.then=="function"?i.then(c=>(s(),c),c=>{throw s(),c}):(s(),u)}o(runInFlowContext,"runInFlowContext"),n(runInFlowContext,"runInFlowContext");function liveFlowContext(){try{return getCtx?.()}catch{return}}o(liveFlowContext,"liveFlowContext"),n(liveFlowContext,"liveFlowContext");function flowContext(){const e=liveFlowContext();if(e)return{...e.flowId?{flowId:e.flowId}:{},...e.requestId?{requestId:e.requestId}:{},...e.runId?{runId:e.runId}:{},...e.originNodeId?{originNodeId:e.originNodeId}:{}}}o(flowContext,"flowContext"),n(flowContext,"flowContext");
1
+ "use strict";var d=Object.defineProperty;var o=(t,e)=>d(t,"name",{value:e,configurable:!0});var l=Object.defineProperty,n=o((t,e)=>l(t,"name",{value:e,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.FLOW_START=exports.CONTEXT_END=void 0,exports.flowContextStatus=flowContextStatus,exports.resetFlowContextForTests=resetFlowContextForTests,exports.isRequireEsmError=isRequireEsmError,exports.isModuleNotFoundError=isModuleNotFoundError,exports.primeFlowContext=primeFlowContext,exports.attachContextEnd=attachContextEnd,exports.contextEndOf=contextEndOf,exports.startLiveFlow=startLiveFlow,exports.runInFlowContext=runInFlowContext,exports.liveFlowContext=liveFlowContext,exports.flowContext=flowContext;let getCtx=null,runWith=null,status={state:"not-primed"},warned=!1;function flowContextStatus(){return status}o(flowContextStatus,"flowContextStatus"),n(flowContextStatus,"flowContextStatus");function resetFlowContextForTests(){getCtx=null,runWith=null,status={state:"not-primed"},warned=!1}o(resetFlowContextForTests,"resetFlowContextForTests"),n(resetFlowContextForTests,"resetFlowContextForTests");function isRequireEsmError(t){const e=t?.code;return e==="ERR_REQUIRE_ESM"||e==="ERR_REQUIRE_ASYNC_MODULE"}o(isRequireEsmError,"isRequireEsmError"),n(isRequireEsmError,"isRequireEsmError");function isModuleNotFoundError(t){const e=t;return e?.code!=="MODULE_NOT_FOUND"&&e?.code!=="ERR_MODULE_NOT_FOUND"?!1:String(e?.message??"").includes("@lensmcp/nest-instrumentation")}o(isModuleNotFoundError,"isModuleNotFoundError"),n(isModuleNotFoundError,"isModuleNotFoundError");function requireNestInstrumentation(){return require("@lensmcp/nest-instrumentation")}o(requireNestInstrumentation,"requireNestInstrumentation"),n(requireNestInstrumentation,"requireNestInstrumentation");function primeFlowContext(t=requireNestInstrumentation){try{const e=t();getCtx=e.currentLensmcpContext,runWith=e.runInLensmcpContext,status={state:"ready"}}catch(e){if(getCtx=null,runWith=null,isModuleNotFoundError(e)){status={state:"absent"};return}const r=e instanceof Error?e.message:String(e);if(status=isRequireEsmError(e)?{state:"runtime-too-old",detail:r}:{state:"failed",detail:r},!warned){warned=!0;const s=status.state==="runtime-too-old"?`this Node (${process.version}) cannot require an ESM package; @lensmcp/node-instrumentation needs >=20.19`:r;console.warn(`[lensmcp] flow correlation is OFF \u2014 could not load @lensmcp/nest-instrumentation: ${s}`)}}}o(primeFlowContext,"primeFlowContext"),n(primeFlowContext,"primeFlowContext"),exports.CONTEXT_END=Symbol.for("lensmcp.contextEnd");function attachContextEnd(t){let e=!1;const r=[],s={get ended(){return e},onEnd(u){e?runEndListener(u):r.push(u)}};return Object.defineProperty(t,exports.CONTEXT_END,{value:s}),()=>{if(!e){e=!0;for(const u of r.splice(0))runEndListener(u)}}}o(attachContextEnd,"attachContextEnd"),n(attachContextEnd,"attachContextEnd");function runEndListener(t){try{t()}catch{}}o(runEndListener,"runEndListener"),n(runEndListener,"runEndListener");function contextEndOf(t){const e=t?.[exports.CONTEXT_END];return e&&typeof e.onEnd=="function"?e:void 0}o(contextEndOf,"contextEndOf"),n(contextEndOf,"contextEndOf"),exports.FLOW_START=Symbol.for("lensmcp.flowStart");function startLiveFlow(){const t=liveFlowContext();if(!t)return;if(t.flowId)return t.flowId;const e=t[exports.FLOW_START];if(typeof e=="function")try{return e()??t.flowId}catch{return t.flowId}}o(startLiveFlow,"startLiveFlow"),n(startLiveFlow,"startLiveFlow");function runInFlowContext(t,e){if(!runWith)return e();const r={sessionId:"pending",requestId:t.requestId,...t.flowId?{flowId:t.flowId}:{}},s=attachContextEnd(r);let u;try{u=runWith(r,e)}catch{return s(),e()}const i=u;return i&&typeof i.then=="function"?i.then(c=>(s(),c),c=>{throw s(),c}):(s(),u)}o(runInFlowContext,"runInFlowContext"),n(runInFlowContext,"runInFlowContext");function liveFlowContext(){try{return getCtx?.()}catch{return}}o(liveFlowContext,"liveFlowContext"),n(liveFlowContext,"liveFlowContext");function flowContext(){const t=liveFlowContext();if(t)return{...t.flowId?{flowId:t.flowId}:{},...t.requestId?{requestId:t.requestId}:{},...t.runId?{runId:t.runId}:{},...t.originNodeId?{originNodeId:t.originNodeId}:{}}}o(flowContext,"flowContext"),n(flowContext,"flowContext");
package/lib/tap-ai.js CHANGED
@@ -1 +1 @@
1
- "use strict";var R=Object.defineProperty;var c=(t,e)=>R(t,"name",{value:e,configurable:!0});var x=Object.defineProperty,d=c((t,e)=>x(t,"name",{value:e,configurable:!0}),"d");Object.defineProperty(exports,"__esModule",{value:!0}),exports.aiCaptureMode=aiCaptureMode,exports.flushAiTapForTests=flushAiTapForTests,exports.resetAiTapForTests=resetAiTapForTests,exports.tapAi=tapAi;const emit_1=require("./emit"),flow_context_1=require("./flow-context"),ai_providers_1=require("./ai-providers"),ai_store_1=require("./ai-store");function aiCaptureMode(t=process.env){const e=(t.LENSMCP_AI_CAPTURE??"").trim().toLowerCase();return e==="content"?"content":e==="metadata"||e==="1"||e==="true"||e==="on"?"metadata":"off"}c(aiCaptureMode,"aiCaptureMode"),d(aiCaptureMode,"aiCaptureMode");const MAX_REQUEST_PARSE_BYTES=2*1024*1024,MAX_RESPONSE_PARSE_BYTES=8*1024*1024,MAX_STREAM_BYTES=8*1024*1024,MAX_STREAM_MS=300*1e3,MAX_ENDPOINT_CHARS=256;function locate(t){try{const e=new URL(typeof t=="string"?t:t instanceof URL?t.href:t.url);return{host:e.hostname,port:e.port,pathname:e.pathname}}catch{return}}c(locate,"locate"),d(locate,"locate");const methodOf=d((t,e)=>{const o=typeof t=="object"&&t!==null&&!(t instanceof URL)?t.method:void 0;return String(e?.method??o??"GET").toUpperCase()},"methodOf"),decode=d(t=>({bytes:t.byteLength,...t.byteLength<=MAX_REQUEST_PARSE_BYTES?{text:Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8")}:{}}),"decode");function takeRequestBody(t,e){const o=e?.body;if(typeof o=="string")return{text:o,bytes:Buffer.byteLength(o)};if(o instanceof ArrayBuffer)return decode(new Uint8Array(o));if(ArrayBuffer.isView(o))return decode(new Uint8Array(o.buffer,o.byteOffset,o.byteLength));if(typeof Blob<"u"&&o instanceof Blob)return{bytes:o.size,...o.size<=MAX_REQUEST_PARSE_BYTES?{pending:o.text().catch(()=>{})}:{}};if(o==null&&typeof t=="object"&&t!==null&&!(t instanceof URL)){const s=t;if(!s.bodyUsed&&s.body)try{return{pending:s.clone().text().catch(()=>{})}}catch{return{}}}return{}}c(takeRequestBody,"takeRequestBody"),d(takeRequestBody,"takeRequestBody");const sum=d(t=>t.reduce((e,o)=>e+o,0),"sum");function parseJson(t){if(t)try{return JSON.parse(t)}catch{return}}c(parseJson,"parseJson"),d(parseJson,"parseJson");function headerOf(t,e){try{return t?.headers?.get?.(e)??""}catch{return""}}c(headerOf,"headerOf"),d(headerOf,"headerOf");const statusOf=d(t=>typeof t?.status=="number"?t.status:void 0,"statusOf");function failureKind(t,e){const o=t?.name,s=o==="TimeoutError";return{timedOut:s,aborted:!s&&(o==="AbortError"||e?.aborted===!0)}}c(failureKind,"failureKind"),d(failureKind,"failureKind");async function readStream(t,e,o){const s=(0,ai_providers_1.createStreamAccumulator)(e);let i,r=!1;const a=t.body;if(a){const l=a.getReader(),n=new TextDecoder;let p=0;try{for(;;){const{done:m,value:g}=await l.read();if(m)break;if(p+=g.byteLength,s.push(n.decode(g,{stream:!0})),i===void 0&&s.eventCount>0&&(i=Date.now()-o),p>MAX_STREAM_BYTES||Date.now()-o>MAX_STREAM_MS){r=!0,await l.cancel().catch(()=>{});break}}}catch{r=!0}}return{response:s.end(),sse:!0,cut:r,...i!==void 0?{firstEventMs:i}:{}}}c(readStream,"readStream"),d(readStream,"readStream");async function readBody(t){const e={completion:"",toolCalls:[],usage:{}};if(!t.res||!t.clone)return{response:e,sse:!1,cut:!1};try{if(headerOf(t.res,"content-type").includes("text/event-stream"))return await readStream(t.clone,t.target,t.startedAt);const o=Number(headerOf(t.res,"content-length"));if(Number.isFinite(o)&&o>MAX_RESPONSE_PARSE_BYTES)return{response:e,sse:!1,cut:!1};const s=await t.clone.text().catch(()=>{});if(s&&/^\s*(?:event|data):/.test(s)){const i=(0,ai_providers_1.createStreamAccumulator)(t.target);return i.push(s),{response:i.end(),sse:!0,cut:!1}}return{response:(0,ai_providers_1.normalizeResponse)(t.target,parseJson(s)),sse:!1,cut:!1}}catch{return{response:e,sse:!1,cut:!1}}}c(readBody,"readBody"),d(readBody,"readBody");async function capture(t){const e=readBody(t),o=t.request.text??await t.request.pending,s=t.request.bytes??(o?Buffer.byteLength(o):void 0),i=s!==void 0&&s>MAX_REQUEST_PARSE_BYTES,r=(0,ai_providers_1.normalizeRequest)(t.target,i?void 0:parseJson(o)),a=statusOf(t.res),l=await e;let n=l.response;const p=r.stream||l.sse,m=l.firstEventMs,g=l.cut,{aborted:y,timedOut:C}=t.failure!==void 0?failureKind(t.failure,t.signal):{aborted:!1,timedOut:!1};t.failure!==void 0&&(n={...n,error:String(t.failure?.message??t.failure)});const b=y||g,A=Date.now()-t.startedAt,u=n.model??r.model,_=sum(r.messages.map(w=>w.content.length))+(r.system?.length??0);let f,h=!1;if(t.mode==="content"&&t.target.api!=="embeddings"){const w={schemaVersion:1,id:(0,ai_store_1.newAiCallId)(),capturedAt:Date.now(),...t.ctx?.flowId?{flowId:t.ctx.flowId}:{},...t.ctx?.requestId?{requestId:t.ctx.requestId}:{},provider:t.target.provider,api:t.target.api,...u?{model:u}:{},host:t.host,endpoint:t.endpoint,stream:p,...a!==void 0?{status:a}:{},request:{...r.system?{system:r.system}:{},messages:r.messages,...r.tools.length?{tools:r.tools}:{},...Object.keys(r.params).length?{params:r.params}:{}},response:{completion:n.completion,...n.reasoning?{reasoning:n.reasoning}:{},...n.toolCalls.length?{toolCalls:n.toolCalls}:{},...n.finishReason?{finishReason:n.finishReason}:{},usage:n.usage,...n.error?{error:n.error}:{}}},S=(0,ai_store_1.writeAiContent)(w);S&&(f=S.id,h=S.truncated)}const v=a!==void 0&&a>=400||t.failure!==void 0&&!y,T=u??"?";(0,emit_1.lensEmit)("ai",t.failure!==void 0&&!y?"error":"info",a!==void 0?`ai ${t.target.provider}/${T} ${t.target.api} \u2192 ${a}`:`ai ${t.target.provider}/${T} ${t.target.api} ${y?"aborted":"failed"}`,{name:t.target.provider,kind:"ai-call",provider:t.target.provider,api:t.target.api,...u?{model:u}:{},...r.model&&u&&r.model!==u?{requestedModel:r.model}:{},host:t.host,endpoint:t.endpoint,method:t.method,...a!==void 0?{status:a}:{},stream:p,startedAt:t.startedAt,durationMs:A,...t.ttfbMs!==void 0?{ttfbMs:t.ttfbMs}:{},...m!==void 0?{firstEventMs:m}:{},...Object.keys(n.usage).length?{usage:n.usage}:{},...n.finishReason?{finishReason:n.finishReason}:{},toolCallCount:n.toolCalls.length,sizes:{messageCount:r.messages.length,toolCount:r.tools.length,promptChars:_,completionChars:n.completion.length,...s!==void 0?{requestBytes:s}:{}},...r.inputCount!==void 0?{inputCount:r.inputCount}:{},...n.vectorCount!==void 0?{vectorCount:n.vectorCount}:{},contentCaptured:f!==void 0,...f?{contentRef:f}:{},...h?{truncated:!0}:{},...b?{aborted:!0}:{},...C?{timedOut:!0}:{},...n.error?{error:(0,ai_store_1.redactAiText)(n.error).slice(0,300)}:{}},v?`ai:error:${t.target.provider}:${T}`:`ai:${t.target.provider}:${T}`,t.ctx,"ai")}c(capture,"capture"),d(capture,"capture");let savedFetch;const pending=new Set;async function flushAiTapForTests(){await Promise.allSettled([...pending])}c(flushAiTapForTests,"flushAiTapForTests"),d(flushAiTapForTests,"flushAiTapForTests");function resetAiTapForTests(){const t=globalThis;savedFetch&&(t.fetch=savedFetch),delete t.__lensmcpAiTap,savedFetch=void 0,pending.clear()}c(resetAiTapForTests,"resetAiTapForTests"),d(resetAiTapForTests,"resetAiTapForTests");function tapAi(){const t=aiCaptureMode();if(t==="off")return;const e=globalThis;if(typeof e.fetch!="function"||e.__lensmcpAiTap)return;e.__lensmcpAiTap=!0;const o=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS),s=e.fetch.bind(globalThis);savedFetch=e.fetch,e.fetch=(async(i,r)=>{const a=locate(i),l=methodOf(i,r),n=a&&l==="POST"?(0,ai_providers_1.matchTarget)(a.host,a.port,a.pathname,o):void 0;if(!a||!n)return s(i,r);const p=Date.now(),m=(0,flow_context_1.flowContext)(),g=a.pathname.length>MAX_ENDPOINT_CHARS?`${a.pathname.slice(0,MAX_ENDPOINT_CHARS)}\u2026`:a.pathname,y=takeRequestBody(i,r),C=r?.signal??(typeof i=="object"&&i!==null&&!(i instanceof URL)?i.signal:void 0),b=d(h=>{const v=capture(h).catch(()=>{});pending.add(v),v.finally(()=>pending.delete(v))},"track"),A={mode:t,target:n,host:a.host,endpoint:g,method:l,startedAt:p,ctx:m,signal:C,request:y};let u;try{u=await s(i,r)}catch(h){throw b({...A,failure:h}),h}const _=Date.now()-p;let f;try{f=u.clone()}catch{f=void 0}return b({...A,ttfbMs:_,res:u,...f?{clone:f}:{}}),u})}c(tapAi,"tapAi"),d(tapAi,"tapAi");
1
+ "use strict";var R=Object.defineProperty;var c=(t,e)=>R(t,"name",{value:e,configurable:!0});var x=Object.defineProperty,d=c((t,e)=>x(t,"name",{value:e,configurable:!0}),"d");Object.defineProperty(exports,"__esModule",{value:!0}),exports.aiCaptureMode=aiCaptureMode,exports.flushAiTapForTests=flushAiTapForTests,exports.resetAiTapForTests=resetAiTapForTests,exports.tapAi=tapAi;const emit_1=require("./emit"),flow_context_1=require("./flow-context"),ai_providers_1=require("./ai-providers"),ai_store_1=require("./ai-store");function aiCaptureMode(t=process.env){const e=(t.LENSMCP_AI_CAPTURE??"").trim().toLowerCase();return e==="content"?"content":e==="metadata"||e==="1"||e==="true"||e==="on"?"metadata":"off"}c(aiCaptureMode,"aiCaptureMode"),d(aiCaptureMode,"aiCaptureMode");const MAX_REQUEST_PARSE_BYTES=2*1024*1024,MAX_RESPONSE_PARSE_BYTES=8*1024*1024,MAX_STREAM_BYTES=8*1024*1024,MAX_STREAM_MS=300*1e3,MAX_ENDPOINT_CHARS=256;function locate(t){try{const e=new URL(typeof t=="string"?t:t instanceof URL?t.href:t.url);return{host:e.hostname,port:e.port,pathname:e.pathname}}catch{return}}c(locate,"locate"),d(locate,"locate");const methodOf=d((t,e)=>{const o=typeof t=="object"&&t!==null&&!(t instanceof URL)?t.method:void 0;return String(e?.method??o??"GET").toUpperCase()},"methodOf"),decode=d(t=>({bytes:t.byteLength,...t.byteLength<=MAX_REQUEST_PARSE_BYTES?{text:Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8")}:{}}),"decode");function takeRequestBody(t,e){const o=e?.body;if(typeof o=="string")return{text:o,bytes:Buffer.byteLength(o)};if(o instanceof ArrayBuffer)return decode(new Uint8Array(o));if(ArrayBuffer.isView(o))return decode(new Uint8Array(o.buffer,o.byteOffset,o.byteLength));if(typeof Blob<"u"&&o instanceof Blob)return{bytes:o.size,...o.size<=MAX_REQUEST_PARSE_BYTES?{pending:o.text().catch(()=>{})}:{}};if(o==null&&typeof t=="object"&&t!==null&&!(t instanceof URL)){const s=t;if(!s.bodyUsed&&s.body)try{return{pending:s.clone().text().catch(()=>{})}}catch{return{}}}return{}}c(takeRequestBody,"takeRequestBody"),d(takeRequestBody,"takeRequestBody");const sum=d(t=>t.reduce((e,o)=>e+o,0),"sum");function parseJson(t){if(t)try{return JSON.parse(t)}catch{return}}c(parseJson,"parseJson"),d(parseJson,"parseJson");function headerOf(t,e){try{return t?.headers?.get?.(e)??""}catch{return""}}c(headerOf,"headerOf"),d(headerOf,"headerOf");const statusOf=d(t=>typeof t?.status=="number"?t.status:void 0,"statusOf");function failureKind(t,e){const o=t?.name,s=o==="TimeoutError";return{timedOut:s,aborted:!s&&(o==="AbortError"||e?.aborted===!0)}}c(failureKind,"failureKind"),d(failureKind,"failureKind");async function readStream(t,e,o){const s=(0,ai_providers_1.createStreamAccumulator)(e);let i,r=!1;const a=t.body;if(a){const l=a.getReader(),n=new TextDecoder;let p=0;try{for(;;){const{done:m,value:g}=await l.read();if(m)break;if(p+=g.byteLength,s.push(n.decode(g,{stream:!0})),i===void 0&&s.eventCount>0&&(i=Date.now()-o),p>MAX_STREAM_BYTES||Date.now()-o>MAX_STREAM_MS){r=!0,await l.cancel().catch(()=>{});break}}}catch{r=!0}}return{response:s.end(),sse:!0,cut:r,...i!==void 0?{firstEventMs:i}:{}}}c(readStream,"readStream"),d(readStream,"readStream");async function readBody(t){const e={completion:"",toolCalls:[],usage:{}};if(!t.res||!t.clone)return{response:e,sse:!1,cut:!1};try{if(headerOf(t.res,"content-type").includes("text/event-stream"))return await readStream(t.clone,t.target,t.startedAt);const o=Number(headerOf(t.res,"content-length"));if(Number.isFinite(o)&&o>MAX_RESPONSE_PARSE_BYTES)return{response:e,sse:!1,cut:!1};const s=await t.clone.text().catch(()=>{});if(s&&/^\s*(?:event|data):/.test(s)){const i=(0,ai_providers_1.createStreamAccumulator)(t.target);return i.push(s),{response:i.end(),sse:!0,cut:!1}}return{response:(0,ai_providers_1.normalizeResponse)(t.target,parseJson(s)),sse:!1,cut:!1}}catch{return{response:e,sse:!1,cut:!1}}}c(readBody,"readBody"),d(readBody,"readBody");async function capture(t){const e=readBody(t),o=t.request.text??await t.request.pending,s=t.request.bytes??(o?Buffer.byteLength(o):void 0),i=s!==void 0&&s>MAX_REQUEST_PARSE_BYTES,r=(0,ai_providers_1.normalizeRequest)(t.target,i?void 0:parseJson(o)),a=statusOf(t.res),l=await e;let n=l.response;const p=r.stream||l.sse,m=l.firstEventMs,g=l.cut,{aborted:y,timedOut:C}=t.failure!==void 0?failureKind(t.failure,t.signal):{aborted:!1,timedOut:!1};t.failure!==void 0&&(n={...n,error:String(t.failure?.message??t.failure)});const b=y||g,A=Date.now()-t.startedAt,u=n.model??r.model,_=sum(r.messages.map(w=>w.content.length))+(r.system?.length??0);let f,h=!1;if(t.mode==="content"&&t.target.api!=="embeddings"){const w={schemaVersion:1,id:(0,ai_store_1.newAiCallId)(),capturedAt:Date.now(),...t.ctx?.flowId?{flowId:t.ctx.flowId}:{},...t.ctx?.requestId?{requestId:t.ctx.requestId}:{},provider:t.target.provider,api:t.target.api,...u?{model:u}:{},host:t.host,endpoint:t.endpoint,stream:p,...a!==void 0?{status:a}:{},request:{...r.system?{system:r.system}:{},messages:r.messages,...r.tools.length?{tools:r.tools}:{},...Object.keys(r.params).length?{params:r.params}:{}},response:{completion:n.completion,...n.reasoning?{reasoning:n.reasoning}:{},...n.toolCalls.length?{toolCalls:n.toolCalls}:{},...n.finishReason?{finishReason:n.finishReason}:{},usage:n.usage,...n.error?{error:n.error}:{}}},S=(0,ai_store_1.writeAiContent)(w);S&&(f=S.id,h=S.truncated)}const v=a!==void 0&&a>=400||t.failure!==void 0&&!y,T=u??"?";(0,emit_1.lensEmit)("ai",t.failure!==void 0&&!y?"error":"info",a!==void 0?`ai ${t.target.provider}/${T} ${t.target.api} \u2192 ${a}`:`ai ${t.target.provider}/${T} ${t.target.api} ${y?"aborted":"failed"}`,{name:t.target.provider,kind:"ai-call",provider:t.target.provider,api:t.target.api,...u?{model:u}:{},...r.model&&u&&r.model!==u?{requestedModel:r.model}:{},host:t.host,endpoint:t.endpoint,method:t.method,...a!==void 0?{status:a}:{},stream:p,startedAt:t.startedAt,durationMs:A,...t.ttfbMs!==void 0?{ttfbMs:t.ttfbMs}:{},...m!==void 0?{firstEventMs:m}:{},...Object.keys(n.usage).length?{usage:n.usage}:{},...n.finishReason?{finishReason:n.finishReason}:{},toolCallCount:n.toolCalls.length,sizes:{messageCount:r.messages.length,toolCount:r.tools.length,promptChars:_,completionChars:n.completion.length,...s!==void 0?{requestBytes:s}:{}},...r.inputCount!==void 0?{inputCount:r.inputCount}:{},...n.vectorCount!==void 0?{vectorCount:n.vectorCount}:{},contentCaptured:f!==void 0,...f?{contentRef:f}:{},...h?{truncated:!0}:{},...b?{aborted:!0}:{},...C?{timedOut:!0}:{},...n.error?{error:(0,ai_store_1.redactAiText)(n.error).slice(0,300)}:{}},v?`ai:error:${t.target.provider}:${T}`:`ai:${t.target.provider}:${T}`,t.ctx,"ai")}c(capture,"capture"),d(capture,"capture");let savedFetch;const pending=new Set;async function flushAiTapForTests(){await Promise.allSettled([...pending])}c(flushAiTapForTests,"flushAiTapForTests"),d(flushAiTapForTests,"flushAiTapForTests");function resetAiTapForTests(){const t=globalThis;savedFetch&&(t.fetch=savedFetch),delete t.__lensmcpAiTap,savedFetch=void 0,pending.clear()}c(resetAiTapForTests,"resetAiTapForTests"),d(resetAiTapForTests,"resetAiTapForTests");function tapAi(){const t=aiCaptureMode();if(t==="off")return;const e=globalThis;if(typeof e.fetch!="function"||e.__lensmcpAiTap)return;e.__lensmcpAiTap=!0;const o=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS),s=e.fetch.bind(globalThis);savedFetch=e.fetch,e.fetch=(async(i,r)=>{const a=locate(i),l=methodOf(i,r),n=a&&l==="POST"?(0,ai_providers_1.matchTarget)(a.host,a.port,a.pathname,o):void 0;if(!a||!n)return s(i,r);const p=Date.now();(0,flow_context_1.startLiveFlow)();const m=(0,flow_context_1.flowContext)(),g=a.pathname.length>MAX_ENDPOINT_CHARS?`${a.pathname.slice(0,MAX_ENDPOINT_CHARS)}\u2026`:a.pathname,y=takeRequestBody(i,r),C=r?.signal??(typeof i=="object"&&i!==null&&!(i instanceof URL)?i.signal:void 0),b=d(h=>{const v=capture(h).catch(()=>{});pending.add(v),v.finally(()=>pending.delete(v))},"track"),A={mode:t,target:n,host:a.host,endpoint:g,method:l,startedAt:p,ctx:m,signal:C,request:y};let u;try{u=await s(i,r)}catch(h){throw b({...A,failure:h}),h}const _=Date.now()-p;let f;try{f=u.clone()}catch{f=void 0}return b({...A,ttfbMs:_,res:u,...f?{clone:f}:{}}),u})}c(tapAi,"tapAi"),d(tapAi,"tapAi");
package/lib/tap-egress.js CHANGED
@@ -1 +1 @@
1
- "use strict";var O=Object.defineProperty;var E=(t,e)=>O(t,"name",{value:e,configurable:!0});var v=Object.defineProperty,d=E((t,e)=>v(t,"name",{value:e,configurable:!0}),"d");Object.defineProperty(exports,"__esModule",{value:!0}),exports.flushEgressCaptureForTests=flushEgressCaptureForTests,exports.tapEgress=tapEgress,exports.resetEgressTapForTests=resetEgressTapForTests;const redact_1=require("@lensmcp/core/redact"),http_exchange_1=require("@lensmcp/core/http-exchange"),emit_1=require("./emit"),flow_context_1=require("./flow-context"),ai_providers_1=require("./ai-providers"),http_capture_1=require("./http-capture"),http_store_1=require("./http-store"),capture_file_1=require("./capture-file"),tap_ai_1=require("./tap-ai"),INTERNAL=[/\.local$/i,/\.internal$/i,/^localhost$/i,/^127\./,/^\[?::1\]?$/,/^0\.0\.0\.0$/,/^10\./,/^192\.168\./,/^172\.(1[6-9]|2\d|3[01])\./,/^169\.254\./,/^\[?[fF][cCdD][0-9a-fA-F]{2}:/,/^\[?[fF][eE]80:/],isInternal=d(t=>t===""||INTERNAL.some(e=>e.test(t)),"isInternal"),CAPTURE_QUERY=process.env.LENSMCP_EGRESS_QUERY==="1",MAX_PATH_CHARS=256,clipPath=d(t=>t.length>MAX_PATH_CHARS?`${t.slice(0,MAX_PATH_CHARS)}\u2026`:t,"clipPath");function capturePath(t,e){const s=t||"/";return!CAPTURE_QUERY||e.length<=1?clipPath(s):(0,redact_1.redactUrl)(`${s}${e}`)}E(capturePath,"capturePath"),d(capturePath,"capturePath");const fingerprintFor=d((t,e)=>e?`external:error:${t}`:`external:${t}`,"fingerprintFor"),PARENT_HEADER="x-lensmcp-parent-request-id",FLOW_HEADER="x-lensmcp-flow-id",ORIGIN_HEADER="x-lensmcp-origin-node-id";let hopsOutside=0;function hopHeaders(t,e){if(!t?.requestId&&!t?.flowId||e(http_exchange_1.LENS_REQUEST_ID_HEADER)||e(PARENT_HEADER)||e(FLOW_HEADER))return;const s={};if(t.requestId){const i=(0,flow_context_1.liveFlowContext)(),r=i?i.hopCount=(i.hopCount??0)+1:++hopsOutside;s[http_exchange_1.LENS_REQUEST_ID_HEADER]=`${t.requestId}.${r}`,s[PARENT_HEADER]=t.requestId}return t.flowId&&(s[FLOW_HEADER]=t.flowId),t.originNodeId&&(s[ORIGIN_HEADER]=t.originNodeId),s}E(hopHeaders,"hopHeaders"),d(hopHeaders,"hopHeaders");function stampFetch(t,e,s){try{const i=e?.headers??(typeof t=="object"&&t!==null&&!(t instanceof URL)?t.headers:void 0),r=new Headers(i),u=hopHeaders(s,n=>r.has(n));if(!u)return{init:e};for(const[n,f]of Object.entries(u))r.set(n,f);const o=u[http_exchange_1.LENS_REQUEST_ID_HEADER];return{init:{...e,headers:r},...o?{calleeRequestId:o}:{}}}catch{return{init:e}}}E(stampFetch,"stampFetch"),d(stampFetch,"stampFetch");function stampRequest(t,e){try{if(t.headersSent||typeof t.setHeader!="function"||typeof t.getHeader!="function")return;const s=hopHeaders(e,i=>t.getHeader(i)!==void 0);if(!s)return;for(const[i,r]of Object.entries(s))t.setHeader(i,r);return s[http_exchange_1.LENS_REQUEST_ID_HEADER]}catch{return}}E(stampRequest,"stampRequest"),d(stampRequest,"stampRequest");const pendingExchanges=new Set;async function flushEgressCaptureForTests(){await Promise.allSettled([...pendingExchanges])}E(flushEgressCaptureForTests,"flushEgressCaptureForTests"),d(flushEgressCaptureForTests,"flushEgressCaptureForTests");async function finishExchange(t){const e=[],s=[],i=await(0,http_capture_1.requestPart)(t.request,t.url.search,t.mode,!!t.aiOpaque,s,e,!!t.ai&&!t.aiOpaque),r=t.reading?await t.reading.catch(()=>{}):void 0,{headers:u,redacted:o}=(0,http_store_1.redactHeaders)(t.responseHeaders??[]);e.push(...o);const n=typeof t.status=="number"?t.status:void 0,f={schemaVersion:1,id:t.id,capturedAt:Date.now(),side:"egress",service:emit_1.PROJECT,...t.ctx?.flowId?{flowId:t.ctx.flowId}:{},...t.ctx?.requestId?{requestId:t.ctx.requestId}:{},method:t.method,url:`${t.url.protocol}//${t.url.host}${t.path}`,host:t.url.hostname,path:t.path,...n!==void 0?{status:n}:{},...t.statusText?{statusText:t.statusText}:{},...t.error?{error:t.error}:{},startedAt:t.startedAt,...t.ttfbMs!==void 0?{ttfbMs:t.ttfbMs}:{},durationMs:(r?.endedAt??Date.now())-t.startedAt,capture:t.mode==="bodies"?"bodies":"headers",request:i,response:{headers:u,...r?.body?{body:r.body}:{},...r?.stream?{stream:r.stream}:{}},redacted:{headers:[...new Set(e)],bodyFields:[...s.map(h=>`request.${h}`),...t.responseFields.map(h=>`response.${h}`)]},...t.ai?{ai:{provider:t.ai}}:{}};(0,http_store_1.writeHttpExchange)(f)}E(finishExchange,"finishExchange"),d(finishExchange,"finishExchange");function trackExchange(t){const e=(typeof t=="function"?new Promise(s=>setImmediate(s)).then(()=>finishExchange(t())):finishExchange(t)).catch(()=>{});pendingExchanges.add(e),e.finally(()=>pendingExchanges.delete(e))}E(trackExchange,"trackExchange"),d(trackExchange,"trackExchange");function tapEgress(){tapFetch(),tapHttpModule("http"),tapHttpModule("https")}E(tapEgress,"tapEgress"),d(tapEgress,"tapEgress");let savedFetch;const httpOriginals=new Map;function resetEgressTapForTests(){const t=globalThis;savedFetch&&(t.fetch=savedFetch),delete t.__lensmcpFetchTap,restoreHttp("http"),restoreHttp("https"),savedFetch=void 0}E(resetEgressTapForTests,"resetEgressTapForTests"),d(resetEgressTapForTests,"resetEgressTapForTests");function restoreHttp(t){const e=httpOriginals.get(t);if(!e)return;const s=require(`node:${t}`);s.request=e.request,s.get=e.get,httpOriginals.delete(t)}E(restoreHttp,"restoreHttp"),d(restoreHttp,"restoreHttp");function tapFetch(){const t=globalThis;if(typeof t.fetch!="function"||t.__lensmcpFetchTap)return;t.__lensmcpFetchTap=!0;const e=t.fetch.bind(globalThis);savedFetch=t.fetch;const s=(0,http_store_1.httpCaptureMode)(),i=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS);t.fetch=(async(r,u)=>{let o="",n="/",f=!1,h;try{const p=new URL(typeof r=="string"?r:r instanceof URL?r.href:r.url);h=p,o=p.hostname,f=p.search.length>1,n=capturePath(p.pathname,p.search)}catch{}if(isInternal(o))return e(r,u);const g=Date.now(),c=(0,flow_context_1.flowContext)(),a=(0,http_exchange_1.isServiceHopHost)(o)?stampFetch(r,u,c):{init:u},x=a.init,m=a.calleeRequestId,_=(0,flow_context_1.liveFlowContext)();_&&(_.externalCallCount=(_.externalCallCount??0)+1);const T=typeof r=="object"&&r!==null&&!(r instanceof URL)?r.method:void 0,R=String(u?.method??T??"GET").toUpperCase(),q=s!=="off"&&h?(0,http_store_1.newExchangeId)():void 0,$=q&&h&&R==="POST"?(0,ai_providers_1.matchTarget)(o,h.port,h.pathname,i)?.provider:void 0,I=!!$&&(0,tap_ai_1.aiCaptureMode)()!=="content"&&!(0,capture_file_1.aiBodiesAsked)(),b=!!$&&!I,H=b&&!!h&&!/\/embeddings\/?$/.test(h.pathname),M=q?(0,http_capture_1.takeRequest)(r,x,s==="bodies",b):void 0,S=d(p=>({id:q,mode:s,method:R,url:h,path:h.pathname.length>256?`${h.pathname.slice(0,256)}\u2026`:h.pathname,startedAt:g,ctx:c,request:M,...$?{ai:$}:{},...I?{aiOpaque:I}:{},responseFields:[],...p}),"job");try{const p=await e(r,x),l=Date.now()-g,y=p.status>=500,w=[];let A;if(q&&s==="bodies")try{A=(0,http_capture_1.readResponse)(p,p.clone(),g,{ai:I,large:H,paths:w})}catch{A=void 0}const C=(0,emit_1.lensEmit)("external","info",`egress ${R} ${o} \u2192 ${p.status}`,{name:o,kind:"egress",method:R,host:o,path:n,status:p.status,...f?{hasQuery:!0}:{},startedAt:g,durationMs:l,...q?{exchangeId:q}:{},...m?{calleeRequestId:m}:{}},fingerprintFor(o,y),c);if(q&&C){const F={status:p.status,...p.statusText?{statusText:p.statusText}:{},responseHeaders:(0,http_capture_1.headerPairs)(p.headers)};trackExchange(S({...F,ttfbMs:l,responseFields:w,...A?{reading:A}:{}}))}return p}catch(p){const l=String(p?.message??p),y=(0,emit_1.lensEmit)("external","error",`egress ${R} ${o} failed`,{name:o,kind:"egress",method:R,host:o,path:n,...f?{hasQuery:!0}:{},error:l,startedAt:g,durationMs:Date.now()-g,...q?{exchangeId:q}:{},...m?{calleeRequestId:m}:{}},fingerprintFor(o,!0),c);throw q&&y&&trackExchange(S({error:l})),p}})}E(tapFetch,"tapFetch"),d(tapFetch,"tapFetch");function tapHttpModule(t){const e=require(`node:${t}`),s=e.request;if(typeof s!="function"||s.__lensmcp)return;httpOriginals.set(t,{request:s,get:e.get});const i=(0,http_store_1.httpCaptureMode)(),r=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS),u=d(function(...o){const n=s.apply(this,o);try{observe(t,o,n,i,r)}catch{}return n},"wrapped");u.__lensmcp=!0,e.request=u,typeof e.get=="function"&&(e.get=function(...o){const n=u.apply(this,o);return n.end(),n})}E(tapHttpModule,"tapHttpModule"),d(tapHttpModule,"tapHttpModule");const headerOf=d((t,e)=>{const s=t?.[e];return Array.isArray(s)?s.join(", "):s??""},"headerOf");function teeRequest(t,e,s,i=!1){const r=e?new http_capture_1.WrittenBody(!s,i):void 0,u=d(()=>{try{return t.getHeaders?.()}catch{return}},"sentHeaders");let o,n=!1;const f=t.write;r&&typeof f=="function"&&(t.write=function(g,...c){try{n||r.take(g,c[0])}catch{}return f.call(this,g,...c)});const h=t.end;return t.end=function(g,...c){try{n||(n=!0,r?.take(g,c[0]),o=u())}catch{}return h.call(this,g,...c)},()=>(0,http_capture_1.writtenRequest)(o??u(),r)}E(teeRequest,"teeRequest"),d(teeRequest,"teeRequest");function teeResponse(t,e,s){let i=!1;const r=d(n=>{if(i)return;i=!0,clearTimeout(u);const f=Date.now();s(()=>e.size?e.finish(n,f):{endedAt:f})},"finish"),u=setTimeout(()=>r(!0),http_capture_1.MAX_STREAM_MS);u.unref?.();const o=t.emit;t.emit=function(n,...f){try{return o.call(this,n,...f)}finally{try{if(n==="data"){const h=i?void 0:(0,http_capture_1.chunkBytes)(f[0],t.readableEncoding);h&&!e.push(h)&&r(!1)}else n==="end"?r(!1):(n==="aborted"||n==="error"||n==="close")&&r(!0)}catch{}}}}E(teeResponse,"teeResponse"),d(teeResponse,"teeResponse");function observe(t,e,s,i,r){const u=parseTarget(e,t),{host:o,method:n,path:f,hasQuery:h}=u;if(isInternal(o))return;const g=Date.now(),c=(0,flow_context_1.flowContext)(),a=(0,http_exchange_1.isServiceHopHost)(o)?stampRequest(s,c):void 0,x=(0,flow_context_1.liveFlowContext)();x&&(x.externalCallCount=(x.externalCallCount??0)+1);const m=i!=="off"?targetUrl(u):void 0,_=m?(0,http_store_1.newExchangeId)():void 0,T=_&&m&&n==="POST"?(0,ai_providers_1.matchTarget)(o,m.port,m.pathname,r)?.provider:void 0,R=!!T&&(0,tap_ai_1.aiCaptureMode)()!=="content"&&!(0,capture_file_1.aiBodiesAsked)(),q=!!T&&!R,$=q&&!!m&&!/\/embeddings\/?$/.test(m.pathname),I=_?teeRequest(s,i==="bodies",R,q):void 0,b=d(l=>({id:_,mode:i,method:n,url:m,path:clipPath(m.pathname),startedAt:g,ctx:c,request:I(),...T?{ai:T}:{},...R?{aiOpaque:R}:{},responseFields:[],...l}),"job");let H=!1;const M=d(l=>{if(H)return;H=!0;const y=l?.statusCode,w=Date.now()-g,A=(0,emit_1.lensEmit)("external","info",`egress ${n} ${o} \u2192 ${y??"?"}`,{name:o,kind:"egress",method:n,host:o,path:f,...y!=null?{status:y}:{},...h?{hasQuery:!0}:{},startedAt:g,durationMs:w,..._?{exchangeId:_}:{},...a?{calleeRequestId:a}:{}},fingerprintFor(o,y!=null&&y>=500),c);if(!_||!A||!l||typeof l.emit!="function")return;const C={...y!=null?{status:y}:{},...l.statusMessage?{statusText:l.statusMessage}:{},responseHeaders:(0,http_capture_1.headerPairs)(l.headers),ttfbMs:w};if(i!=="bodies"){trackExchange(()=>b(C));return}const F=[],P=new http_capture_1.ResponseSink(headerOf(l.headers,"content-type"),g,{ai:R,large:$,paths:F,encoding:headerOf(l.headers,"content-encoding")});teeResponse(l,P,D=>trackExchange(()=>b({...C,responseFields:F,reading:Promise.resolve(D())})))},"onResponse"),S=d(l=>{if(H)return;H=!0;const y=String(l?.message??l),w=(0,emit_1.lensEmit)("external","error",`egress ${n} ${o} failed`,{name:o,kind:"egress",method:n,host:o,path:f,...h?{hasQuery:!0}:{},error:y,startedAt:g,durationMs:Date.now()-g,..._?{exchangeId:_}:{},...a?{calleeRequestId:a}:{}},fingerprintFor(o,!0),c);_&&w&&trackExchange(()=>b({error:y}))},"onError"),p=s.emit;s.emit=function(l,...y){try{l==="response"?M(y[0]):l==="error"&&S(y[0])}catch{}return p.call(this,l,...y)}}E(observe,"observe"),d(observe,"observe");function targetUrl(t){try{const e=t.host.includes(":")&&!t.host.startsWith("[")?`[${t.host}]`:t.host,s=t.pathname.startsWith("/")?t.pathname:`/${t.pathname}`;return new URL(`${t.protocol}//${e}${t.port?`:${t.port}`:""}${s}${t.search}`)}catch{return}}E(targetUrl,"targetUrl"),d(targetUrl,"targetUrl");function parseTarget(t,e="http"){let s="",i="GET",r="/",u=!1,o=`${e}:`,n="",f="/",h="";const g=d((c,a)=>{u=a.length>1,r=capturePath(c,a),f=c||"/",h=a},"take");for(const c of t)if(typeof c=="string")try{const a=new URL(c);s=a.hostname,o=a.protocol,n=a.port,g(a.pathname,a.search)}catch{}else if(c instanceof URL)s=c.hostname,o=c.protocol,n=c.port,g(c.pathname,c.search);else if(c&&typeof c=="object"){const a=c;if(typeof a.method=="string"&&(i=a.method.toUpperCase()),typeof a.socketPath=="string")return{host:"",method:i,path:r,hasQuery:u,protocol:o,port:n,pathname:f,search:h};if(typeof a.path=="string"){const x=a.path,m=x.indexOf("?");g(m===-1?x:x.slice(0,m),m===-1?"":x.slice(m))}if(typeof a.protocol=="string"&&a.protocol&&(o=a.protocol),(typeof a.port=="number"||typeof a.port=="string"&&a.port)&&(n=String(a.port)),typeof a.hostname=="string"&&a.hostname)s=a.hostname;else if(typeof a.host=="string"&&a.host){const[x,m]=a.host.split(":");s=x??"",m&&!(typeof a.port=="number"||typeof a.port=="string")&&(n=m)}}return{host:s,method:i,path:r,hasQuery:u,protocol:o,port:n,pathname:f,search:h}}E(parseTarget,"parseTarget"),d(parseTarget,"parseTarget");
1
+ "use strict";var O=Object.defineProperty;var g=(t,e)=>O(t,"name",{value:e,configurable:!0});var D=Object.defineProperty,c=g((t,e)=>D(t,"name",{value:e,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.flushEgressCaptureForTests=flushEgressCaptureForTests,exports.tapEgress=tapEgress,exports.resetEgressTapForTests=resetEgressTapForTests;const redact_1=require("@lensmcp/core/redact"),http_exchange_1=require("@lensmcp/core/http-exchange"),emit_1=require("./emit"),flow_context_1=require("./flow-context"),ai_providers_1=require("./ai-providers"),http_capture_1=require("./http-capture"),http_store_1=require("./http-store"),capture_file_1=require("./capture-file"),tap_ai_1=require("./tap-ai"),INTERNAL=[/\.local$/i,/\.internal$/i,/^localhost$/i,/^127\./,/^\[?::1\]?$/,/^0\.0\.0\.0$/,/^10\./,/^192\.168\./,/^172\.(1[6-9]|2\d|3[01])\./,/^169\.254\./,/^\[?[fF][cCdD][0-9a-fA-F]{2}:/,/^\[?[fF][eE]80:/],isInternal=c(t=>t===""||INTERNAL.some(e=>e.test(t)),"isInternal"),CAPTURE_QUERY=process.env.LENSMCP_EGRESS_QUERY==="1",MAX_PATH_CHARS=256,clipPath=c(t=>t.length>MAX_PATH_CHARS?`${t.slice(0,MAX_PATH_CHARS)}\u2026`:t,"clipPath");function capturePath(t,e){const s=t||"/";return!CAPTURE_QUERY||e.length<=1?clipPath(s):(0,redact_1.redactUrl)(`${s}${e}`)}g(capturePath,"capturePath"),c(capturePath,"capturePath");const fingerprintFor=c((t,e)=>e?`external:error:${t}`:`external:${t}`,"fingerprintFor"),PARENT_HEADER="x-lensmcp-parent-request-id",FLOW_HEADER="x-lensmcp-flow-id",ORIGIN_HEADER="x-lensmcp-origin-node-id";let hopsOutside=0;function hopHeaders(t,e){if(!t?.requestId&&!t?.flowId||e(http_exchange_1.LENS_REQUEST_ID_HEADER)||e(PARENT_HEADER)||e(FLOW_HEADER))return;const s={};if(t.requestId){const i=(0,flow_context_1.liveFlowContext)(),r=i?i.hopCount=(i.hopCount??0)+1:++hopsOutside;s[http_exchange_1.LENS_REQUEST_ID_HEADER]=`${t.requestId}.${r}`,s[PARENT_HEADER]=t.requestId}return t.flowId&&(s[FLOW_HEADER]=t.flowId),t.originNodeId&&(s[ORIGIN_HEADER]=t.originNodeId),s}g(hopHeaders,"hopHeaders"),c(hopHeaders,"hopHeaders");function isAiCall(t,e,s){return!!t&&e==="POST"&&!!(0,ai_providers_1.matchTarget)(t.hostname,t.port,t.pathname,s)}g(isAiCall,"isAiCall"),c(isAiCall,"isAiCall");function stampFetch(t,e,s){try{const i=e?.headers??(typeof t=="object"&&t!==null&&!(t instanceof URL)?t.headers:void 0),r=new Headers(i),d=hopHeaders(s,n=>r.has(n));if(!d)return{init:e};for(const[n,f]of Object.entries(d))r.set(n,f);const o=d[http_exchange_1.LENS_REQUEST_ID_HEADER];return{init:{...e,headers:r},...o?{calleeRequestId:o}:{}}}catch{return{init:e}}}g(stampFetch,"stampFetch"),c(stampFetch,"stampFetch");function stampRequest(t,e){try{if(t.headersSent||typeof t.setHeader!="function"||typeof t.getHeader!="function")return;const s=hopHeaders(e,i=>t.getHeader(i)!==void 0);if(!s)return;for(const[i,r]of Object.entries(s))t.setHeader(i,r);return s[http_exchange_1.LENS_REQUEST_ID_HEADER]}catch{return}}g(stampRequest,"stampRequest"),c(stampRequest,"stampRequest");const pendingExchanges=new Set;async function flushEgressCaptureForTests(){await Promise.allSettled([...pendingExchanges])}g(flushEgressCaptureForTests,"flushEgressCaptureForTests"),c(flushEgressCaptureForTests,"flushEgressCaptureForTests");async function finishExchange(t){const e=[],s=[],i=await(0,http_capture_1.requestPart)(t.request,t.url.search,t.mode,!!t.aiOpaque,s,e,!!t.ai&&!t.aiOpaque),r=t.reading?await t.reading.catch(()=>{}):void 0,{headers:d,redacted:o}=(0,http_store_1.redactHeaders)(t.responseHeaders??[]);e.push(...o);const n=typeof t.status=="number"?t.status:void 0,f={schemaVersion:1,id:t.id,capturedAt:Date.now(),side:"egress",service:emit_1.PROJECT,...t.ctx?.flowId?{flowId:t.ctx.flowId}:{},...t.ctx?.requestId?{requestId:t.ctx.requestId}:{},method:t.method,url:`${t.url.protocol}//${t.url.host}${t.path}`,host:t.url.hostname,path:t.path,...n!==void 0?{status:n}:{},...t.statusText?{statusText:t.statusText}:{},...t.error?{error:t.error}:{},startedAt:t.startedAt,...t.ttfbMs!==void 0?{ttfbMs:t.ttfbMs}:{},durationMs:(r?.endedAt??Date.now())-t.startedAt,capture:t.mode==="bodies"?"bodies":"headers",request:i,response:{headers:d,...r?.body?{body:r.body}:{},...r?.stream?{stream:r.stream}:{}},redacted:{headers:[...new Set(e)],bodyFields:[...s.map(u=>`request.${u}`),...t.responseFields.map(u=>`response.${u}`)]},...t.ai?{ai:{provider:t.ai}}:{}};(0,http_store_1.writeHttpExchange)(f)}g(finishExchange,"finishExchange"),c(finishExchange,"finishExchange");function trackExchange(t){const e=(typeof t=="function"?new Promise(s=>setImmediate(s)).then(()=>finishExchange(t())):finishExchange(t)).catch(()=>{});pendingExchanges.add(e),e.finally(()=>pendingExchanges.delete(e))}g(trackExchange,"trackExchange"),c(trackExchange,"trackExchange");function tapEgress(){tapFetch(),tapHttpModule("http"),tapHttpModule("https")}g(tapEgress,"tapEgress"),c(tapEgress,"tapEgress");let savedFetch;const httpOriginals=new Map;function resetEgressTapForTests(){const t=globalThis;savedFetch&&(t.fetch=savedFetch),delete t.__lensmcpFetchTap,restoreHttp("http"),restoreHttp("https"),savedFetch=void 0}g(resetEgressTapForTests,"resetEgressTapForTests"),c(resetEgressTapForTests,"resetEgressTapForTests");function restoreHttp(t){const e=httpOriginals.get(t);if(!e)return;const s=require(`node:${t}`);s.request=e.request,s.get=e.get,httpOriginals.delete(t)}g(restoreHttp,"restoreHttp"),c(restoreHttp,"restoreHttp");function tapFetch(){const t=globalThis;if(typeof t.fetch!="function"||t.__lensmcpFetchTap)return;t.__lensmcpFetchTap=!0;const e=t.fetch.bind(globalThis);savedFetch=t.fetch;const s=(0,http_store_1.httpCaptureMode)(),i=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS);t.fetch=(async(r,d)=>{let o="",n="/",f=!1,u;try{const p=new URL(typeof r=="string"?r:r instanceof URL?r.href:r.url);u=p,o=p.hostname,f=p.search.length>1,n=capturePath(p.pathname,p.search)}catch{}if(isInternal(o))return e(r,d);const m=Date.now(),h=typeof r=="object"&&r!==null&&!(r instanceof URL)?r.method:void 0,a=String(d?.method??h??"GET").toUpperCase();isAiCall(u,a,i)&&(0,flow_context_1.startLiveFlow)();const x=(0,flow_context_1.flowContext)(),E=(0,http_exchange_1.isServiceHopHost)(o)?stampFetch(r,d,x):{init:d},w=E.init,T=E.calleeRequestId,q=(0,flow_context_1.liveFlowContext)();q&&(q.externalCallCount=(q.externalCallCount??0)+1);const _=s!=="off"&&u?(0,http_store_1.newExchangeId)():void 0,v=_&&u&&a==="POST"?(0,ai_providers_1.matchTarget)(o,u.port,u.pathname,i)?.provider:void 0,$=!!v&&(0,tap_ai_1.aiCaptureMode)()!=="content"&&!(0,capture_file_1.aiBodiesAsked)(),I=!!v&&!$,b=I&&!!u&&!/\/embeddings\/?$/.test(u.pathname),F=_?(0,http_capture_1.takeRequest)(r,w,s==="bodies",I):void 0,H=c(p=>({id:_,mode:s,method:a,url:u,path:u.pathname.length>256?`${u.pathname.slice(0,256)}\u2026`:u.pathname,startedAt:m,ctx:x,request:F,...v?{ai:v}:{},...$?{aiOpaque:$}:{},responseFields:[],...p}),"job");try{const p=await e(r,w),l=Date.now()-m,y=p.status>=500,R=[];let A;if(_&&s==="bodies")try{A=(0,http_capture_1.readResponse)(p,p.clone(),m,{ai:$,large:b,paths:R})}catch{A=void 0}const C=(0,emit_1.lensEmit)("external","info",`egress ${a} ${o} \u2192 ${p.status}`,{name:o,kind:"egress",method:a,host:o,path:n,status:p.status,...f?{hasQuery:!0}:{},startedAt:m,durationMs:l,..._?{exchangeId:_}:{},...T?{calleeRequestId:T}:{}},fingerprintFor(o,y),x);if(_&&C){const S={status:p.status,...p.statusText?{statusText:p.statusText}:{},responseHeaders:(0,http_capture_1.headerPairs)(p.headers)};trackExchange(H({...S,ttfbMs:l,responseFields:R,...A?{reading:A}:{}}))}return p}catch(p){const l=String(p?.message??p),y=(0,emit_1.lensEmit)("external","error",`egress ${a} ${o} failed`,{name:o,kind:"egress",method:a,host:o,path:n,...f?{hasQuery:!0}:{},error:l,startedAt:m,durationMs:Date.now()-m,..._?{exchangeId:_}:{},...T?{calleeRequestId:T}:{}},fingerprintFor(o,!0),x);throw _&&y&&trackExchange(H({error:l})),p}})}g(tapFetch,"tapFetch"),c(tapFetch,"tapFetch");function tapHttpModule(t){const e=require(`node:${t}`),s=e.request;if(typeof s!="function"||s.__lensmcp)return;httpOriginals.set(t,{request:s,get:e.get});const i=(0,http_store_1.httpCaptureMode)(),r=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS),d=c(function(...o){const n=s.apply(this,o);try{observe(t,o,n,i,r)}catch{}return n},"wrapped");d.__lensmcp=!0,e.request=d,typeof e.get=="function"&&(e.get=function(...o){const n=d.apply(this,o);return n.end(),n})}g(tapHttpModule,"tapHttpModule"),c(tapHttpModule,"tapHttpModule");const headerOf=c((t,e)=>{const s=t?.[e];return Array.isArray(s)?s.join(", "):s??""},"headerOf");function teeRequest(t,e,s,i=!1){const r=e?new http_capture_1.WrittenBody(!s,i):void 0,d=c(()=>{try{return t.getHeaders?.()}catch{return}},"sentHeaders");let o,n=!1;const f=t.write;r&&typeof f=="function"&&(t.write=function(m,...h){try{n||r.take(m,h[0])}catch{}return f.call(this,m,...h)});const u=t.end;return t.end=function(m,...h){try{n||(n=!0,r?.take(m,h[0]),o=d())}catch{}return u.call(this,m,...h)},()=>(0,http_capture_1.writtenRequest)(o??d(),r)}g(teeRequest,"teeRequest"),c(teeRequest,"teeRequest");function teeResponse(t,e,s){let i=!1;const r=c(n=>{if(i)return;i=!0,clearTimeout(d);const f=Date.now();s(()=>e.size?e.finish(n,f):{endedAt:f})},"finish"),d=setTimeout(()=>r(!0),http_capture_1.MAX_STREAM_MS);d.unref?.();const o=t.emit;t.emit=function(n,...f){try{return o.call(this,n,...f)}finally{try{if(n==="data"){const u=i?void 0:(0,http_capture_1.chunkBytes)(f[0],t.readableEncoding);u&&!e.push(u)&&r(!1)}else n==="end"?r(!1):(n==="aborted"||n==="error"||n==="close")&&r(!0)}catch{}}}}g(teeResponse,"teeResponse"),c(teeResponse,"teeResponse");function observe(t,e,s,i,r){const d=parseTarget(e,t),{host:o,method:n,path:f,hasQuery:u}=d;if(isInternal(o))return;const m=Date.now();isAiCall(targetUrl(d),n,r)&&(0,flow_context_1.startLiveFlow)();const h=(0,flow_context_1.flowContext)(),a=(0,http_exchange_1.isServiceHopHost)(o)?stampRequest(s,h):void 0,x=(0,flow_context_1.liveFlowContext)();x&&(x.externalCallCount=(x.externalCallCount??0)+1);const E=i!=="off"?targetUrl(d):void 0,w=E?(0,http_store_1.newExchangeId)():void 0,T=w&&E&&n==="POST"?(0,ai_providers_1.matchTarget)(o,E.port,E.pathname,r)?.provider:void 0,q=!!T&&(0,tap_ai_1.aiCaptureMode)()!=="content"&&!(0,capture_file_1.aiBodiesAsked)(),_=!!T&&!q,v=_&&!!E&&!/\/embeddings\/?$/.test(E.pathname),$=w?teeRequest(s,i==="bodies",q,_):void 0,I=c(l=>({id:w,mode:i,method:n,url:E,path:clipPath(E.pathname),startedAt:m,ctx:h,request:$(),...T?{ai:T}:{},...q?{aiOpaque:q}:{},responseFields:[],...l}),"job");let b=!1;const F=c(l=>{if(b)return;b=!0;const y=l?.statusCode,R=Date.now()-m,A=(0,emit_1.lensEmit)("external","info",`egress ${n} ${o} \u2192 ${y??"?"}`,{name:o,kind:"egress",method:n,host:o,path:f,...y!=null?{status:y}:{},...u?{hasQuery:!0}:{},startedAt:m,durationMs:R,...w?{exchangeId:w}:{},...a?{calleeRequestId:a}:{}},fingerprintFor(o,y!=null&&y>=500),h);if(!w||!A||!l||typeof l.emit!="function")return;const C={...y!=null?{status:y}:{},...l.statusMessage?{statusText:l.statusMessage}:{},responseHeaders:(0,http_capture_1.headerPairs)(l.headers),ttfbMs:R};if(i!=="bodies"){trackExchange(()=>I(C));return}const S=[],M=new http_capture_1.ResponseSink(headerOf(l.headers,"content-type"),m,{ai:q,large:v,paths:S,encoding:headerOf(l.headers,"content-encoding")});teeResponse(l,M,P=>trackExchange(()=>I({...C,responseFields:S,reading:Promise.resolve(P())})))},"onResponse"),H=c(l=>{if(b)return;b=!0;const y=String(l?.message??l),R=(0,emit_1.lensEmit)("external","error",`egress ${n} ${o} failed`,{name:o,kind:"egress",method:n,host:o,path:f,...u?{hasQuery:!0}:{},error:y,startedAt:m,durationMs:Date.now()-m,...w?{exchangeId:w}:{},...a?{calleeRequestId:a}:{}},fingerprintFor(o,!0),h);w&&R&&trackExchange(()=>I({error:y}))},"onError"),p=s.emit;s.emit=function(l,...y){try{l==="response"?F(y[0]):l==="error"&&H(y[0])}catch{}return p.call(this,l,...y)}}g(observe,"observe"),c(observe,"observe");function targetUrl(t){try{const e=t.host.includes(":")&&!t.host.startsWith("[")?`[${t.host}]`:t.host,s=t.pathname.startsWith("/")?t.pathname:`/${t.pathname}`;return new URL(`${t.protocol}//${e}${t.port?`:${t.port}`:""}${s}${t.search}`)}catch{return}}g(targetUrl,"targetUrl"),c(targetUrl,"targetUrl");function parseTarget(t,e="http"){let s="",i="GET",r="/",d=!1,o=`${e}:`,n="",f="/",u="";const m=c((h,a)=>{d=a.length>1,r=capturePath(h,a),f=h||"/",u=a},"take");for(const h of t)if(typeof h=="string")try{const a=new URL(h);s=a.hostname,o=a.protocol,n=a.port,m(a.pathname,a.search)}catch{}else if(h instanceof URL)s=h.hostname,o=h.protocol,n=h.port,m(h.pathname,h.search);else if(h&&typeof h=="object"){const a=h;if(typeof a.method=="string"&&(i=a.method.toUpperCase()),typeof a.socketPath=="string")return{host:"",method:i,path:r,hasQuery:d,protocol:o,port:n,pathname:f,search:u};if(typeof a.path=="string"){const x=a.path,E=x.indexOf("?");m(E===-1?x:x.slice(0,E),E===-1?"":x.slice(E))}if(typeof a.protocol=="string"&&a.protocol&&(o=a.protocol),(typeof a.port=="number"||typeof a.port=="string"&&a.port)&&(n=String(a.port)),typeof a.hostname=="string"&&a.hostname)s=a.hostname;else if(typeof a.host=="string"&&a.host){const[x,E]=a.host.split(":");s=x??"",E&&!(typeof a.port=="number"||typeof a.port=="string")&&(n=E)}}return{host:s,method:i,path:r,hasQuery:d,protocol:o,port:n,pathname:f,search:u}}g(parseTarget,"parseTarget"),c(parseTarget,"parseTarget");
@@ -1 +1 @@
1
- "use strict";var f=Object.defineProperty;var a=(e,t)=>f(e,"name",{value:t,configurable:!0});var E=Object.defineProperty,o=a((e,t)=>E(e,"name",{value:t,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.tapEventLoop=tapEventLoop,exports.stopEventLoopTapForTests=stopEventLoopTapForTests;const node_perf_hooks_1=require("node:perf_hooks"),emit_1=require("./emit"),loop_identity_1=require("./loop-identity"),tap_loop_callbacks_1=require("./tap-loop-callbacks"),loop_watchdog_1=require("./loop-watchdog"),SAMPLE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_SAMPLE_MS);return Number.isFinite(e)&&e>=1e3?e:3e4})(),REPORT_ABOVE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_MIN_MS);return Number.isFinite(e)&&e>=0?e:50})(),RESOLUTION_MS=20;let timer,published=!1,stopMonitoring;function tapEventLoop(){if(process.env.LENSMCP_EVENT_LOOP==="0"||timer)return;const e=(0,node_perf_hooks_1.monitorEventLoopDelay)({resolution:RESOLUTION_MS});e.enable();let t=node_perf_hooks_1.performance.eventLoopUtilization(),p=0;const s=process.env.LENSMCP_EVENT_LOOP_CALLBACKS!=="0";let r=!1;const l=o(()=>`${s?"Callback wall time and loop delay":"Loop delay only; callback measurement disabled"}; ${r?"independent watchdog":"watchdog unavailable or disabled"}`,"coverage"),c=(0,loop_watchdog_1.startLoopWatchdog)(()=>{r=!1,(0,emit_1.lensEmit)("perf","warning","Event loop watchdog unavailable",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:l()},"perf:event-loop-status",void 0,"performance")});r=!!c,(0,emit_1.lensEmit)("perf","info","Event loop monitoring started",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:l()},"perf:event-loop-status",void 0,"performance"),timer=setInterval(()=>{const i=RESOLUTION_MS,d=e.mean/1e6;if(!Number.isFinite(d))return;const v=Math.max(0,d-i),n=Math.max(0,e.max/1e6-i),_=Math.max(0,e.percentile(99)/1e6-i);if(e.reset(),published&&n<REPORT_ABOVE_MS&&Date.now()-p<6e4)return;published=!0,p=Date.now();const m=node_perf_hooks_1.performance.eventLoopUtilization(),L=node_perf_hooks_1.performance.eventLoopUtilization(m,t).utilization;t=m,(0,emit_1.lensEmit)("perf",n>=1e3?"error":"info",`event loop lag ${v.toFixed(1)}ms mean / ${n.toFixed(1)}ms max`,{kind:"event-loop-lag",lagMs:Number(v.toFixed(2)),maxMs:Number(n.toFixed(2)),p99Ms:Number(_.toFixed(2)),windowMs:SAMPLE_MS,loop:loop_identity_1.NODE_LOOP,utilization:Number(L.toFixed(4))},"perf:event-loop-lag",void 0,"performance")},SAMPLE_MS),timer.unref?.();const u=s?(0,tap_loop_callbacks_1.tapLoopCallbacks)(Math.max(50,REPORT_ABOVE_MS)):void 0;stopMonitoring=o(()=>{u?.(),c?.(),e.disable()},"stopMonitoring")}a(tapEventLoop,"tapEventLoop"),o(tapEventLoop,"tapEventLoop");function stopEventLoopTapForTests(){timer&&clearInterval(timer),timer=void 0,stopMonitoring?.(),stopMonitoring=void 0,published=!1}a(stopEventLoopTapForTests,"stopEventLoopTapForTests"),o(stopEventLoopTapForTests,"stopEventLoopTapForTests");
1
+ "use strict";var x=Object.defineProperty;var l=(e,o)=>x(e,"name",{value:o,configurable:!0});var P=Object.defineProperty,n=l((e,o)=>P(e,"name",{value:o,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.tapEventLoop=tapEventLoop,exports.stopEventLoopTapForTests=stopEventLoopTapForTests;const node_perf_hooks_1=require("node:perf_hooks"),emit_1=require("./emit"),loop_identity_1=require("./loop-identity"),tap_loop_callbacks_1=require("./tap-loop-callbacks"),loop_watchdog_1=require("./loop-watchdog"),SAMPLE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_SAMPLE_MS);return Number.isFinite(e)&&e>=1e3?e:3e4})(),REPORT_ABOVE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_MIN_MS);return Number.isFinite(e)&&e>=0?e:50})(),STEP_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_STEP_MS);return Math.min(Number.isFinite(e)&&e>=1e3?e:5e3,SAMPLE_MS)})(),MAX_CARRIED=Math.ceil(Math.max(12e4,2*SAMPLE_MS)/STEP_MS),RESOLUTION_MS=20,round=n((e,o)=>Number(e.toFixed(o)),"round");let timer,published=!1,stopMonitoring;function tapEventLoop(){if(process.env.LENSMCP_EVENT_LOOP==="0"||timer)return;const e=(0,node_perf_hooks_1.monitorEventLoopDelay)({resolution:RESOLUTION_MS});e.enable();let o=node_perf_hooks_1.performance.eventLoopUtilization(),v=0;const d=process.env.LENSMCP_EVENT_LOOP_CALLBACKS!=="0";let c=!1;const _=n(()=>`${d?"Callback wall time and loop delay":"Loop delay only; callback measurement disabled"}; ${c?"independent watchdog":"watchdog unavailable or disabled"}`,"coverage"),E=(0,loop_watchdog_1.startLoopWatchdog)(()=>{c=!1,(0,emit_1.lensEmit)("perf","warning","Event loop watchdog unavailable",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:_()},"perf:event-loop-status",void 0,"performance")});c=!!E,(0,emit_1.lensEmit)("perf","info","Event loop monitoring started",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:_()},"perf:event-loop-status",void 0,"performance");let t=[],m=[],M=Date.now(),L=o;timer=setInterval(()=>{const u=RESOLUTION_MS,f=e.mean/1e6;if(!Number.isFinite(f))return;const a=Date.now(),i=node_perf_hooks_1.performance.eventLoopUtilization(),O=[a,round(Math.max(0,f-u),2),round(Math.max(0,e.percentile(99)/1e6-u),2),round(Math.max(0,e.max/1e6-u),2),round(node_perf_hooks_1.performance.eventLoopUtilization(i,L).utilization,4)];if(L=i,e.reset(),t.push(O),t.length>MAX_CARRIED&&t.splice(0,t.length-MAX_CARRIED),m.push(O),a-M<SAMPLE_MS-STEP_MS/2)return;M=a;const p=m;m=[];const b=p.reduce((r,S)=>r+S[1],0)/p.length,N=Math.max(...p.map(r=>r[2])),s=Math.max(...p.map(r=>r[3])),g=node_perf_hooks_1.performance.eventLoopUtilization(i,o).utilization;o=i,!(published&&s<REPORT_ABOVE_MS&&a-v<6e4)&&(published=!0,v=a,(0,emit_1.lensEmit)("perf",s>=1e3?"error":"info",`event loop lag ${b.toFixed(1)}ms mean / ${s.toFixed(1)}ms max`,{kind:"event-loop-lag",lagMs:round(b,2),maxMs:round(s,2),p99Ms:round(N,2),windowMs:SAMPLE_MS,loop:loop_identity_1.NODE_LOOP,utilization:round(g,4),stepMs:STEP_MS,samples:t},"perf:event-loop-lag",void 0,"performance")&&(t=[]))},STEP_MS),timer.unref?.();const h=d?(0,tap_loop_callbacks_1.tapLoopCallbacks)(Math.max(50,REPORT_ABOVE_MS)):void 0;stopMonitoring=n(()=>{h?.(),E?.(),e.disable()},"stopMonitoring")}l(tapEventLoop,"tapEventLoop"),n(tapEventLoop,"tapEventLoop");function stopEventLoopTapForTests(){timer&&clearInterval(timer),timer=void 0,stopMonitoring?.(),stopMonitoring=void 0,published=!1}l(stopEventLoopTapForTests,"stopEventLoopTapForTests"),n(stopEventLoopTapForTests,"stopEventLoopTapForTests");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/node-instrumentation",
3
- "version": "1.25.32",
3
+ "version": "1.25.33",
4
4
  "main": "./index.js",
5
5
  "types": "./index.d.ts",
6
6
  "exports": {
@@ -15,9 +15,9 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@lensmcp/core": "1.25.32",
19
- "@lensmcp/nest-instrumentation": "1.25.32",
20
- "@lensmcp/protocol-types": "1.25.32",
18
+ "@lensmcp/core": "1.25.33",
19
+ "@lensmcp/nest-instrumentation": "1.25.33",
20
+ "@lensmcp/protocol-types": "1.25.33",
21
21
  "tslib": "^2.3.0"
22
22
  },
23
23
  "optionalDependencies": {