@lensmcp/node-instrumentation 1.25.23 → 1.25.25

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/ai-store.d.ts CHANGED
@@ -9,12 +9,11 @@ export declare function fitAiContent(src: AiCallContent): {
9
9
  json: string;
10
10
  content: AiCallContent;
11
11
  };
12
- /** Delete one call's file — used when its event was dropped, so nothing is left pointing nowhere. */
13
- export declare function removeAiContent(id: string): void;
14
12
  /**
15
13
  * Persist one call. Best-effort: returns `undefined` on any failure so the tap can emit the event
16
14
  * without a `contentRef` rather than lose the metadata. Written to a temp name and renamed, so a
17
- * reader never sees half a file.
15
+ * reader never sees half a file. Captured AI records are retained without automatic pruning,
16
+ * independently of the event bus. The former LENSMCP_AI_MAX_FILES limit is intentionally ignored.
18
17
  */
19
18
  export declare function writeAiContent(src: AiCallContent): {
20
19
  id: string;
package/lib/ai-store.js CHANGED
@@ -1 +1 @@
1
- "use strict";var g=Object.defineProperty;var l=(e,r)=>g(e,"name",{value:r,configurable:!0});var m=Object.defineProperty,i=l((e,r)=>m(e,"name",{value:r,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactAiText=void 0,exports.newAiCallId=newAiCallId,exports.fitAiContent=fitAiContent,exports.removeAiContent=removeAiContent,exports.writeAiContent=writeAiContent;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),ai_content_1=require("@lensmcp/core/ai-content"),redact_1=require("@lensmcp/core/redact"),emit_1=require("./emit"),mkdirSync=nodeFs.mkdirSync,writeFileSync=nodeFs.writeFileSync,renameSync=nodeFs.renameSync,readdirSync=nodeFs.readdirSync,unlinkSync=nodeFs.unlinkSync;exports.redactAiText=redact_1.redactSecretText;const intEnv=i((e,r,o,t)=>{const s=Number(process.env[e]);return Number.isFinite(s)&&s>=o&&s<=t?Math.floor(s):r},"intEnv"),maxFieldChars=i(()=>intEnv("LENSMCP_AI_MAX_CHARS",32*1024,256,4*1024*1024),"maxFieldChars"),maxFileBytes=i(()=>intEnv("LENSMCP_AI_MAX_BYTES",256*1024,4*1024,16*1024*1024),"maxFileBytes"),maxFiles=i(()=>intEnv("LENSMCP_AI_MAX_FILES",200,1,1e4),"maxFiles");function newAiCallId(){const e=Date.now().toString(36).padStart(9,"0"),r=Math.random().toString(36).slice(2,8).padEnd(6,"0");return`ai_${e}${r}`}l(newAiCallId,"newAiCallId"),i(newAiCallId,"newAiCallId");const PRE_CLIP_FACTOR=4,MAX_MESSAGES=400;function sanitize(e,r){const o={cut:e.truncated===!0},t=i((n,u=r)=>{if(n===void 0)return;const a=u*PRE_CLIP_FACTOR;let d=n;return d.length>a&&(o.cut=!0,d=d.slice(0,a)),(0,exports.redactAiText)(d)},"clean"),s={...e,request:{...e.request,...e.request.system!==void 0?{system:t(e.request.system)}:{},messages:e.request.messages.slice(0,MAX_MESSAGES*4).map(n=>({role:n.role,content:t(n.content)})),...e.request.tools?{tools:e.request.tools.map(n=>({name:t(n.name,200),...n.description?{description:t(n.description,512)}:{}}))}:{}},...e.response?{response:{...e.response,...e.response.completion!==void 0?{completion:t(e.response.completion)}:{},...e.response.reasoning!==void 0?{reasoning:t(e.response.reasoning)}:{},...e.response.error!==void 0?{error:t(e.response.error,2048)}:{},...e.response.toolCalls?{toolCalls:e.response.toolCalls.map(n=>({...n.id?{id:n.id}:{},name:t(n.name,200),...n.arguments!==void 0?{arguments:t(n.arguments,8192)}:{}}))}:{}}}:{}};return e.request.messages.length>MAX_MESSAGES*4&&(o.cut=!0),o.cut&&(s.truncated=!0),s}l(sanitize,"sanitize"),i(sanitize,"sanitize");function clipTo(e,r,o){const t={cut:e.truncated===!0},s=i((c,p=r)=>{if(c!==void 0)return c.length>p&&(t.cut=!0),(0,redact_1.clip)(c,p)},"c"),n=e.request.messages;let u=n;if(n.length>o){const c=Math.min(2,o);u=[...n.slice(0,c),...n.slice(n.length-(o-c))],t.cut=!0}const a=e.response,d={...e,request:{...e.request,...e.request.system!==void 0?{system:s(e.request.system)}:{},messages:u.map(c=>({role:c.role,content:s(c.content)}))},...a?{response:{...a,...a.completion!==void 0?{completion:s(a.completion)}:{},...a.reasoning!==void 0?{reasoning:s(a.reasoning)}:{},...a.toolCalls?{toolCalls:a.toolCalls.map(c=>({...c,...c.arguments!==void 0?{arguments:s(c.arguments,Math.min(r,8192))}:{}}))}:{}}}:{}};return t.cut?d.truncated=!0:delete d.truncated,d}l(clipTo,"clipTo"),i(clipTo,"clipTo");function fitAiContent(e){const r=maxFileBytes();let o=maxFieldChars(),t=MAX_MESSAGES;const s=sanitize(e,o);let n=clipTo(s,o,t),u=JSON.stringify(n);for(;u.length>r&&(o>256||t>2);)o>256?o=Math.max(256,Math.floor(o/2)):t=Math.max(2,Math.floor(t/2)),n=clipTo(s,o,t),u=JSON.stringify(n);return{json:u,content:n}}l(fitAiContent,"fitAiContent"),i(fitAiContent,"fitAiContent");let writes=0;function prune(e){try{const r=readdirSync(e).filter(t=>/^ai_[0-9a-z]+\.json$/.test(t)).sort(),o=r.length-maxFiles();for(let t=0;t<o;t+=1)try{unlinkSync(`${e}/${r[t]}`)}catch{}}catch{}}l(prune,"prune"),i(prune,"prune");function removeAiContent(e){try{unlinkSync((0,ai_content_1.aiContentFile)(emit_1.EVENT_FILE,e))}catch{}}l(removeAiContent,"removeAiContent"),i(removeAiContent,"removeAiContent");function writeAiContent(e){try{const r=(0,ai_content_1.aiContentDir)(emit_1.EVENT_FILE);mkdirSync(r,{recursive:!0,mode:448});const{json:o,content:t}=fitAiContent(e),s=`${r}/${e.id}.json`;return writeFileSync(`${s}.tmp`,o,{mode:384}),renameSync(`${s}.tmp`,s),writes+=1,(writes===1||writes%20===0)&&prune(r),{id:e.id,truncated:t.truncated===!0}}catch{return}}l(writeAiContent,"writeAiContent"),i(writeAiContent,"writeAiContent");
1
+ "use strict";var g=Object.defineProperty;var d=(e,s)=>g(e,"name",{value:s,configurable:!0});var m=Object.defineProperty,c=d((e,s)=>m(e,"name",{value:s,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactAiText=void 0,exports.newAiCallId=newAiCallId,exports.fitAiContent=fitAiContent,exports.writeAiContent=writeAiContent;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),ai_content_1=require("@lensmcp/core/ai-content"),redact_1=require("@lensmcp/core/redact"),emit_1=require("./emit"),mkdirSync=nodeFs.mkdirSync,writeFileSync=nodeFs.writeFileSync,renameSync=nodeFs.renameSync;exports.redactAiText=redact_1.redactSecretText;const intEnv=c((e,s,o,n)=>{const r=Number(process.env[e]);return Number.isFinite(r)&&r>=o&&r<=n?Math.floor(r):s},"intEnv"),maxFieldChars=c(()=>intEnv("LENSMCP_AI_MAX_CHARS",32*1024,256,4*1024*1024),"maxFieldChars"),maxFileBytes=c(()=>intEnv("LENSMCP_AI_MAX_BYTES",256*1024,4*1024,16*1024*1024),"maxFileBytes");function newAiCallId(){const e=Date.now().toString(36).padStart(9,"0"),s=Math.random().toString(36).slice(2,8).padEnd(6,"0");return`ai_${e}${s}`}d(newAiCallId,"newAiCallId"),c(newAiCallId,"newAiCallId");const PRE_CLIP_FACTOR=4,MAX_MESSAGES=400;function sanitize(e,s){const o={cut:e.truncated===!0},n=c((t,l=s)=>{if(t===void 0)return;const a=l*PRE_CLIP_FACTOR;let u=t;return u.length>a&&(o.cut=!0,u=u.slice(0,a)),(0,exports.redactAiText)(u)},"clean"),r={...e,request:{...e.request,...e.request.system!==void 0?{system:n(e.request.system)}:{},messages:e.request.messages.slice(0,MAX_MESSAGES*4).map(t=>({role:t.role,content:n(t.content)})),...e.request.tools?{tools:e.request.tools.map(t=>({name:n(t.name,200),...t.description?{description:n(t.description,512)}:{}}))}:{}},...e.response?{response:{...e.response,...e.response.completion!==void 0?{completion:n(e.response.completion)}:{},...e.response.reasoning!==void 0?{reasoning:n(e.response.reasoning)}:{},...e.response.error!==void 0?{error:n(e.response.error,2048)}:{},...e.response.toolCalls?{toolCalls:e.response.toolCalls.map(t=>({...t.id?{id:t.id}:{},name:n(t.name,200),...t.arguments!==void 0?{arguments:n(t.arguments,8192)}:{}}))}:{}}}:{}};return e.request.messages.length>MAX_MESSAGES*4&&(o.cut=!0),o.cut&&(r.truncated=!0),r}d(sanitize,"sanitize"),c(sanitize,"sanitize");function clipTo(e,s,o){const n={cut:e.truncated===!0},r=c((i,p=s)=>{if(i!==void 0)return i.length>p&&(n.cut=!0),(0,redact_1.clip)(i,p)},"c"),t=e.request.messages;let l=t;if(t.length>o){const i=Math.min(2,o);l=[...t.slice(0,i),...t.slice(t.length-(o-i))],n.cut=!0}const a=e.response,u={...e,request:{...e.request,...e.request.system!==void 0?{system:r(e.request.system)}:{},messages:l.map(i=>({role:i.role,content:r(i.content)}))},...a?{response:{...a,...a.completion!==void 0?{completion:r(a.completion)}:{},...a.reasoning!==void 0?{reasoning:r(a.reasoning)}:{},...a.toolCalls?{toolCalls:a.toolCalls.map(i=>({...i,...i.arguments!==void 0?{arguments:r(i.arguments,Math.min(s,8192))}:{}}))}:{}}}:{}};return n.cut?u.truncated=!0:delete u.truncated,u}d(clipTo,"clipTo"),c(clipTo,"clipTo");function fitAiContent(e){const s=maxFileBytes();let o=maxFieldChars(),n=MAX_MESSAGES;const r=sanitize(e,o);let t=clipTo(r,o,n),l=JSON.stringify(t);for(;l.length>s&&(o>256||n>2);)o>256?o=Math.max(256,Math.floor(o/2)):n=Math.max(2,Math.floor(n/2)),t=clipTo(r,o,n),l=JSON.stringify(t);return{json:l,content:t}}d(fitAiContent,"fitAiContent"),c(fitAiContent,"fitAiContent");function writeAiContent(e){try{const s=(0,ai_content_1.aiContentDir)(emit_1.EVENT_FILE);mkdirSync(s,{recursive:!0,mode:448});const{json:o,content:n}=fitAiContent(e),r=`${s}/${e.id}.json`;return writeFileSync(`${r}.tmp`,o,{mode:384}),renameSync(`${r}.tmp`,r),{id:e.id,truncated:n.truncated===!0}}catch{return}}d(writeAiContent,"writeAiContent"),c(writeAiContent,"writeAiContent");
package/lib/emit.d.ts CHANGED
@@ -46,9 +46,8 @@ export interface FlowCtx {
46
46
  originType?: string;
47
47
  }
48
48
  /**
49
- * Returns whether the event was actually written. Almost every caller ignores it; a producer that
50
- * wrote something ELSE to go with the event (the AI tap's sidecar file) needs to know when the flood
51
- * guard dropped the event, or it leaves that file behind with nothing pointing at it.
49
+ * Returns whether the event was actually written. Captured AI records are retained independently,
50
+ * including when their metadata event is suppressed or the event bus is unavailable.
52
51
  */
53
52
  export declare function lensEmit(source: string, severity: LensSeverity, title: string, raw: Record<string, unknown>, fingerprint?: string, flowCtx?: FlowCtx,
54
53
  /** Event category. Defaults to `cluster` — what every tap emitted before this existed. */
package/lib/emit.js CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";var m=Object.defineProperty;var p=(r,s)=>m(r,"name",{value:s,configurable:!0});var a=Object.defineProperty,i=p((r,s)=>a(r,"name",{value:s,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EVENT_FILE=exports.PROJECT=exports.EVENT_MAX_FILES=exports.EVENT_MAX_BYTES=void 0,exports.maybeRotateEventFile=maybeRotateEventFile,exports.isLensWriting=isLensWriting,exports.lensEmit=lensEmit,exports.lensEmitOutcome=lensEmitOutcome,exports.lensLog=lensLog;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),node_path_1=require("node:path"),bus_rotation_1=require("@lensmcp/core/bus-rotation"),flood_guard_1=require("./flood-guard"),flow_context_1=require("./flow-context"),appendFileSync=nodeFs.appendFileSync,mkdirSync=nodeFs.mkdirSync,statSync=nodeFs.statSync,renameSync=nodeFs.renameSync,unlinkSync=nodeFs.unlinkSync,openSync=nodeFs.openSync,closeSync=nodeFs.closeSync,BUS_CAPS=(0,bus_rotation_1.busCapsFromEnv)();exports.EVENT_MAX_BYTES=BUS_CAPS.maxBytes,exports.EVENT_MAX_FILES=BUS_CAPS.maxFiles;const rotators=new Map,BUS_FS={statSync,renameSync,unlinkSync,openSync,closeSync};function maybeRotateEventFile(r,s,e){if(e!==void 0){const n=e.maxBytes??exports.EVENT_MAX_BYTES,t=e.maxFiles??exports.EVENT_MAX_FILES;(0,bus_rotation_1.createBusRotator)(r,{maxBytes:n,maxFiles:t,fs:BUS_FS,checkEveryBytes:0})(s);return}let o=rotators.get(r);o||(o=(0,bus_rotation_1.createBusRotator)(r,{maxBytes:exports.EVENT_MAX_BYTES,maxFiles:exports.EVENT_MAX_FILES,fs:BUS_FS}),rotators.set(r,o)),o(s)}p(maybeRotateEventFile,"maybeRotateEventFile"),i(maybeRotateEventFile,"maybeRotateEventFile"),exports.PROJECT=process.env.LENSMCP_PROJECT||process.env.SERVICE_NAME||"app",exports.EVENT_FILE=process.env.LENSMCP_EVENT_FILE||`${process.cwd()}/.lensmcp/events.jsonl`;let writing=!1,dirReady=!1;const emitGuard=(0,flood_guard_1.createEventFloodGuard)({burst:30,perSec:5}),logGuard=(0,flood_guard_1.createEventFloodGuard)({burst:120,perSec:20});function isLensWriting(){return writing}p(isLensWriting,"isLensWriting"),i(isLensWriting,"isLensWriting");function lensEmit(r,s,e,o,n,t,c="cluster"){return lensEmitOutcome(r,s,e,o,n,t,c)==="written"}p(lensEmit,"lensEmit"),i(lensEmit,"lensEmit");function lensEmitOutcome(r,s,e,o,n,t,c="cluster",l=emitGuard){try{writing=!0;const E=n??`${r}:${String(o.name??r)}`,d=l.admit(E);if(!d.ok)return"suppressed";dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const u=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:r,category:c,severity:s,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{},...t?.originNodeId?{originNodeId:t.originNodeId}:{},...t?.originType?{originType:t.originType}:{}},fingerprint:E,title:e,raw:{project:exports.PROJECT,...o,...d.suppressed>0?{floodSuppressed:d.suppressed}:{}}})+`
2
- `;return maybeRotateEventFile(exports.EVENT_FILE,u.length),appendFileSync(exports.EVENT_FILE,u),"written"}catch{return"failed"}finally{writing=!1}}p(lensEmitOutcome,"lensEmitOutcome"),i(lensEmitOutcome,"lensEmitOutcome");function lensLog(r,s,e){try{writing=!0;const o=e?.bucket!==void 0?`log:${exports.PROJECT}:${e.bucket}`:r==="error"||r==="fatal"?`log:error:${exports.PROJECT}`:`log:${exports.PROJECT}`,n=e?.bucket!==void 0?{ok:!0,suppressed:e.suppressed??0}:logGuard.admit(o);if(!n.ok)return!1;dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const t=(0,flow_context_1.flowContext)(),c=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:"nestjs",category:"runtime",severity:r,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{}},fingerprint:o,title:s,raw:{project:exports.PROJECT,kind:"log",...e?.stack?{request:{error:{message:s,stack:e.stack}}}:{},...e?.text?{text:e.text}:{},...e?.continues?{continues:!0}:{},...e?.stream?{stream:e.stream}:{},...e?.level?{level:e.level}:{},...e?.logger?{logger:e.logger}:{},...n.suppressed>0?{floodSuppressed:n.suppressed}:{}}})+`
3
- `;return maybeRotateEventFile(exports.EVENT_FILE,c.length),appendFileSync(exports.EVENT_FILE,c),!0}catch{return!1}finally{writing=!1}}p(lensLog,"lensLog"),i(lensLog,"lensLog");
1
+ "use strict";var m=Object.defineProperty;var i=(r,s)=>m(r,"name",{value:s,configurable:!0});var a=Object.defineProperty,c=i((r,s)=>a(r,"name",{value:s,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EVENT_FILE=exports.PROJECT=exports.EVENT_MAX_FILES=exports.EVENT_MAX_BYTES=void 0,exports.maybeRotateEventFile=maybeRotateEventFile,exports.isLensWriting=isLensWriting,exports.lensEmit=lensEmit,exports.lensEmitOutcome=lensEmitOutcome,exports.lensLog=lensLog;const tslib_1=require("tslib"),nodeFs=tslib_1.__importStar(require("node:fs")),node_path_1=require("node:path"),bus_rotation_1=require("@lensmcp/core/bus-rotation"),flood_guard_1=require("./flood-guard"),flow_context_1=require("./flow-context"),loop_identity_1=require("./loop-identity"),appendFileSync=nodeFs.appendFileSync,mkdirSync=nodeFs.mkdirSync,statSync=nodeFs.statSync,renameSync=nodeFs.renameSync,unlinkSync=nodeFs.unlinkSync,openSync=nodeFs.openSync,closeSync=nodeFs.closeSync,BUS_CAPS=(0,bus_rotation_1.busCapsFromEnv)();exports.EVENT_MAX_BYTES=BUS_CAPS.maxBytes,exports.EVENT_MAX_FILES=BUS_CAPS.maxFiles;const rotators=new Map,BUS_FS={statSync,renameSync,unlinkSync,openSync,closeSync};function maybeRotateEventFile(r,s,e){if(e!==void 0){const n=e.maxBytes??exports.EVENT_MAX_BYTES,t=e.maxFiles??exports.EVENT_MAX_FILES;(0,bus_rotation_1.createBusRotator)(r,{maxBytes:n,maxFiles:t,fs:BUS_FS,checkEveryBytes:0})(s);return}let o=rotators.get(r);o||(o=(0,bus_rotation_1.createBusRotator)(r,{maxBytes:exports.EVENT_MAX_BYTES,maxFiles:exports.EVENT_MAX_FILES,fs:BUS_FS}),rotators.set(r,o)),o(s)}i(maybeRotateEventFile,"maybeRotateEventFile"),c(maybeRotateEventFile,"maybeRotateEventFile"),exports.PROJECT=process.env.LENSMCP_PROJECT||process.env.SERVICE_NAME||"app",exports.EVENT_FILE=process.env.LENSMCP_EVENT_FILE||`${process.cwd()}/.lensmcp/events.jsonl`;let writing=!1,dirReady=!1;const emitGuard=(0,flood_guard_1.createEventFloodGuard)({burst:30,perSec:5}),logGuard=(0,flood_guard_1.createEventFloodGuard)({burst:120,perSec:20});function isLensWriting(){return writing}i(isLensWriting,"isLensWriting"),c(isLensWriting,"isLensWriting");function lensEmit(r,s,e,o,n,t,p="cluster"){return lensEmitOutcome(r,s,e,o,n,t,p)==="written"}i(lensEmit,"lensEmit"),c(lensEmit,"lensEmit");function lensEmitOutcome(r,s,e,o,n,t,p="cluster",u=emitGuard){try{writing=!0;const E=n??`${r}:${String(o.name??r)}`,d=u.admit(E);if(!d.ok)return"suppressed";dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const l=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:r,category:p,severity:s,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{},...t?.originNodeId?{originNodeId:t.originNodeId}:{},...t?.originType?{originType:t.originType}:{}},fingerprint:E,title:e,raw:{project:exports.PROJECT,...o,loopId:loop_identity_1.NODE_LOOP.id,...d.suppressed>0?{floodSuppressed:d.suppressed}:{}}})+`
2
+ `;return maybeRotateEventFile(exports.EVENT_FILE,l.length),appendFileSync(exports.EVENT_FILE,l),"written"}catch{return"failed"}finally{writing=!1}}i(lensEmitOutcome,"lensEmitOutcome"),c(lensEmitOutcome,"lensEmitOutcome");function lensLog(r,s,e){try{writing=!0;const o=e?.bucket!==void 0?`log:${exports.PROJECT}:${e.bucket}`:r==="error"||r==="fatal"?`log:error:${exports.PROJECT}`:`log:${exports.PROJECT}`,n=e?.bucket!==void 0?{ok:!0,suppressed:e.suppressed??0}:logGuard.admit(o);if(!n.ok)return!1;dirReady||(mkdirSync((0,node_path_1.dirname)(exports.EVENT_FILE),{recursive:!0}),dirReady=!0);const t=(0,flow_context_1.flowContext)(),p=JSON.stringify({id:Date.now().toString(36)+Math.random().toString(36).slice(2,10),sessionId:"pending",timestamp:Date.now(),source:"nestjs",category:"runtime",severity:r,context:{sessionId:"pending",projectName:exports.PROJECT,...t?.flowId?{flowId:t.flowId}:{},...t?.requestId?{requestId:t.requestId}:{},...t?.runId?{runId:t.runId}:{}},fingerprint:o,title:s,raw:{project:exports.PROJECT,kind:"log",...e?.stack?{request:{error:{message:s,stack:e.stack}}}:{},...e?.text?{text:e.text}:{},...e?.continues?{continues:!0}:{},...e?.stream?{stream:e.stream}:{},...e?.level?{level:e.level}:{},...e?.logger?{logger:e.logger}:{},...n.suppressed>0?{floodSuppressed:n.suppressed}:{}}})+`
3
+ `;return maybeRotateEventFile(exports.EVENT_FILE,p.length),appendFileSync(exports.EVENT_FILE,p),!0}catch{return!1}finally{writing=!1}}i(lensLog,"lensLog"),c(lensLog,"lensLog");
@@ -30,7 +30,7 @@ export declare const DEFAULT_HTTP_MAX_FILES = 10000;
30
30
  export declare const DEFAULT_HTTP_MAX_AGE_MS: number;
31
31
  /** When an exchange was written — its id starts with the time (`newExchangeId`), so no file needs a stat. */
32
32
  export declare const exchangeTime: (file: string) => number | undefined;
33
- /** Oldest first, by name (= by time): beyond the count, and older than the age. */
33
+ /** Oldest first, by name (= by time). AI exchanges are retained even beyond the count/age budgets. */
34
34
  export declare function pruneHttpExchanges(dir: string, now?: number): number;
35
35
  /** Persist one exchange (temp name, then rename). Best-effort: false on any failure — the call goes on regardless. */
36
36
  export declare function writeHttpExchange(x: HttpExchange): boolean;
package/lib/http-store.js CHANGED
@@ -1 +1 @@
1
- "use strict";var d=Object.defineProperty;var u=(e,t)=>d(e,"name",{value:t,configurable:!0});var f=Object.defineProperty,i=u((e,t)=>f(e,"name",{value:t,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.exchangeTime=exports.DEFAULT_HTTP_MAX_AGE_MS=exports.DEFAULT_HTTP_MAX_FILES=exports.newExchangeId=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,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"),i(httpCaptureMode,"httpCaptureMode"),exports.REMOVED="\u2022\u2022\u2022\u2022 redacted",exports.MAX_KEPT_BYTES=64*1024;const MAX_PARSE_BYTES=2*1024*1024,newExchangeId=i(()=>`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"),i(jwtClaims,"jwtClaims");function redactHeaders(e){const t=[],r=[];for(const[n,a]of e){const o=n.toLowerCase();if(CREDENTIAL_HEADERS.test(o)||(0,redact_1.isSecretParam)(o)){r.push(o);const s=/^bearer\s+(\S+)$/i.exec(a.trim()),c=s?jwtClaims(s[1]):void 0,p=/^(\w+)\s+\S/.exec(a.trim())?.[1];t.push([o,c?`Bearer ${c}`:p&&o.endsWith("authorization")?`${p} ${exports.REMOVED}`:exports.REMOVED])}else t.push([o,(0,ai_store_1.redactAiText)(a).slice(0,2048)])}return{headers:t,redacted:r}}u(redactHeaders,"redactHeaders"),i(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"),i(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((o,s)=>redactJson(o,`${t}[${s}]`,r,n+1));const a={};for(const[o,s]of Object.entries(e)){const c=t?`${t}.${o}`:o,p=typeof s=="number"&&/tokens/i.test(o);CREDENTIAL_FIELD.test(o)&&s!==null&&typeof s!="object"&&!p?(a[o]=exports.REMOVED,r.length<50&&r.push(c)):a[o]=redactJson(s,c,r,n+1)}return a}u(redactJson,"redactJson"),i(redactJson,"redactJson");const textual=i(e=>/json|^text\/|x-www-form-urlencoded|xml|javascript|graphql|event-stream/i.test(e)||e==="","textual");function keepBody(e,t,r,n,a){const o=e.split(";")[0].trim().toLowerCase();if(!textual(o))return{contentType:o||"application/octet-stream",size:r,kept:0,truncated:!1,omitted:"binary",...a?{sha256:a}:{}};const s=t.byteLength>=r&&r<=MAX_PARSE_BYTES,c=Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8");let p;if(s&&/json/.test(o))try{p=JSON.stringify(redactJson(JSON.parse(c),"",n))}catch{p=(0,ai_store_1.redactAiText)(c)}else s&&/x-www-form-urlencoded/.test(o)?p=new URLSearchParams(redactQuery(c)).toString():p=(0,ai_store_1.redactAiText)(c);const x=Buffer.byteLength(p)>exports.MAX_KEPT_BYTES?Buffer.from(p).subarray(0,exports.MAX_KEPT_BYTES).toString("utf8"):p;return{contentType:o,size:r,kept:Buffer.byteLength(x),text:x,truncated:r>t.byteLength||x.length<p.length}}u(keepBody,"keepBody"),i(keepBody,"keepBody"),exports.DEFAULT_HTTP_MAX_FILES=1e4,exports.DEFAULT_HTTP_MAX_AGE_MS=1440*6e4;const PRUNE_EVERY_MS=3e4,maxFiles=i(()=>{const e=Number(process.env.LENSMCP_HTTP_MAX_FILES);return Number.isInteger(e)&&e>=10?e:exports.DEFAULT_HTTP_MAX_FILES},"maxFiles"),maxAgeMs=i(()=>{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=i(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()){let r=0;try{const n=readdirSync(e).filter(s=>/^hx_[0-9a-z]+\.json$/.test(s)).sort(),a=n.length-maxFiles(),o=t-maxAgeMs();for(let s=0;s<n.length;s+=1){const c=(0,exports.exchangeTime)(n[s]);if(s>=a&&(c===void 0||c>=o))break;try{unlinkSync(`${e}/${n[s]}`),r+=1}catch{}}}catch{}return r}u(pruneHttpExchanges,"pruneHttpExchanges"),i(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 a=Date.now();return a-(lastPrune.get(t)??0)>=PRUNE_EVERY_MS&&(lastPrune.set(t,a),pruneHttpExchanges(r,a)),!0}catch{return!1}}u(writeHttpExchange,"writeHttpExchange"),i(writeHttpExchange,"writeHttpExchange");
1
+ "use strict";var d=Object.defineProperty;var u=(e,t)=>d(e,"name",{value:t,configurable:!0});var f=Object.defineProperty,a=u((e,t)=>f(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_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;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,i]of e){const o=n.toLowerCase();if(CREDENTIAL_HEADERS.test(o)||(0,redact_1.isSecretParam)(o)){r.push(o);const s=/^bearer\s+(\S+)$/i.exec(i.trim()),c=s?jwtClaims(s[1]):void 0,p=/^(\w+)\s+\S/.exec(i.trim())?.[1];t.push([o,c?`Bearer ${c}`:p&&o.endsWith("authorization")?`${p} ${exports.REMOVED}`:exports.REMOVED])}else t.push([o,(0,ai_store_1.redactAiText)(i).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((o,s)=>redactJson(o,`${t}[${s}]`,r,n+1));const i={};for(const[o,s]of Object.entries(e)){const c=t?`${t}.${o}`:o,p=typeof s=="number"&&/tokens/i.test(o);CREDENTIAL_FIELD.test(o)&&s!==null&&typeof s!="object"&&!p?(i[o]=exports.REMOVED,r.length<50&&r.push(c)):i[o]=redactJson(s,c,r,n+1)}return i}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,i){const o=e.split(";")[0].trim().toLowerCase();if(!textual(o))return{contentType:o||"application/octet-stream",size:r,kept:0,truncated:!1,omitted:"binary",...i?{sha256:i}:{}};const s=t.byteLength>=r&&r<=MAX_PARSE_BYTES,c=Buffer.from(t.buffer,t.byteOffset,t.byteLength).toString("utf8");let p;if(s&&/json/.test(o))try{p=JSON.stringify(redactJson(JSON.parse(c),"",n))}catch{p=(0,ai_store_1.redactAiText)(c)}else s&&/x-www-form-urlencoded/.test(o)?p=new URLSearchParams(redactQuery(c)).toString():p=(0,ai_store_1.redactAiText)(c);const x=Buffer.byteLength(p)>exports.MAX_KEPT_BYTES?Buffer.from(p).subarray(0,exports.MAX_KEPT_BYTES).toString("utf8"):p;return{contentType:o,size:r,kept:Buffer.byteLength(x),text:x,truncated:r>t.byteLength||x.length<p.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()){let r=0;try{const n=readdirSync(e).filter(s=>/^hx_[0-9a-z]+\.json$/.test(s)).sort(),i=n.length-maxFiles(),o=t-maxAgeMs();for(let s=0;s<n.length;s+=1){const c=(0,exports.exchangeTime)(n[s]);if(s>=i&&(c===void 0||c>=o))break;try{if(JSON.parse(readFileSync(`${e}/${n[s]}`,"utf8"))?.ai)continue;unlinkSync(`${e}/${n[s]}`),r+=1}catch{}}}catch{}return r}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 i=Date.now();return i-(lastPrune.get(t)??0)>=PRUNE_EVERY_MS&&(lastPrune.set(t,i),pruneHttpExchanges(r,i)),!0}catch{return!1}}u(writeHttpExchange,"writeHttpExchange"),a(writeHttpExchange,"writeHttpExchange");
@@ -0,0 +1,8 @@
1
+ /** Stable across module copies, distinct across restarts and worker threads. */
2
+ export declare const NODE_LOOP: {
3
+ id: string;
4
+ runtime: "node";
5
+ project: string;
6
+ pid: number;
7
+ threadId: number;
8
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.NODE_LOOP=void 0;const node_perf_hooks_1=require("node:perf_hooks"),node_worker_threads_1=require("node:worker_threads");exports.NODE_LOOP={id:`node:${process.pid}:${node_worker_threads_1.threadId}:${node_perf_hooks_1.performance.timeOrigin}`,runtime:"node",project:process.env.LENSMCP_PROJECT||process.env.SERVICE_NAME||"app",pid:process.pid,threadId:node_worker_threads_1.threadId};
@@ -0,0 +1 @@
1
+ export declare function startLoopWatchdog(onUnavailable?: () => void): (() => void) | undefined;
@@ -0,0 +1,38 @@
1
+ "use strict";var p=Object.defineProperty;var i=(t,e)=>p(t,"name",{value:e,configurable:!0});var d=Object.defineProperty,o=i((t,e)=>d(t,"name",{value:e,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.startLoopWatchdog=startLoopWatchdog;const node_worker_threads_1=require("node:worker_threads"),node_path_1=require("node:path"),loop_identity_1=require("./loop-identity"),emit_1=require("./emit"),WATCHER=`
2
+ const { workerData } = require('node:worker_threads');
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const cells = new BigInt64Array(workerData.shared);
6
+ const loop = workerData.loop;
7
+ let incident;
8
+ let lastWrite = 0;
9
+ let serial = 0;
10
+ function write(raw) {
11
+ const now = Date.now();
12
+ const row = { id: raw.incidentId + ':' + (++serial), timestamp: now, source: 'perf', category: 'performance',
13
+ severity: raw.state === 'unresponsive' ? 'error' : 'warning', title: 'Event loop ' + raw.state + ' (' + Math.round(raw.durationMs) + 'ms)',
14
+ context: { projectName: loop.project }, raw: { ...raw, loop, loopId: loop.id, kind: 'event-loop-stall', mechanism: 'heartbeat' } };
15
+ try {
16
+ fs.mkdirSync(path.dirname(workerData.file), { recursive: true, mode: 448 });
17
+ fs.writeFileSync(workerData.file + '.tmp', JSON.stringify(row), { mode: 384 });
18
+ fs.renameSync(workerData.file + '.tmp', workerData.file);
19
+ } catch {}
20
+ lastWrite = now;
21
+ }
22
+ setInterval(() => {
23
+ const now = Date.now();
24
+ const beat = Atomics.load(cells, 0);
25
+ if (!beat) return;
26
+ const delay = Number(process.hrtime.bigint() - beat) / 1e6;
27
+ if (delay >= workerData.threshold) {
28
+ if (!incident) incident = { incidentId: loop.id + ':watch:' + now, startedAt: now - delay, durationMs: delay, state: 'unresponsive' };
29
+ incident.durationMs = delay;
30
+ if (now - lastWrite >= 1000) write(incident);
31
+ } else if (incident) {
32
+ // The last heartbeat proves recovery; the exact end is bounded by the heartbeat resolution.
33
+ incident.durationMs = Math.max(incident.durationMs, Number(Atomics.load(cells, 1)) - incident.startedAt);
34
+ write({ ...incident, state: 'recovered' });
35
+ incident = undefined;
36
+ }
37
+ }, 250);
38
+ `;function startLoopWatchdog(t){if(process.env.LENSMCP_EVENT_LOOP_WATCHDOG==="0")return;const e=new SharedArrayBuffer(16),s=new BigInt64Array(e),c=o(()=>{Atomics.store(s,0,process.hrtime.bigint()),Atomics.store(s,1,BigInt(Date.now()))},"beat");c();const r=setInterval(c,20);r.unref();try{let a=!1;const n=new node_worker_threads_1.Worker(WATCHER,{eval:!0,execArgv:[],env:{...process.env,NODE_OPTIONS:"",LENSMCP_AUTO:"0",LENSMCP_EVENT_LOOP:"0"},workerData:{shared:e,loop:loop_identity_1.NODE_LOOP,threshold:1e3,file:(0,node_path_1.join)((0,node_path_1.dirname)(emit_1.EVENT_FILE),"event-loops",`${process.pid}-${loop_identity_1.NODE_LOOP.threadId}-${Math.floor(performance.timeOrigin)}.json`)}}),l=o(()=>{clearInterval(r),a||(a=!0,t?.())},"unavailable");return n.on("error",l),n.on("exit",l),n.unref(),()=>{a=!0,clearInterval(r),n.terminate()}}catch{clearInterval(r);return}}i(startLoopWatchdog,"startLoopWatchdog"),o(startLoopWatchdog,"startLoopWatchdog");
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 f=a.getReader(),n=new TextDecoder;let p=0;try{for(;;){const{done:m,value:g}=await f.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 f.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),f=await e;let n=f.response;const p=r.stream||f.sse,m=f.firstEventMs,g=f.cut,{aborted:y,timedOut:T}=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 l,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&&(l=S.id,h=S.truncated)}const v=a!==void 0&&a>=400||t.failure!==void 0&&!y,C=u??"?";!(0,emit_1.lensEmit)("ai",t.failure!==void 0&&!y?"error":"info",a!==void 0?`ai ${t.target.provider}/${C} ${t.target.api} \u2192 ${a}`:`ai ${t.target.provider}/${C} ${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:l!==void 0,...l?{contentRef:l}:{},...h?{truncated:!0}:{},...b?{aborted:!0}:{},...T?{timedOut:!0}:{},...n.error?{error:(0,ai_store_1.redactAiText)(n.error).slice(0,300)}:{}},v?`ai:error:${t.target.provider}:${C}`:`ai:${t.target.provider}:${C}`,t.ctx,"ai")&&l&&(0,ai_store_1.removeAiContent)(l)}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),f=methodOf(i,r),n=a&&f==="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),T=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:f,startedAt:p,ctx:m,signal:T,request:y};let u;try{u=await s(i,r)}catch(h){throw b({...A,failure:h}),h}const _=Date.now()-p;let l;try{l=u.clone()}catch{l=void 0}return b({...A,ttfbMs:_,res:u,...l?{clone:l}:{}}),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(),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 +1 @@
1
- "use strict";var m=Object.defineProperty;var r=(e,o)=>m(e,"name",{value:o,configurable:!0});var a=Object.defineProperty,n=r((e,o)=>a(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"),SAMPLE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_SAMPLE_MS);return Number.isFinite(e)&&e>=1e3?e:3e4})(),REPORT_ABOVE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_MIN_MS);return Number.isFinite(e)&&e>=0?e:50})(),RESOLUTION_MS=20;let timer,published=!1;function tapEventLoop(){if(process.env.LENSMCP_EVENT_LOOP==="0"||timer)return;const e=(0,node_perf_hooks_1.monitorEventLoopDelay)({resolution:RESOLUTION_MS});e.enable(),timer=setInterval(()=>{const o=RESOLUTION_MS,s=e.mean/1e6;if(!Number.isFinite(s))return;const i=Math.max(0,s-o),t=Math.max(0,e.max/1e6-o),p=Math.max(0,e.percentile(99)/1e6-o);e.reset(),!(published&&t<REPORT_ABOVE_MS)&&(published=!0,(0,emit_1.lensEmit)("perf",t>=1e3?"error":"info",`event loop lag ${i.toFixed(1)}ms mean / ${t.toFixed(1)}ms max`,{kind:"event-loop-lag",lagMs:Number(i.toFixed(2)),maxMs:Number(t.toFixed(2)),p99Ms:Number(p.toFixed(2)),windowMs:SAMPLE_MS},"perf:event-loop-lag",void 0,"performance"))},SAMPLE_MS),timer.unref?.()}r(tapEventLoop,"tapEventLoop"),n(tapEventLoop,"tapEventLoop");function stopEventLoopTapForTests(){timer&&clearInterval(timer),timer=void 0,published=!1}r(stopEventLoopTapForTests,"stopEventLoopTapForTests"),n(stopEventLoopTapForTests,"stopEventLoopTapForTests");
1
+ "use strict";var f=Object.defineProperty;var a=(e,t)=>f(e,"name",{value:t,configurable:!0});var E=Object.defineProperty,o=a((e,t)=>E(e,"name",{value:t,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.tapEventLoop=tapEventLoop,exports.stopEventLoopTapForTests=stopEventLoopTapForTests;const node_perf_hooks_1=require("node:perf_hooks"),emit_1=require("./emit"),loop_identity_1=require("./loop-identity"),tap_loop_callbacks_1=require("./tap-loop-callbacks"),loop_watchdog_1=require("./loop-watchdog"),SAMPLE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_SAMPLE_MS);return Number.isFinite(e)&&e>=1e3?e:3e4})(),REPORT_ABOVE_MS=(()=>{const e=Number(process.env.LENSMCP_EVENT_LOOP_MIN_MS);return Number.isFinite(e)&&e>=0?e:50})(),RESOLUTION_MS=20;let timer,published=!1,stopMonitoring;function tapEventLoop(){if(process.env.LENSMCP_EVENT_LOOP==="0"||timer)return;const e=(0,node_perf_hooks_1.monitorEventLoopDelay)({resolution:RESOLUTION_MS});e.enable();let t=node_perf_hooks_1.performance.eventLoopUtilization(),p=0;const s=process.env.LENSMCP_EVENT_LOOP_CALLBACKS!=="0";let r=!1;const l=o(()=>`${s?"Callback wall time and loop delay":"Loop delay only; callback measurement disabled"}; ${r?"independent watchdog":"watchdog unavailable or disabled"}`,"coverage"),c=(0,loop_watchdog_1.startLoopWatchdog)(()=>{r=!1,(0,emit_1.lensEmit)("perf","warning","Event loop watchdog unavailable",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:l()},"perf:event-loop-status",void 0,"performance")});r=!!c,(0,emit_1.lensEmit)("perf","info","Event loop monitoring started",{kind:"event-loop-status",loop:loop_identity_1.NODE_LOOP,coverage:l()},"perf:event-loop-status",void 0,"performance"),timer=setInterval(()=>{const i=RESOLUTION_MS,d=e.mean/1e6;if(!Number.isFinite(d))return;const v=Math.max(0,d-i),n=Math.max(0,e.max/1e6-i),_=Math.max(0,e.percentile(99)/1e6-i);if(e.reset(),published&&n<REPORT_ABOVE_MS&&Date.now()-p<6e4)return;published=!0,p=Date.now();const m=node_perf_hooks_1.performance.eventLoopUtilization(),L=node_perf_hooks_1.performance.eventLoopUtilization(m,t).utilization;t=m,(0,emit_1.lensEmit)("perf",n>=1e3?"error":"info",`event loop lag ${v.toFixed(1)}ms mean / ${n.toFixed(1)}ms max`,{kind:"event-loop-lag",lagMs:Number(v.toFixed(2)),maxMs:Number(n.toFixed(2)),p99Ms:Number(_.toFixed(2)),windowMs:SAMPLE_MS,loop:loop_identity_1.NODE_LOOP,utilization:Number(L.toFixed(4))},"perf:event-loop-lag",void 0,"performance")},SAMPLE_MS),timer.unref?.();const u=s?(0,tap_loop_callbacks_1.tapLoopCallbacks)(Math.max(50,REPORT_ABOVE_MS)):void 0;stopMonitoring=o(()=>{u?.(),c?.(),e.disable()},"stopMonitoring")}a(tapEventLoop,"tapEventLoop"),o(tapEventLoop,"tapEventLoop");function stopEventLoopTapForTests(){timer&&clearInterval(timer),timer=void 0,stopMonitoring?.(),stopMonitoring=void 0,published=!1}a(stopEventLoopTapForTests,"stopEventLoopTapForTests"),o(stopEventLoopTapForTests,"stopEventLoopTapForTests");
@@ -0,0 +1,2 @@
1
+ /** Measures callback execution, not the time a promise/request spent awaiting I/O. No per-callback stack capture. */
2
+ export declare function tapLoopCallbacks(minMs?: number): () => void;
@@ -0,0 +1 @@
1
+ "use strict";var y=Object.defineProperty;var d=(r,e)=>y(r,"name",{value:e,configurable:!0});var _=Object.defineProperty,f=d((r,e)=>_(r,"name",{value:e,configurable:!0}),"f");Object.defineProperty(exports,"__esModule",{value:!0}),exports.tapLoopCallbacks=tapLoopCallbacks;const node_async_hooks_1=require("node:async_hooks"),node_perf_hooks_1=require("node:perf_hooks"),emit_1=require("./emit"),flow_context_1=require("./flow-context"),loop_identity_1=require("./loop-identity");function tapLoopCallbacks(r=50){const e=new Map,a=new Map;let n=!1,p=!1,c=!1,i=0;const s=[],u=(0,node_async_hooks_1.createHook)({init(t,l){n||(e.set(t,l),e.size>2e4&&e.delete(e.keys().next().value))},before(t){if(!(n||!e.has(t)))try{a.set(t,{start:node_perf_hooks_1.performance.now(),at:Date.now(),type:e.get(t)??"unknown"})}catch{}},after(t){const l=a.get(t);if(a.delete(t),!l||n)return;const k=node_perf_hooks_1.performance.now()-l.start;if(!(k<r)){if(s.length>=50){i++;return}s.push({...l,duration:k,id:t,context:(0,flow_context_1.flowContext)()}),!c&&(c=!0,n=!0,setImmediate(()=>{if(c=!1,!p){n=!0;try{for(const o of s.splice(0))(0,emit_1.lensEmit)("perf",o.duration>=1e3?"error":"warning",`Event loop callback blocked for ${o.duration.toFixed(1)}ms (${o.type})`,{kind:"event-loop-stall",incidentId:`${loop_identity_1.NODE_LOOP.id}:callback:${o.id}:${o.start}`,loop:loop_identity_1.NODE_LOOP,startedAt:o.at,durationMs:Math.round(o.duration*100)/100,state:"recovered",mechanism:"callback",callbackType:o.type,...i?{droppedIncidents:i}:{}},"perf:event-loop-stall",o.context,"performance");i=0}finally{n=!1}}}),n=!1)}},destroy(t){e.delete(t),a.delete(t)}});return u.enable(),()=>{p=!0,u.disable(),e.clear(),a.clear(),s.length=0}}d(tapLoopCallbacks,"tapLoopCallbacks"),f(tapLoopCallbacks,"tapLoopCallbacks");
package/lib/tap-redis.js CHANGED
@@ -1 +1 @@
1
- "use strict";var $=Object.defineProperty;var d=(t,e)=>$(t,"name",{value:e,configurable:!0});var E=Object.defineProperty,p=d((t,e)=>E(t,"name",{value:e,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redisFingerprint=redisFingerprint,exports.redisTap=redisTap;const key_pattern_1=require("@lensmcp/core/key-pattern"),redact_1=require("@lensmcp/core/redact"),emit_1=require("./emit"),flow_context_1=require("./flow-context"),sql_values_1=require("./sql-values"),HOUSEKEEPING=new Set(["info","ping","hello","auth","select","client","cluster","command","subscribe","psubscribe","unsubscribe","punsubscribe","echo","quit","wait","script","config","debug","memory","readonly","readwrite"]),MAX_PER_SEC=100,MAX_SHAPES=200,SHARED_BUCKET="redis:cache";let emitted=0,windowStart=0,capped=0;const shapes=new Set;function redisFingerprint(t,e){const r=`redis:${t} ${(0,key_pattern_1.foldKeyPattern)(e).slice(0,120)}`;return shapes.has(r)?r:shapes.size>=MAX_SHAPES?SHARED_BUCKET:(shapes.add(r),r)}d(redisFingerprint,"redisFingerprint"),p(redisFingerprint,"redisFingerprint");function isBullTraffic(t){const e=String(t.name??"").toLowerCase();if(e==="eval"||e==="evalsha")return!0;const r=t.args??[];for(let n=0;n<Math.min(r.length,4);n++){const i=String(r[n]??"");if(i.startsWith("bull:")||i.startsWith("{bull"))return!0}return!1}d(isBullTraffic,"isBullTraffic"),p(isBullTraffic,"isBullTraffic");function redisTap(t){const e=t,r=(e?.default??e)?.prototype,n=r?.sendCommand;if(!r||typeof n!="function")return!1;if(n.__lensmcp)return!0;const i=p(function(s,c){const a=n.call(this,s,c);try{observe(s)}catch{}return a},"wrapped");return i.__lensmcp=!0,r.sendCommand=i,!0}d(redisTap,"redisTap"),p(redisTap,"redisTap");const clip=p((t,e)=>t.length>e?t.slice(0,e-1)+"\u2026":t,"clip");function commandLine(t,e){const r=e.slice(0,6).map((n,i)=>{const s=String(n??"");return i>0&&s.length>48?`\u2026(${(s.length/1024).toFixed(1)}kb)`:clip(s,64)});return[t.toUpperCase(),...r].join(" ")}d(commandLine,"commandLine"),p(commandLine,"commandLine");function observe(t){const e=String(t?.name??"").toLowerCase();if(!e||HOUSEKEEPING.has(e)||isBullTraffic(t))return;const r=(0,flow_context_1.liveFlowContext)();r&&(r.redisCallCount=(r.redisCallCount??0)+1);const n=Date.now();if(n-windowStart>1e3&&(windowStart=n,emitted=0),++emitted>MAX_PER_SEC){capped+=1;return}const i=t?.args??[],s=String(i[0]??""),c=(0,redact_1.isSecretCacheKey)(s),a=c?(0,key_pattern_1.foldKeyPattern)(s):s,C=(0,flow_context_1.flowContext)(),y=c?`${e.toUpperCase()} ${a} \u2026`:(0,redact_1.redactSecretText)(commandLine(e,i)),g=redisFingerprint(e,s),_=capped;capped=0;const S={name:"cache",kind:"redis-op",op:e,key:a,command:y,startedAt:n,...c?{secretKey:!0}:{},..._?{capSuppressed:_}:{}},h=i.slice(1);if(e==="get"&&t.promise&&typeof t.promise.then=="function"){const l=n;t.promise.then(f=>{const o=f!=null,u=o&&!c?(0,sql_values_1.newCacheValuesId)(l):void 0,x=(0,emit_1.lensEmitOutcome)("redis","info",`cache ${o?"hit":"miss"} ${a}`,{...S,hit:o,durationMs:Date.now()-l,...o&&!c?{value:clip((0,redact_1.redactSecretText)(String(f).slice(0,4096)),120)}:{},...u?{valuesId:u}:{}},g,C);u&&x==="written"&&(0,sql_values_1.keepCacheValues)(u,l,e,s,h,f)},()=>{});return}const m=c?void 0:(0,sql_values_1.newCacheValuesId)(n),v=m?{valuesId:m}:{};let w;if(e==="set"){const l=i.map(u=>String(u)),f=l.findIndex(u=>u.toUpperCase()==="EX"),o=f>=0?Number(l[f+1]):void 0;w=(0,emit_1.lensEmitOutcome)("redis","info",o?`cache set ${a} (ttl ${o}s)`:`cache set ${a}`,{...S,...o?{ttlSec:o}:{},...v},g,C)}else w=(0,emit_1.lensEmitOutcome)("redis","info",`cache ${e} ${a}`,{...S,...v},g,C);if(!m||w!=="written")return;const b=t.promise;b&&typeof b.then=="function"?b.then(l=>(0,sql_values_1.keepCacheValues)(m,n,e,s,h,l),()=>(0,sql_values_1.keepCacheValues)(m,n,e,s,h)):(0,sql_values_1.keepCacheValues)(m,n,e,s,h)}d(observe,"observe"),p(observe,"observe");
1
+ "use strict";var $=Object.defineProperty;var d=(t,e)=>$(t,"name",{value:e,configurable:!0});var E=Object.defineProperty,p=d((t,e)=>E(t,"name",{value:e,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redisFingerprint=redisFingerprint,exports.redisTap=redisTap;const key_pattern_1=require("@lensmcp/core/key-pattern"),redact_1=require("@lensmcp/core/redact"),redis_expiry_1=require("@lensmcp/core/redis-expiry"),emit_1=require("./emit"),flow_context_1=require("./flow-context"),sql_values_1=require("./sql-values"),HOUSEKEEPING=new Set(["info","ping","hello","auth","select","client","cluster","command","subscribe","psubscribe","unsubscribe","punsubscribe","echo","quit","wait","script","config","debug","memory","readonly","readwrite"]),MAX_PER_SEC=100,MAX_SHAPES=200,SHARED_BUCKET="redis:cache";let emitted=0,windowStart=0,capped=0;const shapes=new Set;function redisFingerprint(t,e){const n=`redis:${t} ${(0,key_pattern_1.foldKeyPattern)(e).slice(0,120)}`;return shapes.has(n)?n:shapes.size>=MAX_SHAPES?SHARED_BUCKET:(shapes.add(n),n)}d(redisFingerprint,"redisFingerprint"),p(redisFingerprint,"redisFingerprint");function isBullTraffic(t){const e=String(t.name??"").toLowerCase();if(e==="eval"||e==="evalsha")return!0;const n=t.args??[];for(let r=0;r<Math.min(n.length,4);r++){const i=String(n[r]??"");if(i.startsWith("bull:")||i.startsWith("{bull"))return!0}return!1}d(isBullTraffic,"isBullTraffic"),p(isBullTraffic,"isBullTraffic");function redisTap(t){const e=t,n=(e?.default??e)?.prototype,r=n?.sendCommand;if(!n||typeof r!="function")return!1;if(r.__lensmcp)return!0;const i=p(function(s,o){const a=r.call(this,s,o);try{observe(s)}catch{}return a},"wrapped");return i.__lensmcp=!0,n.sendCommand=i,!0}d(redisTap,"redisTap"),p(redisTap,"redisTap");const clip=p((t,e)=>t.length>e?t.slice(0,e-1)+"\u2026":t,"clip");function commandLine(t,e){const n=e.slice(0,6).map((r,i)=>{const s=String(r??"");return i>0&&s.length>48?`\u2026(${(s.length/1024).toFixed(1)}kb)`:clip(s,64)});return[t.toUpperCase(),...n].join(" ")}d(commandLine,"commandLine"),p(commandLine,"commandLine");function observe(t){const e=String(t?.name??"").toLowerCase();if(!e||HOUSEKEEPING.has(e)||isBullTraffic(t))return;const n=(0,flow_context_1.liveFlowContext)();n&&(n.redisCallCount=(n.redisCallCount??0)+1);const r=Date.now();if(r-windowStart>1e3&&(windowStart=r,emitted=0),++emitted>MAX_PER_SEC){capped+=1;return}const i=t?.args??[],s=String(i[0]??""),o=(0,redact_1.isSecretCacheKey)(s),a=o?(0,key_pattern_1.foldKeyPattern)(s):s,C=(0,flow_context_1.flowContext)(),x=o?`${e.toUpperCase()} ${a} \u2026`:(0,redact_1.redactSecretText)(commandLine(e,i)),g=redisFingerprint(e,s),b=capped;capped=0;const y={name:"cache",kind:"redis-op",op:e,key:a,command:x,startedAt:r,expiry:(0,redis_expiry_1.redisExpiry)(e,i),...o?{secretKey:!0}:{},...b?{capSuppressed:b}:{}},h=i.slice(1);if(e==="get"&&t.promise&&typeof t.promise.then=="function"){const l=r;t.promise.then(f=>{const c=f!=null,u=c&&!o?(0,sql_values_1.newCacheValuesId)(l):void 0,w=(0,emit_1.lensEmitOutcome)("redis","info",`cache ${c?"hit":"miss"} ${a}`,{...y,hit:c,durationMs:Date.now()-l,...c&&!o?{value:clip((0,redact_1.redactSecretText)(String(f).slice(0,4096)),120)}:{},...u?{valuesId:u}:{}},g,C);u&&w==="written"&&(0,sql_values_1.keepCacheValues)(u,l,e,s,h,f)},()=>{});return}const m=o?void 0:(0,sql_values_1.newCacheValuesId)(r),v=m?{valuesId:m}:{};let S;if(e==="set"){const l=i.map(u=>String(u)),f=l.findIndex((u,w)=>w>=2&&u.toUpperCase()==="EX"),c=f>=0?Number(l[f+1]):void 0;S=(0,emit_1.lensEmitOutcome)("redis","info",c?`cache set ${a} (ttl ${c}s)`:`cache set ${a}`,{...y,...c?{ttlSec:c}:{},...v},g,C)}else S=(0,emit_1.lensEmitOutcome)("redis","info",`cache ${e} ${a}`,{...y,...v},g,C);if(!m||S!=="written")return;const _=t.promise;_&&typeof _.then=="function"?_.then(l=>(0,sql_values_1.keepCacheValues)(m,r,e,s,h,l),()=>(0,sql_values_1.keepCacheValues)(m,r,e,s,h)):(0,sql_values_1.keepCacheValues)(m,r,e,s,h)}d(observe,"observe"),p(observe,"observe");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/node-instrumentation",
3
- "version": "1.25.23",
3
+ "version": "1.25.25",
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.23",
19
- "@lensmcp/nest-instrumentation": "1.25.23",
20
- "@lensmcp/protocol-types": "1.25.23",
18
+ "@lensmcp/core": "1.25.25",
19
+ "@lensmcp/nest-instrumentation": "1.25.25",
20
+ "@lensmcp/protocol-types": "1.25.25",
21
21
  "tslib": "^2.3.0"
22
22
  },
23
23
  "optionalDependencies": {