@lensmcp/node-instrumentation 1.25.31 → 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 +19 -1
- package/lib/flow-context.js +1 -1
- package/lib/http-capture.d.ts +6 -3
- package/lib/http-capture.js +2 -2
- package/lib/http-store.d.ts +13 -3
- package/lib/http-store.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/lib/tap-ingress.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 {
|
|
@@ -33,6 +34,8 @@ interface LiveCtx {
|
|
|
33
34
|
dbCallCount?: number;
|
|
34
35
|
redisCallCount?: number;
|
|
35
36
|
externalCallCount?: number;
|
|
37
|
+
/** Hops this request made to the cluster's own services (tap-egress `hopHeaders`, `nextHopHeaders`): `<request>.<n>`. */
|
|
38
|
+
hopCount?: number;
|
|
36
39
|
}
|
|
37
40
|
type CtxGetter = () => LiveCtx | undefined;
|
|
38
41
|
type CtxRunner = <T>(ctx: {
|
|
@@ -107,6 +110,21 @@ export interface ContextEndSignal {
|
|
|
107
110
|
export declare function attachContextEnd(ctx: object): () => void;
|
|
108
111
|
/** The end signal a live context carries — undefined when whoever opened it gives none. */
|
|
109
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;
|
|
110
128
|
/**
|
|
111
129
|
* Run `fn` inside a fresh flow context (queue workers, timers — async
|
|
112
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/http-capture.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface RequestSide {
|
|
|
16
16
|
omitted?: HttpExchangeBody['omitted'];
|
|
17
17
|
}
|
|
18
18
|
/** The request's headers and body, taken BEFORE the call. A stream or FormData is not read — it would be consumed. */
|
|
19
|
-
export declare function takeRequest(input: FetchInput, init: FetchInit, bodies: boolean): RequestSide;
|
|
19
|
+
export declare function takeRequest(input: FetchInput, init: FetchInit, bodies: boolean, large?: boolean): RequestSide;
|
|
20
20
|
/** A chunk as `http` passes it — a `Buffer`, or a string once the reader set an encoding — as bytes. */
|
|
21
21
|
export declare function chunkBytes(chunk: unknown, encoding?: unknown): Uint8Array | undefined;
|
|
22
22
|
/**
|
|
@@ -25,10 +25,11 @@ export declare function chunkBytes(chunk: unknown, encoding?: unknown): Uint8Arr
|
|
|
25
25
|
*/
|
|
26
26
|
export declare class WrittenBody {
|
|
27
27
|
private readonly keep;
|
|
28
|
+
private readonly large;
|
|
28
29
|
private readonly chunks;
|
|
29
30
|
private read;
|
|
30
31
|
size: number;
|
|
31
|
-
constructor(keep: boolean);
|
|
32
|
+
constructor(keep: boolean, large?: boolean);
|
|
32
33
|
take(chunk: unknown, encoding?: unknown): void;
|
|
33
34
|
/** Every byte of it was read. */
|
|
34
35
|
get whole(): boolean;
|
|
@@ -74,6 +75,7 @@ export declare class ResponseSink {
|
|
|
74
75
|
private readonly decoder;
|
|
75
76
|
constructor(contentType: string, startedAt: number, opts: {
|
|
76
77
|
ai: boolean;
|
|
78
|
+
large?: boolean;
|
|
77
79
|
paths: string[];
|
|
78
80
|
encoding?: string;
|
|
79
81
|
});
|
|
@@ -92,8 +94,9 @@ export declare class ResponseSink {
|
|
|
92
94
|
*/
|
|
93
95
|
export declare function readResponse(res: Response, clone: Response, startedAt: number, opts: {
|
|
94
96
|
ai: boolean;
|
|
97
|
+
large?: boolean;
|
|
95
98
|
paths: string[];
|
|
96
99
|
}): Promise<ResponseRead>;
|
|
97
100
|
/** The request side as kept: headers redacted, the query, the body (or why there is none). */
|
|
98
|
-
export declare function requestPart(side: RequestSide, search: string, mode: HttpCaptureMode, ai: boolean, paths: string[], redacted: string[]): Promise<HttpExchange['request']>;
|
|
101
|
+
export declare function requestPart(side: RequestSide, search: string, mode: HttpCaptureMode, ai: boolean, paths: string[], redacted: string[], large?: boolean): Promise<HttpExchange['request']>;
|
|
99
102
|
export {};
|
package/lib/http-capture.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var g=Object.defineProperty;var
|
|
2
|
-
`)}:void 0}
|
|
1
|
+
"use strict";var g=Object.defineProperty;var p=(t,e)=>g(t,"name",{value:e,configurable:!0});var l=Object.defineProperty,c=p((t,e)=>l(t,"name",{value:e,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.ResponseSink=exports.WrittenBody=exports.headerPairs=exports.MAX_STREAM_MS=void 0,exports.takeRequest=takeRequest,exports.chunkBytes=chunkBytes,exports.unpacked=unpacked,exports.writtenRequest=writtenRequest,exports.readResponse=readResponse,exports.requestPart=requestPart;const tslib_1=require("tslib"),node_crypto_1=require("node:crypto"),zlib=tslib_1.__importStar(require("node:zlib")),ai_store_1=require("./ai-store"),http_store_1=require("./http-store"),MAX_EVENTS=2e3,MAX_EVENT_DATA=4*1024,MAX_STREAM_KEPT=256*1024;exports.MAX_STREAM_MS=300*1e3;const MAX_HASH_BYTES=1024*1024,MAX_READ=http_store_1.MAX_KEPT_BYTES*32,LIMITS={standard:{read:MAX_READ,keep:http_store_1.MAX_KEPT_BYTES,events:MAX_EVENTS,eventData:MAX_EVENT_DATA,streamKept:MAX_STREAM_KEPT},large:{read:http_store_1.MAX_AI_KEPT_BYTES,keep:http_store_1.MAX_AI_KEPT_BYTES,events:2e4,eventData:1024*1024,streamKept:8*1024*1024}},limitsOf=c(t=>t?LIMITS.large:LIMITS.standard,"limitsOf"),headerPairs=c(t=>{try{if(!t)return[];if(typeof t.forEach=="function"&&typeof t.get=="function"){const e=[];return t.forEach((s,n)=>e.push([n,s])),e}return Array.isArray(t)?t.map(([e,s])=>[String(e),String(s)]):Object.entries(t).map(([e,s])=>[e,Array.isArray(s)?s.join(", "):String(s)])}catch{return[]}},"headerPairs");exports.headerPairs=headerPairs;function takeRequest(t,e,s,n=!1){const r=typeof t=="object"&&t!==null&&!(t instanceof URL)?t:void 0,a=[...(0,exports.headerPairs)(r?.headers),...(0,exports.headerPairs)(e?.headers)],h=a.find(([d])=>d.toLowerCase()==="content-type")?.[1]??"",o={headers:a,contentType:h};if(!s)return o;const i=e?.body,u=c(d=>new TextEncoder().encode(d),"enc");if(typeof i=="string")return{...o,bytes:u(i),size:Buffer.byteLength(i)};if(i instanceof URLSearchParams)return{...o,contentType:h||"application/x-www-form-urlencoded",bytes:u(i.toString())};if(i instanceof ArrayBuffer)return{...o,bytes:new Uint8Array(i),size:i.byteLength};if(ArrayBuffer.isView(i))return{...o,bytes:new Uint8Array(i.buffer,i.byteOffset,i.byteLength),size:i.byteLength};if(typeof Blob<"u"&&i instanceof Blob)return{...o,size:i.size,...i.size<=limitsOf(n).read?{pending:i.arrayBuffer().then(d=>new Uint8Array(d)).catch(()=>{})}:{}};if(typeof FormData<"u"&&i instanceof FormData)return{...o,omitted:"form-data"};if(i!=null)return{...o,omitted:"stream"};if(r&&!r.bodyUsed&&r.body)try{return{...o,pending:r.clone().arrayBuffer().then(d=>new Uint8Array(d)).catch(()=>{})}}catch{return o}return o}p(takeRequest,"takeRequest"),c(takeRequest,"takeRequest");function chunkBytes(t,e){if(t instanceof Uint8Array)return t;if(typeof t=="string")return Buffer.from(t,typeof e=="string"&&Buffer.isEncoding(e)?e:"utf8")}p(chunkBytes,"chunkBytes"),c(chunkBytes,"chunkBytes");class WrittenBody{static{p(this,"WrittenBody")}static{c(this,"WrittenBody")}constructor(e,s=!1){this.keep=e,this.large=s,this.chunks=[],this.read=0,this.size=0}take(e,s){if(e==null||typeof e=="function")return;const n=chunkBytes(e,s);n&&(this.size+=n.byteLength,this.keep&&this.read<limitsOf(this.large).read&&(this.chunks.push(n),this.read+=n.byteLength))}get whole(){return this.read>=this.size}bytes(){return Buffer.concat(this.chunks)}}exports.WrittenBody=WrittenBody;function unpacked(t,e){try{if(t==="gzip"||t==="x-gzip")return zlib.gunzipSync(e,{maxOutputLength:MAX_READ});if(t==="br")return zlib.brotliDecompressSync(e,{maxOutputLength:MAX_READ});if(t==="deflate")return zlib.inflateSync(e,{maxOutputLength:MAX_READ})}catch{return}}p(unpacked,"unpacked"),c(unpacked,"unpacked");const encodingOf=c(t=>{const e=(t??"").trim().toLowerCase();return e==="identity"?"":e},"encodingOf");function writtenRequest(t,e){const s=(0,exports.headerPairs)(t),n=c(o=>s.find(([i])=>i.toLowerCase()===o)?.[1],"header"),r={headers:s,contentType:n("content-type")??""};if(!e||!e.size)return r;const a=encodingOf(n("content-encoding"));if(!a)return{...r,bytes:e.bytes(),size:e.size};const h=e.whole?unpacked(a,e.bytes()):void 0;return h?{...r,bytes:h,size:h.length}:{...r,size:e.size,omitted:"encoded"}}p(writtenRequest,"writtenRequest"),c(writtenRequest,"writtenRequest");function sseEvent(t){let e="message";const s=[];for(const n of t.split(/\r?\n/)){if(!n||n.startsWith(":"))continue;const r=n.indexOf(":"),a=r<0?n:n.slice(0,r),h=r<0?"":n.slice(r+1).replace(/^ /,"");a==="event"?e=h||"message":a==="data"&&s.push(h)}return s.length||e!=="message"?{event:e,data:s.join(`
|
|
2
|
+
`)}:void 0}p(sseEvent,"sseEvent"),c(sseEvent,"sseEvent");class ResponseSink{static{p(this,"ResponseSink")}static{c(this,"ResponseSink")}constructor(e,s,n){this.contentType=e,this.startedAt=s,this.opts=n,this.total=0,this.kept=[],this.keptBytes=0,this.hash=(0,node_crypto_1.createHash)("sha256"),this.events=[],this.eventTotal=0,this.eventData=0,this.cut=!1,this.pendingText="",this.decoder=new TextDecoder,this.encoding=encodingOf(n.encoding),this.sse=!this.encoding&&/text\/event-stream/i.test(e),this.parsing=this.sse,this.hashing=!/json|^text\/|xml|form/i.test(e)}get size(){return this.total}take(e){const s=sseEvent(e);if(!s)return;this.eventTotal++;const n=Date.now()-this.startedAt;this.firstEventMs??=n;const r=limitsOf(this.opts.large);if(this.events.length>=r.events||this.eventData>=r.streamKept){this.cut=!0;return}const a=this.opts.ai?void 0:(0,ai_store_1.redactAiText)(s.data).slice(0,r.eventData);a&&(this.eventData+=a.length),this.events.push({atMs:n,event:s.event,bytes:Buffer.byteLength(e),...a!==void 0?{data:a}:{}})}push(e){if(this.total+=e.byteLength,this.sse){if(!this.parsing)return this.inTime();this.pendingText+=this.decoder.decode(e,{stream:!0});let s;for(;(s=this.pendingText.search(/\r?\n\r?\n/))>=0;){const n=/\r?\n\r?\n/.exec(this.pendingText.slice(s));this.take(this.pendingText.slice(0,s)),this.pendingText=this.pendingText.slice(s+n[0].length)}this.pendingText.length>limitsOf(this.opts.large).read&&(this.pendingText="",this.parsing=!1,this.cut=!0)}else this.opts.ai||(this.keptBytes<limitsOf(this.opts.large).read&&(this.kept.push(e),this.keptBytes+=e.byteLength),this.hashing&&!this.encoding&&(this.total<=MAX_HASH_BYTES?this.hash.update(e):this.hashing=!1));return this.inTime()}inTime(){return Date.now()-this.startedAt<=exports.MAX_STREAM_MS?!0:(this.cut=!0,!1)}finish(e=!1,s=Date.now()){e?this.cut=!0:this.parsing&&this.pendingText.trim()&&this.take(this.pendingText);const n=this.total,r=this.contentType.split(";")[0].trim();if(this.sse)return{endedAt:s,stream:{protocol:"sse",...this.firstEventMs!==void 0?{firstEventMs:this.firstEventMs}:{},events:this.events,bytes:n,total:this.eventTotal,...this.cut?{cut:!0}:{}}};if(this.opts.ai)return{endedAt:s,body:{contentType:r,size:n,kept:0,truncated:!1,omitted:"ai"}};const a=Buffer.concat(this.kept);if(this.encoding){const o=n<=MAX_READ?unpacked(this.encoding,a):void 0;if(!o)return{endedAt:s,body:{contentType:r.toLowerCase(),size:n,kept:0,truncated:!1,omitted:"encoded"}};const i=this.hashing&&o.length<=MAX_HASH_BYTES?(0,node_crypto_1.createHash)("sha256").update(o).digest("hex"):void 0;return{endedAt:s,body:(0,http_store_1.keepBody)(this.contentType,o,o.length,this.opts.paths,i,limitsOf(this.opts.large).keep)}}const h=this.hashing&&n<=MAX_HASH_BYTES?this.hash.digest("hex"):void 0;return{endedAt:s,body:(0,http_store_1.keepBody)(this.contentType,a,n,this.opts.paths,h,limitsOf(this.opts.large).keep)}}}exports.ResponseSink=ResponseSink;function readResponse(t,e,s,n){const r=(()=>{try{return t.headers.get("content-type")??""}catch{return""}})(),a=e.body;if(!a)return Promise.resolve({endedAt:Date.now()});const h=a.getReader(),o=new ResponseSink(r,s,n);return(async()=>{try{for(;;){const{done:i,value:u}=await h.read();if(i)break;if(!o.push(u)){await h.cancel().catch(()=>{});break}}}catch{return o.finish(!0)}return o.finish()})()}p(readResponse,"readResponse"),c(readResponse,"readResponse");async function requestPart(t,e,s,n,r,a,h=!1){const{headers:o,redacted:i}=(0,http_store_1.redactHeaders)(t.headers);a.push(...i);const u=s==="bodies"&&e.length>1?(0,http_store_1.redactQuery)(e):[];let d;if(s==="bodies"){const y=t.contentType.split(";")[0].trim().toLowerCase();if(n)d={contentType:y,size:t.size??t.bytes?.byteLength??0,kept:0,truncated:!1,omitted:"ai"};else if(t.omitted)d={contentType:y,size:t.size??0,kept:0,truncated:!1,omitted:t.omitted};else{const f=t.bytes??await t.pending;f&&f.byteLength&&(d=(0,http_store_1.keepBody)(t.contentType,f,t.size??f.byteLength,r,void 0,limitsOf(h).keep))}}return{headers:o,...u.length?{query:u}:{},...d?{body:d}:{}}}p(requestPart,"requestPart"),c(requestPart,"requestPart");
|
package/lib/http-store.d.ts
CHANGED
|
@@ -6,6 +6,12 @@ export declare function httpCaptureMode(env?: Record<string, string | undefined>
|
|
|
6
6
|
export declare const REMOVED = "\u2022\u2022\u2022\u2022 redacted";
|
|
7
7
|
/** A body keeps its first 64 KB as text. */
|
|
8
8
|
export declare const MAX_KEPT_BYTES: number;
|
|
9
|
+
/**
|
|
10
|
+
* An AI provider's call whose bodies are asked for keeps them whole, up to 4 MB each: its prompt and answer are what it
|
|
11
|
+
* is opened for, and a conversation's newest turns are at the END of its request — 64 KB kept its system prompt and cut
|
|
12
|
+
* the message being answered (FoodGuard: 729 of 1,932 requests, median 107 KB).
|
|
13
|
+
*/
|
|
14
|
+
export declare const MAX_AI_KEPT_BYTES: number;
|
|
9
15
|
export declare const newExchangeId: () => string;
|
|
10
16
|
/** Headers as kept: credential values removed (a bearer JWT shown as its claims), the names that were. */
|
|
11
17
|
export declare function redactHeaders(entries: ReadonlyArray<readonly [string, string]>): {
|
|
@@ -18,7 +24,7 @@ export declare function redactQuery(search: string): Array<[string, string]>;
|
|
|
18
24
|
* A body as kept: its text (the first 64 KB), redacted by what it is — JSON and form fields by name, any text by the
|
|
19
25
|
* secret shapes — or, binary, its size (and digest). `bytes` is what was read; `size` every byte it had.
|
|
20
26
|
*/
|
|
21
|
-
export declare function keepBody(contentType: string, bytes: Uint8Array, size: number, paths: string[], sha256?: string): HttpExchangeBody;
|
|
27
|
+
export declare function keepBody(contentType: string, bytes: Uint8Array, size: number, paths: string[], sha256?: string, keep?: number): HttpExchangeBody;
|
|
22
28
|
/**
|
|
23
29
|
* How much of `.lensmcp/http/` is kept (issues/closed/foodguard-79). It was 500 files — about a minute at FoodGuard's
|
|
24
30
|
* ten exchanges a second, while the events that point at them stay on the bus for many minutes: a quarter of the
|
|
@@ -30,7 +36,11 @@ export declare const DEFAULT_HTTP_MAX_FILES = 10000;
|
|
|
30
36
|
export declare const DEFAULT_HTTP_MAX_AGE_MS: number;
|
|
31
37
|
/** When an exchange was written — its id starts with the time (`newExchangeId`), so no file needs a stat. */
|
|
32
38
|
export declare const exchangeTime: (file: string) => number | undefined;
|
|
33
|
-
/**
|
|
34
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Oldest first, by name (= by time). AI exchanges are retained even beyond the count/age budgets: they live in `ai/`,
|
|
41
|
+
* which is never pruned; one written here before that place existed is moved there the first time a prune meets it,
|
|
42
|
+
* so it is opened once — not on every prune, for good.
|
|
43
|
+
*/
|
|
44
|
+
export declare function pruneHttpExchanges(dir: string, now?: number, aiDir?: string): number;
|
|
35
45
|
/** Persist one exchange (temp name, then rename). Best-effort: false on any failure — the call goes on regardless. */
|
|
36
46
|
export declare function writeHttpExchange(x: HttpExchange): boolean;
|
package/lib/http-store.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var _=Object.defineProperty;var u=(e,t)=>_(e,"name",{value:t,configurable:!0});var x=Object.defineProperty,a=u((e,t)=>x(e,"name",{value:t,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.exchangeTime=exports.DEFAULT_HTTP_MAX_AGE_MS=exports.DEFAULT_HTTP_MAX_FILES=exports.newExchangeId=exports.MAX_AI_KEPT_BYTES=exports.MAX_KEPT_BYTES=exports.REMOVED=void 0,exports.httpCaptureMode=httpCaptureMode,exports.redactHeaders=redactHeaders,exports.redactQuery=redactQuery,exports.keepBody=keepBody,exports.pruneHttpExchanges=pruneHttpExchanges,exports.writeHttpExchange=writeHttpExchange;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),node_crypto_1=require("node:crypto"),http_exchange_1=require("@lensmcp/core/http-exchange"),redact_1=require("@lensmcp/core/redact"),ai_store_1=require("./ai-store"),emit_1=require("./emit"),mkdirSync=nodeFs.mkdirSync,writeFileSync=nodeFs.writeFileSync,renameSync=nodeFs.renameSync,readdirSync=nodeFs.readdirSync,readFileSync=nodeFs.readFileSync,unlinkSync=nodeFs.unlinkSync;function httpCaptureMode(e=process.env){const t=(e.LENSMCP_HTTP_CAPTURE??"").trim().toLowerCase();return t==="off"||t==="0"||t==="false"||t==="none"?"off":t==="headers"?"headers":"bodies"}u(httpCaptureMode,"httpCaptureMode"),a(httpCaptureMode,"httpCaptureMode"),exports.REMOVED="\u2022\u2022\u2022\u2022 redacted",exports.MAX_KEPT_BYTES=64*1024,exports.MAX_AI_KEPT_BYTES=4*1024*1024;const MAX_PARSE_BYTES=2*1024*1024,newExchangeId=a(()=>`hx_${Date.now().toString(36)}${(0,node_crypto_1.randomBytes)(5).toString("hex")}`,"newExchangeId");exports.newExchangeId=newExchangeId;const CREDENTIAL_HEADERS=/^(authorization|proxy-authorization|cookie|set-cookie|x-api-key|api-key|openai-organization|openai-project|x-internal-token|x-amz-security-token)$|-api-key$|-token$|-secret$/i,CREDENTIAL_FIELD=/pass(word|wd)?|secret|token|api[-_]?key|authorization|credential|cookie|signature/i;function jwtClaims(e){const t=e.split(".");if(t.length===3)try{const r=JSON.parse(Buffer.from(t[1].replace(/-/g,"+").replace(/_/g,"/"),"base64").toString("utf8"));return`JWT \xB7 ${["iss","aud","sub","exp"].filter(n=>r[n]!==void 0).map(n=>`${n}=${n==="exp"&&typeof r[n]=="number"?new Date(r[n]*1e3).toISOString():String(r[n]).slice(0,80)}`).join(" \xB7 ")||"no claims"} \xB7 ${exports.REMOVED.split(" ")[0]} signature`}catch{return}}u(jwtClaims,"jwtClaims"),a(jwtClaims,"jwtClaims");function redactHeaders(e){const t=[],r=[];for(const[n,o]of e){const s=n.toLowerCase();if(CREDENTIAL_HEADERS.test(s)||(0,redact_1.isSecretParam)(s)){r.push(s);const c=/^bearer\s+(\S+)$/i.exec(o.trim()),i=c?jwtClaims(c[1]):void 0,p=/^(\w+)\s+\S/.exec(o.trim())?.[1];t.push([s,i?`Bearer ${i}`:p&&s.endsWith("authorization")?`${p} ${exports.REMOVED}`:exports.REMOVED])}else t.push([s,(0,ai_store_1.redactAiText)(o).slice(0,2048)])}return{headers:t,redacted:r}}u(redactHeaders,"redactHeaders"),a(redactHeaders,"redactHeaders");function redactQuery(e){const t=[];try{for(const[r,n]of new URLSearchParams(e))t.push([r,(0,redact_1.isSecretParam)(r)||CREDENTIAL_FIELD.test(r)?exports.REMOVED:(0,ai_store_1.redactAiText)(n).slice(0,1024)])}catch{}return t.slice(0,100)}u(redactQuery,"redactQuery"),a(redactQuery,"redactQuery");function redactJson(e,t,r,n=0){if(n>32||e===null||typeof e!="object")return typeof e=="string"?(0,ai_store_1.redactAiText)(e):e;if(Array.isArray(e))return e.map((s,c)=>redactJson(s,`${t}[${c}]`,r,n+1));const o={};for(const[s,c]of Object.entries(e)){const i=t?`${t}.${s}`:s,p=typeof c=="number"&&/tokens/i.test(s);CREDENTIAL_FIELD.test(s)&&c!==null&&typeof c!="object"&&!p?(o[s]=exports.REMOVED,r.length<50&&r.push(i)):o[s]=redactJson(c,i,r,n+1)}return o}u(redactJson,"redactJson"),a(redactJson,"redactJson");const textual=a(e=>/json|^text\/|x-www-form-urlencoded|xml|javascript|graphql|event-stream/i.test(e)||e==="","textual");function keepBody(e,t,r,n,o,s=exports.MAX_KEPT_BYTES){const c=e.split(";")[0].trim().toLowerCase();if(!textual(c))return{contentType:c||"application/octet-stream",size:r,kept:0,truncated:!1,omitted:"binary",...o?{sha256:o}:{}};const i=t.byteLength>=r&&r<=Math.max(MAX_PARSE_BYTES,s),p=Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8");let E;if(i&&/json/.test(c))try{E=JSON.stringify(redactJson(JSON.parse(p),"",n))}catch{E=(0,ai_store_1.redactAiText)(p)}else i&&/x-www-form-urlencoded/.test(c)?E=new URLSearchParams(redactQuery(p)).toString():E=(0,ai_store_1.redactAiText)(p);const d=Buffer.byteLength(E)>s?Buffer.from(E).subarray(0,s).toString("utf8"):E;return{contentType:c,size:r,kept:Buffer.byteLength(d),text:d,truncated:r>t.byteLength||d.length<E.length}}u(keepBody,"keepBody"),a(keepBody,"keepBody"),exports.DEFAULT_HTTP_MAX_FILES=1e4,exports.DEFAULT_HTTP_MAX_AGE_MS=1440*6e4;const PRUNE_EVERY_MS=3e4,maxFiles=a(()=>{const e=Number(process.env.LENSMCP_HTTP_MAX_FILES);return Number.isInteger(e)&&e>=10?e:exports.DEFAULT_HTTP_MAX_FILES},"maxFiles"),maxAgeMs=a(()=>{const e=Number(process.env.LENSMCP_HTTP_MAX_AGE_MS);return Number.isFinite(e)&&e>=6e4?e:exports.DEFAULT_HTTP_MAX_AGE_MS},"maxAgeMs"),exchangeTime=a(e=>{const t=/^hx_([0-9a-z]{8})/.exec(e),r=t?parseInt(t[1],36):NaN;return Number.isFinite(r)?r:void 0},"exchangeTime");exports.exchangeTime=exchangeTime;const lastPrune=new Map;function pruneHttpExchanges(e,t=Date.now(),r=`${e}/ai`){let n=0;try{const o=readdirSync(e).filter(i=>/^hx_[0-9a-z]+\.json$/.test(i)).sort(),s=o.length-maxFiles(),c=t-maxAgeMs();for(let i=0;i<o.length;i+=1){const p=(0,exports.exchangeTime)(o[i]);if(i>=s&&(p===void 0||p>=c))break;try{if(JSON.parse(readFileSync(`${e}/${o[i]}`,"utf8"))?.ai){mkdirSync(r,{recursive:!0,mode:448}),renameSync(`${e}/${o[i]}`,`${r}/${o[i]}`);continue}unlinkSync(`${e}/${o[i]}`),n+=1}catch{}}}catch{}return n}u(pruneHttpExchanges,"pruneHttpExchanges"),a(pruneHttpExchanges,"pruneHttpExchanges");function writeHttpExchange(e){try{const t=(0,http_exchange_1.exchangePlace)(e),r=(0,http_exchange_1.httpExchangeDir)(emit_1.EVENT_FILE,t);mkdirSync(r,{recursive:!0,mode:448});const n=(0,http_exchange_1.httpExchangeFile)(emit_1.EVENT_FILE,e.id,t);writeFileSync(`${n}.tmp`,JSON.stringify(e),{mode:384}),renameSync(`${n}.tmp`,n);const o=Date.now();return t!=="ai"&&o-(lastPrune.get(t)??0)>=PRUNE_EVERY_MS&&(lastPrune.set(t,o),pruneHttpExchanges(r,o,(0,http_exchange_1.httpExchangeDir)(emit_1.EVENT_FILE,"ai"))),!0}catch{return!1}}u(writeHttpExchange,"writeHttpExchange"),a(writeHttpExchange,"writeHttpExchange");
|
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 P=Object.defineProperty;var x=(t,e)=>P(t,"name",{value:e,configurable:!0});var H=Object.defineProperty,c=x((t,e)=>H(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"),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 r=t||"/";return!CAPTURE_QUERY||e.length<=1?clipPath(r):(0,redact_1.redactUrl)(`${r}${e}`)}x(capturePath,"capturePath"),c(capturePath,"capturePath");const fingerprintFor=c((t,e)=>e?`external:error:${t}`:`external:${t}`,"fingerprintFor"),pendingExchanges=new Set;async function flushEgressCaptureForTests(){await Promise.allSettled([...pendingExchanges])}x(flushEgressCaptureForTests,"flushEgressCaptureForTests"),c(flushEgressCaptureForTests,"flushEgressCaptureForTests");async function finishExchange(t){const e=[],r=[],d=await(0,http_capture_1.requestPart)(t.request,t.url.search,t.mode,!!t.aiOpaque,r,e),a=t.reading?await t.reading.catch(()=>{}):void 0,{headers:u,redacted:s}=(0,http_store_1.redactHeaders)(t.responseHeaders??[]);e.push(...s);const o=typeof t.status=="number"?t.status:void 0,g={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,...o!==void 0?{status:o}:{},...t.statusText?{statusText:t.statusText}:{},...t.error?{error:t.error}:{},startedAt:t.startedAt,...t.ttfbMs!==void 0?{ttfbMs:t.ttfbMs}:{},durationMs:(a?.endedAt??Date.now())-t.startedAt,capture:t.mode==="bodies"?"bodies":"headers",request:d,response:{headers:u,...a?.body?{body:a.body}:{},...a?.stream?{stream:a.stream}:{}},redacted:{headers:[...new Set(e)],bodyFields:[...r.map(i=>`request.${i}`),...t.responseFields.map(i=>`response.${i}`)]},...t.ai?{ai:{provider:t.ai}}:{}};(0,http_store_1.writeHttpExchange)(g)}x(finishExchange,"finishExchange"),c(finishExchange,"finishExchange");function trackExchange(t){const e=(typeof t=="function"?new Promise(r=>setImmediate(r)).then(()=>finishExchange(t())):finishExchange(t)).catch(()=>{});pendingExchanges.add(e),e.finally(()=>pendingExchanges.delete(e))}x(trackExchange,"trackExchange"),c(trackExchange,"trackExchange");function tapEgress(){tapFetch(),tapHttpModule("http"),tapHttpModule("https")}x(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}x(resetEgressTapForTests,"resetEgressTapForTests"),c(resetEgressTapForTests,"resetEgressTapForTests");function restoreHttp(t){const e=httpOriginals.get(t);if(!e)return;const r=require(`node:${t}`);r.request=e.request,r.get=e.get,httpOriginals.delete(t)}x(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 r=(0,http_store_1.httpCaptureMode)(),d=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS);t.fetch=(async(a,u)=>{let s="",o="/",g=!1,i;try{const h=new URL(typeof a=="string"?a:a instanceof URL?a.href:a.url);i=h,s=h.hostname,g=h.search.length>1,o=capturePath(h.pathname,h.search)}catch{}if(isInternal(s))return e(a,u);const l=Date.now(),f=(0,flow_context_1.flowContext)(),n=(0,flow_context_1.liveFlowContext)();n&&(n.externalCallCount=(n.externalCallCount??0)+1);const w=typeof a=="object"&&a!==null&&!(a instanceof URL)?a.method:void 0,p=String(u?.method??w??"GET").toUpperCase(),E=r!=="off"&&i?(0,http_store_1.newExchangeId)():void 0,b=E?(0,http_capture_1.takeRequest)(a,u,r==="bodies"):void 0,q=E&&i&&p==="POST"?(0,ai_providers_1.matchTarget)(s,i.port,i.pathname,d)?.provider:void 0,T=!!q&&(0,tap_ai_1.aiCaptureMode)()!=="content"&&!(0,capture_file_1.aiBodiesAsked)(),_=c(h=>({id:E,mode:r,method:p,url:i,path:i.pathname.length>256?`${i.pathname.slice(0,256)}\u2026`:i.pathname,startedAt:l,ctx:f,request:b,...q?{ai:q}:{},...T?{aiOpaque:T}:{},responseFields:[],...h}),"job");try{const h=await e(a,u),$=Date.now()-l,C=h.status>=500,m=[];let y;if(E&&r==="bodies")try{y=(0,http_capture_1.readResponse)(h,h.clone(),l,{ai:T,paths:m})}catch{y=void 0}const v=(0,emit_1.lensEmit)("external","info",`egress ${p} ${s} \u2192 ${h.status}`,{name:s,kind:"egress",method:p,host:s,path:o,status:h.status,...g?{hasQuery:!0}:{},startedAt:l,durationMs:$,...E?{exchangeId:E}:{}},fingerprintFor(s,C),f);if(E&&v){const A={status:h.status,...h.statusText?{statusText:h.statusText}:{},responseHeaders:(0,http_capture_1.headerPairs)(h.headers)};trackExchange(_({...A,ttfbMs:$,responseFields:m,...y?{reading:y}:{}}))}return h}catch(h){const $=String(h?.message??h),C=(0,emit_1.lensEmit)("external","error",`egress ${p} ${s} failed`,{name:s,kind:"egress",method:p,host:s,path:o,...g?{hasQuery:!0}:{},error:$,startedAt:l,durationMs:Date.now()-l,...E?{exchangeId:E}:{}},fingerprintFor(s,!0),f);throw E&&C&&trackExchange(_({error:$})),h}})}x(tapFetch,"tapFetch"),c(tapFetch,"tapFetch");function tapHttpModule(t){const e=require(`node:${t}`),r=e.request;if(typeof r!="function"||r.__lensmcp)return;httpOriginals.set(t,{request:r,get:e.get});const d=(0,http_store_1.httpCaptureMode)(),a=(0,ai_providers_1.parseExtraHosts)(process.env.LENSMCP_AI_HOSTS),u=c(function(...s){const o=r.apply(this,s);try{observe(t,s,o,d,a)}catch{}return o},"wrapped");u.__lensmcp=!0,e.request=u,typeof e.get=="function"&&(e.get=function(...s){const o=u.apply(this,s);return o.end(),o})}x(tapHttpModule,"tapHttpModule"),c(tapHttpModule,"tapHttpModule");const headerOf=c((t,e)=>{const r=t?.[e];return Array.isArray(r)?r.join(", "):r??""},"headerOf");function teeRequest(t,e,r){const d=e?new http_capture_1.WrittenBody(!r):void 0,a=c(()=>{try{return t.getHeaders?.()}catch{return}},"sentHeaders");let u,s=!1;const o=t.write;d&&typeof o=="function"&&(t.write=function(i,...l){try{s||d.take(i,l[0])}catch{}return o.call(this,i,...l)});const g=t.end;return t.end=function(i,...l){try{s||(s=!0,d?.take(i,l[0]),u=a())}catch{}return g.call(this,i,...l)},()=>(0,http_capture_1.writtenRequest)(u??a(),d)}x(teeRequest,"teeRequest"),c(teeRequest,"teeRequest");function teeResponse(t,e,r){let d=!1;const a=c(o=>{if(d)return;d=!0,clearTimeout(u);const g=Date.now();r(()=>e.size?e.finish(o,g):{endedAt:g})},"finish"),u=setTimeout(()=>a(!0),http_capture_1.MAX_STREAM_MS);u.unref?.();const s=t.emit;t.emit=function(o,...g){try{return s.call(this,o,...g)}finally{try{if(o==="data"){const i=d?void 0:(0,http_capture_1.chunkBytes)(g[0],t.readableEncoding);i&&!e.push(i)&&a(!1)}else o==="end"?a(!1):(o==="aborted"||o==="error"||o==="close")&&a(!0)}catch{}}}}x(teeResponse,"teeResponse"),c(teeResponse,"teeResponse");function observe(t,e,r,d,a){const u=parseTarget(e,t),{host:s,method:o,path:g,hasQuery:i}=u;if(isInternal(s))return;const l=Date.now(),f=(0,flow_context_1.flowContext)(),n=(0,flow_context_1.liveFlowContext)();n&&(n.externalCallCount=(n.externalCallCount??0)+1);const w=d!=="off"?targetUrl(u):void 0,p=w?(0,http_store_1.newExchangeId)():void 0,E=p&&w&&o==="POST"?(0,ai_providers_1.matchTarget)(s,w.port,w.pathname,a)?.provider:void 0,b=!!E&&(0,tap_ai_1.aiCaptureMode)()!=="content"&&!(0,capture_file_1.aiBodiesAsked)(),q=p?teeRequest(r,d==="bodies",b):void 0,T=c(m=>({id:p,mode:d,method:o,url:w,path:clipPath(w.pathname),startedAt:l,ctx:f,request:q(),...E?{ai:E}:{},...b?{aiOpaque:b}:{},responseFields:[],...m}),"job");let _=!1;const h=c(m=>{if(_)return;_=!0;const y=m?.statusCode,v=Date.now()-l,A=(0,emit_1.lensEmit)("external","info",`egress ${o} ${s} \u2192 ${y??"?"}`,{name:s,kind:"egress",method:o,host:s,path:g,...y!=null?{status:y}:{},...i?{hasQuery:!0}:{},startedAt:l,durationMs:v,...p?{exchangeId:p}:{}},fingerprintFor(s,y!=null&&y>=500),f);if(!p||!A||!m||typeof m.emit!="function")return;const M={...y!=null?{status:y}:{},...m.statusMessage?{statusText:m.statusMessage}:{},responseHeaders:(0,http_capture_1.headerPairs)(m.headers),ttfbMs:v};if(d!=="bodies"){trackExchange(()=>T(M));return}const F=[],R=new http_capture_1.ResponseSink(headerOf(m.headers,"content-type"),l,{ai:b,paths:F,encoding:headerOf(m.headers,"content-encoding")});teeResponse(m,R,S=>trackExchange(()=>T({...M,responseFields:F,reading:Promise.resolve(S())})))},"onResponse"),$=c(m=>{if(_)return;_=!0;const y=String(m?.message??m),v=(0,emit_1.lensEmit)("external","error",`egress ${o} ${s} failed`,{name:s,kind:"egress",method:o,host:s,path:g,...i?{hasQuery:!0}:{},error:y,startedAt:l,durationMs:Date.now()-l,...p?{exchangeId:p}:{}},fingerprintFor(s,!0),f);p&&v&&trackExchange(()=>T({error:y}))},"onError"),C=r.emit;r.emit=function(m,...y){try{m==="response"?h(y[0]):m==="error"&&$(y[0])}catch{}return C.call(this,m,...y)}}x(observe,"observe"),c(observe,"observe");function targetUrl(t){try{const e=t.host.includes(":")&&!t.host.startsWith("[")?`[${t.host}]`:t.host,r=t.pathname.startsWith("/")?t.pathname:`/${t.pathname}`;return new URL(`${t.protocol}//${e}${t.port?`:${t.port}`:""}${r}${t.search}`)}catch{return}}x(targetUrl,"targetUrl"),c(targetUrl,"targetUrl");function parseTarget(t,e="http"){let r="",d="GET",a="/",u=!1,s=`${e}:`,o="",g="/",i="";const l=c((f,n)=>{u=n.length>1,a=capturePath(f,n),g=f||"/",i=n},"take");for(const f of t)if(typeof f=="string")try{const n=new URL(f);r=n.hostname,s=n.protocol,o=n.port,l(n.pathname,n.search)}catch{}else if(f instanceof URL)r=f.hostname,s=f.protocol,o=f.port,l(f.pathname,f.search);else if(f&&typeof f=="object"){const n=f;if(typeof n.method=="string"&&(d=n.method.toUpperCase()),typeof n.socketPath=="string")return{host:"",method:d,path:a,hasQuery:u,protocol:s,port:o,pathname:g,search:i};if(typeof n.path=="string"){const w=n.path,p=w.indexOf("?");l(p===-1?w:w.slice(0,p),p===-1?"":w.slice(p))}if(typeof n.protocol=="string"&&n.protocol&&(s=n.protocol),(typeof n.port=="number"||typeof n.port=="string"&&n.port)&&(o=String(n.port)),typeof n.hostname=="string"&&n.hostname)r=n.hostname;else if(typeof n.host=="string"&&n.host){const[w,p]=n.host.split(":");r=w??"",p&&!(typeof n.port=="number"||typeof n.port=="string")&&(o=p)}}return{host:r,method:d,path:a,hasQuery:u,protocol:s,port:o,pathname:g,search:i}}x(parseTarget,"parseTarget"),c(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/lib/tap-ingress.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var A=Object.defineProperty;var c=(r,e)=>A(r,"name",{value:e,configurable:!0});var x=Object.defineProperty,a=c((r,e)=>x(r,"name",{value:e,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EXCHANGE_ID=void 0,exports.tapIngress=tapIngress,exports.resetIngressTapForTests=resetIngressTapForTests;const tslib_1=require("tslib"),http=tslib_1.__importStar(require("node:http")),zlib=tslib_1.__importStar(require("node:zlib")),headless_flow_1=require("@lensmcp/core/headless-flow"),http_exchange_1=require("@lensmcp/core/http-exchange"),emit_1=require("./emit"),http_store_1=require("./http-store");exports.EXCHANGE_ID=Symbol.for("lensmcp.exchangeId");const MAX_READ=2*1024*1024,MAX_PATH_CHARS=256;let originalEmit;function tapIngress(){if(originalEmit)return;const r=(0,http_store_1.httpCaptureMode)();if(r==="off")return;const e=http.Server.prototype,s=e.emit;originalEmit=s,e.emit=function(t,...i){if(t==="request")try{watch(i[0],i[1],r)}catch{}return s.call(this,t,...i)}}c(tapIngress,"tapIngress"),a(tapIngress,"tapIngress");function resetIngressTapForTests(){originalEmit&&(http.Server.prototype.emit=originalEmit),originalEmit=void 0}c(resetIngressTapForTests,"resetIngressTapForTests"),a(resetIngressTapForTests,"resetIngressTapForTests");const one=a(r=>r===void 0?void 0:Array.isArray(r)?r.join(", "):String(r),"one"),pairs=a(r=>Object.entries(r).flatMap(([e,s])=>s===void 0?[]:[[e,one(s)]]),"pairs");class Tee{static{c(this,"Tee")}static{a(this,"Tee")}constructor(){this.chunks=[],this.read=0,this.size=0}take(e,s){if(e==null||typeof e=="function")return;const t=Buffer.isBuffer(e)?e:e instanceof Uint8Array?Buffer.from(e):typeof e=="string"?Buffer.from(e,typeof s=="string"?s:"utf8"):void 0;t&&(this.size+=t.length,this.read<MAX_READ&&(this.chunks.push(t),this.read+=t.length))}bytes(){return Buffer.concat(this.chunks)}}function watch(r,e,s){if(!r||!e||r[exports.EXCHANGE_ID])return;const t=r.url??"/",i=t.indexOf("?"),l=i<0?t:t.slice(0,i);if((0,headless_flow_1.isProbePath)(l))return;const h=(0,http_store_1.newExchangeId)();r[exports.EXCHANGE_ID]=h;const y=Date.now(),f=s==="bodies",g=new Tee,d=new Tee;if(f){const m=r.emit;r.emit=function(o,...n){return o==="data"&&g.take(n[0]),m.call(this,o,...n)};const w=e.write,b=e.end;e.write=function(o,...n){return d.take(o,n[0]),w.call(this,o,...n)},e.end=function(o,...n){return d.take(o,n[0]),b.call(this,o,...n)}}let u=!1;const p=a(()=>{if(!u){u=!0;try{(0,http_store_1.writeHttpExchange)(exchangeOf(h,r,e,{url:t,q:i,path:l,startedAt:y,bodies:f,sent:g,answered:d}))}catch{}}},"finish");e.once("finish",p),e.once("close",p)}c(watch,"watch"),a(watch,"watch");function unpacked(r,e){try{if(r==="gzip"||r==="x-gzip")return zlib.gunzipSync(e,{maxOutputLength:MAX_READ});if(r==="br")return zlib.brotliDecompressSync(e,{maxOutputLength:MAX_READ});if(r==="deflate")return zlib.inflateSync(e,{maxOutputLength:MAX_READ})}catch{return}}c(unpacked,"unpacked"),a(unpacked,"unpacked");function exchangeOf(r,e,s,t){const i=[],l=[],h=[],y=(0,http_store_1.redactHeaders)(pairs(e.headers));i.push(...y.redacted);const f=(0,http_store_1.redactHeaders)(pairs(s.getHeaders()));i.push(...f.redacted);const g=t.bodies&&t.q>=0?(0,http_store_1.redactQuery)(t.url.slice(t.q)):[];let d;t.bodies&&t.sent.size&&(d=(0,http_store_1.keepBody)(one(e.headers["content-type"])??"",t.sent.bytes(),t.sent.size,l));let u;if(t.bodies&&t.answered.size){const n=one(s.getHeader("content-type"))??"",_=(one(s.getHeader("content-encoding"))??"").trim().toLowerCase();if(!_||_==="identity")u=(0,http_store_1.keepBody)(n,t.answered.bytes(),t.answered.size,h);else{const E=t.answered.size<=MAX_READ?unpacked(_,t.answered.bytes()):void 0;u=E?(0,http_store_1.keepBody)(n,E,E.length,h):{contentType:n.split(";")[0].trim().toLowerCase(),size:t.answered.size,kept:0,truncated:!1,omitted:"encoded"}}}const p=one(e.headers.host)??"localhost",m=t.path.length>MAX_PATH_CHARS?`${t.path.slice(0,MAX_PATH_CHARS)}\u2026`:t.path,w=one(e.headers["x-lensmcp-flow-id"]),b=one(e.headers[http_exchange_1.LENS_REQUEST_ID_HEADER])||one(e.headers["x-request-id"]),o=!s.writableFinished;return{schemaVersion:1,id:r,capturedAt:Date.now(),side:"ingress",service:emit_1.PROJECT,...w?{flowId:w}:{},...b?{requestId:b}:{},method:(e.method??"GET").toUpperCase(),url:`http://${p}${m}`,host:p.split(":")[0]??p,path:m,status:s.statusCode,...s.statusMessage?{statusText:s.statusMessage}:{},...o?{error:"the client went away before the answer ended"}:{},startedAt:t.startedAt,durationMs:Date.now()-t.startedAt,capture:t.bodies?"bodies":"headers",request:{headers:y.headers,...g.length?{query:g}:{},...d?{body:d}:{}},response:{headers:f.headers,...u?{body:u}:{}},redacted:{headers:[...new Set(i)],bodyFields:[...l.map(n=>`request.${n}`),...h.map(n=>`response.${n}`)]}}}c(exchangeOf,"exchangeOf"),a(exchangeOf,"exchangeOf");
|
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": {
|