@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.
- package/lib/flow-context.d.ts +17 -1
- package/lib/flow-context.js +1 -1
- package/lib/tap-ai.js +1 -1
- package/lib/tap-egress.js +1 -1
- package/lib/tap-event-loop.js +1 -1
- package/package.json +4 -4
package/lib/flow-context.d.ts
CHANGED
|
@@ -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
|
package/lib/flow-context.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var d=Object.defineProperty;var o=(e
|
|
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");
|
package/lib/tap-event-loop.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
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.
|
|
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.
|
|
19
|
-
"@lensmcp/nest-instrumentation": "1.25.
|
|
20
|
-
"@lensmcp/protocol-types": "1.25.
|
|
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": {
|