@lensmcp/bridge 1.21.4 → 1.22.0
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/constants.d.ts +17 -0
- package/lib/constants.js +1 -1
- package/lib/env-sink.js +3 -3
- package/lib/event-mappers.d.ts +1 -1
- package/lib/event-mappers.js +3 -3
- package/lib/server.js +1 -1
- package/main.js +1 -1
- package/package.json +3 -3
package/lib/constants.d.ts
CHANGED
|
@@ -22,3 +22,20 @@ export declare const ENV_WS_HOST = "LENSMCP_WS_HOST";
|
|
|
22
22
|
export declare const ENV_WS_PORT = "LENSMCP_WS_PORT";
|
|
23
23
|
/** Env var: session id stamped onto every event the bridge publishes. */
|
|
24
24
|
export declare const ENV_SESSION_ID = "LENSMCP_SESSION_ID";
|
|
25
|
+
/**
|
|
26
|
+
* Largest single WebSocket frame the bridge will accept, in bytes.
|
|
27
|
+
*
|
|
28
|
+
* `ws` defaults to 100 MiB per message, and `handleClientEnvelope` JSON-parses whatever arrives —
|
|
29
|
+
* so the default made one frame from any page the developer visits an out-of-memory lever
|
|
30
|
+
* (issues/codex-security/32). Browser envelopes are small; the `raw` payload is separately clamped
|
|
31
|
+
* to 32 KiB downstream, so 1 MiB is generous for a legitimate producer and useless as a weapon.
|
|
32
|
+
*/
|
|
33
|
+
export declare const MAX_FRAME_BYTES: number;
|
|
34
|
+
/**
|
|
35
|
+
* Largest number of inner envelopes one `batch` frame may carry.
|
|
36
|
+
*
|
|
37
|
+
* Batching exists to coalesce a render burst, not to admit an unbounded stream in a single message
|
|
38
|
+
* (issues/codex-security/19, 21, 32). The client batches per animation frame, so this is far above
|
|
39
|
+
* anything it produces.
|
|
40
|
+
*/
|
|
41
|
+
export declare const MAX_BATCH_EVENTS = 1024;
|
package/lib/constants.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";export const DEFAULT_WS_HOST="127.0.0.1",DEFAULT_WS_PORT=5747,ENV_WS_HOST="LENSMCP_WS_HOST",ENV_WS_PORT="LENSMCP_WS_PORT",ENV_SESSION_ID="LENSMCP_SESSION_ID";
|
|
1
|
+
"use strict";export const DEFAULT_WS_HOST="127.0.0.1",DEFAULT_WS_PORT=5747,ENV_WS_HOST="LENSMCP_WS_HOST",ENV_WS_PORT="LENSMCP_WS_PORT",ENV_SESSION_ID="LENSMCP_SESSION_ID",MAX_FRAME_BYTES=1048576,MAX_BATCH_EVENTS=1024;
|
package/lib/env-sink.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var y=Object.defineProperty;var f=(
|
|
2
|
-
`;
|
|
3
|
-
`);
|
|
1
|
+
"use strict";var y=Object.defineProperty;var f=(n,t)=>y(n,"name",{value:t,configurable:!0});var k=Object.defineProperty,c=f((n,t)=>k(n,"name",{value:t,configurable:!0}),"i");import{connect as _}from"node:net";import{createSocket as M}from"node:dgram";import{appendFileSync as b,renameSync as S,statSync as A,unlinkSync as C}from"node:fs";const g=(()=>{const n=Number(process.env.LENSMCP_EVENT_MAX_BYTES);return Number.isFinite(n)&&n>0?n:50*1024*1024})(),E=(()=>{const n=Number(process.env.LENSMCP_EVENT_MAX_FILES);return Number.isFinite(n)&&n>=1&&n<=20?Math.floor(n):5})(),L=1024*1024;let p=0;function d(n,t){if(p+=t,!(p<L)){p=0;try{if(A(n).size<=g)return}catch{return}try{C(`${n}.${E}`)}catch{}for(let e=E-1;e>=1;e--)try{S(`${n}.${e}`,`${n}.${e+1}`)}catch{}try{S(n,`${n}.1`)}catch{}}}f(d,"C"),c(d,"rotateIfLarge");const a=(()=>{const n=process.env.LENSMCP_EVENT_MAX_EPS;if(n==="off")return 1/0;const t=Number(n);return n!==void 0&&n!==""&&Number.isFinite(t)?t<=0?1/0:t:5})(),h=Math.max(30,a===1/0?0:a*4),l=new Map;function v(n){if(a===1/0)return 0;const t=Date.now();let e=l.get(n);if(!e){if(l.size>=2048){for(const[r,s]of l)t-s.lastSeenAt>6e4&&l.delete(r);l.size>=2048&&l.clear()}e={tokens:h,lastRefillAt:t,suppressed:0,lastSeenAt:t},l.set(n,e)}if(e.lastSeenAt=t,e.tokens=Math.min(h,e.tokens+(t-e.lastRefillAt)/1e3*a),e.lastRefillAt=t,e.tokens>=1){e.tokens-=1;const r=e.suppressed;return e.suppressed=0,r}return e.suppressed+=1,-1}f(v,"T"),c(v,"floodAdmit");let i;export function resolveEnvSink(){if(i!==void 0)return i;const n=process.env.LENSMCP_EVENT_FILE;if(n)return i=c(r=>{try{const s=v(r.fingerprint);if(s<0)return;const u=JSON.stringify(s>0?{...r,raw:{...r.raw??{},floodSuppressed:s}}:r)+`
|
|
2
|
+
`;d(n,u.length),b(n,u)}catch{}},"cachedEnvSink"),i;const t=process.env.LENSMCP_UDS;if(t)return i=udsEnvSink(t),i;const e=process.env.LENSMCP_IPC_SOCKET;if(e){const[r,s]=e.split(":"),u=Number(s);if(r&&Number.isFinite(u)){const o=M("udp4");return o.unref(),i=c(N=>{try{const m=Buffer.from(JSON.stringify(N));o.send(m,u,r,()=>{})}catch{}},"cachedEnvSink"),i}}return i=null,null}f(resolveEnvSink,"resolveEnvSink"),c(resolveEnvSink,"resolveEnvSink");export function resetEnvSinkCache(){i=void 0}f(resetEnvSinkCache,"resetEnvSinkCache"),c(resetEnvSinkCache,"resetEnvSinkCache");export function udsEnvSink(n){const t=[];let e=null,r=!1;const s=c(()=>{if(!(!e||!e.writable))for(;t.length>0;)e.write(t.shift())},"flush"),u=c(()=>{if(r||e)return;r=!0;const o=_(n);o.on("connect",()=>{r=!1,e=o,s()}),o.on("drain",s),o.on("error",()=>{r=!1,e=null}),o.on("close",()=>{e=null}),o.unref()},"open");return u(),o=>{for(t.push(JSON.stringify(o)+`
|
|
3
|
+
`);t.length>1e4;)t.shift();if(e&&e.writable){s();return}if(e){try{e.destroy()}catch{}e=null}u()}}f(udsEnvSink,"udsEnvSink"),c(udsEnvSink,"udsEnvSink");
|
package/lib/event-mappers.d.ts
CHANGED
package/lib/event-mappers.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
"use strict";var I=Object.defineProperty;var c=(
|
|
2
|
-
`)[0]??
|
|
3
|
-
`).slice(0,2);for(const s of
|
|
1
|
+
"use strict";var I=Object.defineProperty;var c=(e,n)=>I(e,"name",{value:n,configurable:!0});var x=Object.defineProperty,d=c((e,n)=>x(e,"name",{value:n,configurable:!0}),"n");import{fingerprint as a,ulid as o}from"@lensmcp/core";import{redactDeep as y,redactText as m,redactUrl as g,safeText as u,safeTitle as l}from"./redact.js";const b="pending";export function envelopeToEvent(e,n=b){const s=e.tabId?{tabId:e.tabId}:{};switch(e.kind){case"error":{const r=!e.stack&&!e.source&&(!e.message||e.message==="Script error."),i=e.name&&e.name!=="Error"?`${e.name}: `:"",t=r?"Uncaught cross-origin script error (no detail available \u2014 add crossorigin to the script tag)":e.message?`${i}${e.message}`:`Uncaught ${e.name??"error"}`;return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:n,...s},fingerprint:a({kind:"runtime",identity:`error:${h(e.stack)}`,file:e.source,detail:e.message??e.name}),title:l(t),message:u(e.stack??e.message),location:e.source?{file:g(e.source),line:e.lineno,column:e.colno}:void 0,raw:{...e,...e.message?{message:m(e.message)}:{},...e.stack?{stack:u(e.stack)}:{},...e.source?{source:g(e.source)}:{}}}}case"resource-error":{const r=e.url?g(e.url):void 0;return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:n,...s},fingerprint:a({kind:"runtime",identity:`resource-error:${e.tagName}:${v(e.url??"")}`}),title:l(`Failed to load <${e.tagName}>${r?` ${r}`:""}`),...r?{relatedUrls:[r]}:{},raw:{...e,...r?{url:r}:{}}}}case"unhandledrejection":{const r=e.name&&e.name!=="Error"?`${e.name}: `:"",i=e.reason.split(`
|
|
2
|
+
`)[0]??e.reason;return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"error",context:{sessionId:n,...s},fingerprint:a({kind:"runtime",identity:`unhandledrejection:${e.reason.slice(0,80)}`}),title:l(`Unhandled promise rejection: ${r}${i}`),message:u(e.stack??e.reason),raw:{...e,reason:m(e.reason),...e.stack?{stack:u(e.stack)}:{}}}}case"console":{const r=e.level==="error"?"error":e.level==="warn"?"warning":"info",i=y(e.args),t=i.map(k).join(" ");return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:r,context:{sessionId:n,...s},fingerprint:a({kind:"runtime",identity:`console:${e.level}:${t.slice(0,80)}`}),title:`console.${e.level}`,message:u(t),raw:{...e,args:i}}}case"fetch":{const r=e.error!==void 0||e.status!==void 0&&e.status>=400,i=g(e.url),t=e.status!==void 0?` \u2192 ${e.status}${e.statusText?` ${e.statusText}`:""}`:"";return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"network",severity:r?"error":"info",context:{sessionId:n,...s},fingerprint:a({kind:"network",identity:`${e.method}:${v(e.url)}:${e.status??"err"}`}),title:l(`${e.method} ${i}${t}`),message:u(e.error),relatedUrls:[i],raw:{...e,url:i}}}case"route":return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:n,url:e.url,...s},fingerprint:a({kind:"runtime",identity:`route:${e.url}`}),title:`Route \u2192 ${e.url}`,raw:e};case"ready":return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:n,url:e.url,...s},fingerprint:a({kind:"runtime",identity:"client-ready"}),title:"Client runtime ready",message:e.reloadCause?`${e.userAgent} \u2014 after a Vite full reload${e.reloadCause.triggeredBy?` triggered by ${u(m(e.reloadCause.triggeredBy))}`:""}`:e.userAgent,raw:e};case"hmr":{const r=e.triggeredBy?m(e.triggeredBy):void 0,i=e.path?m(e.path):void 0,t=e.event==="full-reload"?`full-reload:${r??i??"?"}`:e.event==="invalidate"?`invalidate:${i??"?"}`:e.event,p=e.event==="full-reload"?`Vite full reload${r?` \u2014 ${l(r)} could not hot-update`:i?` \u2014 ${l(i)}`:""}`:e.event==="update"?`Vite HMR update (${e.count??e.updates?.length??0} module${(e.count??e.updates?.length??0)===1?"":"s"})`:e.event==="invalidate"?`Vite HMR invalidate \u2014 ${l(i??"?")}`:e.event==="ws-disconnect"?"Vite HMR socket lost \u2014 the page will poll and reload":"Vite HMR socket connected";return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"build",severity:e.event==="full-reload"||e.event==="ws-disconnect"?"warning":"info",context:{sessionId:n,...s},fingerprint:a({kind:"hmr",identity:t}),title:p,...e.message?{message:u(m(e.message))}:{},...r||i?{location:{file:r??i??""}}:{},...e.updates?.length?{relatedFiles:e.updates.map(f=>m(f))}:{},raw:{...e,...r?{triggeredBy:r}:{},...i?{path:i}:{}}}}case"gone":return{id:o(),sessionId:n,timestamp:e.at,source:"client-runtime",category:"runtime",severity:"info",context:{sessionId:n,...s},fingerprint:a({kind:"runtime",identity:"client-gone"}),title:"Client runtime gone",raw:e};case"publish":{if(!w(e.source))return;const r=$(e.category,e.raw),i=r&&r.status!==void 0&&r.status>=400?"error":e.severity??"info",t=d(f=>r&&r.url!==r.redactedUrl?f.split(r.url).join(r.redactedUrl):f,"scrub"),p=t(e.title);return{id:o(),sessionId:n,timestamp:e.at,source:e.source,category:e.category,severity:i,context:{...s,...e.context??{},sessionId:n},fingerprint:e.fingerprint?t(e.fingerprint):a({kind:"publish",identity:p}),title:l(p),message:u(e.message),...r?{relatedUrls:[r.redactedUrl]}:{},raw:r?{...y(e.raw),url:r.redactedUrl}:y(e.raw)}}}}c(envelopeToEvent,"envelopeToEvent"),d(envelopeToEvent,"envelopeToEvent");function $(e,n){if(e!=="network"||!n||typeof n!="object")return;const s=n;if(!(typeof s.url!="string"||s.url.length===0))return{url:s.url,redactedUrl:g(s.url),...typeof s.status=="number"?{status:s.status}:{}}}c($,"k"),d($,"networkDetails");function h(e){if(!e)return"unknown";const n=e.split(`
|
|
3
|
+
`).slice(0,2);for(const s of n){const r=s.match(/at\s+(\S+)/);if(r&&r[1])return r[1]}return"unknown"}c(h,"w"),d(h,"normaliseStackTopFrame");function v(e){try{return`${new URL(e,"http://x").pathname}`}catch{return e}}c(v,"y"),d(v,"normaliseUrl");function k(e){if(typeof e=="string")return e;try{return JSON.stringify(e)}catch{return String(e)}}c(k,"x"),d(k,"stringify");const U=new Set(["react","valtio","memory","client-runtime"]);function w(e){return typeof e=="string"&&U.has(e)}c(w,"b"),d(w,"isBrowserPublishableSource");
|
package/lib/server.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var b=Object.defineProperty;var s=(r,e)=>b(r,"name",{value:e,configurable:!0});var m=Object.defineProperty,i=s((r,e)=>m(r,"name",{value:e,configurable:!0}),"i");import{createServer as v}from"node:net";import{WebSocketServer as y}from"ws";import{DEFAULT_WS_HOST as h,DEFAULT_WS_PORT as S,MAX_BATCH_EVENTS as g,MAX_FRAME_BYTES as E}from"./constants.js";import{resolveEnvSink as k}from"./env-sink.js";import{envelopeToEvent as P}from"./event-mappers.js";export function makePublisher(r){let e;return t=>{if(r.bus){r.bus.publish(t);return}if(r.sink){r.sink(t);return}if(e===void 0&&(e=k()),e){e(t);return}(t.severity==="error"||t.severity==="fatal")&&console.debug(`[lensmcp-bridge] ${t.severity}: ${t.title}`)}}s(makePublisher,"makePublisher"),i(makePublisher,"makePublisher");export function handleClientEnvelope(r,e,t){let o;try{o=JSON.parse(r)}catch{return}if(!(!o||typeof o!="object"||!("kind"in o))){if(o.kind==="batch"){if(!Array.isArray(o.events))return;for(const n of o.events.slice(0,g))!n||typeof n!="object"||!("kind"in n)||n.kind!=="batch"&&d(n,e,t);return}d(o,e,t)}}s(handleClientEnvelope,"handleClientEnvelope"),i(handleClientEnvelope,"handleClientEnvelope");function d(r,e,t){let o;try{o=P(r,t)}catch{return}if(o)try{e(o)}catch{}}s(d,"l"),i(d,"mapAndPublish");export function readTabId(r){try{const e=JSON.parse(r);return typeof e?.tabId=="string"&&e.tabId?e.tabId:void 0}catch{return}}s(readTabId,"readTabId"),i(readTabId,"readTabId");export async function createBridgeServer(r={}){const e=r.host??h,t=r.port??S,o=t===0?await findFreePort():t,n=makePublisher(r),a=new y({host:e,port:o,maxPayload:E});return a.on("connection",c=>{let f;c.on("message",u=>{const l=u.toString();f??=readTabId(l),handleClientEnvelope(l,n,r.sessionId)}),c.on("close",()=>{f&&handleClientEnvelope(JSON.stringify({kind:"gone",at:Date.now(),tabId:f}),n,r.sessionId)})}),await new Promise((c,f)=>{const u=i(p=>{a.off("listening",l),f(p)},"onError"),l=i(()=>{a.off("error",u),c()},"onListening");a.once("error",u),a.once("listening",l)}),{port:o,host:e,close:i(()=>new Promise(c=>{a.close(()=>c())}),"close")}}s(createBridgeServer,"createBridgeServer"),i(createBridgeServer,"createBridgeServer");export function findFreePort(){return new Promise((r,e)=>{const t=v();t.unref(),t.on("error",e),t.listen(0,()=>{const o=t.address();if(!o){t.close(),e(new Error("Could not allocate free port"));return}const n=o.port;t.close(()=>r(n))})})}s(findFreePort,"findFreePort"),i(findFreePort,"findFreePort");
|
package/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var S=Object.defineProperty;var n=(s,e)=>S(s,"name",{value:e,configurable:!0});var d=Object.defineProperty,i=n((s,e)=>d(s,"name",{value:e,configurable:!0}),"n");import{DEFAULT_WS_HOST as _,DEFAULT_WS_PORT as g,ENV_SESSION_ID as v,ENV_WS_HOST as u,ENV_WS_PORT as a}from"./lib/constants.js";import{createBridgeServer as T}from"./lib/server.js";async function l(){const s=process.env[u]??_,e=process.env[a],o=e!==void 0&&e!==""?Number(e):g;(!Number.isFinite(o)||o<0)&&(console.error(`[lensmcp-bridge] invalid ${a}=${e}`),process.exit(2));const p=process.env[v];let r;try{r=await T({host:s,port:o,sessionId:p})}catch(m){console.error(`[lensmcp-bridge] failed to listen on ws://${s}:${o} \u2014 ${m.message}`),process.exit(1)}console.log(`[lensmcp-bridge] listening on ws://${r.host}:${r.port}`);let t=!1;const c=i(()=>{t||(t=!0,r.close().then(()=>process.exit(0)))},"shutdown");process.on("SIGINT",c),process.on("SIGTERM",c)}n(l,"g"),i(l,"main"),l();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.22.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@lensmcp/core": "1.
|
|
23
|
-
"@lensmcp/protocol-types": "1.
|
|
22
|
+
"@lensmcp/core": "1.22.0",
|
|
23
|
+
"@lensmcp/protocol-types": "1.22.0",
|
|
24
24
|
"tslib": "^2.3.0",
|
|
25
25
|
"ws": "^8.21.0"
|
|
26
26
|
},
|