@lensmcp/cluster 1.23.7 → 1.23.8
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/main.devserver.js +7 -7
- package/package.json +4 -4
- package/stdio-guard.d.ts +19 -0
- package/stdio-guard.js +1 -0
package/main.devserver.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var Oe=Object.defineProperty;var u=(n,h)=>Oe(n,"name",{value:h,configurable:!0});var Ce=Object.defineProperty,i=u((n,h)=>Ce(n,"name",{value:h,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DEBUG_FIXED_PORTS_ENV=void 0,exports.isOwnerOnlyTransport=isOwnerOnlyTransport,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor,exports.redactInspectorUrls=redactInspectorUrls,exports.inspectorPortOf=inspectorPortOf;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),node_child_process_1=require("node:child_process"),http=tslib_1.__importStar(require("node:http")),readline=tslib_1.__importStar(require("node:readline")),inspector=tslib_1.__importStar(require("node:inspector")),util=tslib_1.__importStar(require("node:util")),v8=tslib_1.__importStar(require("node:v8")),vm=tslib_1.__importStar(require("node:vm")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),schema_first_instance_1=require("./schema-first-instance"),redis_disconnect_fix_1=require("./redis-disconnect-fix"),error_stack_release_1=require("./error-stack-release"),swap_recycle_1=require("./swap-recycle"),httpProxy=require("http-proxy"),{prettyFactory}=require("pino-pretty"),BUNDLE_PATH=process.env.BUNDLE_PATH?.trim()||"./main.js",SERVICE_NAME=process.env.SERVICE_NAME||"",SERVICE_PREFIX=process.env.SERVE_PREFIX?`/${process.env.SERVE_PREFIX}`:"",GATEWAY_MIDDLEWARE_PATH=process.env.GATEWAY_MIDDLEWARE||"",GATEWAY_CONFIG=(()=>{const n=process.env.GATEWAY_CONFIG;if(!n)return{};try{return JSON.parse(n)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const n=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof n=="function"?n:typeof n.default=="function"?n.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(n){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,n)}function applyGatewayMiddleware(n){if(gatewayMiddleware)try{gatewayMiddleware(n,GATEWAY_CONFIG)}catch(h){console.error("[gateway] Middleware error:",h)}}u(applyGatewayMiddleware,"applyGatewayMiddleware"),i(applyGatewayMiddleware,"applyGatewayMiddleware");const GATEWAY_ROUTES=(()=>{try{return JSON.parse(process.env.GATEWAY_ROUTES||"[]")}catch{return[]}})(),GATEWAY_HTTPS=process.env.GATEWAY_HTTPS==="1";function hostMatches(n,h){if(n.startsWith("*.")){const g=n.slice(1);return h.endsWith(g)||h===n.slice(2)}return h===n}u(hostMatches,"hostMatches"),i(hostMatches,"hostMatches");function matchGatewayRoute(n){if(GATEWAY_ROUTES.length===0)return;const h=(n.headers.host||"").split(":")[0],g=n.url||"/";for(const _ of GATEWAY_ROUTES)if(!(_.host&&!hostMatches(_.host,h))&&!(_.prefix&&!g.startsWith(_.prefix)))return _}u(matchGatewayRoute,"matchGatewayRoute"),i(matchGatewayRoute,"matchGatewayRoute");const WORKER_NAMES=(()=>{try{return JSON.parse(process.env.WORKERS||"[]")}catch{return[]}})(),LENSMCP_WS_KEY=process.env.LENSMCP_WS_KEY||"",SOCK_DIR=LENSMCP_WS_KEY?path.join(os.tmpdir(),LENSMCP_WS_KEY,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`),POD_GENERATION=(()=>{const n=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(n)&&n>1?Math.floor(n):1})();function childSockPath(n){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${n}-g${POD_GENERATION}.sock`:`child-${n}.sock`)}u(childSockPath,"childSockPath"),i(childSockPath,"childSockPath");function sockOwnerPath(n){return`${n}.owner`}u(sockOwnerPath,"sockOwnerPath"),i(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(n){return`${n}.retiring`}u(sockRetiringPath,"sockRetiringPath"),i(sockRetiringPath,"sockRetiringPath");function isOwnerOnlyTransport(n){try{return typeof n.socket?.server?.address?.()=="string"}catch{return!1}}u(isOwnerOnlyTransport,"isOwnerOnlyTransport"),i(isOwnerOnlyTransport,"isOwnerOnlyTransport"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(n=process.env){const h=(n[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return h==="1"||h==="true"||h==="yes"||h==="on"}u(usesFixedInspectorPorts,"usesFixedInspectorPorts"),i(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(n,h,g=process.env){if(!usesFixedInspectorPorts(g))return 0;const _=Math.trunc(n)+Math.trunc(h);return Number.isInteger(_)&&_>1024&&_<=65535?_:0}u(inspectorPortFor,"inspectorPortFor"),i(inspectorPortFor,"inspectorPortFor");const INSPECTOR_TOKEN_RE=/((?:wss?:\/\/[^\s/]+\/|ws=[^\s/]+\/))([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi;function redactInspectorUrls(n){return n.replace(INSPECTOR_TOKEN_RE,"$1<inspector-token-redacted>")}u(redactInspectorUrls,"redactInspectorUrls"),i(redactInspectorUrls,"redactInspectorUrls");function inspectorPortOf(n){const h=/^wss?:\/\/[^/]*?:(\d+)\//.exec(n??""),g=h?Number(h[1]):NaN;return Number.isInteger(g)&&g>0?g:null}u(inspectorPortOf,"inspectorPortOf"),i(inspectorPortOf,"inspectorPortOf");function ensureOwnerOnlyDir(n){try{fs.mkdirSync(n,{recursive:!0,mode:448})}catch{}try{const h=fs.lstatSync(n);h.isDirectory()&&(h.mode&63)!==0&&fs.chmodSync(n,448)}catch{}}u(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),i(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(n,h=process.pid){ensureOwnerOnlyDir(path.dirname(n));try{const g=sockOwnerPath(n);try{fs.unlinkSync(g)}catch{}fs.writeFileSync(g,String(h),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(n)}catch{}try{fs.unlinkSync(sockRetiringPath(n))}catch{}}u(claimSock,"claimSock"),i(claimSock,"claimSock");function cleanupSock(n,h=process.pid){if(n){try{const g=fs.readFileSync(sockOwnerPath(n),"utf8").trim();if(g&&g!==String(h))return}catch{}try{fs.unlinkSync(n)}catch{}try{fs.unlinkSync(sockOwnerPath(n))}catch{}try{fs.unlinkSync(sockRetiringPath(n))}catch{}}}u(cleanupSock,"cleanupSock"),i(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const n=process.env.LENSMCP_SERVICE_KEY;if(!n)return;const{createHash:h}=require("node:crypto");return h("sha256").update(n).digest("hex").slice(0,8)}u(serviceKeyFingerprint,"serviceKeyFingerprint"),i(serviceKeyFingerprint,"serviceKeyFingerprint");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const requested=path.isAbsolute(spec)?spec:path.join(__dirname,spec);let resolved=requested;try{resolved=fs.realpathSync(requested)}catch{}return eval(`(() => {
|
|
1
|
+
"use strict";var Oe=Object.defineProperty;var u=(n,h)=>Oe(n,"name",{value:h,configurable:!0});var Ce=Object.defineProperty,i=u((n,h)=>Ce(n,"name",{value:h,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DEBUG_FIXED_PORTS_ENV=void 0,exports.isOwnerOnlyTransport=isOwnerOnlyTransport,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor,exports.redactInspectorUrls=redactInspectorUrls,exports.inspectorPortOf=inspectorPortOf;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),node_child_process_1=require("node:child_process"),http=tslib_1.__importStar(require("node:http")),readline=tslib_1.__importStar(require("node:readline")),inspector=tslib_1.__importStar(require("node:inspector")),util=tslib_1.__importStar(require("node:util")),v8=tslib_1.__importStar(require("node:v8")),vm=tslib_1.__importStar(require("node:vm")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),schema_first_instance_1=require("./schema-first-instance"),redis_disconnect_fix_1=require("./redis-disconnect-fix"),error_stack_release_1=require("./error-stack-release"),swap_recycle_1=require("./swap-recycle"),stdio_guard_1=require("./stdio-guard"),httpProxy=require("http-proxy"),{prettyFactory}=require("pino-pretty"),BUNDLE_PATH=process.env.BUNDLE_PATH?.trim()||"./main.js",SERVICE_NAME=process.env.SERVICE_NAME||"",SERVICE_PREFIX=process.env.SERVE_PREFIX?`/${process.env.SERVE_PREFIX}`:"",GATEWAY_MIDDLEWARE_PATH=process.env.GATEWAY_MIDDLEWARE||"",GATEWAY_CONFIG=(()=>{const n=process.env.GATEWAY_CONFIG;if(!n)return{};try{return JSON.parse(n)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const n=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof n=="function"?n:typeof n.default=="function"?n.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(n){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,n)}function applyGatewayMiddleware(n){if(gatewayMiddleware)try{gatewayMiddleware(n,GATEWAY_CONFIG)}catch(h){console.error("[gateway] Middleware error:",h)}}u(applyGatewayMiddleware,"applyGatewayMiddleware"),i(applyGatewayMiddleware,"applyGatewayMiddleware");const GATEWAY_ROUTES=(()=>{try{return JSON.parse(process.env.GATEWAY_ROUTES||"[]")}catch{return[]}})(),GATEWAY_HTTPS=process.env.GATEWAY_HTTPS==="1";function hostMatches(n,h){if(n.startsWith("*.")){const g=n.slice(1);return h.endsWith(g)||h===n.slice(2)}return h===n}u(hostMatches,"hostMatches"),i(hostMatches,"hostMatches");function matchGatewayRoute(n){if(GATEWAY_ROUTES.length===0)return;const h=(n.headers.host||"").split(":")[0],g=n.url||"/";for(const _ of GATEWAY_ROUTES)if(!(_.host&&!hostMatches(_.host,h))&&!(_.prefix&&!g.startsWith(_.prefix)))return _}u(matchGatewayRoute,"matchGatewayRoute"),i(matchGatewayRoute,"matchGatewayRoute");const WORKER_NAMES=(()=>{try{return JSON.parse(process.env.WORKERS||"[]")}catch{return[]}})(),LENSMCP_WS_KEY=process.env.LENSMCP_WS_KEY||"",SOCK_DIR=LENSMCP_WS_KEY?path.join(os.tmpdir(),LENSMCP_WS_KEY,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`),POD_GENERATION=(()=>{const n=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(n)&&n>1?Math.floor(n):1})();function childSockPath(n){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${n}-g${POD_GENERATION}.sock`:`child-${n}.sock`)}u(childSockPath,"childSockPath"),i(childSockPath,"childSockPath");function sockOwnerPath(n){return`${n}.owner`}u(sockOwnerPath,"sockOwnerPath"),i(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(n){return`${n}.retiring`}u(sockRetiringPath,"sockRetiringPath"),i(sockRetiringPath,"sockRetiringPath");function isOwnerOnlyTransport(n){try{return typeof n.socket?.server?.address?.()=="string"}catch{return!1}}u(isOwnerOnlyTransport,"isOwnerOnlyTransport"),i(isOwnerOnlyTransport,"isOwnerOnlyTransport"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(n=process.env){const h=(n[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return h==="1"||h==="true"||h==="yes"||h==="on"}u(usesFixedInspectorPorts,"usesFixedInspectorPorts"),i(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(n,h,g=process.env){if(!usesFixedInspectorPorts(g))return 0;const _=Math.trunc(n)+Math.trunc(h);return Number.isInteger(_)&&_>1024&&_<=65535?_:0}u(inspectorPortFor,"inspectorPortFor"),i(inspectorPortFor,"inspectorPortFor");const INSPECTOR_TOKEN_RE=/((?:wss?:\/\/[^\s/]+\/|ws=[^\s/]+\/))([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi;function redactInspectorUrls(n){return n.replace(INSPECTOR_TOKEN_RE,"$1<inspector-token-redacted>")}u(redactInspectorUrls,"redactInspectorUrls"),i(redactInspectorUrls,"redactInspectorUrls");function inspectorPortOf(n){const h=/^wss?:\/\/[^/]*?:(\d+)\//.exec(n??""),g=h?Number(h[1]):NaN;return Number.isInteger(g)&&g>0?g:null}u(inspectorPortOf,"inspectorPortOf"),i(inspectorPortOf,"inspectorPortOf");function ensureOwnerOnlyDir(n){try{fs.mkdirSync(n,{recursive:!0,mode:448})}catch{}try{const h=fs.lstatSync(n);h.isDirectory()&&(h.mode&63)!==0&&fs.chmodSync(n,448)}catch{}}u(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),i(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(n,h=process.pid){ensureOwnerOnlyDir(path.dirname(n));try{const g=sockOwnerPath(n);try{fs.unlinkSync(g)}catch{}fs.writeFileSync(g,String(h),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(n)}catch{}try{fs.unlinkSync(sockRetiringPath(n))}catch{}}u(claimSock,"claimSock"),i(claimSock,"claimSock");function cleanupSock(n,h=process.pid){if(n){try{const g=fs.readFileSync(sockOwnerPath(n),"utf8").trim();if(g&&g!==String(h))return}catch{}try{fs.unlinkSync(n)}catch{}try{fs.unlinkSync(sockOwnerPath(n))}catch{}try{fs.unlinkSync(sockRetiringPath(n))}catch{}}}u(cleanupSock,"cleanupSock"),i(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const n=process.env.LENSMCP_SERVICE_KEY;if(!n)return;const{createHash:h}=require("node:crypto");return h("sha256").update(n).digest("hex").slice(0,8)}u(serviceKeyFingerprint,"serviceKeyFingerprint"),i(serviceKeyFingerprint,"serviceKeyFingerprint");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const requested=path.isAbsolute(spec)?spec:path.join(__dirname,spec);let resolved=requested;try{resolved=fs.realpathSync(requested)}catch{}return eval(`(() => {
|
|
2
2
|
const key = ${JSON.stringify(resolved)};
|
|
3
3
|
const previous = require.cache[key];
|
|
4
4
|
delete require.cache[key]; // delete from CJS cache
|
|
@@ -9,13 +9,13 @@
|
|
|
9
9
|
const at = owner && Array.isArray(owner.children) ? owner.children.indexOf(previous) : -1;
|
|
10
10
|
if (at >= 0) owner.children.splice(at, 1);
|
|
11
11
|
}
|
|
12
|
-
})()`),eval(`require(${JSON.stringify(resolved)})`)}u(importFresh,"importFresh"),i(importFresh,"importFresh");function debounce(n,h){let g=null;return(..._)=>{g&&clearTimeout(g),g=setTimeout(()=>n(..._),h)}}if(u(debounce,"debounce"),i(debounce,"debounce"),process.env.APP_RUNNER==="1"){const n=i(g=>{if(!inspector.url())try{inspector.open(g,"127.0.0.1",!1)}catch(B){console.warn(`[child] inspector.open(${g}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,B.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const _=inspector.url();_&&process.send&&process.send({type:"inspector-url",url:_})},"openInspector"),h=process.env.CHILD_DEBUG_PORT;if(h!==void 0&&h!==""){const g=Number(h);Number.isInteger(g)&&(g===0||g>1024)&&n(g)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let g=null,_=!1,B,j=!1;const v=(0,hot_swap_registries_1.registryPruningEnabled)(),Z=i((t,s)=>{const f=(0,hot_swap_registries_1.describePrune)(t,s);f&&console.log(f)},"logPrune");let $={app:null,handler:i((t,s)=>{s.statusCode=503,s.end("starting")},"handler"),inflight:0};const ee=(()=>{const t=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(t)&&t>=0?t:15*6e4})(),Y=i(t=>{const s=globalThis.__lensmcpActiveWork;if(typeof s!="function")return{count:0};try{const f=s(t);return{count:Number(f?.count??0),...f?.oldestStartedAt?{oldestStartedAt:f.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),te=(()=>{const t=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(t)&&t>=1?Math.floor(t):1})(),x=new Set,P=(0,swap_recycle_1.swapRecyclePolicy)();let oe,le=0;const ge=(()=>{const t=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(t)&&t>=0?t:3500})();let K=!1,V=!1,d;const E=i(()=>{if(d===void 0)try{v8.setFlagsFromString("--expose-gc");const t=vm.runInNewContext("gc");d=typeof t=="function"?t:null}catch{d=null}try{d?.()}catch{}return process.memoryUsage().heapUsed},"liveHeap"),w=(()=>{if(!(0,error_stack_release_1.errorFrameReleaseEnabled)())return null;try{const t=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH))+path.sep,s=`${path.sep}node_modules${path.sep}`,f=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:i(y=>f.has(y)||y.startsWith(t)&&!y.slice(t.length).includes(s)&&!y.slice(t.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let O=!1;function W(){if(!(!w||O))try{const t=w();t.errors>0&&console.log(`[child] read the stack of ${t.errors} Error object(s) that libraries created while loading and keep for good \u2014 unread, each held the functions that were on its stack, this bundle's included`)}catch(t){console.error("[child] could not release library error frames:",t),O=!0}}u(W,"z"),i(W,"releaseLibraryErrorFrames");const D=new Map;let b=Date.now();const T=i(()=>P.settleMs<=0||!_&&!j&&D.size===0&&Date.now()-b>=P.settleMs,"settled");let R;function A(t){if(!(!P.enabled||K||V)){if(P.settleMs<=0){F(t);return}R&&clearTimeout(R),R=setTimeout(()=>{R=void 0,T()?F(t):A(t)},P.settleMs),R.unref?.()}}u(A,"N"),i(A,"recycleCheckWhenSettled");const ne=i(()=>{if(P.stuckCloseMs<=0)return 0;const t=Date.now();let s=0;for(const f of D.values())t-f>=P.stuckCloseMs&&(s+=1);return s},"stuckCloses");function F(t){if(!P.enabled||K||V||!process.send)return;const s=v8.getHeapStatistics().heap_size_limit,f=i(m=>({swaps:le,usedBytes:m,limitBytes:s,stuckCloses:ne(),settled:T(),...oe===void 0?{}:{baselineBytes:oe}}),"state");if(!(0,swap_recycle_1.recycleReason)(P,f(process.memoryUsage().heapUsed)))return;const y=(0,swap_recycle_1.recycleReason)(P,f(E()));y&&(K=!0,console.log(`[child] asking for a fresh pod ${t} \u2014 ${y}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),process.send({type:"recycle-request",reason:y}))}u(F,"K"),i(F,"considerRecycle");async function J(t,s){if(t.closed||(t.closed=!0,x.delete(t),s&&console.error(`[child] ${s}`),!t.app))return;D.set(t,Date.now());const f=P.enabled&&P.stuckCloseMs>0?setTimeout(()=>{D.has(t)&&(console.error(`[child] a previous app has been closing for ${Math.round(P.stuckCloseMs/1e3)}s and its close() has not returned \u2014 one of its shutdown hooks is waiting on something that does not end (a queue worker, a connection, a stream). It stays in memory until this pod is replaced.`),F("with a previous app stuck in close()"))},P.stuckCloseMs):void 0;f?.unref?.();try{await t.app.close()}catch(y){console.error("[child] error closing a previous app:",y)}finally{D.delete(t),b=Date.now(),f&&clearTimeout(f),t.app=null,t.handler=(y,m)=>{m.statusCode=503,m.end("closed")}}if(t.superseded){try{Z((0,hot_swap_registries_1.pruneRegistries)(t.superseded,"serve"),"after closing the previous app")}catch(y){console.error("[child] could not release the previous app's class registrations:",y)}t.superseded=void 0}A("after closing the previous app")}u(J,"Q"),i(J,"closeGeneration");async function pe(t){for(;x.size>=te;){const m=x.values().next().value;if(!m)break;await J(m,`closing a still-draining generation early \u2014 ${x.size} were pending and the cap is ${te} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}x.add(t);const s=Date.now(),f=i(()=>({inflight:t.inflight,work:Y(t.workGen).count}),"busy");for(;ee>0&&Date.now()-s<ee;){if(t.closed)return;const m=f();if(m.inflight===0&&m.work===0)break;await new Promise(C=>{setTimeout(C,250).unref?.()})}if(t.closed)return;const y=f();y.inflight>0||y.work>0?console.error(`[child] swap drain expired after ${Date.now()-s}ms \u2014 closing the previous app with ${y.inflight} in-flight request(s) and ${y.work} background job(s) still running. A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${ee}ms) if this is legitimate work.`):Date.now()-s>250&&console.log(`[child] previous app drained in ${Date.now()-s}ms \u2014 closing it`),await J(t)}u(pe,"he"),i(pe,"drainThenClose");async function M(){if(_)return j=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),g;_=!0,b=Date.now(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(S,L)=>{console.error(`[child] uncaughtException (${L}):`,S)}),process.on("unhandledRejection",S=>{const L=S instanceof Error?S.stack||S.message:String(S);L.includes("FronteggContextScope")||L.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",S)});let t;if(v)try{t=(0,hot_swap_registries_1.captureRegistries)()}catch{t=void 0}try{try{await importFresh(BUNDLE_PATH)}catch(S){if(!(S instanceof SyntaxError))throw S;console.warn(`[child] bundle was unreadable mid-write (${S.message}) \u2014 retrying the swap once`),await new Promise(L=>setTimeout(L,400)),await importFresh(BUNDLE_PATH)}}catch(S){throw _=!1,b=Date.now(),S}if(typeof global.createChildApp!="function")throw _=!1,b=Date.now(),new Error(`Bundle '${BUNDLE_PATH}' does not export createChildApp() and the zero-touch bridge is not active. Either install @lensmcp/node-instrumentation (a plain NestFactory.create + app.listen main.ts then just works) or set global.createChildApp in main.ts when DEVSERVER_MODE === '1'.`);let s;if(t)try{s=(0,hot_swap_registries_1.supersededSince)(t),Z((0,hot_swap_registries_1.pruneRegistries)(s,"build"),"at the swap")}catch(S){console.error("[child] could not release superseded class registrations:",S)}const f=global.__lensmcpBeginWorkGeneration,y=typeof f=="function"?f():void 0;let m;try{m=await global.createChildApp()}catch(S){throw _=!1,b=Date.now(),S}const C=$;return s?.size&&(C.app?C.superseded=s:Z((0,hot_swap_registries_1.pruneRegistries)(s,"serve"),"at the swap")),g=m,$={app:m,handler:m.handler,inflight:0,...y===void 0?{}:{workGen:y}},console.log("[child] Swapped to fresh app (previous draining in background)"),W(),C.app&&(le+=1,pe(C).catch(S=>console.error("[child] background close error:",S)),setTimeout(()=>F("30s after a hot swap"),3e4).unref?.()),_=!1,b=Date.now(),j?(j=!1,M()):m}u(M,"V"),i(M,"swapNow");const re=debounce(async()=>{try{console.log("[child] /webpack/reload"),await M()}catch(t){console.error("[child] reload failed:",t)}},250),G=http.createServer((t,s)=>{if(t.method==="POST"&&t.url==="/webpack/reload"){s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!0})),re();return}if(t.method==="GET"&&t.url==="/__lensmcp/alive"){const y=t.headers["x-internal-token"],m=process.env.LENSMCP_SERVICE_KEY;if(m&&y!==m){s.writeHead(404,{"content-type":"text/plain"}),s.end("not found");return}const C=Y();s.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),s.end(JSON.stringify({ok:!0,pid:process.pid,swapping:_,inflight:$.inflight,oldestInflightAt:$.oldestAt,activeWork:C.count,oldestWorkAt:C.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...B?{watcher:B}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const f=$;f.inflight+=1,f.oldestAt===void 0&&(f.oldestAt=Date.now()),s.on("close",()=>{f.inflight-=1,f.inflight===0&&(f.oldestAt=void 0)}),f.handler(t,s)});G.keepAliveTimeout=0;const I=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(I),process.exit(0)});try{const t=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);if((0,schema_first_instance_1.schemaPrewarmEnabled)())try{const s=(0,schema_first_instance_1.prewarmZodFor)(t);s?.created&&console.log(`[child] zod${s.version?` ${s.version}`:""}: built ${s.created} throwaway schemas first, so none of the bundle's own is the first of its class`)}catch{}if((0,redis_disconnect_fix_1.redisDisconnectFixEnabled)())try{const s=(0,redis_disconnect_fix_1.finishStrandedDisconnectsFor)(t);s?.patched&&console.log(`[child] ioredis${s.versions.length?` ${s.versions.join(", ")}`:""}: a disconnect() between reconnect attempts now ends the client \u2014 it used to leave a queue worker's close() waiting for good`)}catch{}await M(),P.enabled&&P.heapFactor>0&&(oe=E()),console.log(`[child] Using bundle: ${t}`),claimSock(I),G.listen(I,()=>{console.log(`[child] Listening on ${I}`),process.send&&process.send({type:"ready",socketPath:I})})}catch(t){console.error("[child] Startup error:",t),process.send&&process.send({type:"boot-error",error:String(t?.stack||t)}),process.exitCode=1;return}process.on("message",async t=>{if(!(!t||typeof t!="object")){if(t.type==="debug-open"){const s=Number(t.port);n(Number.isInteger(s)&&s>1024?s:0);return}if(t.type==="watcher"){const s=t.testimony;s&&typeof s=="object"&&(B=s);return}if(t.type==="recycle-declined"){K=!1;return}if(t.type==="retire"){U();return}if(t.type==="reload"){if(V)return;try{await M(),process.send&&process.send({type:"reloaded",socketPath:I})}catch(s){console.error("[child] reload error:",s),process.send&&process.send({type:"reload-error",error:String(s?.stack||s)})}}}});async function U(){if(V)return;V=!0,console.log("[child] retiring \u2014 a fresh pod has taken over; finishing in-flight work, then exiting");try{fs.writeFileSync(sockRetiringPath(I),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(s=>{setTimeout(s,ge).unref?.()}),cleanupSock(I),G.close();const t=Date.now();for(;Date.now()-t<ee&&(G.closeIdleConnections?.(),!($.inflight===0&&Y().count===0));)await new Promise(s=>{setTimeout(s,250).unref?.()});setTimeout(()=>process.exit(0),3e4).unref?.();try{await g?.close()}catch(s){console.error("[child] close error while retiring:",s)}process.exit(0)}u(U,"ee"),i(U,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const X=i(async t=>{console.log(`[child ${t}] shutting down\u2026`);const s=g?.close().catch(y=>console.error("[child] close error:",y)),f=new Promise(y=>G.close(()=>y()));cleanupSock(I),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([s,f]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(t=>{process.on(t,()=>{inspector?.close?.(),X(t)})})})()}else{let n=u(function(d){x.push(d),x.length>te&&x.splice(0,x.length-te),ee?.write(d.replace(Y,""));for(const E of P)try{const w=E.plain?d.replace(Y,""):d;E.res.write(E.redact?redactInspectorUrls(w):w)}catch{P.delete(E)}},"P");i(n,"emitLogLine");const h=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],g="\x1B[0m",_=Math.max(1,Number(process.env.CHILD_COUNT||1)),B=LENSMCP_WS_KEY||path.basename(process.cwd()),j=i(d=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(B,SERVICE_NAME||"service",d)),"execPathFor");let v=(()=>{const d=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(d)&&d>1024?d:0})();const Z=20;v&&(console.warn("[parent] DEBUGGER ENABLED \u2014 pods and workers open a Node inspector. Node's inspector has NO authentication: the ws:// URL's UUID is the whole credential, and any account on this host that obtains it can run code as you. Do not enable this on a shared machine. (issues/codex-security/06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${v}+slot, workers ${v}+${Z}+i). Any account on this host can connect to 127.0.0.1:${v} and read the session UUID from /json/list. Unset it to use ephemeral ports.`:`[parent] Inspectors bind an EPHEMERAL loopback port; attach via the printed "Debugger listening" URL, or \`lensmcp debug <service>\` (control socket). Set ${exports.DEBUG_FIXED_PORTS_ENV}=1 for fixed ports if your IDE needs a pre-configured attach port \u2014 any account on this host can then reach them.`));const $=process.env.LENSMCP_LOG_FILE||"",ee=$?(()=>{try{fs.mkdirSync(path.dirname($),{recursive:!0});const d=fs.createWriteStream($,{flags:"a",mode:384});try{fs.chmodSync($,384)}catch{}return d}catch(d){return console.warn(`[parent] cannot open log file ${$}:`,d.message),null}})():null,Y=/\x1b\[[0-9;]*m/g,te=2e3,x=[],P=new Set;for(const d of["log","info","warn","error"]){const E=console[d].bind(console);console[d]=(...w)=>{E(...w),n(w.map(O=>typeof O=="string"?O:util.inspect(O)).join(" ")+`
|
|
13
|
-
`)}}const oe=i(
|
|
12
|
+
})()`),eval(`require(${JSON.stringify(resolved)})`)}u(importFresh,"importFresh"),i(importFresh,"importFresh");function debounce(n,h){let g=null;return(..._)=>{g&&clearTimeout(g),g=setTimeout(()=>n(..._),h)}}if(u(debounce,"debounce"),i(debounce,"debounce"),process.env.APP_RUNNER==="1"){(0,stdio_guard_1.guardBrokenStdio)();const n=i(g=>{if(!inspector.url())try{inspector.open(g,"127.0.0.1",!1)}catch(B){console.warn(`[child] inspector.open(${g}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,B.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const _=inspector.url();_&&process.send&&process.send({type:"inspector-url",url:_})},"openInspector"),h=process.env.CHILD_DEBUG_PORT;if(h!==void 0&&h!==""){const g=Number(h);Number.isInteger(g)&&(g===0||g>1024)&&n(g)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let g=null,_=!1,B,j=!1;const v=(0,hot_swap_registries_1.registryPruningEnabled)(),Z=i((t,s)=>{const f=(0,hot_swap_registries_1.describePrune)(t,s);f&&console.log(f)},"logPrune");let $={app:null,handler:i((t,s)=>{s.statusCode=503,s.end("starting")},"handler"),inflight:0};const ee=(()=>{const t=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(t)&&t>=0?t:15*6e4})(),Y=i(t=>{const s=globalThis.__lensmcpActiveWork;if(typeof s!="function")return{count:0};try{const f=s(t);return{count:Number(f?.count??0),...f?.oldestStartedAt?{oldestStartedAt:f.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),te=(()=>{const t=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(t)&&t>=1?Math.floor(t):1})(),x=new Set,P=(0,swap_recycle_1.swapRecyclePolicy)();let oe,le=0;const ge=(()=>{const t=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(t)&&t>=0?t:3500})();let K=!1,V=!1,p;const E=i(()=>{if(p===void 0)try{v8.setFlagsFromString("--expose-gc");const t=vm.runInNewContext("gc");p=typeof t=="function"?t:null}catch{p=null}try{p?.()}catch{}return process.memoryUsage().heapUsed},"liveHeap"),w=(()=>{if(!(0,error_stack_release_1.errorFrameReleaseEnabled)())return null;try{const t=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH))+path.sep,s=`${path.sep}node_modules${path.sep}`,f=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:i(y=>f.has(y)||y.startsWith(t)&&!y.slice(t.length).includes(s)&&!y.slice(t.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let O=!1;function W(){if(!(!w||O))try{const t=w();t.errors>0&&console.log(`[child] read the stack of ${t.errors} Error object(s) that libraries created while loading and keep for good \u2014 unread, each held the functions that were on its stack, this bundle's included`)}catch(t){console.error("[child] could not release library error frames:",t),O=!0}}u(W,"z"),i(W,"releaseLibraryErrorFrames");const D=new Map;let b=Date.now();const T=i(()=>P.settleMs<=0||!_&&!j&&D.size===0&&Date.now()-b>=P.settleMs,"settled");let R;function A(t){if(!(!P.enabled||K||V)){if(P.settleMs<=0){F(t);return}R&&clearTimeout(R),R=setTimeout(()=>{R=void 0,T()?F(t):A(t)},P.settleMs),R.unref?.()}}u(A,"N"),i(A,"recycleCheckWhenSettled");const ne=i(()=>{if(P.stuckCloseMs<=0)return 0;const t=Date.now();let s=0;for(const f of D.values())t-f>=P.stuckCloseMs&&(s+=1);return s},"stuckCloses");function F(t){if(!P.enabled||K||V||!process.send)return;const s=v8.getHeapStatistics().heap_size_limit,f=i(m=>({swaps:le,usedBytes:m,limitBytes:s,stuckCloses:ne(),settled:T(),...oe===void 0?{}:{baselineBytes:oe}}),"state");if(!(0,swap_recycle_1.recycleReason)(P,f(process.memoryUsage().heapUsed)))return;const y=(0,swap_recycle_1.recycleReason)(P,f(E()));y&&(K=!0,console.log(`[child] asking for a fresh pod ${t} \u2014 ${y}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),process.send({type:"recycle-request",reason:y}))}u(F,"K"),i(F,"considerRecycle");async function J(t,s){if(t.closed||(t.closed=!0,x.delete(t),s&&console.error(`[child] ${s}`),!t.app))return;D.set(t,Date.now());const f=P.enabled&&P.stuckCloseMs>0?setTimeout(()=>{D.has(t)&&(console.error(`[child] a previous app has been closing for ${Math.round(P.stuckCloseMs/1e3)}s and its close() has not returned \u2014 one of its shutdown hooks is waiting on something that does not end (a queue worker, a connection, a stream). It stays in memory until this pod is replaced.`),F("with a previous app stuck in close()"))},P.stuckCloseMs):void 0;f?.unref?.();try{await t.app.close()}catch(y){console.error("[child] error closing a previous app:",y)}finally{D.delete(t),b=Date.now(),f&&clearTimeout(f),t.app=null,t.handler=(y,m)=>{m.statusCode=503,m.end("closed")}}if(t.superseded){try{Z((0,hot_swap_registries_1.pruneRegistries)(t.superseded,"serve"),"after closing the previous app")}catch(y){console.error("[child] could not release the previous app's class registrations:",y)}t.superseded=void 0}A("after closing the previous app")}u(J,"Q"),i(J,"closeGeneration");async function de(t){for(;x.size>=te;){const m=x.values().next().value;if(!m)break;await J(m,`closing a still-draining generation early \u2014 ${x.size} were pending and the cap is ${te} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}x.add(t);const s=Date.now(),f=i(()=>({inflight:t.inflight,work:Y(t.workGen).count}),"busy");for(;ee>0&&Date.now()-s<ee;){if(t.closed)return;const m=f();if(m.inflight===0&&m.work===0)break;await new Promise(C=>{setTimeout(C,250).unref?.()})}if(t.closed)return;const y=f();y.inflight>0||y.work>0?console.error(`[child] swap drain expired after ${Date.now()-s}ms \u2014 closing the previous app with ${y.inflight} in-flight request(s) and ${y.work} background job(s) still running. A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${ee}ms) if this is legitimate work.`):Date.now()-s>250&&console.log(`[child] previous app drained in ${Date.now()-s}ms \u2014 closing it`),await J(t)}u(de,"he"),i(de,"drainThenClose");async function M(){if(_)return j=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),g;_=!0,b=Date.now(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(S,L)=>{console.error(`[child] uncaughtException (${L}):`,S)}),process.on("unhandledRejection",S=>{const L=S instanceof Error?S.stack||S.message:String(S);L.includes("FronteggContextScope")||L.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",S)});let t;if(v)try{t=(0,hot_swap_registries_1.captureRegistries)()}catch{t=void 0}try{try{await importFresh(BUNDLE_PATH)}catch(S){if(!(S instanceof SyntaxError))throw S;console.warn(`[child] bundle was unreadable mid-write (${S.message}) \u2014 retrying the swap once`),await new Promise(L=>setTimeout(L,400)),await importFresh(BUNDLE_PATH)}}catch(S){throw _=!1,b=Date.now(),S}if(typeof global.createChildApp!="function")throw _=!1,b=Date.now(),new Error(`Bundle '${BUNDLE_PATH}' does not export createChildApp() and the zero-touch bridge is not active. Either install @lensmcp/node-instrumentation (a plain NestFactory.create + app.listen main.ts then just works) or set global.createChildApp in main.ts when DEVSERVER_MODE === '1'.`);let s;if(t)try{s=(0,hot_swap_registries_1.supersededSince)(t),Z((0,hot_swap_registries_1.pruneRegistries)(s,"build"),"at the swap")}catch(S){console.error("[child] could not release superseded class registrations:",S)}const f=global.__lensmcpBeginWorkGeneration,y=typeof f=="function"?f():void 0;let m;try{m=await global.createChildApp()}catch(S){throw _=!1,b=Date.now(),S}const C=$;return s?.size&&(C.app?C.superseded=s:Z((0,hot_swap_registries_1.pruneRegistries)(s,"serve"),"at the swap")),g=m,$={app:m,handler:m.handler,inflight:0,...y===void 0?{}:{workGen:y}},console.log("[child] Swapped to fresh app (previous draining in background)"),W(),C.app&&(le+=1,de(C).catch(S=>console.error("[child] background close error:",S)),setTimeout(()=>F("30s after a hot swap"),3e4).unref?.()),_=!1,b=Date.now(),j?(j=!1,M()):m}u(M,"V"),i(M,"swapNow");const re=debounce(async()=>{try{console.log("[child] /webpack/reload"),await M()}catch(t){console.error("[child] reload failed:",t)}},250),G=http.createServer((t,s)=>{if(t.method==="POST"&&t.url==="/webpack/reload"){s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({ok:!0})),re();return}if(t.method==="GET"&&t.url==="/__lensmcp/alive"){const y=t.headers["x-internal-token"],m=process.env.LENSMCP_SERVICE_KEY;if(m&&y!==m){s.writeHead(404,{"content-type":"text/plain"}),s.end("not found");return}const C=Y();s.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),s.end(JSON.stringify({ok:!0,pid:process.pid,swapping:_,inflight:$.inflight,oldestInflightAt:$.oldestAt,activeWork:C.count,oldestWorkAt:C.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...B?{watcher:B}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const f=$;f.inflight+=1,f.oldestAt===void 0&&(f.oldestAt=Date.now()),s.on("close",()=>{f.inflight-=1,f.inflight===0&&(f.oldestAt=void 0)}),f.handler(t,s)});G.keepAliveTimeout=0;const I=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(I),process.exit(0)});try{const t=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);if((0,schema_first_instance_1.schemaPrewarmEnabled)())try{const s=(0,schema_first_instance_1.prewarmZodFor)(t);s?.created&&console.log(`[child] zod${s.version?` ${s.version}`:""}: built ${s.created} throwaway schemas first, so none of the bundle's own is the first of its class`)}catch{}if((0,redis_disconnect_fix_1.redisDisconnectFixEnabled)())try{const s=(0,redis_disconnect_fix_1.finishStrandedDisconnectsFor)(t);s?.patched&&console.log(`[child] ioredis${s.versions.length?` ${s.versions.join(", ")}`:""}: a disconnect() between reconnect attempts now ends the client \u2014 it used to leave a queue worker's close() waiting for good`)}catch{}await M(),P.enabled&&P.heapFactor>0&&(oe=E()),console.log(`[child] Using bundle: ${t}`),claimSock(I),G.listen(I,()=>{console.log(`[child] Listening on ${I}`),process.send&&process.send({type:"ready",socketPath:I})})}catch(t){console.error("[child] Startup error:",t),process.send&&process.send({type:"boot-error",error:String(t?.stack||t)}),process.exitCode=1;return}process.on("message",async t=>{if(!(!t||typeof t!="object")){if(t.type==="debug-open"){const s=Number(t.port);n(Number.isInteger(s)&&s>1024?s:0);return}if(t.type==="watcher"){const s=t.testimony;s&&typeof s=="object"&&(B=s);return}if(t.type==="recycle-declined"){K=!1;return}if(t.type==="retire"){U();return}if(t.type==="reload"){if(V)return;try{await M(),process.send&&process.send({type:"reloaded",socketPath:I})}catch(s){console.error("[child] reload error:",s),process.send&&process.send({type:"reload-error",error:String(s?.stack||s)})}}}});async function U(){if(V)return;V=!0,console.log("[child] retiring \u2014 a fresh pod has taken over; finishing in-flight work, then exiting");try{fs.writeFileSync(sockRetiringPath(I),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(s=>{setTimeout(s,ge).unref?.()}),cleanupSock(I),G.close();const t=Date.now();for(;Date.now()-t<ee&&(G.closeIdleConnections?.(),!($.inflight===0&&Y().count===0));)await new Promise(s=>{setTimeout(s,250).unref?.()});setTimeout(()=>process.exit(0),3e4).unref?.();try{await g?.close()}catch(s){console.error("[child] close error while retiring:",s)}process.exit(0)}u(U,"ee"),i(U,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const X=i(async t=>{console.log(`[child ${t}] shutting down\u2026`);const s=g?.close().catch(y=>console.error("[child] close error:",y)),f=new Promise(y=>G.close(()=>y()));cleanupSock(I),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([s,f]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(t=>{process.on(t,()=>{inspector?.close?.(),X(t)})})})()}else{let n=u(function(p){x.push(p),x.length>te&&x.splice(0,x.length-te),ee?.write(p.replace(Y,""));for(const E of P)try{const w=E.plain?p.replace(Y,""):p;E.res.write(E.redact?redactInspectorUrls(w):w)}catch{P.delete(E)}},"P");i(n,"emitLogLine");const h=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],g="\x1B[0m",_=Math.max(1,Number(process.env.CHILD_COUNT||1)),B=LENSMCP_WS_KEY||path.basename(process.cwd()),j=i(p=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(B,SERVICE_NAME||"service",p)),"execPathFor");let v=(()=>{const p=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(p)&&p>1024?p:0})();const Z=20;v&&(console.warn("[parent] DEBUGGER ENABLED \u2014 pods and workers open a Node inspector. Node's inspector has NO authentication: the ws:// URL's UUID is the whole credential, and any account on this host that obtains it can run code as you. Do not enable this on a shared machine. (issues/codex-security/06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${v}+slot, workers ${v}+${Z}+i). Any account on this host can connect to 127.0.0.1:${v} and read the session UUID from /json/list. Unset it to use ephemeral ports.`:`[parent] Inspectors bind an EPHEMERAL loopback port; attach via the printed "Debugger listening" URL, or \`lensmcp debug <service>\` (control socket). Set ${exports.DEBUG_FIXED_PORTS_ENV}=1 for fixed ports if your IDE needs a pre-configured attach port \u2014 any account on this host can then reach them.`));const $=process.env.LENSMCP_LOG_FILE||"",ee=$?(()=>{try{fs.mkdirSync(path.dirname($),{recursive:!0});const p=fs.createWriteStream($,{flags:"a",mode:384});try{fs.chmodSync($,384)}catch{}return p}catch(p){return console.warn(`[parent] cannot open log file ${$}:`,p.message),null}})():null,Y=/\x1b\[[0-9;]*m/g,te=2e3,x=[],P=new Set;for(const p of["log","info","warn","error"]){const E=console[p].bind(console);console[p]=(...w)=>{E(...w),n(w.map(O=>typeof O=="string"?O:util.inspect(O)).join(" ")+`
|
|
13
|
+
`)}}const oe=i(p=>h[(p-1)%h.length],"colorFor"),le=i(p=>{if(_===1)return"";const E=`[child#${p}] `;return`${oe(p)}${E}${g}`},"tagFor"),ge=i(p=>p.startsWith("{")&&p.endsWith("}"),"isJsonLog"),K=i((p,E,w)=>{const O=prettyFactory({sync:!0,colorize:!0,crlf:!0,messageKey:"message",errorLikeObjectKeys:["err","error"],errorProps:"type,message,stack",ignore:["logContext","context","hostname","req","res","err.driverError","module","cloudEnvironment","frontegg-application-id","frontegg-tenant-id","frontegg-trace-id","frontegg-vendor-id","host","service","version","err","error"].join(","),messageFormat:"{if module}[{module}] {end}{if context}[{context}] {end}{if logContext}[{logContext}] {end}{message}",customPrettifiers:{stack:i(b=>`
|
|
14
14
|
`+String(b),"stack")}}),W=i((b,T)=>{const R=b==="stdout"?process.stdout:process.stderr;try{w?.(T)}catch{}const A=ge(T)?O(T):`${T}
|
|
15
|
-
`;R.write(`${E}${A}`),n(`${E}${A}`)},"write"),D=i((b,T)=>{if(!b)return;const R=readline.createInterface({input:b});R.on("line",A=>W(T,A)),R.on("close",()=>{})},"attach");D(d.stdout,"stdout"),D(d.stderr,"stderr")},"wireProcLogging"),V=i(d=>K(d.proc,le(d.id),E=>d.tail.push(E)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const d=httpProxy.createProxyServer({});let E=Number(process.env.PORT)||0;E||(E=9090);const w=[],O=new Set;function W(){let e=0;for(;O.has(e);)e++;return O.add(e),e}u(W,"z"),i(W,"allocDebugSlot");let D=1,b=0,T=!1,R=_,A=[],ne=0;const F=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),J=i(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),pe=i(e=>e.booting&&!J(e)&&Date.now()-e.spawnedAt<F,"stillBooting");function M(){return w.filter(e=>e.healthy&&typeof e.socketPath=="string")}u(M,"V"),i(M,"healthyChildren");let re;function G(e){if(!e||typeof e!="object")return;const r=e;if(r.schemaVersion===1){re=r;for(const p of w)try{p.proc.send?.({type:"watcher",testimony:r})}catch{}}}u(G,"U"),i(G,"recordWatcherTestimony");function I(e,r=256*1024){return new Promise(p=>{let a="",c=!1;e.on("data",o=>{c||(a+=o,a.length>r&&(c=!0))}),e.on("end",()=>{if(c||!a)return p(void 0);try{p(JSON.parse(a))}catch{p(void 0)}}),e.on("error",()=>p(void 0))})}u(I,"v"),i(I,"readJsonBody");function U(){const e=M();if(e.length===0)return null;const r=b%e.length;return b=(b+1)%e.length,e[r]}u(U,"ee"),i(U,"pickChild");async function X(){const e=D++,r=childSockPath(e);cleanupSock(r);const p=["--enable-source-maps"];__filename.endsWith(".ts")&&p.unshift("--require","@swc-node/register");const a=v?W():-1,c=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:r,...a>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(v,a))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:p,execPath:j("pod")}),o={id:e,proc:c,healthy:!1,lastError:null,debugSlot:a,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:ne};return c.on("message",k=>{if(!k||typeof k!="object")return;const l=k;if(l.type==="ready"){if(o.socketPath=String(l.socketPath),o.healthy=!0,o.booting=!1,console.log(`[parent] Child#${o.id} ready on socket ${o.socketPath}`),re)try{c.send?.({type:"watcher",testimony:re})}catch{}if(ne!==o.bootReloadSeq)try{c.send?.({type:"reload"})}catch{}}else l.type==="recycle-request"?L(o,String(l.reason||"it has outgrown its bound")):l.type==="inspector-url"?(o.inspectorUrl=String(l.url),console.log(`[parent] Child#${o.id} Debugger listening on ${l.url}`)):l.type==="reloaded"?(o.socketPath=String(l.socketPath),o.healthy=!0,console.log(`[parent] Child#${o.id} hot-swapped on socket ${o.socketPath}`)):l.type==="reload-error"?(o.lastError=String(l.error||"unknown reload error"),console.error(`[parent] Child#${o.id} reload error: ${o.lastError}`),o.healthy=!1):l.type==="boot-error"&&(o.lastError=String(l.error||"unknown boot error"),console.error(`[parent] Child#${o.id} boot error: ${o.lastError}`),o.healthy=!1,o.booting=!1)}),c.on("exit",(k,l)=>{o.booting=!1,o.debugSlot>=0&&(O.delete(o.debugSlot),o.debugSlot=-1);const N=l==="SIGINT"||l==="SIGTERM"||k===0;if(N?console.log(`[parent] Child#${o.id} exited cleanly (code=${k}, signal=${l??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${o.id} crashed (code=${k}, signal=${l??"none"}, pid=${o.proc.pid}, ${o.healthy?"was serving":"was not ready"})`,k,l,o.tail.snapshot())),o.lastError||(o.lastError=`Child exited abnormally (code=${k}, signal=${l??"none"})`)),o.healthy=!1,cleanupSock(o.socketPath,o.proc.pid),o.socketPath=void 0,!N&&!T){const q=w.indexOf(o);q>=0&&w.splice(q,1);const ae=Date.now();if(A=A.filter(Q=>ae-Q<3e4),A.length>=5){console.error(`[parent] crash-loop detected (${A.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}A.push(ae),setTimeout(()=>{const Q=w.filter($e=>$e.proc.killed===!1).length;!T&&Q<R&&(console.log(`[parent] respawning a crashed pod (${Q}/${R} live)`),X().catch(()=>{}))},1500).unref?.()}}),w.push(o),V(o),o}u(X,"te"),i(X,"spawnChild");async function t(e){R=Math.max(R,e);const r=w.filter(a=>a.proc.killed===!1),p=e-r.length;for(let a=0;a<p;a++)await X()}u(t,"r"),i(t,"ensurePoolSize");async function s(){const e=w.filter(r=>(!r.healthy||typeof r.socketPath!="string")&&!pe(r));await Promise.all(e.map(async r=>{try{r.proc.kill("SIGTERM")}catch{}cleanupSock(r.socketPath,r.proc.pid),r.socketPath=void 0;const p=w.indexOf(r);p>=0&&w.splice(p,1),await X()}))}u(s,"c"),i(s,"respawnUnhealthy");const f=new Set;let y=!1,m=0;const C=3e4,S=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function L(e,r){const p=i(c=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${r}) \u2014 not now: ${c}`);try{e.proc.send?.({type:"recycle-declined"})}catch{}},"decline");if(T||!w.includes(e))return;if(y)return p("another pod is already being replaced");const a=Date.now()-m;if(a<C)return p(`a pod was replaced ${Math.round(a/1e3)}s ago`);y=!0,console.log(`[parent] Child#${e.id} asked for a fresh pod \u2014 ${r}. Starting one; Child#${e.id} keeps serving until it is ready.`);try{const c=await X(),o=Date.now()+F;for(;!c.healthy&&!c.lastError&&!J(c)&&Date.now()<o;)await new Promise(N=>{setTimeout(N,100).unref?.()});if(T)return;if(!c.healthy){const N=w.indexOf(c);N>=0&&w.splice(N,1);try{c.proc.kill("SIGTERM")}catch{}return p(`its successor Child#${c.id} did not come up (${c.lastError??"no answer in time"})`)}const k=w.indexOf(e);k>=0&&w.splice(k,1),e.healthy=!1,f.add(e);const l=setTimeout(()=>{if(!J(e)){console.error(`[parent] Child#${e.id} was still running ${S}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},S);l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),f.delete(e)});try{e.proc.send?.({type:"retire"})}catch{try{e.proc.kill("SIGTERM")}catch{}}m=Date.now(),console.log(`[parent] Child#${c.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${c.proc.pid})`)}catch(c){p(`could not start a successor: ${c?.message??String(c)}`)}finally{y=!1}}u(L,"x"),i(L,"recyclePod");const H=[];function ye(e){const r=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),p=path.join(r,`${e}.js`);if(!fs.existsSync(p))return console.error(`[parent] Worker "${e}" bundle not found: ${p}`),null;const a=["--enable-source-maps"];__filename.endsWith(".ts")&&a.unshift("--require","@swc-node/register");try{a.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const c=inspectorPortFor(v||9229,Z+Math.max(0,WORKER_NAMES.indexOf(e)));a.push(v?`--inspect=127.0.0.1:${c}`:`--inspect-port=127.0.0.1:${c}`);const o=(0,node_child_process_1.fork)(p,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:a,execPath:j(`w-${e}`)}),k={name:e,proc:o,debugPort:c};return H.push(k),K(o,`\x1B[35m[worker:${e}] \x1B[0m`,l=>{const N=/Debugger listening on (wss?:\/\/\S+)/.exec(l);N&&(k.inspectorUrl=N[1])}),o.on("exit",(l,N)=>{N==="SIGINT"||N==="SIGTERM"||l===0||console.error(`[parent] Worker "${e}" crashed (code=${l}, signal=${N??"none"})`);const q=H.indexOf(k);q>=0&&H.splice(q,1)}),console.log(`[parent] Worker "${e}" started (pid=${o.pid})`),k}u(ye,"we"),i(ye,"spawnWorkerProcess");const me=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),Se=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),se=new Set,Pe=i(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function de(e,r){if(se.delete(e),!Pe(e)){console.error(`[parent] Worker "${e.name}" (pid=${e.proc.pid}) ${r} \u2014 killing it. A worker that returns from its SIGTERM handler but leaves a handle open (a Redis client handed to BullMQ is not closed by worker.close()) never exits on its own: close what it opened, or call process.exit().`);try{e.proc.kill("SIGKILL")}catch{}}}u(de,"fe"),i(de,"killDrainingWorker");function ke(e){try{e.proc.kill("SIGTERM")}catch{}if(Pe(e))return;const r=[...se].filter(a=>a.name===e.name);for(;r.length>=Se;)de(r.shift(),`was still running when a later reload replaced its successor (cap ${Se})`);se.add(e);const p=setTimeout(()=>de(e,`was still running ${me}ms after SIGTERM`),me);p.unref?.(),e.proc.once("exit",()=>{clearTimeout(p),se.delete(e)})}u(ke,"Ae"),i(ke,"retireWorker");function be(){for(const e of[...H])ke(e);H.length=0;for(const e of WORKER_NAMES)ye(e)}u(be,"Te"),i(be,"restartWorkers");const Ae=debounce(async()=>{ne+=1;const e=M();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const r of e)r.proc.send?.({type:"reload"})}await s(),WORKER_NAMES.length>0&&be()},200),ie=i(async(e,r)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0})),I(e).then(c=>G(c?.watcher)),Ae();return}if(e.method==="POST"&&e.url==="/webpack/watching"){const c=await I(e);G(c),r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,relayed:re!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){let c="";e.on("data",o=>{c+=o}),e.on("end",async()=>{let o=0;try{o=Number(JSON.parse(c||"{}").pods)||0}catch{}o=Math.max(1,Math.min(o,16)),await t(o),r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,pods:o}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const c=new URL(e.url,"http://localhost").searchParams,o=Math.min(Math.max(Number(c.get("tail")??"100")||0,0),te),k=c.get("follow")!=="0",l=c.get("plain")==="1",N=!isOwnerOnlyTransport(e);r.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const q=o===0?"":x.slice(-o).join(""),ae=l?q.replace(Y,""):q;if(r.write(N?redactInspectorUrls(ae):ae),!k){r.end();return}const Q={res:r,plain:l,redact:N};P.add(Q),e.on("close",()=>P.delete(Q));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!isOwnerOnlyTransport(e)){r.writeHead(404,{"content-type":"application/json"}),r.end(JSON.stringify({error:"not found"}));return}const c=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(c)&&c>1024&&(v=c),v||(v=9229);const o=w.filter(l=>l.proc.exitCode===null&&l.proc.signalCode===null);for(const l of o){l.debugSlot<0&&(l.debugSlot=W());try{l.proc.send?.({type:"debug-open",port:inspectorPortFor(v,l.debugSlot)})}catch{}}for(const l of H)try{l.proc.kill("SIGUSR1")}catch{}const k=Date.now()+2e3;for(;Date.now()<k&&o.some(l=>!l.inspectorUrl);)await new Promise(l=>setTimeout(l,50));r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,basePort:v,fixedPorts:usesFixedInspectorPorts(),pods:o.map(l=>({id:l.id,inspectorUrl:l.inspectorUrl??null})),workers:H.map(l=>({name:l.name,port:l.debugPort||inspectorPortOf(l.inspectorUrl),inspectorUrl:l.inspectorUrl??null}))},null,2)+`
|
|
16
|
-
`);return}const
|
|
15
|
+
`;R.write(`${E}${A}`),n(`${E}${A}`)},"write"),D=i((b,T)=>{if(!b)return;const R=readline.createInterface({input:b});R.on("line",A=>W(T,A)),R.on("close",()=>{})},"attach");D(p.stdout,"stdout"),D(p.stderr,"stderr")},"wireProcLogging"),V=i(p=>K(p.proc,le(p.id),E=>p.tail.push(E)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const p=httpProxy.createProxyServer({});let E=Number(process.env.PORT)||0;E||(E=9090);const w=[],O=new Set;function W(){let e=0;for(;O.has(e);)e++;return O.add(e),e}u(W,"z"),i(W,"allocDebugSlot");let D=1,b=0,T=!1,R=_,A=[],ne=0;const F=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),J=i(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),de=i(e=>e.booting&&!J(e)&&Date.now()-e.spawnedAt<F,"stillBooting");function M(){return w.filter(e=>e.healthy&&typeof e.socketPath=="string")}u(M,"V"),i(M,"healthyChildren");let re;function G(e){if(!e||typeof e!="object")return;const r=e;if(r.schemaVersion===1){re=r;for(const d of w)try{d.proc.send?.({type:"watcher",testimony:r})}catch{}}}u(G,"U"),i(G,"recordWatcherTestimony");function I(e,r=256*1024){return new Promise(d=>{let a="",c=!1;e.on("data",o=>{c||(a+=o,a.length>r&&(c=!0))}),e.on("end",()=>{if(c||!a)return d(void 0);try{d(JSON.parse(a))}catch{d(void 0)}}),e.on("error",()=>d(void 0))})}u(I,"v"),i(I,"readJsonBody");function U(){const e=M();if(e.length===0)return null;const r=b%e.length;return b=(b+1)%e.length,e[r]}u(U,"ee"),i(U,"pickChild");async function X(){const e=D++,r=childSockPath(e);cleanupSock(r);const d=["--enable-source-maps"];__filename.endsWith(".ts")&&d.unshift("--require","@swc-node/register");const a=v?W():-1,c=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:r,...a>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(v,a))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:d,execPath:j("pod")}),o={id:e,proc:c,healthy:!1,lastError:null,debugSlot:a,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:ne};return c.on("message",k=>{if(!k||typeof k!="object")return;const l=k;if(l.type==="ready"){if(o.socketPath=String(l.socketPath),o.healthy=!0,o.booting=!1,console.log(`[parent] Child#${o.id} ready on socket ${o.socketPath}`),re)try{c.send?.({type:"watcher",testimony:re})}catch{}if(ne!==o.bootReloadSeq)try{c.send?.({type:"reload"})}catch{}}else l.type==="recycle-request"?L(o,String(l.reason||"it has outgrown its bound")):l.type==="inspector-url"?(o.inspectorUrl=String(l.url),console.log(`[parent] Child#${o.id} Debugger listening on ${l.url}`)):l.type==="reloaded"?(o.socketPath=String(l.socketPath),o.healthy=!0,console.log(`[parent] Child#${o.id} hot-swapped on socket ${o.socketPath}`)):l.type==="reload-error"?(o.lastError=String(l.error||"unknown reload error"),console.error(`[parent] Child#${o.id} reload error: ${o.lastError}`),o.healthy=!1):l.type==="boot-error"&&(o.lastError=String(l.error||"unknown boot error"),console.error(`[parent] Child#${o.id} boot error: ${o.lastError}`),o.healthy=!1,o.booting=!1)}),c.on("exit",(k,l)=>{o.booting=!1,o.debugSlot>=0&&(O.delete(o.debugSlot),o.debugSlot=-1);const N=l==="SIGINT"||l==="SIGTERM"||k===0;if(N?console.log(`[parent] Child#${o.id} exited cleanly (code=${k}, signal=${l??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${o.id} crashed (code=${k}, signal=${l??"none"}, pid=${o.proc.pid}, ${o.healthy?"was serving":"was not ready"})`,k,l,o.tail.snapshot())),o.lastError||(o.lastError=`Child exited abnormally (code=${k}, signal=${l??"none"})`)),o.healthy=!1,cleanupSock(o.socketPath,o.proc.pid),o.socketPath=void 0,!N&&!T){const q=w.indexOf(o);q>=0&&w.splice(q,1);const ae=Date.now();if(A=A.filter(Q=>ae-Q<3e4),A.length>=5){console.error(`[parent] crash-loop detected (${A.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}A.push(ae),setTimeout(()=>{const Q=w.filter($e=>$e.proc.killed===!1).length;!T&&Q<R&&(console.log(`[parent] respawning a crashed pod (${Q}/${R} live)`),X().catch(()=>{}))},1500).unref?.()}}),w.push(o),V(o),o}u(X,"te"),i(X,"spawnChild");async function t(e){R=Math.max(R,e);const r=w.filter(a=>a.proc.killed===!1),d=e-r.length;for(let a=0;a<d;a++)await X()}u(t,"r"),i(t,"ensurePoolSize");async function s(){const e=w.filter(r=>(!r.healthy||typeof r.socketPath!="string")&&!de(r));await Promise.all(e.map(async r=>{try{r.proc.kill("SIGTERM")}catch{}cleanupSock(r.socketPath,r.proc.pid),r.socketPath=void 0;const d=w.indexOf(r);d>=0&&w.splice(d,1),await X()}))}u(s,"c"),i(s,"respawnUnhealthy");const f=new Set;let y=!1,m=0;const C=3e4,S=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function L(e,r){const d=i(c=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${r}) \u2014 not now: ${c}`);try{e.proc.send?.({type:"recycle-declined"})}catch{}},"decline");if(T||!w.includes(e))return;if(y)return d("another pod is already being replaced");const a=Date.now()-m;if(a<C)return d(`a pod was replaced ${Math.round(a/1e3)}s ago`);y=!0,console.log(`[parent] Child#${e.id} asked for a fresh pod \u2014 ${r}. Starting one; Child#${e.id} keeps serving until it is ready.`);try{const c=await X(),o=Date.now()+F;for(;!c.healthy&&!c.lastError&&!J(c)&&Date.now()<o;)await new Promise(N=>{setTimeout(N,100).unref?.()});if(T)return;if(!c.healthy){const N=w.indexOf(c);N>=0&&w.splice(N,1);try{c.proc.kill("SIGTERM")}catch{}return d(`its successor Child#${c.id} did not come up (${c.lastError??"no answer in time"})`)}const k=w.indexOf(e);k>=0&&w.splice(k,1),e.healthy=!1,f.add(e);const l=setTimeout(()=>{if(!J(e)){console.error(`[parent] Child#${e.id} was still running ${S}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},S);l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),f.delete(e)});try{e.proc.send?.({type:"retire"})}catch{try{e.proc.kill("SIGTERM")}catch{}}m=Date.now(),console.log(`[parent] Child#${c.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${c.proc.pid})`)}catch(c){d(`could not start a successor: ${c?.message??String(c)}`)}finally{y=!1}}u(L,"x"),i(L,"recyclePod");const H=[];function ye(e){const r=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),d=path.join(r,`${e}.js`);if(!fs.existsSync(d))return console.error(`[parent] Worker "${e}" bundle not found: ${d}`),null;const a=["--enable-source-maps"];__filename.endsWith(".ts")&&a.unshift("--require","@swc-node/register");try{a.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const c=inspectorPortFor(v||9229,Z+Math.max(0,WORKER_NAMES.indexOf(e)));a.push(v?`--inspect=127.0.0.1:${c}`:`--inspect-port=127.0.0.1:${c}`);const o=(0,node_child_process_1.fork)(d,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:a,execPath:j(`w-${e}`)}),k={name:e,proc:o,debugPort:c};return H.push(k),K(o,`\x1B[35m[worker:${e}] \x1B[0m`,l=>{const N=/Debugger listening on (wss?:\/\/\S+)/.exec(l);N&&(k.inspectorUrl=N[1])}),o.on("exit",(l,N)=>{N==="SIGINT"||N==="SIGTERM"||l===0||console.error(`[parent] Worker "${e}" crashed (code=${l}, signal=${N??"none"})`);const q=H.indexOf(k);q>=0&&H.splice(q,1)}),console.log(`[parent] Worker "${e}" started (pid=${o.pid})`),k}u(ye,"we"),i(ye,"spawnWorkerProcess");const me=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),Se=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),se=new Set,Pe=i(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function pe(e,r){if(se.delete(e),!Pe(e)){console.error(`[parent] Worker "${e.name}" (pid=${e.proc.pid}) ${r} \u2014 killing it. A worker that returns from its SIGTERM handler but leaves a handle open (a Redis client handed to BullMQ is not closed by worker.close()) never exits on its own: close what it opened, or call process.exit().`);try{e.proc.kill("SIGKILL")}catch{}}}u(pe,"fe"),i(pe,"killDrainingWorker");function ke(e){try{e.proc.kill("SIGTERM")}catch{}if(Pe(e))return;const r=[...se].filter(a=>a.name===e.name);for(;r.length>=Se;)pe(r.shift(),`was still running when a later reload replaced its successor (cap ${Se})`);se.add(e);const d=setTimeout(()=>pe(e,`was still running ${me}ms after SIGTERM`),me);d.unref?.(),e.proc.once("exit",()=>{clearTimeout(d),se.delete(e)})}u(ke,"Ae"),i(ke,"retireWorker");function be(){for(const e of[...H])ke(e);H.length=0;for(const e of WORKER_NAMES)ye(e)}u(be,"Te"),i(be,"restartWorkers");const Ae=debounce(async()=>{ne+=1;const e=M();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const r of e)r.proc.send?.({type:"reload"})}await s(),WORKER_NAMES.length>0&&be()},200),ie=i(async(e,r)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0})),I(e).then(c=>G(c?.watcher)),Ae();return}if(e.method==="POST"&&e.url==="/webpack/watching"){const c=await I(e);G(c),r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,relayed:re!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){let c="";e.on("data",o=>{c+=o}),e.on("end",async()=>{let o=0;try{o=Number(JSON.parse(c||"{}").pods)||0}catch{}o=Math.max(1,Math.min(o,16)),await t(o),r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,pods:o}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const c=new URL(e.url,"http://localhost").searchParams,o=Math.min(Math.max(Number(c.get("tail")??"100")||0,0),te),k=c.get("follow")!=="0",l=c.get("plain")==="1",N=!isOwnerOnlyTransport(e);r.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const q=o===0?"":x.slice(-o).join(""),ae=l?q.replace(Y,""):q;if(r.write(N?redactInspectorUrls(ae):ae),!k){r.end();return}const Q={res:r,plain:l,redact:N};P.add(Q),e.on("close",()=>P.delete(Q));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!isOwnerOnlyTransport(e)){r.writeHead(404,{"content-type":"application/json"}),r.end(JSON.stringify({error:"not found"}));return}const c=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(c)&&c>1024&&(v=c),v||(v=9229);const o=w.filter(l=>l.proc.exitCode===null&&l.proc.signalCode===null);for(const l of o){l.debugSlot<0&&(l.debugSlot=W());try{l.proc.send?.({type:"debug-open",port:inspectorPortFor(v,l.debugSlot)})}catch{}}for(const l of H)try{l.proc.kill("SIGUSR1")}catch{}const k=Date.now()+2e3;for(;Date.now()<k&&o.some(l=>!l.inspectorUrl);)await new Promise(l=>setTimeout(l,50));r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,basePort:v,fixedPorts:usesFixedInspectorPorts(),pods:o.map(l=>({id:l.id,inspectorUrl:l.inspectorUrl??null})),workers:H.map(l=>({name:l.name,port:l.debugPort||inspectorPortOf(l.inspectorUrl),inspectorUrl:l.inspectorUrl??null}))},null,2)+`
|
|
16
|
+
`);return}const d=matchGatewayRoute(e);if(d?.prependPrefix&&!e.url?.startsWith(d.prependPrefix)&&(e.url=d.prependPrefix+(e.url??"/")),d?.target){applyGatewayMiddleware(e),p.web(e,r,{target:d.target,autoRewrite:typeof d.target=="string",changeOrigin:!0},c=>{console.error(`[parent] proxy error to ${d.target}:`,c),r.statusCode=502,r.setHeader("content-type","text/plain; charset=utf-8"),r.end(`Upstream error from ${d.target}.`)});return}if(SERVICE_PREFIX){if(!e.url?.startsWith(SERVICE_PREFIX)){r.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),r.end(`Not found. This service is mounted at ${SERVICE_PREFIX}/`);return}e.url=e.url.slice(SERVICE_PREFIX.length)||"/"}applyGatewayMiddleware(e);const a=U();if(!a){r.statusCode=503,r.setHeader("content-type","text/plain; charset=utf-8");const c=w.map(o=>`#${o.id}: ${o.lastError??"no error recorded"}`).join(`
|
|
17
17
|
`);r.end(`No healthy children available. POST /webpack/reload to recover.
|
|
18
18
|
|
|
19
19
|
Last errors:
|
|
20
|
-
${c}`);return}
|
|
21
|
-
${a.lastError}`)})},"requestHandler");let z,ce,we;const ue=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),r=GATEWAY_ROUTES.map(a=>a.host).filter(a=>!!a),
|
|
20
|
+
${c}`);return}p.web(e,r,{target:{socketPath:a.socketPath},headers:{"x-proxy-child-id":String(a.id)}},c=>{console.error(`[parent] proxy error to Child#${a.id}:`,c),a.healthy=!1,a.lastError=String(c?.stack||c),r.statusCode=502,r.setHeader("content-type","text/plain; charset=utf-8"),r.end(`Upstream error from Child#${a.id}. Try /webpack/reload.
|
|
21
|
+
${a.lastError}`)})},"requestHandler");let z,ce,we;const ue=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),r=GATEWAY_ROUTES.map(a=>a.host).filter(a=>!!a),d=path.join(process.cwd(),"node_modules",".cache","davnx-webpack");ce=e.getCertificateSync(d,SERVICE_NAME||"lensmcp.dev",r),we=e.caCertPath(),z=require("node:https").createServer({key:ce,cert:ce},ie)}else z=http.createServer(ie);const Ne=i((e,r,d)=>{const a=matchGatewayRoute(e);if(a?.prependPrefix&&!e.url?.startsWith(a.prependPrefix)&&(e.url=a.prependPrefix+(e.url??"/")),a?.target){p.ws(e,r,d,{target:a.target,changeOrigin:!0},()=>r.destroy());return}const c=U();if(!c){r.destroy();return}p.ws(e,r,d,{target:{socketPath:c.socketPath}},()=>r.destroy())},"upgradeHandler");z.on("upgrade",Ne);const Te=2e3;let ve=!1;z.on("error",e=>{if(e.code==="EADDRINUSE"){ve||(ve=!0,console.warn(`[parent] public port ${E} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${Te/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!T&&!z.listening&&z.listen(E)},Te).unref?.();return}console.error("[parent] public server error:",e)}),await t(_);for(const e of WORKER_NAMES)ye(e);z.listen(E,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${E}`),SERVICE_PREFIX&&console.log(`[parent] Service prefix: "${SERVICE_PREFIX}" (enforced \u2014 requests without it will get 404)`),gatewayMiddleware&&console.log(`[parent] Gateway middleware: ACTIVE (${GATEWAY_MIDDLEWARE_PATH})`),GATEWAY_ROUTES.length>0&&console.log(`[parent] Gateway routes: ${GATEWAY_ROUTES.map(r=>`${r.host??"*"}${r.prefix??"/"}\u2192${r.target??"children"}`).join(" ")}`),we&&(console.log("[parent] HTTPS dev CA (trust ONCE, then rotations/new hostnames stay green):"),console.log(`[parent] sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${we}`)),console.log(`POST ${e}://localhost:${E}/webpack/reload to trigger rolling swap/respawn`)});const he=Number(process.env.INTERNAL_PORT||0);if(he>0){const e=i((d,a)=>{const c=U();if(!c){a.statusCode=503,a.setHeader("content-type","text/plain; charset=utf-8"),a.end("No healthy children available.");return}p.web(d,a,{target:{socketPath:c.socketPath}},o=>{c.healthy=!1,c.lastError=String(o?.stack||o),a.statusCode=502,a.end(`Upstream error from Child#${c.id}.`)})},"internalHandler"),r=http.createServer(e);r.on("upgrade",(d,a,c)=>{const o=U();if(!o){a.destroy();return}p.ws(d,a,c,{target:{socketPath:o.socketPath}},()=>a.destroy())}),r.on("error",d=>{console.warn(`[parent] internal port ${he} unavailable (${d.code}).`)}),r.listen(he,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${he}`)}),ue.push(r)}const Ie=(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of Ie){if(!Number.isInteger(e)||e===Number(E))continue;const r=GATEWAY_HTTPS?require("node:https").createServer({key:ce,cert:ce},ie):http.createServer(ie);r.on("upgrade",Ne),r.on("error",d=>{console.warn(`[parent] extra port ${e} unavailable (${d.code}) \u2014 main port still up.`)}),r.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),ue.push(r)}const _e=path.join(SOCK_DIR,"parent.sock");claimSock(_e);const Ee=http.createServer(ie);Ee.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),Ee.listen(_e),ue.push(Ee);const Re=i(async e=>{T=!0,console.log(`[${e}] [parent] shutting down\u2026`),cleanupSock(_e);for(const r of ue)try{r.close()}catch{}for(const r of H)try{r.proc.kill("SIGTERM")}catch{}for(const r of[...se])pe(r,"was still draining when its devserver shut down");for(const r of f)try{r.proc.kill("SIGTERM")}catch{}for(const r of w){try{r.proc.kill("SIGTERM")}catch{}r.socketPath&&cleanupSock(r.socketPath,r.proc.pid)}try{fs.rmdirSync(SOCK_DIR)}catch{}setTimeout(()=>process.exit(0),700).unref()},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(e=>process.on(e,()=>{Re(e)}));const fe=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(fe)&&fe>0){const e=setInterval(()=>{try{process.kill(fe,0)}catch(r){if(r.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${fe}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),Re("gateway-gone")}},5e3);e.unref()}})()}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/cluster",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.8",
|
|
4
4
|
"description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -57,9 +57,9 @@
|
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@lensmcp/core": "1.23.
|
|
61
|
-
"@lensmcp/node-instrumentation": "1.23.
|
|
62
|
-
"@lensmcp/nx-plugin": "1.23.
|
|
60
|
+
"@lensmcp/core": "1.23.8",
|
|
61
|
+
"@lensmcp/node-instrumentation": "1.23.8",
|
|
62
|
+
"@lensmcp/nx-plugin": "1.23.8",
|
|
63
63
|
"fastify": "^5.8.5",
|
|
64
64
|
"@fastify/reply-from": "^12.6.2",
|
|
65
65
|
"@typescript/native-preview": "^7.0.0-dev.20260707.2",
|
package/stdio-guard.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pod child's stdout and stderr are PIPES to its devserver parent, and the parent can go first
|
|
3
|
+
* (issues/foodguard/25). A write to a pipe nobody reads fails with EPIPE, which Node delivers as an
|
|
4
|
+
* `'error'` EVENT on the stream; with no listener that is an uncaught exception. The devserver's
|
|
5
|
+
* catch-all then printed `[child] uncaughtException (uncaughtException): Error: write EPIPE`, and the
|
|
6
|
+
* stdout tap filed it — stack and all — as a server-side error. So a `lensmcp setup` or a gateway
|
|
7
|
+
* restart that caught a pod mid-boot left the lens reporting a failing workspace over a pod that had
|
|
8
|
+
* only been told to stop.
|
|
9
|
+
*
|
|
10
|
+
* `console.*` already ignores stream errors. What does not is a DIRECT `process.stdout.write`, which
|
|
11
|
+
* is how Nest's `ConsoleLogger` prints — so the guard sits on the streams themselves.
|
|
12
|
+
*
|
|
13
|
+
* EPIPE only. It means exactly "the reader is gone", which for a pod is its own teardown: the IPC
|
|
14
|
+
* `disconnect` guard or the parent's SIGTERM ends the process either way, and there is nobody left to
|
|
15
|
+
* tell. Any other stream error is re-thrown, so it reaches the handler it reached before.
|
|
16
|
+
*/
|
|
17
|
+
import type { EventEmitter } from 'node:events';
|
|
18
|
+
/** Swallow EPIPE on this process's stdout/stderr. Idempotent — a stream is guarded once. */
|
|
19
|
+
export declare function guardBrokenStdio(streams?: readonly EventEmitter[]): void;
|
package/stdio-guard.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var c=Object.defineProperty;var r=(o,e)=>c(o,"name",{value:e,configurable:!0});var d=Object.defineProperty,s=r((o,e)=>d(o,"name",{value:e,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.guardBrokenStdio=guardBrokenStdio;function guardBrokenStdio(o=[process.stdout,process.stderr]){for(const e of o){const t=e;t.__lensmcpStdioGuard||(t.__lensmcpStdioGuard=!0,e.on("error",n=>{if(n?.code!=="EPIPE")throw n}))}}r(guardBrokenStdio,"guardBrokenStdio"),s(guardBrokenStdio,"guardBrokenStdio");
|