@lensmcp/cluster 1.24.3 → 1.24.4

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.
@@ -1 +1 @@
1
- "use strict";var T=Object.defineProperty;var p=(r,e)=>T(r,"name",{value:e,configurable:!0});var O=Object.defineProperty,c=p((r,e)=>O(r,"name",{value:e,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DevBuildFinalizePlugin=exports.DevServerReloadPlugin=exports.devserverControlSock=exports.DevserverNotifier=void 0,exports.devCacheOptions=devCacheOptions,exports.devModuleFilename=devModuleFilename,exports.createDevWebpackConfig=createDevWebpackConfig;const tslib_1=require("tslib"),app_plugin_1=require("@nx/webpack/app-plugin"),node_crypto_1=require("node:crypto"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),webpack_node_externals_1=tslib_1.__importDefault(require("webpack-node-externals")),build_scope_patterns_1=require("./build-scope-patterns"),checkout_cache_1=require("./checkout-cache"),dev_devtool_1=require("./dev-devtool"),tsgo_check_plugin_1=require("./tsgo-check-plugin"),fork_ts_check_reporter_1=require("./fork-ts-check-reporter"),perf_rules_plugin_1=require("./perf/perf-rules-plugin"),devserver_notifier_1=require("./devserver-notifier"),workspace_paths_1=require("./workspace-paths");var devserver_notifier_2=require("./devserver-notifier");Object.defineProperty(exports,"DevserverNotifier",{enumerable:!0,get:c(function(){return devserver_notifier_2.DevserverNotifier},"get")}),Object.defineProperty(exports,"devserverControlSock",{enumerable:!0,get:c(function(){return devserver_notifier_2.devserverControlSock},"get")});const webpack=require("webpack");class DevServerReloadPlugin{static{p(this,"DevServerReloadPlugin")}static{c(this,"DevServerReloadPlugin")}constructor(e,n=!1,a,o=Number(process.env.LENSMCP_GATEWAY_PID??0)>0){this.notifier=new devserver_notifier_1.DevserverNotifier(e,n,a,o)}snapshot(){return this.notifier.snapshot()}apply(e){const n="DevServerReloadPlugin";let a=!1;e.hooks.watchRun?.tap(n,o=>{const s=[...o?.modifiedFiles??[],...o?.removedFiles??[]].filter(i=>typeof i=="string");if(!a){const i=o?.watchFileSystem?.watcher;i&&typeof i.on=="function"&&(a=!0,i.on("error",l=>this.notifier.degrade(`watcher error: ${l?.message??String(l)}`)))}this.notifier.watchRun(s)}),e.hooks.watchClose?.tap(n,()=>this.notifier.degrade("watch closed")),e.hooks.failed?.tap(n,o=>this.notifier.degrade(`compiler failed: ${o?.message??String(o)}`)),e.hooks.done.tap(n,async o=>{if(o.hasErrors()){this.notifier.tellWatching();return}await this.notifier.built()})}}exports.DevServerReloadPlugin=DevServerReloadPlugin;const DEV_CACHE_MEMORY_GENERATIONS=5,DEV_CACHE_MAX_AGE_MS=10080*60*1e3,LOCKFILES=["yarn.lock","package-lock.json","pnpm-lock.yaml","bun.lock"];function clusterVersion(){for(const r of[path.join(__dirname,"package.json"),path.join(__dirname,"..","package.json")])try{const e=JSON.parse(fs.readFileSync(r,"utf8"));if(e.name==="@lensmcp/cluster"&&e.version)return e.version}catch{}return"unknown"}p(clusterVersion,"clusterVersion"),c(clusterVersion,"clusterVersion");function devCacheOptions(r,e){const{appName:n,appRoot:a,workspaceRoot:o,tsConfig:s,webpackConfigPath:i}=r,l=clusterVersion(),_=JSON.stringify({lensmcp:l,devtool:e,buildLibsFromSource:r.buildLibsFromSource??!0,orgScopes:r.orgScopes??[],bundlePackages:r.bundlePackages??[],nodeExternalsConfig:r.nodeExternalsConfig??null}),g=c(f=>f.filter(d=>fs.existsSync(d)),"existing"),k=(0,workspace_paths_1.readWorkspacePaths)(s,o)?.configFiles??[path.resolve(o,s)],v={lensmcp:[__filename],lockfile:g(LOCKFILES.map(f=>path.join(o,f))),tsconfig:g([...new Set([...k,path.join(o,"tsconfig.base.json")])])};return i&&(v.overrides=[path.resolve(a,i)]),{type:"filesystem",cacheDirectory:(0,checkout_cache_1.checkoutCacheDir)(o,"lensmcp-webpack"),name:n.replace(/[^\w.-]+/g,"_"),version:`${l}-${(0,node_crypto_1.createHash)("sha256").update(_).digest("hex").slice(0,16)}`,buildDependencies:v,maxMemoryGenerations:DEV_CACHE_MEMORY_GENERATIONS,maxAge:DEV_CACHE_MAX_AGE_MS}}p(devCacheOptions,"devCacheOptions"),c(devCacheOptions,"devCacheOptions");function devModuleFilename(r){const e=r.absoluteResourcePath;return path.isAbsolute(e)?`file://${e}`:e}p(devModuleFilename,"devModuleFilename"),c(devModuleFilename,"devModuleFilename");class DevBuildFinalizePlugin{static{p(this,"DevBuildFinalizePlugin")}static{c(this,"DevBuildFinalizePlugin")}constructor(e){this.options=e}apply(e){const{cache:n}=this.options;e.options.cache={...n,buildDependencies:{...n.buildDependencies}},e.options.context&&(e.context=e.options.context);const a=e.options.resolve?.plugins??[];if(a.some(o=>o instanceof workspace_paths_1.WorkspacePathsResolverPlugin)){const o=new Set(a);e.hooks.afterPlugins.tap("DevBuildFinalizePlugin",()=>{const s=e.options.resolve;s.plugins=s.plugins.filter(i=>o.has(i)||i?.constructor?.name!=="TsconfigPathsPlugin")})}if(this.options.tsSourceMaps)for(const o of e.options.module?.rules??[])typeof o?.loader!="string"||!/[\\/]ts-loader[\\/]/.test(o.loader)||(o.options={...o.options,compilerOptions:{...o.options?.compilerOptions,sourceMap:!0,inlineSourceMap:!1}})}}exports.DevBuildFinalizePlugin=DevBuildFinalizePlugin;function createDevWebpackConfig(r){const{appRoot:e,outputDir:n,main:a,tsConfig:o,workspaceRoot:s,assets:i=[],port:l,memoryLimit:_=8192,buildLibsFromSource:g=!0,orgScopes:k=[],bundlePackages:v=[],additionalEntryPoints:f=[],nodeExternalsConfig:d,webpackConfigPath:b}=r,{allowlistPatterns:y,scopePrefixes:x,scopePatterns:M}=(0,build_scope_patterns_1.buildScopePatterns)(k),C=process.env.LENSMCP_TYPECHECK,S=C==="tsgo"?(0,tsgo_check_plugin_1.resolveTsgoBin)(s,process.env.LENSMCP_TSGO_BIN):null;if(C==="tsgo"&&!S){const t=`${tsgo_check_plugin_1.TSGO_MISSING_CAUSE}. Fell back to fork-ts-checker, which reports to the console only \u2014 so typecheck:// has no producer for this project until it is fixed`;console.warn(`[serve] ${r.appName}: LENSMCP_TYPECHECK=tsgo but ${t}.`),(0,tsgo_check_plugin_1.reportTsgoUnavailable)({appName:r.appName,tsConfig:o,workspaceRoot:s,detail:t})}const w=!!S,P=(0,dev_devtool_1.resolveDevDevtool)(r.devtool);P.rejected&&console.warn(`[serve] ${r.appName}: '${P.rejected}' (devtool / ${dev_devtool_1.DEV_DEVTOOL_ENV}) is not a webpack devtool \u2014 building with '${dev_devtool_1.DEFAULT_DEV_DEVTOOL}'.`);const h=P.devtool,E=g?(0,workspace_paths_1.readWorkspacePaths)(o,s):null,D=v.map(t=>new RegExp(`^${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(/|$)`)),R=[/webpack\/hot\/poll\?100/,...y,...D,...d?.allowlist||[]],N={output:{path:n,...process.env.NODE_ENV!=="production"&&{devtoolModuleFilenameTemplate:devModuleFilename},clean:!0},externals:[(0,webpack_node_externals_1.default)({allowlist:R,additionalModuleDirs:d?.additionalModuleDirs||[],...d?.importType&&{importType:d.importType}}),({request:t},m)=>{if(t&&path.isAbsolute(t)&&t.startsWith(s)&&!t.includes("node_modules")||t&&(t.startsWith(path.join(s,"apps"))||t.startsWith(path.join(s,"libs")))||t&&x.some(u=>t.startsWith(u))||t&&M.some(u=>u.test(t))||t&&D.some(u=>u.test(t)))return m();if(t&&!(t.startsWith("./")||t.startsWith(".."))){const u=t.startsWith("@")?t.split("/").slice(0,2).join("/"):t.split("/")[0];return fs.existsSync(path.join(s,"node_modules",u))?m(null,`commonjs ${t}`):m()}return m()}],mode:"development",devtool:h,...(0,workspace_paths_1.hasPathMappings)(E)&&{resolve:{plugins:[new workspace_paths_1.WorkspacePathsResolverPlugin(E)]}},plugins:[new webpack.HotModuleReplacementPlugin,new app_plugin_1.NxAppWebpackPlugin({target:"node22",compiler:"tsc",main:a,additionalEntryPoints:f,verbose:!0,sourceMap:h,mergeExternals:!0,externalDependencies:[],memoryLimit:_,tsConfig:o,assets:i,optimization:!1,progress:!1,outputHashing:"none",generatePackageJson:!1,watchDependencies:!0,typeCheckOptions:process.env.LENSMCP_TYPECHECK==="off"||w?!1:{async:!0},buildLibsFromSource:g}),new DevBuildFinalizePlugin({cache:devCacheOptions(r,h),tsSourceMaps:(0,dev_devtool_1.bundleCarriesSourceMaps)(h)}),new DevServerReloadPlugin(l,r.httpsReload===!0,(0,devserver_notifier_1.devserverControlSock)(r.serviceName)),new perf_rules_plugin_1.PerfRulesPlugin({project:process.env.LENSMCP_PROJECT||r.appName,workspaceRoot:s,tsConfig:o}),...w?[new tsgo_check_plugin_1.TsgoCheckPlugin({appName:r.appName,tsConfig:o,workspaceRoot:s,...process.env.LENSMCP_TSGO_BIN?{binPath:process.env.LENSMCP_TSGO_BIN}:{}})]:process.env.LENSMCP_TYPECHECK==="off"?[]:[new fork_ts_check_reporter_1.ForkTsCheckReporterPlugin({appName:r.appName,tsConfig:o,workspaceRoot:s})]],snapshot:{managedPaths:[/^(.+?[\\/])?node_modules[\\/]/]},watch:!0,watchOptions:{ignored:["**/*.env.template","**/config.template.json","**/*.md","**/dist/**","**/migrations/**"]}};if(b){const t=require(path.resolve(e,b));return(t.default||t)(N)}return N}p(createDevWebpackConfig,"createDevWebpackConfig"),c(createDevWebpackConfig,"createDevWebpackConfig");
1
+ "use strict";var T=Object.defineProperty;var p=(r,e)=>T(r,"name",{value:e,configurable:!0});var O=Object.defineProperty,c=p((r,e)=>O(r,"name",{value:e,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DevBuildFinalizePlugin=exports.DevServerReloadPlugin=exports.devserverControlSock=exports.DevserverNotifier=void 0,exports.devCacheOptions=devCacheOptions,exports.devModuleFilename=devModuleFilename,exports.createDevWebpackConfig=createDevWebpackConfig;const tslib_1=require("tslib"),app_plugin_1=require("@nx/webpack/app-plugin"),node_crypto_1=require("node:crypto"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),webpack_node_externals_1=tslib_1.__importDefault(require("webpack-node-externals")),build_scope_patterns_1=require("./build-scope-patterns"),checkout_cache_1=require("./checkout-cache"),dev_devtool_1=require("./dev-devtool"),tsgo_check_plugin_1=require("./tsgo-check-plugin"),fork_ts_check_reporter_1=require("./fork-ts-check-reporter"),perf_rules_plugin_1=require("./perf/perf-rules-plugin"),devserver_notifier_1=require("./devserver-notifier"),workspace_paths_1=require("./workspace-paths");var devserver_notifier_2=require("./devserver-notifier");Object.defineProperty(exports,"DevserverNotifier",{enumerable:!0,get:c(function(){return devserver_notifier_2.DevserverNotifier},"get")}),Object.defineProperty(exports,"devserverControlSock",{enumerable:!0,get:c(function(){return devserver_notifier_2.devserverControlSock},"get")});const webpack=require("webpack");class DevServerReloadPlugin{static{p(this,"DevServerReloadPlugin")}static{c(this,"DevServerReloadPlugin")}constructor(e,n=!1,a,o=Number(process.env.LENSMCP_GATEWAY_PID??0)>0){this.notifier=new devserver_notifier_1.DevserverNotifier(e,n,a,o)}snapshot(){return this.notifier.snapshot()}apply(e){const n="DevServerReloadPlugin";let a=!1;e.hooks.watchRun?.tap(n,o=>{const s=[...o?.modifiedFiles??[],...o?.removedFiles??[]].filter(i=>typeof i=="string");if(!a){const i=o?.watchFileSystem?.watcher;i&&typeof i.on=="function"&&(a=!0,i.on("error",l=>this.notifier.degrade(`watcher error: ${l?.message??String(l)}`)))}this.notifier.watchRun(s)}),e.hooks.watchClose?.tap(n,()=>this.notifier.degrade("watch closed")),e.hooks.failed?.tap(n,o=>this.notifier.degrade(`compiler failed: ${o?.message??String(o)}`)),e.hooks.done.tap(n,async o=>{if(o.hasErrors()){this.notifier.failed();return}await this.notifier.built()})}}exports.DevServerReloadPlugin=DevServerReloadPlugin;const DEV_CACHE_MEMORY_GENERATIONS=5,DEV_CACHE_MAX_AGE_MS=10080*60*1e3,LOCKFILES=["yarn.lock","package-lock.json","pnpm-lock.yaml","bun.lock"];function clusterVersion(){for(const r of[path.join(__dirname,"package.json"),path.join(__dirname,"..","package.json")])try{const e=JSON.parse(fs.readFileSync(r,"utf8"));if(e.name==="@lensmcp/cluster"&&e.version)return e.version}catch{}return"unknown"}p(clusterVersion,"clusterVersion"),c(clusterVersion,"clusterVersion");function devCacheOptions(r,e){const{appName:n,appRoot:a,workspaceRoot:o,tsConfig:s,webpackConfigPath:i}=r,l=clusterVersion(),_=JSON.stringify({lensmcp:l,devtool:e,buildLibsFromSource:r.buildLibsFromSource??!0,orgScopes:r.orgScopes??[],bundlePackages:r.bundlePackages??[],nodeExternalsConfig:r.nodeExternalsConfig??null}),f=c(g=>g.filter(d=>fs.existsSync(d)),"existing"),k=(0,workspace_paths_1.readWorkspacePaths)(s,o)?.configFiles??[path.resolve(o,s)],v={lensmcp:[__filename],lockfile:f(LOCKFILES.map(g=>path.join(o,g))),tsconfig:f([...new Set([...k,path.join(o,"tsconfig.base.json")])])};return i&&(v.overrides=[path.resolve(a,i)]),{type:"filesystem",cacheDirectory:(0,checkout_cache_1.checkoutCacheDir)(o,"lensmcp-webpack"),name:n.replace(/[^\w.-]+/g,"_"),version:`${l}-${(0,node_crypto_1.createHash)("sha256").update(_).digest("hex").slice(0,16)}`,buildDependencies:v,maxMemoryGenerations:DEV_CACHE_MEMORY_GENERATIONS,maxAge:DEV_CACHE_MAX_AGE_MS}}p(devCacheOptions,"devCacheOptions"),c(devCacheOptions,"devCacheOptions");function devModuleFilename(r){const e=r.absoluteResourcePath;return path.isAbsolute(e)?`file://${e}`:e}p(devModuleFilename,"devModuleFilename"),c(devModuleFilename,"devModuleFilename");class DevBuildFinalizePlugin{static{p(this,"DevBuildFinalizePlugin")}static{c(this,"DevBuildFinalizePlugin")}constructor(e){this.options=e}apply(e){const{cache:n}=this.options;e.options.cache={...n,buildDependencies:{...n.buildDependencies}},e.options.context&&(e.context=e.options.context);const a=e.options.resolve?.plugins??[];if(a.some(o=>o instanceof workspace_paths_1.WorkspacePathsResolverPlugin)){const o=new Set(a);e.hooks.afterPlugins.tap("DevBuildFinalizePlugin",()=>{const s=e.options.resolve;s.plugins=s.plugins.filter(i=>o.has(i)||i?.constructor?.name!=="TsconfigPathsPlugin")})}if(this.options.tsSourceMaps)for(const o of e.options.module?.rules??[])typeof o?.loader!="string"||!/[\\/]ts-loader[\\/]/.test(o.loader)||(o.options={...o.options,compilerOptions:{...o.options?.compilerOptions,sourceMap:!0,inlineSourceMap:!1}})}}exports.DevBuildFinalizePlugin=DevBuildFinalizePlugin;function createDevWebpackConfig(r){const{appRoot:e,outputDir:n,main:a,tsConfig:o,workspaceRoot:s,assets:i=[],port:l,memoryLimit:_=8192,buildLibsFromSource:f=!0,orgScopes:k=[],bundlePackages:v=[],additionalEntryPoints:g=[],nodeExternalsConfig:d,webpackConfigPath:b}=r,{allowlistPatterns:y,scopePrefixes:x,scopePatterns:M}=(0,build_scope_patterns_1.buildScopePatterns)(k),C=process.env.LENSMCP_TYPECHECK,S=C==="tsgo"?(0,tsgo_check_plugin_1.resolveTsgoBin)(s,process.env.LENSMCP_TSGO_BIN):null;if(C==="tsgo"&&!S){const t=`${tsgo_check_plugin_1.TSGO_MISSING_CAUSE}. Fell back to fork-ts-checker, which reports to the console only \u2014 so typecheck:// has no producer for this project until it is fixed`;console.warn(`[serve] ${r.appName}: LENSMCP_TYPECHECK=tsgo but ${t}.`),(0,tsgo_check_plugin_1.reportTsgoUnavailable)({appName:r.appName,tsConfig:o,workspaceRoot:s,detail:t})}const w=!!S,P=(0,dev_devtool_1.resolveDevDevtool)(r.devtool);P.rejected&&console.warn(`[serve] ${r.appName}: '${P.rejected}' (devtool / ${dev_devtool_1.DEV_DEVTOOL_ENV}) is not a webpack devtool \u2014 building with '${dev_devtool_1.DEFAULT_DEV_DEVTOOL}'.`);const h=P.devtool,E=f?(0,workspace_paths_1.readWorkspacePaths)(o,s):null,D=v.map(t=>new RegExp(`^${t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(/|$)`)),R=[/webpack\/hot\/poll\?100/,...y,...D,...d?.allowlist||[]],N={output:{path:n,...process.env.NODE_ENV!=="production"&&{devtoolModuleFilenameTemplate:devModuleFilename},clean:!0},externals:[(0,webpack_node_externals_1.default)({allowlist:R,additionalModuleDirs:d?.additionalModuleDirs||[],...d?.importType&&{importType:d.importType}}),({request:t},m)=>{if(t&&path.isAbsolute(t)&&t.startsWith(s)&&!t.includes("node_modules")||t&&(t.startsWith(path.join(s,"apps"))||t.startsWith(path.join(s,"libs")))||t&&x.some(u=>t.startsWith(u))||t&&M.some(u=>u.test(t))||t&&D.some(u=>u.test(t)))return m();if(t&&!(t.startsWith("./")||t.startsWith(".."))){const u=t.startsWith("@")?t.split("/").slice(0,2).join("/"):t.split("/")[0];return fs.existsSync(path.join(s,"node_modules",u))?m(null,`commonjs ${t}`):m()}return m()}],mode:"development",devtool:h,...(0,workspace_paths_1.hasPathMappings)(E)&&{resolve:{plugins:[new workspace_paths_1.WorkspacePathsResolverPlugin(E)]}},plugins:[new webpack.HotModuleReplacementPlugin,new app_plugin_1.NxAppWebpackPlugin({target:"node22",compiler:"tsc",main:a,additionalEntryPoints:g,verbose:!0,sourceMap:h,mergeExternals:!0,externalDependencies:[],memoryLimit:_,tsConfig:o,assets:i,optimization:!1,progress:!1,outputHashing:"none",generatePackageJson:!1,watchDependencies:!0,typeCheckOptions:process.env.LENSMCP_TYPECHECK==="off"||w?!1:{async:!0},buildLibsFromSource:f}),new DevBuildFinalizePlugin({cache:devCacheOptions(r,h),tsSourceMaps:(0,dev_devtool_1.bundleCarriesSourceMaps)(h)}),new DevServerReloadPlugin(l,r.httpsReload===!0,(0,devserver_notifier_1.devserverControlSock)(r.serviceName)),new perf_rules_plugin_1.PerfRulesPlugin({project:process.env.LENSMCP_PROJECT||r.appName,workspaceRoot:s,tsConfig:o}),...w?[new tsgo_check_plugin_1.TsgoCheckPlugin({appName:r.appName,tsConfig:o,workspaceRoot:s,...process.env.LENSMCP_TSGO_BIN?{binPath:process.env.LENSMCP_TSGO_BIN}:{}})]:process.env.LENSMCP_TYPECHECK==="off"?[]:[new fork_ts_check_reporter_1.ForkTsCheckReporterPlugin({appName:r.appName,tsConfig:o,workspaceRoot:s})]],snapshot:{managedPaths:[/^(.+?[\\/])?node_modules[\\/]/]},watch:!0,watchOptions:{ignored:["**/*.env.template","**/config.template.json","**/*.md","**/dist/**","**/migrations/**"]}};if(b){const t=require(path.resolve(e,b));return(t.default||t)(N)}return N}p(createDevWebpackConfig,"createDevWebpackConfig"),c(createDevWebpackConfig,"createDevWebpackConfig");
@@ -20,6 +20,15 @@ export declare class DevserverNotifier {
20
20
  /** Bounded, newest-last paths of the last few `watchRun`s — diagnostics for the register, not policy. */
21
21
  private static readonly RECENT_PATH_CAP;
22
22
  private testimony;
23
+ /** Most changed paths one build names; past it they are counted (`moreChanged`). */
24
+ private static readonly BUILD_PATH_CAP;
25
+ /**
26
+ * The build in progress — from the first `watchRun` since the last build ended to its `built` / `failed`
27
+ * (issues/closed/foodguard-58). `recentPaths` above is a rolling window over several runs, kept for the gateway's
28
+ * register; this is ONE build's own account: when it began and which files woke it. It is what lets the devserver
29
+ * say, at the swap a build ends in, «these files are now what the pod serves».
30
+ */
31
+ private building;
23
32
  constructor(port: number, secure?: boolean, controlSock?: string, managed?: boolean);
24
33
  /**
25
34
  * Deliver to the parent's CONTROL SOCKET first (`$TMPDIR[/<wsKey>]/<service>-devserver/parent.sock`), then
@@ -32,6 +41,10 @@ export declare class DevserverNotifier {
32
41
  snapshot(): PodWatcherTestimonyWire;
33
42
  /** The watcher started a run for these changed paths. */
34
43
  watchRun(paths: string[]): void;
44
+ /** The open build as it goes on the wire. */
45
+ private buildAccount;
46
+ /** A build finished WITH errors: nothing is swapped, and the devserver is told the build is over — and how. */
47
+ failed(): void;
35
48
  /** The watcher can no longer be trusted to see every change. */
36
49
  degrade(reason: string): void;
37
50
  /** Send the testimony as it stands. */
@@ -54,4 +67,14 @@ export interface PodWatcherTestimonyWire {
54
67
  buildsSinceSpawn: number;
55
68
  degraded: false | string;
56
69
  recentPaths: string[];
70
+ /** The build in progress, while there is one (issues/closed/foodguard-58). Additive: a reader of version 1 that does not know it ignores it. */
71
+ building?: BuildAccountWire;
72
+ /** When the last build that ended WITH errors ended. Absent until one has. */
73
+ lastFailedAt?: number;
74
+ }
75
+ /** One build's own account: when it began, and the paths that woke it (capped; `moreChanged` counts the rest). */
76
+ export interface BuildAccountWire {
77
+ startedAt: number;
78
+ changedFiles: string[];
79
+ moreChanged?: number;
57
80
  }
@@ -1 +1 @@
1
- "use strict";var u=Object.defineProperty;var h=(r,e)=>u(r,"name",{value:e,configurable:!0});var p=Object.defineProperty,a=h((r,e)=>p(r,"name",{value:e,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DevserverNotifier=void 0,exports.devserverControlSock=devserverControlSock;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path"));class DevserverNotifier{static{h(this,"DevserverNotifier")}static{a(this,"DevserverNotifier")}static{this.RECENT_PATH_CAP=32}constructor(e,t=!1,s,c=Number(process.env.LENSMCP_GATEWAY_PID??0)>0){this.testimony={schemaVersion:1,lastWatchRunAt:0,lastBuildAt:0,buildsSinceSpawn:0,degraded:!1,recentPaths:[]},this.managed=c,this.secure=t,this.base=`${t?"https":"http"}://localhost:${e}`,this.controlSock=s}post(e,t){const s=JSON.stringify(t),c=a(()=>new Promise((o,l)=>{const i=(this.secure?require("node:https"):require("node:http")).request(`${this.base}${e}`,{method:"POST",headers:{"content-type":"application/json"},rejectUnauthorized:!1},n=>{n.resume(),n.on("end",o)});i.on("error",l),i.end(s)}),"viaTcp"),d=this.controlSock;return!d||!fs.existsSync(d)?this.managed?Promise.reject(new Error("the devserver control socket is not up yet")):c():new Promise((o,l)=>{const i=require("node:http").request({socketPath:d,path:e,method:"POST",headers:{"content-type":"application/json"}},n=>{n.resume(),n.on("end",o)});i.on("error",l),i.end(s)}).catch(o=>{if(this.managed)throw o;return c()})}snapshot(){return{...this.testimony,recentPaths:[...this.testimony.recentPaths]}}watchRun(e){if(this.testimony.lastWatchRunAt=Date.now(),e.length){const t=[...this.testimony.recentPaths,...e];this.testimony.recentPaths=t.slice(-DevserverNotifier.RECENT_PATH_CAP)}this.tellWatching()}degrade(e){this.testimony.degraded=e,this.tellWatching()}tellWatching(){this.post("/webpack/watching",this.testimony).catch(()=>{})}async built(e=!0){if(this.testimony.lastBuildAt=Date.now(),this.testimony.buildsSinceSpawn+=1,!e){this.tellWatching();return}try{await this.post("/webpack/reload",{watcher:this.testimony}),console.log("[DevServerReloadPlugin] notified devserver to reload")}catch{console.log("[DevServerReloadPlugin] devserver not running yet")}}}exports.DevserverNotifier=DevserverNotifier;function devserverControlSock(r,e=process.env){const t=e.LENSMCP_SOCK_NS||e.LENSMCP_WS_KEY||"",s=t?path.join(os.tmpdir(),t,`${r}-devserver`):path.join(os.tmpdir(),`${r}-devserver`);return path.join(s,"parent.sock")}h(devserverControlSock,"devserverControlSock"),a(devserverControlSock,"devserverControlSock");
1
+ "use strict";var g=Object.defineProperty;var a=(i,t)=>g(i,"name",{value:t,configurable:!0});var u=Object.defineProperty,c=a((i,t)=>u(i,"name",{value:t,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DevserverNotifier=void 0,exports.devserverControlSock=devserverControlSock;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path"));class DevserverNotifier{static{a(this,"DevserverNotifier")}static{c(this,"DevserverNotifier")}static{this.RECENT_PATH_CAP=32}static{this.BUILD_PATH_CAP=50}constructor(t,e=!1,s,h=Number(process.env.LENSMCP_GATEWAY_PID??0)>0){this.testimony={schemaVersion:1,lastWatchRunAt:0,lastBuildAt:0,buildsSinceSpawn:0,degraded:!1,recentPaths:[]},this.managed=h,this.secure=e,this.base=`${e?"https":"http"}://localhost:${t}`,this.controlSock=s}post(t,e){const s=JSON.stringify(e),h=c(()=>new Promise((n,l)=>{const o=(this.secure?require("node:https"):require("node:http")).request(`${this.base}${t}`,{method:"POST",headers:{"content-type":"application/json"},rejectUnauthorized:!1},r=>{r.resume(),r.on("end",n)});o.on("error",l),o.end(s)}),"viaTcp"),d=this.controlSock;return!d||!fs.existsSync(d)?this.managed?Promise.reject(new Error("the devserver control socket is not up yet")):h():new Promise((n,l)=>{const o=require("node:http").request({socketPath:d,path:t,method:"POST",headers:{"content-type":"application/json"}},r=>{r.resume(),r.on("end",n)});o.on("error",l),o.end(s)}).catch(n=>{if(this.managed)throw n;return h()})}snapshot(){return{...this.testimony,recentPaths:[...this.testimony.recentPaths]}}watchRun(t){if(this.testimony.lastWatchRunAt=Date.now(),t.length){const e=[...this.testimony.recentPaths,...t];this.testimony.recentPaths=e.slice(-DevserverNotifier.RECENT_PATH_CAP)}this.building??={startedAt:this.testimony.lastWatchRunAt,changed:new Set,more:0};for(const e of t)this.building.changed.has(e)||(this.building.changed.size>=DevserverNotifier.BUILD_PATH_CAP?this.building.more+=1:this.building.changed.add(e));this.testimony.building=this.buildAccount(),this.tellWatching()}buildAccount(){if(this.building)return{startedAt:this.building.startedAt,changedFiles:[...this.building.changed],...this.building.more>0?{moreChanged:this.building.more}:{}}}failed(){const t=this.buildAccount();this.building=void 0,delete this.testimony.building;const e=Date.now();this.testimony.lastFailedAt=e,this.post("/webpack/watching",{...this.testimony,...t?{failed:{...t,endedAt:e}}:{}}).catch(()=>{})}degrade(t){this.testimony.degraded=t,this.tellWatching()}tellWatching(){this.post("/webpack/watching",this.testimony).catch(()=>{})}async built(t=!0){this.testimony.lastBuildAt=Date.now(),this.testimony.buildsSinceSpawn+=1;const e=this.buildAccount();if(this.building=void 0,delete this.testimony.building,!t){this.tellWatching();return}try{await this.post("/webpack/reload",{watcher:this.testimony,...e?{build:{...e,endedAt:this.testimony.lastBuildAt}}:{}}),console.log("[DevServerReloadPlugin] notified devserver to reload")}catch{console.log("[DevServerReloadPlugin] devserver not running yet")}}}exports.DevserverNotifier=DevserverNotifier;function devserverControlSock(i,t=process.env){const e=t.LENSMCP_SOCK_NS||t.LENSMCP_WS_KEY||"",s=e?path.join(os.tmpdir(),e,`${i}-devserver`):path.join(os.tmpdir(),`${i}-devserver`);return path.join(s,"parent.sock")}a(devserverControlSock,"devserverControlSock"),c(devserverControlSock,"devserverControlSock");
@@ -1 +1 @@
1
- "use strict";var b=Object.defineProperty;var w=(p,i)=>b(p,"name",{value:i,configurable:!0});var S=Object.defineProperty,h=w((p,i)=>S(p,"name",{value:i,configurable:!0}),"h");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHandler=createHandler;const manifest_1=require("../manifest"),service_keys_1=require("./service-keys"),gateway_errors_1=require("../gateway-errors"),discovery_1=require("./discovery"),edge_1=require("./edge"),observability_1=require("./observability"),chooser_1=require("./chooser");function createHandler(p){const{rt:i,obs:d,auth:y,proxy:m,hooks:c}=p,g=h(e=>(e.headers.host||"").split(":")[0],"hostOf"),_=h((e,r)=>{if((0,edge_1.cors)(e,r,i.routes),e.method==="OPTIONS"){r.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_NOROUTE,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),r.end();return}const s=(e.headers.host||"").split(":")[0],t=i.routes.filter(o=>o.host&&(0,manifest_1.hostMatches)(o.host,s)&&o.prefix),n=(e.url??"/").split("?")[0];if((n==="/"||n==="/index.html"||n==="")&&i.routes.some(o=>o.host&&(0,manifest_1.hostMatches)(o.host,s)&&o.project===observability_1.LENS_DASHBOARD_PROJECT)){(0,chooser_1.serveChooser)(r,i.wsKey,e);return}if(t.length>0&&(n==="/health"||n==="/healthz")){const o=t.map(l=>({project:l.project,prefix:l.prefix,pods:l.pool?((0,discovery_1.pickSock)(l.pool,0),l.pool.socks.length):void 0,state:l.svc?.state??(l.target?"up":"unknown")})),f=o.every(l=>l.state!=="down"||!0);r.writeHead(f?200:503,{"content-type":"application/json"}),r.end(JSON.stringify({status:"ok",host:s,services:o},null,2));return}if(t.length>0&&n==="/.well-known/services"){r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({host:s,services:t.map(o=>({prefix:o.prefix,project:o.project,internal:!!o.internal}))},null,2));return}(0,edge_1.sendError)(r,e,404,"not_found",t.length>0?`No service mounted at ${n}. Mounted on ${s}: ${[...new Set(t.map(o=>o.prefix))].join(", ")} (+ /health, /.well-known/services)`:"No gateway route for this hostname.")},"handleNoRoute"),O=h(async(e,r)=>{if(!e.headers.host){const n=e.authority??e.headers[":authority"];n&&(e.headers.host=n)}(0,edge_1.stripOwnedHeaders)(e);const s=d.beginTrace(e);(0,gateway_errors_1.ensureRequestId)(e),s&&(d.traceStep(s,"received",{host:s.host,method:s.method,url:s.url}),e.__lensmcpTrace=s);const t=c.httpContext(e,r,s);try{const n=e.url??"/",o=(0,edge_1.canonicalize)(n);if(o===void 0){d.traceStep(s,"bad-path",{}),d.finishTrace(s,"gateway",400),(0,edge_1.sendError)(r,e,400,"bad_request","Bad Request");return}const f=o.policy,l=o.path+o.query;if(l!==n&&(0,edge_1.rememberRawTarget)(e,n),e.url=l,(0,edge_1.resolveClientIp)(i,e),c.has("onRequest")&&(await c.run("onRequest",t),t.responded()))return;const a=(0,manifest_1.matchRoute)(i.routes,g(e),f+o.query);if(!a){_(e,r);return}if(t.route=a,d.traceStep(s,"route-match",{project:a.project,...a.prefix?{prefix:a.prefix}:{},...a.host?{routeHost:a.host}:{},...a.internal?{internal:!0}:{}}),e.method==="OPTIONS"){(0,edge_1.cors)(e,r,i.routes),r.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_ROUTED,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),r.end();return}if((0,edge_1.cors)(e,r,i.routes),c.has("beforeAuth")&&(await c.run("beforeAuth",t),t.responded()))return;if(a.internal){const u=y.internal(e,a,s);if(!u.ok){d.finishTrace(s,a.project,u.status,void 0,a),(0,edge_1.sendError)(r,e,u.status,u.reason,u.detail);return}t.caller=u.caller}else{const u=y.public(e,a,s,f);if(!u.ok){d.finishTrace(s,a.project,u.status,void 0,a),(0,edge_1.sendError)(r,e,u.status,u.reason,u.detail);return}if(t.claims=u.claims,c.has("afterAuth")&&(await c.run("afterAuth",t),t.responded()))return}const v=a.pool?i.serviceKeys[(0,service_keys_1.serviceKeyId)(a.svc?.wsKey,a.project)]:void 0;if(v&&(e.headers["x-internal-token"]=v),(0,edge_1.stampForwardingHeaders)(i,e),c.has("beforeForward")&&(await c.run("beforeForward",t),t.responded()))return;c.has("onResponse")&&r.on("close",()=>{t.status=r.statusCode??0,c.run("onResponse",t)}),m.forward(a,e,r)}catch(n){t.error=n,console.error(`[gateway] ${new Date().toISOString()} pipeline error traceId=${(0,gateway_errors_1.ensureRequestId)(e)}: ${n?.stack??String(n)}`),c.has("onError")&&await c.run("onError",t),t.responded()||(0,edge_1.sendError)(r,e,500,"internal","Internal gateway error.")}},"runPipeline");return(e,r)=>{O(e,r)}}w(createHandler,"createHandler"),h(createHandler,"createHandler");
1
+ "use strict";var q=Object.defineProperty;var y=(f,n)=>q(f,"name",{value:n,configurable:!0});var x=Object.defineProperty,p=y((f,n)=>x(f,"name",{value:n,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHandler=createHandler;const manifest_1=require("../manifest"),service_keys_1=require("./service-keys"),gateway_errors_1=require("../gateway-errors"),discovery_1=require("./discovery"),edge_1=require("./edge"),observability_1=require("./observability"),chooser_1=require("./chooser");function createHandler(f){const{rt:n,obs:d,auth:v,proxy:_,hooks:i}=f,O=p(e=>(e.headers.host||"").split(":")[0],"hostOf"),S=p((e,t)=>{if((0,edge_1.cors)(e,t,n.routes),e.method==="OPTIONS"){t.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_NOROUTE,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),t.end();return}const o=(e.headers.host||"").split(":")[0],u=n.routes.filter(r=>r.host&&(0,manifest_1.hostMatches)(r.host,o)&&r.prefix),s=(e.url??"/").split("?")[0];if((s==="/"||s==="/index.html"||s==="")&&n.routes.some(r=>r.host&&(0,manifest_1.hostMatches)(r.host,o)&&r.project===observability_1.LENS_DASHBOARD_PROJECT)){(0,chooser_1.serveChooser)(t,n.wsKey,e);return}if(u.length>0&&(s==="/health"||s==="/healthz")){const r=u.map(l=>({project:l.project,prefix:l.prefix,pods:l.pool?((0,discovery_1.pickSock)(l.pool,0),l.pool.socks.length):void 0,state:l.svc?.state??(l.target?"up":"unknown")})),h=r.every(l=>l.state!=="down"||!0);t.writeHead(h?200:503,{"content-type":"application/json"}),t.end(JSON.stringify({status:"ok",host:o,services:r},null,2));return}if(u.length>0&&s==="/.well-known/services"){t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({host:o,services:u.map(r=>({prefix:r.prefix,project:r.project,internal:!!r.internal}))},null,2));return}(0,edge_1.sendError)(t,e,404,"not_found",u.length>0?`No service mounted at ${s}. Mounted on ${o}: ${[...new Set(u.map(r=>r.prefix))].join(", ")} (+ /health, /.well-known/services)`:"No gateway route for this hostname.")},"handleNoRoute"),b=p(async(e,t)=>{if(!e.headers.host){const r=e.authority??e.headers[":authority"];r&&(e.headers.host=r)}(0,edge_1.stripOwnedHeaders)(e);let o=d.beginTrace(e);const u=Date.now();(0,gateway_errors_1.ensureRequestId)(e),o&&(d.traceStep(o,"received",{host:o.host,method:o.method,url:o.url}),e.__lensmcpTrace=o);const s=i.httpContext(e,t,o);try{const r=e.url??"/",h=(0,edge_1.canonicalize)(r);if(h===void 0){d.traceStep(o,"bad-path",{}),d.finishTrace(o,"gateway",400),(0,edge_1.sendError)(t,e,400,"bad_request","Bad Request");return}const l=h.policy,m=h.path+h.query;if(m!==r&&(0,edge_1.rememberRawTarget)(e,r),e.url=m,(0,edge_1.resolveClientIp)(n,e),i.has("onRequest")&&(await i.run("onRequest",s),s.responded()))return;const a=(0,manifest_1.matchRoute)(n.routes,O(e),l+h.query);if(!a){S(e,t);return}s.route=a;const w=d.startHeadlessFlow(e,a,o,u);if(w!==o&&(o=w,s.trace=w,e.__lensmcpTrace=w),d.traceStep(o,"route-match",{project:a.project,...a.prefix?{prefix:a.prefix}:{},...a.host?{routeHost:a.host}:{},...a.internal?{internal:!0}:{}}),e.method==="OPTIONS"){(0,edge_1.cors)(e,t,n.routes),t.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_ROUTED,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),t.end();return}if((0,edge_1.cors)(e,t,n.routes),i.has("beforeAuth")&&(await i.run("beforeAuth",s),s.responded()))return;if(a.internal){const c=v.internal(e,a,o);if(!c.ok){d.finishTrace(o,a.project,c.status,void 0,a),(0,edge_1.sendError)(t,e,c.status,c.reason,c.detail);return}s.caller=c.caller}else{const c=v.public(e,a,o,l);if(!c.ok){d.finishTrace(o,a.project,c.status,void 0,a),(0,edge_1.sendError)(t,e,c.status,c.reason,c.detail);return}if(s.claims=c.claims,i.has("afterAuth")&&(await i.run("afterAuth",s),s.responded()))return}const g=a.pool?n.serviceKeys[(0,service_keys_1.serviceKeyId)(a.svc?.wsKey,a.project)]:void 0;if(g&&(e.headers["x-internal-token"]=g),(0,edge_1.stampForwardingHeaders)(n,e),i.has("beforeForward")&&(await i.run("beforeForward",s),s.responded()))return;i.has("onResponse")&&t.on("close",()=>{s.status=t.statusCode??0,i.run("onResponse",s)}),_.forward(a,e,t)}catch(r){s.error=r,console.error(`[gateway] ${new Date().toISOString()} pipeline error traceId=${(0,gateway_errors_1.ensureRequestId)(e)}: ${r?.stack??String(r)}`),i.has("onError")&&await i.run("onError",s),s.responded()||(0,edge_1.sendError)(t,e,500,"internal","Internal gateway error.")}},"runPipeline");return(e,t)=>{b(e,t)}}y(createHandler,"createHandler"),p(createHandler,"createHandler");
@@ -282,6 +282,8 @@ export interface ServiceLayer {
282
282
  rollPod(svc: ServiceCtl, reason: string): Promise<boolean>;
283
283
  /** Self-heal a WEDGED service (live parent, 0 live pods): recycle it, within the crash-loop budget. */
284
284
  recycleWedged(svc: ServiceCtl): void;
285
+ /** Stop every tree a handoff replaced that is still draining — at gateway stop, where nothing waits behind it. */
286
+ stopRetiringTrees(): void;
285
287
  /** Stop the build host of the workspace at `root` — one that unregistered — or, with no root, every build host
286
288
  * (shared-build.ts): after the service trees, at gateway stop. */
287
289
  stopBuildHost(root?: string): Promise<void>;
@@ -298,6 +300,13 @@ export interface ServiceLayerDeps {
298
300
  rollReadyMs?: number;
299
301
  /** Poll interval while waiting for the standby. Default 750ms. */
300
302
  rollPollMs?: number;
303
+ /** How long a tree a handoff replaced may drain before its group is killed; `0` = plain SIGTERM, no drain.
304
+ * Default {@link handoffDrainBoundMs}. */
305
+ handoffDrainMs?: number;
306
+ /** Is any process of this tree's GROUP still running? Default: signal 0 to the group. */
307
+ treeAlive?: (proc: ChildProcess) => boolean;
308
+ /** How often a retiring tree is looked at. Default 1 s. */
309
+ retirePollMs?: number;
301
310
  /** Rolls allowed in flight at once across every service. Default {@link POD_MAX_CONCURRENT_ROLLS}; `0` = no cap. */
302
311
  maxConcurrentRolls?: number;
303
312
  /** Cold starts allowed at once across every service. Default {@link MAX_COLD_STARTS}; `0` = no cap. */
@@ -1,3 +1,3 @@
1
- "use strict";var Oe=Object.defineProperty;var w=(t,c)=>Oe(t,"name",{value:c,configurable:!0});var De=Object.defineProperty,s=w((t,c)=>De(t,"name",{value:c,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.scaleTarget=scaleTarget,exports.isLoopbackHostname=isLoopbackHostname,exports.isOwnRunnerCommand=isOwnRunnerCommand,exports.isRunnerOf=isRunnerOf,exports.isTestSourcePath=isTestSourcePath,exports.sourceScanPass=sourceScanPass,exports.sourceSetSnapshot=sourceSetSnapshot,exports.sourceSetSignature=sourceSetSignature,exports.diffSourceSets=diffSourceSets,exports.sourceChangeWindowMs=sourceChangeWindowMs,exports.sourceScopeDirs=sourceScopeDirs,exports.podRecycleReason=podRecycleReason,exports.lensFrontendStale=lensFrontendStale,exports.lensWatcherVerdict=lensWatcherVerdict,exports.podWatcherVerdict=podWatcherVerdict,exports.lensChildWedged=lensChildWedged,exports.isPortConflictExit=isPortConflictExit,exports.createServiceLayer=createServiceLayer,exports.chooseStalePodKillTarget=chooseStalePodKillTarget;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),node_crypto_1=require("node:crypto"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),net=tslib_1.__importStar(require("node:net")),path=tslib_1.__importStar(require("node:path")),node_util_1=require("node:util"),discovery_1=require("./discovery"),scope_1=require("./scope"),pod_env_1=require("./pod-env"),pod_probe_1=require("./pod-probe"),service_keys_1=require("./service-keys"),workspace_log_1=require("./workspace-log"),inspector_secret_1=require("../../../inspector-secret"),pod_output_1=require("./pod-output"),serve_launch_1=require("./serve-launch"),shared_build_1=require("./shared-build"),protocol_1=require("../../../shared-build/protocol"),types_1=require("./types"),exit_reason_1=require("../../../exit-reason"),process_name_1=require("../../../process-name");function eastWestEnv(t,c){const a={},i=t.serviceKeys[(0,service_keys_1.serviceKeyId)(c.wsKey,c.project)];if(i&&(a.LENSMCP_SERVICE_KEY=i),t.https){const h=require("../../../basic-ssl").caCertPath();fs.existsSync(h)&&(a.NODE_EXTRA_CA_CERTS=h)}return a}w(eastWestEnv,"eastWestEnv"),s(eastWestEnv,"eastWestEnv");const sleep=s(t=>new Promise(c=>setTimeout(c,t)),"sleep"),SRC_EXCLUDE=new Set(["node_modules","dist","tmp","coverage","build","out","target"]),SRC_EXT=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".css",".scss",".sass",".less",".json",".html",".htm",".vue",".svelte",".mdx"]),SCAN_FILE_CAP=1e5;function fnv1a(t){let c=2166136261;for(let a=0;a<t.length;a+=1)c^=t.charCodeAt(a),c=Math.imul(c,16777619);return c>>>0}w(fnv1a,"fnv1a"),s(fnv1a,"fnv1a");const execFileP=(0,node_util_1.promisify)(node_child_process_1.execFile);async function gitIgnoredPaths(t){try{const{stdout:c}=await execFileP("git",["ls-files","--others","--directory","--ignored","--exclude-standard","-z"],{cwd:t,maxBuffer:33554432,timeout:1e4,windowsHide:!0});return new Set(c.split("\0").filter(Boolean))}catch{return new Set}}w(gitIgnoredPaths,"gitIgnoredPaths"),s(gitIgnoredPaths,"gitIgnoredPaths");function scaleTarget(t,c=fs.existsSync){if(t.decl.adminUrl)return{url:t.decl.adminUrl};const a=path.join(t.pool.dir,"parent.sock");return c(a)?{socketPath:a}:t.decl.port?{url:`http://localhost:${t.decl.port}`}:void 0}w(scaleTarget,"scaleTarget"),s(scaleTarget,"scaleTarget");function isLoopbackHostname(t){const c=t.toLowerCase().replace(/^\[(.*)\]$/,"$1").replace(/\.$/,"");return c==="localhost"||c.endsWith(".localhost")?!0:net.isIPv4(c)?c.split(".")[0]==="127":net.isIPv6(c)&&new URL(`http://[${c}]/`).hostname==="[::1]"}w(isLoopbackHostname,"isLoopbackHostname"),s(isLoopbackHostname,"isLoopbackHostname");function postScale(t,c,a){let i;if("socketPath"in t)i=http.request({socketPath:t.socketPath,path:"/webpack/scale",method:"POST",headers:{"content-type":"application/json"}},h=>h.resume());else{const h=t.url.startsWith("https"),A=h?require("node:https"):http,S={method:"POST"};h&&isLoopbackHostname(new URL(t.url).hostname)&&(S.rejectUnauthorized=!1),a&&(S.lookup=a),i=A.request(`${t.url}/webpack/scale`,S,v=>v.resume())}return i.on("error",()=>{}),i.end(JSON.stringify({pods:c})),i}w(postScale,"postScale"),s(postScale,"postScale");function isOwnRunnerCommand(t,c){return(0,serve_launch_1.isServeRunnerCommand)(t)?(0,serve_launch_1.isServeRunnerCommandOf)(t,c):t.includes("/node_modules/nx/")?t.includes(`${c.replace(/\/+$/,"")}/node_modules/nx/`):!0}w(isOwnRunnerCommand,"isOwnRunnerCommand"),s(isOwnRunnerCommand,"isOwnRunnerCommand");function isRunnerOf(t,c,a){const i=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return!new RegExp(`(^|\\s)run ${i}(:|\\s|$)`).test(t)||!(0,serve_launch_1.isServeRunnerCommand)(t)&&!/\bnx(\.js)?\b/.test(t)?!1:isOwnRunnerCommand(t,a)}w(isRunnerOf,"isRunnerOf"),s(isRunnerOf,"isRunnerOf");const TEST_FILE_RE=/\.(spec|test|e2e|e2e-spec|stories)\.[cm]?[jt]sx?$/,TEST_DIR_SEGMENTS=new Set(["__tests__","__mocks__","__snapshots__"]);function isTestSourcePath(t){const c=t.split("/"),a=c[c.length-1]??"";if(TEST_FILE_RE.test(a))return!0;for(let i=0;i<c.length-1;i+=1)if(TEST_DIR_SEGMENTS.has(c[i]))return!0;return!1}w(isTestSourcePath,"isTestSourcePath"),s(isTestSourcePath,"isTestSourcePath");function sourceScanPass(t,c,a){const i=a?.ignoreTests??!0;let h;const A=new Map,S=`tree:${path.resolve(t)}`,v=s(g=>path.relative(t,g).split(path.sep).join("/"),"rel"),k=s(async(g,_)=>{const j=await(h??=gitIgnoredPaths(t)),P=[],F=new Set;let p=!1;const I=s(async(G,re)=>{if(P.length>=SCAN_FILE_CAP){p=!0;return}let se;try{se=await fs.promises.readdir(G,{withFileTypes:!0})}catch{return}re&&F.add(v(G));for(const U of se){if(P.length>=SCAN_FILE_CAP){p=!0;return}const L=U.name,b=path.join(G,L);if(U.isDirectory()){if(!re||L.startsWith(".")||SRC_EXCLUDE.has(L)||c?.has(L)||L.endsWith("-devserver")||j.has(`${v(b)}/`))continue;await I(b,!0)}else if(U.isFile()){const ne=L.lastIndexOf(".");if(ne<=0||!SRC_EXT.has(L.slice(ne)))continue;const Q=v(b);if(j.has(Q)||i&&isTestSourcePath(Q))continue;P.push(Q)}}},"walk");return await I(g,_),{paths:P,entered:F,truncated:p}},"walkDir"),O=s((g,_)=>{let j=A.get(g);return j||(j=_(),A.set(g,j)),j},"memo"),z=s(async()=>{const g=A.get(S);if(!g)return;const _=await g;return _.truncated?void 0:_},"wideWalk"),V=new Set,oe=s(()=>O(`files:${path.resolve(t)}`,async()=>{const g=await z();return g?{paths:g.paths.filter(_=>!_.includes("/")),entered:V,truncated:!1}:k(t,!1)}),"rootFiles"),J=s(g=>O(`tree:${path.resolve(g)}`,async()=>{const _=await z(),j=v(g);if(_?.entered.has(j)){const P=`${j}/`;return{paths:j===""?_.paths:_.paths.filter(F=>F.startsWith(P)),entered:V,truncated:!1}}return k(g,!0)}),"subtree"),D=s(g=>{const _=new Set;let j=0,P=g.some(p=>p.truncated);const F=s(p=>_.has(p)?!0:_.size>=SCAN_FILE_CAP?(P=!0,!1):(_.add(p),j^=fnv1a(p),!0),"add");for(const p of g)if(!p.paths.every(F))break;return{signature:`${_.size}:${(j>>>0).toString(36)}`,paths:_,truncated:P}},"compose");return{async snapshot(g){if(!g||g.length===0)return D([await O(S,()=>k(t,!0))]);const _=[await oe()];for(const j of g)_.push(await J(j));return D(_)}}}w(sourceScanPass,"sourceScanPass"),s(sourceScanPass,"sourceScanPass");async function sourceSetSnapshot(t,c,a,i){return sourceScanPass(t,a,i).snapshot(c)}w(sourceSetSnapshot,"sourceSetSnapshot"),s(sourceSetSnapshot,"sourceSetSnapshot");async function sourceSetSignature(t,c,a,i){return(await sourceSetSnapshot(t,c,a,i)).signature}w(sourceSetSignature,"sourceSetSignature"),s(sourceSetSignature,"sourceSetSignature");const SOURCE_DIFF_PATH_CAP=128;function diffSourceSets(t,c,a,i){if(t.paths.size===c.paths.size){let v=!0;for(const k of t.paths)if(!c.paths.has(k)){v=!1;break}if(v)return null}const h=[],A=[];let S=t.truncated||c.truncated;for(const v of c.paths)t.paths.has(v)||(h.length+A.length<SOURCE_DIFF_PATH_CAP?h.push(v):S=!0);for(const v of t.paths)c.paths.has(v)||(h.length+A.length<SOURCE_DIFF_PATH_CAP?A.push(v):S=!0);return{changedAt:a,added:h,removed:A,truncated:S,...i!==void 0?{windowMs:i}:{}}}w(diffSourceSets,"diffSourceSets"),s(diffSourceSets,"diffSourceSets");function sourceChangeWindowMs(t,c,a,i){const h=c&&c>0?t-c:a;return Math.max(h,a)+i}w(sourceChangeWindowMs,"sourceChangeWindowMs"),s(sourceChangeWindowMs,"sourceChangeWindowMs");function sourceScopeDirs(t,c,a,i,h){const A=s(k=>{const O=path.relative(t,k).split(path.sep).filter(Boolean);return O.length===0||O[0].startsWith("..")?"":O[0]},"bucketOf")(c);if(!A)return[];const S=new Set(a.filter(Boolean)),v=new Set([A]);for(const k of i)k.startsWith(".")||SRC_EXCLUDE.has(k)||h?.has(k)||k.endsWith("-devserver")||S.has(k)||v.add(k);return[...v].sort().map(k=>path.join(t,k))}w(sourceScopeDirs,"sourceScopeDirs"),s(sourceScopeDirs,"sourceScopeDirs");function podRecycleReason(t,c,a,i){return!t.proc||t.state!=="up"||t.spawnAt==null||t.inflight>0||(t.activeWork??0)>0||c-t.lastUsed<i.settleMs||t.lastStaleRecycleAt!=null&&c-t.lastStaleRecycleAt<i.cooldownMs?null:a>0&&a-t.spawnAt>i.graceMs?"stale-source-set":i.maxAgeMs>0&&c-t.spawnAt>i.maxAgeMs?"max-age":null}w(podRecycleReason,"podRecycleReason"),s(podRecycleReason,"podRecycleReason");function lensFrontendStale(t,c,a,i,h){return!(i-c<h.cooldownMs||a<=0||a-t<=h.graceMs||i-a<h.settleMs)}w(lensFrontendStale,"lensFrontendStale"),s(lensFrontendStale,"lensFrontendStale");function lensWatcherVerdict(t,c,a,i=[]){if(!t)return"unknown";if("schemaVersion"in t&&t.schemaVersion>=2&&i.length>0){const h=c-a,A=new Set(t.recentEvents.filter(S=>S.at>=h).map(S=>path.resolve(S.path)));return i.every(S=>A.has(path.resolve(S)))?"heard":"missed"}return t.lastFsSetEventAt>=c-a?"heard":"missed"}w(lensWatcherVerdict,"lensWatcherVerdict"),s(lensWatcherVerdict,"lensWatcherVerdict");function podWatcherVerdict(t,c,a){if(!t)return"unknown";if(t.degraded)return"degraded";const i=c-a;return t.lastWatchRunAt>=i||t.lastBuildAt>=i?"heard":"silent"}w(podWatcherVerdict,"podWatcherVerdict"),s(podWatcherVerdict,"podWatcherVerdict");function lensChildWedged(t,c,a,i){const h=t.hasListened?i.graceMs:Math.max(i.graceMs,i.neverListenedGraceMs);return a-t.spawnedAt<h||a-c<i.cooldownMs||t.probeFails<Math.max(2,i.failureThreshold)?null:"wedged-no-listener"}w(lensChildWedged,"lensChildWedged"),s(lensChildWedged,"lensChildWedged");function isPortConflictExit(t,c){return t.signal||t.code===null||t.code===0||t.ranMs>c.fastFailMs?!1:t.portHeld}w(isPortConflictExit,"isPortConflictExit"),s(isPortConflictExit,"isPortConflictExit");function createServiceLayer(t,c,a={}){const i=a.spawnProcess??node_child_process_1.spawn,h=a.probeAlive??pod_probe_1.probePodAlive,A=a.killTree??((e,n="SIGTERM")=>{try{process.kill(-e.pid,n)}catch{try{e.kill(n)}catch{}}}),S=a.sharedBuild??(0,shared_build_1.createSharedBuildRelay)(t),v=s((e,n="SIGTERM")=>{S.detach(e),A(e,n)},"killTree"),k=a.rollReadyMs??types_1.POD_ROLL_READY_MS,O=a.rollPollMs??750,z=a.maxConcurrentRolls??types_1.POD_MAX_CONCURRENT_ROLLS,V=z>0?z:Number.POSITIVE_INFINITY,oe=a.maxConcurrentColdStarts??types_1.MAX_COLD_STARTS,J=oe>0?oe:Number.POSITIVE_INFINITY;let D=0;const g=[],_=s(()=>D<J?(D+=1,Promise.resolve()):new Promise(e=>{g.push(()=>{D+=1,e()})}),"acquireColdSlot"),j=s(()=>{D-=1,g.shift()?.()},"releaseColdSlot"),{emit:P}=c,F=s(e=>e.wsKey===t.wsKey?void 0:path.join(e.root,".lensmcp","events.jsonl"),"serviceBus"),p=s((e,n,r,o,d)=>P(n,r,o,{wsKey:e.wsKey,...d},void 0,F(e)),"emitSvc"),I=s(e=>(e.scannedAt=0,(0,discovery_1.pickSock)(e,t.scanTtlMs)?e.socks.length:0),"scanNow"),G=s((e,n)=>{let r;try{r=(0,node_child_process_1.execFileSync)("ps",["ax","-ww","-o","pid=,pgid=,command="],{encoding:"utf8",timeout:5e3})}catch{return 0}const o=e.proc?.pid;let d=0;for(const l of r.split(`
2
- `)){const u=l.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);if(!u)continue;const f=Number(u[1]),C=Number(u[2]);if(isRunnerOf(u[3],n,e.root)&&!(f===process.pid||f===o)&&!(o!==void 0&&C===o)){try{process.kill(-C,"SIGKILL")}catch{try{process.kill(f,"SIGKILL")}catch{}}d+=1}}return d>0&&(console.log(`[gateway] ${n}: reaped ${d} stale runner tree(s) \u2014 a foreign generation of this service (holding nx's task lock, or its pool)`),p(e,"warning",`stale-runner reap: ${e.project} (${d} foreign tree(s))`,`cluster-service:${e.project}`,{kind:"stale-runner-reap",project:e.project,reaped:d})),d},"reapForeignRunners"),re=s((e,n)=>({...(0,pod_env_1.podBaseEnv)(process.env,t.root,e.root),...e.instanceEnv?{...e.instanceEnv,LENSMCP_SOCKET_ONLY:"1"}:{},...e.frozen?{LENSMCP_FREEZE:"1",LENSMCP_TYPECHECK:"off"}:{},NX_DAEMON:"false",NX_ISOLATE_PLUGINS:"false",...e.wsKey?{LENSMCP_WS_KEY:e.wsKey}:{},...e.sockNs?{LENSMCP_SOCK_NS:e.sockNs}:{},LENSMCP_EVENT_FILE:e.wsKey===t.wsKey?t.eventFile:path.join(e.root,".lensmcp","events.jsonl"),LENSMCP_GATEWAY_PID:String(process.pid),LENSMCP_POD_GENERATION:String(n),...eastWestEnv(t,e)}),"serviceEnv"),se=s((e,n,r,o)=>{const d=re(e,n),l=e.wsKey||path.basename(e.root),u=(0,serve_launch_1.planServeLaunch)({stores:t.graphStores,root:e.root,project:e.project,target:e.decl.serveTarget??"serve-hmr",childCount:o,baseEnv:d});if(u.kind==="runner"){const E=S.offeredTo(e),m=i((0,process_name_1.namedExecPath)((0,process_name_1.processName)(l,e.project,serve_launch_1.SERVE_RUNNER_ROLE)),[...u.script.execArgv,u.script.path,...(0,serve_launch_1.serveRunnerArgs)(r,o,e.root)],{cwd:e.root,detached:!0,env:E?{...u.env,[protocol_1.SHARED_BUILD_ENV]:"1"}:u.env,stdio:["ignore","pipe","pipe","ipc"]});return E&&S.attach(e,n,m),m.send?.(u.message,$=>{$&&console.error(`[gateway] ${r}: could not hand the task to its runner (${$.message})`)}),{proc:m,launcher:"runner"}}console.log(`[gateway] ${r}: starting through nx run \u2014 ${u.reason}`);const f=path.join(e.root,"node_modules","nx","dist","bin","nx.js"),C=(0,process_name_1.namedExecPath)((0,process_name_1.processName)(l,e.project,"nx")),[N,R]=fs.existsSync(f)?[C,[f,"run",r,`--childCount=${o}`]]:["yarn",["nx","run",r,`--childCount=${o}`]];return{proc:i(N,R,{cwd:e.root,detached:!0,env:d,stdio:["ignore","pipe","pipe"]}),launcher:"nx"}},"startServiceTree"),U=new WeakMap,L=s((e,n,r)=>{const o=`${e.project}:${e.decl.serveTarget??"serve-hmr"}`,d=Math.max(1,e.decl.minPods??1),{proc:l,launcher:u}=se(e,n,o,d);U.set(l,u),r==="active"&&(e.launcher=u),r==="active"&&(e.lastErrors=[],e.buildFailed=!1,e.faults=new pod_output_1.PodFaults);const f=s(R=>{const E=R.toString();for(const m of E.split(`
3
- `)){const $=(0,pod_output_1.classifyPodLine)(m),{clean:x,broken:X,healthy:y}=$,W=x;if(X){e.lastErrors.push(x),e.lastErrors.length>12&&e.lastErrors.shift(),e.buildFailed=!0;const M=Date.now();(x!==e.lastErrorEmitSig||M-(e.lastErrorEmitAt??0)>=types_1.SERVICE_ERROR_THROTTLE_MS)&&(e.lastErrorEmitSig=x,e.lastErrorEmitAt=M,p(e,"error",`service error: ${e.project}`,`cluster-service:${e.project}`,{kind:"service-error",project:e.project,line:x,fault:$.fault}))}y&&(e.buildFailed=!1);const T=(e.faults??=new pod_output_1.PodFaults).observe($);if(T){e.lastErrorEmitSig=void 0;const M=T.open.length===0;p(e,"info",`${M?"service recovered":`${T.closed.join(" + ")} recovered`}: ${e.project}`,`cluster-service:${e.project}`,{kind:M?"service-recovered":"service-fault-closed",project:e.project,line:x,closed:T.closed,open:T.open})}!e.lockBlockedAt&&/Waiting for .+ in another nx process/.test(W)&&(e.lockBlockedAt=Date.now(),console.log(`[gateway] ${o}: spawn blocked by a stale nx runner \u2014 breaking the lock + respawning`),p(e,"warning",`nx task lock: ${e.project} blocked by a stale runner \u2014 auto-breaking`,`cluster-service:${e.project}`,{kind:"nx-lock-break",project:e.project}),setTimeout(()=>{t.stopped()||!e.lockBlockedAt||(q(e,"parked on a foreign nx task lock \u2014 recycling",{announce:!1}),G(e,o),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&b(e)},1500).unref?.())},250).unref?.())}},"sniff"),C=e.wsKey&&e.wsKey!==t.wsKey&&e.root?(0,workspace_log_1.workspaceLogWriter)(e.root):void 0,N=s(R=>E=>{if(t.stopped())return;const m=(0,inspector_secret_1.redactInspectorUrls)(E.toString());f(m);const $=`[${e.project}] ${m}`;R.write($),C?.($)},"relay");return l.stdout?.on("data",N(process.stdout)),l.stderr?.on("data",N(process.stderr)),l.on("exit",(R,E)=>{if(e.proc!==l||(e.proc=void 0,e.state="down",t.stopped()))return;const m=(0,exit_reason_1.describeExit)(R,E);if(console.log(`[gateway] ${o} exited (${R??"no code"}${E?`, ${E}`:""}${e.inflight?`, ${e.inflight} request(s) in flight`:""})${m?` \u2014 ${m}`:""}`),p(e,R===0?"info":"warning",`service down: ${e.project} (exit ${R})`,`cluster-service:${e.project}`,{kind:"service-down",project:e.project,code:R,...E?{signal:E}:{}}),R===0||e.buildFailed)return;const $=Date.now();if(e.restarts=(e.restarts??[]).filter(X=>$-X<types_1.SERVICE_RESTART_WINDOW_MS),e.restarts.length>=types_1.SERVICE_MAX_RESTARTS){p(e,"error",`service crash-looping: ${e.project} \u2014 ${types_1.SERVICE_MAX_RESTARTS}+ restarts/min, not auto-respawning (the next request retries)`,`cluster-service:${e.project}`,{kind:"service-crash-loop",project:e.project});return}e.restarts.push($);const x=Math.min(8e3,500*2**(e.restarts.length-1));setTimeout(()=>{t.stopped()||e.proc||e.state!=="down"||(console.log(`[gateway] self-heal: respawning ${e.project} (restart ${e.restarts.length}/${types_1.SERVICE_MAX_RESTARTS})`),p(e,"info",`self-heal: respawning ${e.project}`,`cluster-service:${e.project}`,{kind:"service-respawn",project:e.project,attempt:e.restarts.length}),b(e))},x)}),l},"launchServiceProcess"),b=s(e=>{if(t.stopped())return;const n=`${e.project}:${e.decl.serveTarget??"serve-hmr"}`,r=Math.max(1,e.decl.minPods??1);a.reapForeign!==!1&&G(e,n),e.lockBlockedAt=void 0,console.log(`[gateway] starting ${n} (pods=${r})\u2026`),p(e,"info",`service starting: ${e.project} (${r} pod${r>1?"s":""})`,`cluster-service:${e.project}`,{kind:"service-starting",project:e.project,pods:r}),e.state="starting",e.spawnAt=Date.now(),e.generation=(e.generation??0)+1,e.proc=L(e,e.generation,"active")},"spawnService"),ne=s(async e=>{const n=e.pool.socks[0];if(!n)return;const r=await(0,pod_probe_1.probePodAlive)(n,void 0,t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.wsKey,e.project)]);r&&(e.activeWork=r.activeWork,r.watcher&&(e.watcher=r.watcher))},"refreshActiveWork"),Q=s(e=>{try{const n=Number(fs.readFileSync(`${e}.owner`,"utf8").trim());return Number.isInteger(n)&&n>1?n:void 0}catch{return}},"readOwnerSidecar"),ve=s(async e=>{const n=t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.wsKey,e.project)];if(!n)return[];const r=(0,node_crypto_1.createHash)("sha256").update(n).digest("hex").slice(0,8),o=[];for(const d of e.pool.socks){const l=await(0,pod_probe_1.probePodAlive)(d,void 0,n);l&&(!l.keyFingerprint||l.keyFingerprint!==r)&&o.push({sock:d,pid:l.pid})}return o},"staleAdoptedSocks"),_e=s((e,n)=>{const r=n.map(o=>o.sock);for(const{sock:o,pid:d}of n){const l=chooseStalePodKillTarget(d,Q(o),process.pid);if(l!==void 0)try{process.kill(l,"SIGTERM")}catch{}try{fs.unlinkSync(o)}catch{}try{fs.unlinkSync(`${o}.owner`)}catch{}}e.pool.socks=e.pool.socks.filter(o=>!r.includes(o)),e.pool.idx=-1,e.pool.scannedAt=0},"evictStaleAdopted"),ae=s((e,n)=>{if(e.state==="up")return!1;e.state="up",e.wedgeRecycles=0;const r=e.pool.socks.length;return p(e,"info",`service up: ${e.project} (${r} pods, ${n})`,`cluster-service:${e.project}`,{kind:"service-up",project:e.project,pods:r,adopted:n==="adopted"}),!0},"markUp"),ye=s(async e=>{if(I(e.pool)>0){const o=await ve(e);if(o.length>0&&(console.error(`[gateway] ${e.project}: ${o.length} adopted pod(s) hold a foreign east-west key \u2014 evicting instead of adopting.`),p(e,"warning",`stale adoption: ${e.project} (foreign east-west key)`,`cluster-service:${e.project}`,{kind:"pod-recycle",project:e.project,reason:"stale-adoption",pods:o.length}),e.proc&&q(e,"stale adoption (foreign east-west key)"),_e(e,o)),e.pool.socks.length>0)return ae(e,"adopted"),!0}if(t.stopped())return!1;let n=!1;const r=s(()=>e.state!=="starting"&&!e.proc,"notStarted");if(r())if(e.coldWait)await e.coldWait;else{D>=J&&(console.log(`[gateway] cold start queued: ${e.project}${e.wsKey&&e.wsKey!==t.wsKey?` (${e.wsKey})`:""} \u2014 ${D} starting, ${g.length} ahead (LENSMCP_MAX_COLD_STARTS=${J}).`),p(e,"info",`cold start queued: ${e.project}`,`cluster-service:${e.project}`,{kind:"cold-start-queued",project:e.project,inFlight:D,ahead:g.length}));const o=_();if(e.coldWait=o,await o,e.coldWait=void 0,n=!0,t.stopped())return j(),!1;r()&&b(e)}try{return await Ee(e)}finally{n&&j()}},"ensureUp"),Ee=s(async e=>{const n=Date.now()+t.startupTimeoutMs,r=Date.now()+12e3;for(;!t.stopped()&&Date.now()<n;){if(await sleep(Math.min(700,t.startupTimeoutMs/4)),e.buildFailed&&Date.now()>r)return!1;if(I(e.pool)>0){e.state="up",e.buildFailed=!1;const o=e.spawnAt?Date.now()-e.spawnAt:void 0;return console.log(`[gateway] ${e.project} up (${e.pool.socks.length} pods${o?`, cold start ${o}ms`:""})`),p(e,"info",`cold start: ${e.project} (${e.pool.socks.length} pods, ${o??"?"}ms)`,`cluster-service:${e.project}`,{kind:"cold-start",project:e.project,pods:e.pool.socks.length,durationMs:o}),!0}if(e.state==="down")return!1}return!1},"waitForPods"),me=s(e=>{const n=e.decl.maxPods??e.decl.minPods??1,r=e.pool.socks.length,o=scaleTarget(e);!o||r===0||r>=n||e.inflight<=r||Date.now()-e.lastScaleAt<5e3||(e.lastScaleAt=Date.now(),postScale(o,r+1,a.scaleLookup),console.log(`[gateway] autoscale ${e.project}: ${r} \u2192 ${r+1} pods (inflight ${e.inflight})`),p(e,"info",`autoscale: ${e.project} ${r} \u2192 ${r+1} pods`,`cluster-service:${e.project}`,{kind:"autoscale",project:e.project,from:r,to:r+1,inflight:e.inflight}))},"scaleUp"),q=s((e,n,r)=>{if(!e.proc)return;console.log(`[gateway] ${e.project} ${n} \u2014 scaling to zero.`),r?.announce!==!1&&p(e,"info",`idle kill: ${e.project} (${n})`,`cluster-service:${e.project}`,{kind:"idle-kill",project:e.project,why:n});const o=e.proc;try{o.stdout?.removeAllListeners("data"),o.stderr?.removeAllListeners("data"),o.removeAllListeners("exit")}catch{}v(o,"SIGTERM"),e.proc=void 0,e.state="down",setTimeout(()=>{ie(e),e.pool.socks=[],e.pool.scannedAt=0},1200).unref?.()},"killSpawned"),ie=s(e=>{try{for(const n of fs.readdirSync(e.pool.dir)){if(!n.endsWith(".sock"))continue;const r=path.join(e.pool.dir,n);try{const o=Number(fs.readFileSync(`${r}.owner`,"utf8").trim());if(Number.isFinite(o)&&o>0)try{process.kill(o,0);continue}catch{}}catch{}try{fs.unlinkSync(r)}catch{}try{fs.unlinkSync(`${r}.owner`)}catch{}}}catch{}},"sweepDeadOwnerSocks");let B=0;const le=[],$e=s(()=>B<V?(B+=1,Promise.resolve()):new Promise(e=>{le.push(()=>{B+=1,e()})}),"acquireRollSlot"),ke=s(()=>{B-=1,le.shift()?.()},"releaseRollSlot"),je=s(async(e,n,r)=>{I(e.pool);for(const o of e.pool.socks){const d=await h(o,void 0,r);if(d&&n(d.podGeneration))return!0}return!1},"anyPodAnswers"),ge=s((e,n)=>{if(e.rolling)return e.rolling;const r=e.proc;if(!r||t.stopped())return Promise.resolve(!1);if(e.launcher==="nx")return console.log(`[gateway] pod handoff skipped: ${e.project} runs through nx run, which starts one instance of a target at a time \u2014 no standby can overlap it (reason=${n}).`),Promise.resolve(!1);B>=V&&console.log(`[gateway] pod handoff queued: ${e.project} reason=${n} \u2014 ${B} roll(s) in flight, ${le.length} ahead (LENSMCP_POD_MAX_CONCURRENT_ROLLS=${V}).`);const o=(async()=>{await $e();try{return await Re(e,r,n)}finally{ke()}})().finally(()=>{e.rolling=void 0});return e.rolling=o,o},"rollPod"),Re=s(async(e,n,r)=>{const o=e.proc;if(o!==n||t.stopped())return!1;const d=e.generation??0,l=d+1,u=Date.now();e.lastStaleRecycleAt=u,console.log(`[gateway] pod handoff start: ${e.project} g${d}\u2192g${l} reason=${r} \u2014 standby spawning, old generation keeps serving.`),p(e,"info",`pod handoff starting: ${e.project} (${r})`,`cluster-service:${e.project}`,{kind:"pod-handoff-start",project:e.project,reason:r,generation:l,fromGeneration:d});const f=L(e,l,"standby"),C=t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.wsKey,e.project)],N=u+k;let R;for(;!t.stopped()&&Date.now()<N&&!(f.exitCode!==null||f.signalCode!==null);){I(e.pool);for(const E of e.pool.socks)if((await h(E,void 0,C))?.podGeneration===l){R=E;break}if(R)break;await sleep(O)}if(!R||t.stopped()){const E=t.stopped()?"gateway stopping":f.exitCode!==null||f.signalCode!==null?`standby exited (${f.exitCode??f.signalCode})`:`standby not ready within ${Math.round(k/1e3)}s`;try{f.stdout?.removeAllListeners("data"),f.stderr?.removeAllListeners("data")}catch{}v(f,"SIGTERM");const m=!t.stopped()&&e.proc===o&&o.exitCode===null&&o.signalCode===null&&await je(e,x=>x!==l,C),$=m?`old generation g${d} kept serving`:`and the old generation g${d} is NOT serving (${e.proc!==o||o.exitCode!==null||o.signalCode!==null?"its tree is gone":"none of its pods answers"}) \u2014 the service is down until it recovers`;return console.error(`[gateway] pod handoff failed: ${e.project} g${l}: ${E} \u2014 ${$}.`),p(e,m?"warning":"error",`pod handoff failed: ${e.project}`,`cluster-service:${e.project}`,{kind:"pod-handoff-failed",project:e.project,reason:r,generation:l,error:E,durationMs:Date.now()-u,oldServing:m}),setTimeout(()=>ie(e),1200).unref?.(),!1}if(e.proc!==o)return v(f,"SIGTERM"),!1;e.proc=f,e.launcher=U.get(f),e.generation=l,e.spawnAt=u,e.state="up",e.watcher=void 0,e.staleVerifiedAt=void 0,e.staleKeptAt=void 0,e.buildFailed=!1,e.lastErrors=[];try{o.stdout?.removeAllListeners("data"),o.stderr?.removeAllListeners("data"),o.removeAllListeners("exit")}catch{}return v(o,"SIGTERM"),setTimeout(()=>{ie(e),e.pool.scannedAt=0},1200).unref?.(),console.log(`[gateway] pod handoff ready: ${e.project} g${l} answered on ${path.basename(R)} in ${Date.now()-u}ms; retiring g${d}.`),p(e,"info",`pod handoff complete: ${e.project}`,`cluster-service:${e.project}`,{kind:"pod-handoff-complete",project:e.project,reason:r,generation:l,fromGeneration:d,durationMs:Date.now()-u}),!0},"runRoll"),fe=s(e=>{const n=Date.now();e.restarts=(e.restarts??[]).filter(o=>n-o<types_1.SERVICE_RESTART_WINDOW_MS);const r=(e.wedgeRecycles??0)+1;if(e.restarts.length>=types_1.SERVICE_MAX_RESTARTS||r>types_1.SERVICE_MAX_RESTARTS){const o=e.restarts.length>=types_1.SERVICE_MAX_RESTARTS?`wedged ${types_1.SERVICE_MAX_RESTARTS}+\xD7/min`:`recycled ${types_1.SERVICE_MAX_RESTARTS}\xD7 in a row and no pod came up`;p(e,"error",`service crash-looping: ${e.project} \u2014 ${o}, not auto-recycling (the next request retries)`,`cluster-service:${e.project}`,{kind:"service-crash-loop",project:e.project,recycles:r-1}),q(e,"wedged (crash-loop cap) \u2014 leaving down for the next request",{announce:!1});return}e.wedgeRecycles=r,e.restarts.push(n),console.log(`[gateway] self-heal: recycling WEDGED ${e.project} (live parent, 0 live pods; attempt ${r}/${types_1.SERVICE_MAX_RESTARTS} with no pod up since the first, ${e.restarts.length} in the last minute; ${e.inflight??0} request(s) in flight \u2014 jobs held by the dead pod keep their locks until they expire)`),p(e,"warning",`self-heal: recycling wedged ${e.project} (0 live pods)`,`cluster-service:${e.project}`,{kind:"service-recycle",project:e.project,attempt:r,inWindow:e.restarts.length}),q(e,"wedged: 0 live pods \u2014 recycling",{announce:!1}),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&b(e)},1500).unref?.()},"recycleWedged"),Ae={maxAgeMs:types_1.POD_MAX_AGE_MS,graceMs:types_1.POD_STALE_GRACE_MS,settleMs:types_1.POD_RECYCLE_SETTLE_MS,cooldownMs:types_1.POD_STALE_RECYCLE_COOLDOWN_MS};let de;const Z=new Map,we=new Map;let pe=!1,ue=0,ce=0;const Ce=s(()=>{const e=t.projectRoots??{},n=new Set(t.services.map(o=>o.project));for(const o of t.routes)o.lens&&n.add(o.lens.project);const r=new Set;for(const o of n){const d=e[o];if(!d)continue;const l=path.relative(t.root,d).split(path.sep).filter(Boolean)[0];l&&!l.startsWith("..")&&r.add(l)}return[...r]},"managedBuckets"),Te=s((e,n,r,o)=>{const d=t.projectSourceRoots?.[e];if(d?.length)return d;const l=t.projectRoots?.[e];if(!l)return;const u=sourceScopeDirs(t.root,l,r,n,o);return u.length>0?u:void 0},"scopeFor"),Me=s(()=>{if(pe||Date.now()-ue<types_1.POD_STALE_SCAN_MS)return;pe=!0,ue=Date.now();const e=ue;(async()=>{const n=(0,scope_1.readSourceSetOptions)(t.root),r=n.exclude,o={ignoreTests:n.ignoreTests},d=sourceScanPass(t.root,r,o),l=await d.snapshot(),u=Date.now(),f=sourceChangeWindowMs(u,ce,types_1.POD_STALE_SCAN_MS,types_1.POD_RECYCLE_SETTLE_MS),C=de?diffSourceSets(de,l,u,f):null;if(C&&(t.sourceSetChangedAt=u,t.sourceSetChange=C,P("info","source file set changed \u2014 stale pods recycle when idle","cluster-gateway",{kind:"source-set-changed",added:C.added,removed:C.removed,truncated:C.truncated})),de=l,!t.projectRoots){ce=e;return}let N;try{N=(await fs.promises.readdir(t.root,{withFileTypes:!0})).filter(y=>y.isDirectory()).map(y=>y.name)}catch{return}const R=Ce(),E=new Set(t.services.map(y=>y.project));for(const y of t.routes)y.lens&&E.add(y.lens.project);const m=new Map;for(const y of E){const W=Te(y,N,R,r);if(!W)continue;const T=W.join("|"),M=m.get(T)??{dirs:W,projects:[]};M.projects.push(y),m.set(T,M)}const $=Date.now(),x=sourceChangeWindowMs($,ce,types_1.POD_STALE_SCAN_MS,types_1.POD_RECYCLE_SETTLE_MS);ce=e;const X=s((y,W,T)=>{t.sourceSetChangedFor??=new Map,t.sourceSetChangesFor??=new Map;for(const M of y)t.sourceSetChangedFor.set(M,W),t.sourceSetChangesFor.set(M,T)},"stampScoped");for(const[y,{dirs:W,projects:T}]of m){const M=await d.snapshot(W),Se=Z.get(y);Z.set(y,M);const he=new Map;for(const H of T){const K=we.get(H);if(K===void 0||K===y)continue;const Y=he.get(K)??[];Y.push(H),he.set(K,Y)}for(const H of T)we.set(H,y);for(const[H,K]of he){const Y=Z.get(H),te=Y?diffSourceSets(Y,M,$,x):C;te&&(X(K,Y?$:u,te),P("info",`source file set changed \u2014 ${K.join(", ")}'s watch scope was re-keyed`,"cluster-gateway",{kind:"source-set-changed",scoped:!0,rescoped:!0,projects:K,added:te.added,removed:te.removed,truncated:te.truncated}))}if(!Se)continue;const ee=diffSourceSets(Se,M,$,x);ee&&(X(T,$,ee),P("info",`source file set changed in ${T.join(", ")}'s scope`,"cluster-gateway",{kind:"source-set-changed",scoped:!0,projects:T,added:ee.added,removed:ee.removed,truncated:ee.truncated}))}for(const y of[...Z.keys()])m.has(y)||Z.delete(y)})().catch(()=>{}).finally(()=>{pe=!1})},"maybeScanSourceSet"),Pe=s((e,n)=>{const r=(t.sourceSetChangesFor?.get(e.project)??t.sourceSetChange)?.windowMs??types_1.POD_STALE_SCAN_MS+types_1.POD_RECYCLE_SETTLE_MS,o=podWatcherVerdict(e.watcher,n,r),d=s(l=>(e.staleKeptAt!==n&&(e.staleKeptAt=n,console.log(`[gateway] stale source set: pod ${e.project} ${l} \u2014 no recycle.`)),!1),"keep");return o==="heard"?(e.staleVerifiedAt=n,p(e,"info",`stale check: ${e.project} watcher confirmed \u2014 no recycle`,`cluster-service:${e.project}`,{kind:"pod-stale-verified",project:e.project,watcher:o}),d(`watcher CONFIRMED alive across the change (last build ${new Date(e.watcher?.lastBuildAt??0).toISOString()})`)):o==="degraded"?(console.log(`[gateway] stale source set: pod ${e.project} watcher DEGRADED (${e.watcher?.degraded}) \u2014 recycling.`),!0):o==="silent"?types_1.POD_WATCHER_SILENT==="keep"?d("watcher alive but quiet across the change \u2014 keeping it (LENSMCP_POD_WATCHER_SILENT=keep; a live watcher cannot miss an add it will need)"):!0:types_1.POD_WATCHER_UNVERIFIED==="keep"?d("unverifiable (no watcher testimony \u2014 older devserver bundle?) \u2014 keeping it (LENSMCP_POD_WATCHER_UNVERIFIED=keep)"):!0},"podStaleRecycleAllowed"),xe=s((e,n)=>{if(e.lastStaleRecycleAt=Date.now(),types_1.POD_RECYCLE_MODE==="rolling"&&e.proc&&e.launcher!=="nx"){ge(e,n);return}console.log(`[gateway] zombie recycle: ${e.project} (${n}) \u2014 stale fs view; fresh pod incoming`),p(e,"warning",`zombie recycle: ${e.project} (${n})`,`cluster-service:${e.project}`,{kind:"pod-recycle",project:e.project,reason:n});const r=!!e.decl.eager;q(e,`${n} \u2014 recycling stale pod`,{announce:!1}),r&&setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&b(e)},1500).unref?.()},"recyclePod"),Le=s(()=>{const e=setInterval(()=>{be().catch(n=>console.error("[gateway] sweep error:",n))},t.sweepMs);return e.unref(),e},"startSweeper"),be=s(async()=>{{if(t.stopped())return;for(const e of t.graphStores?.all()??[])e.refreshIfStale();types_1.POD_RECYCLE_ENABLED&&Me();for(const e of t.registry?t.registry.allServices():t.services){if(t.stopped()||!e.proc){e.wedgedSince=void 0;continue}const n=path.resolve(e.root)===path.resolve(t.root),r=e.decl.idleKillSec;if(r&&e.inflight===0&&Date.now()-e.lastUsed>r*1e3){q(e,`idle ${r}s`),e.wedgedSince=void 0;continue}const o=e.state==="starting"&&e.spawnAt!==void 0&&Date.now()-e.spawnAt<t.startupTimeoutMs,d=I(e.pool)>0;if(d||o){if(e.wedgedSince=void 0,d&&ae(e,"observed"),d&&!o&&types_1.POD_RECYCLE_ENABLED&&n){const l=t.sourceSetChangedFor?.get(e.project)??t.sourceSetChangedAt??0;await ne(e);const u=(e.staleVerifiedAt??0)>=l&&l>0,f=podRecycleReason(e,Date.now(),u?0:l,Ae);if(f==="stale-source-set"&&!Pe(e,l))continue;f&&xe(e,f)}continue}e.wedgedSince??=Date.now(),Date.now()-e.wedgedSince>types_1.WEDGE_RECYCLE_GRACE_MS&&(e.wedgedSince=void 0,fe(e))}}},"sweepOnce");return{scanNow:I,spawnService:b,ensureUp:ye,scaleUp:me,killSpawned:q,markUp:ae,startSweeper:Le,rollPod:ge,recycleWedged:fe,stopBuildHost:s(e=>e===void 0?S.stop():S.release(e),"stopBuildHost")}}w(createServiceLayer,"createServiceLayer"),s(createServiceLayer,"createServiceLayer");function chooseStalePodKillTarget(t,c,a){if(!(t===void 0||!Number.isInteger(t)||t<=1)&&!(c!==void 0&&c!==t)&&t!==a)return t}w(chooseStalePodKillTarget,"chooseStalePodKillTarget"),s(chooseStalePodKillTarget,"chooseStalePodKillTarget");
1
+ "use strict";var qe=Object.defineProperty;var w=(t,c)=>qe(t,"name",{value:c,configurable:!0});var Ke=Object.defineProperty,s=w((t,c)=>Ke(t,"name",{value:c,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.scaleTarget=scaleTarget,exports.isLoopbackHostname=isLoopbackHostname,exports.isOwnRunnerCommand=isOwnRunnerCommand,exports.isRunnerOf=isRunnerOf,exports.isTestSourcePath=isTestSourcePath,exports.sourceScanPass=sourceScanPass,exports.sourceSetSnapshot=sourceSetSnapshot,exports.sourceSetSignature=sourceSetSignature,exports.diffSourceSets=diffSourceSets,exports.sourceChangeWindowMs=sourceChangeWindowMs,exports.sourceScopeDirs=sourceScopeDirs,exports.podRecycleReason=podRecycleReason,exports.lensFrontendStale=lensFrontendStale,exports.lensWatcherVerdict=lensWatcherVerdict,exports.podWatcherVerdict=podWatcherVerdict,exports.lensChildWedged=lensChildWedged,exports.isPortConflictExit=isPortConflictExit,exports.createServiceLayer=createServiceLayer,exports.chooseStalePodKillTarget=chooseStalePodKillTarget;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),node_crypto_1=require("node:crypto"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),net=tslib_1.__importStar(require("node:net")),path=tslib_1.__importStar(require("node:path")),node_util_1=require("node:util"),discovery_1=require("./discovery"),scope_1=require("./scope"),pod_env_1=require("./pod-env"),pod_probe_1=require("./pod-probe"),service_keys_1=require("./service-keys"),workspace_log_1=require("./workspace-log"),inspector_secret_1=require("../../../inspector-secret"),pod_output_1=require("./pod-output"),serve_launch_1=require("./serve-launch"),shared_build_1=require("./shared-build"),protocol_1=require("../../../shared-build/protocol"),types_1=require("./types"),exit_reason_1=require("../../../exit-reason"),handoff_retire_1=require("../../../handoff-retire"),process_name_1=require("../../../process-name");function eastWestEnv(t,c){const a={},i=t.serviceKeys[(0,service_keys_1.serviceKeyId)(c.wsKey,c.project)];if(i&&(a.LENSMCP_SERVICE_KEY=i),t.https){const f=require("../../../basic-ssl").caCertPath();fs.existsSync(f)&&(a.NODE_EXTRA_CA_CERTS=f)}return a}w(eastWestEnv,"eastWestEnv"),s(eastWestEnv,"eastWestEnv");const sleep=s(t=>new Promise(c=>setTimeout(c,t)),"sleep"),SRC_EXCLUDE=new Set(["node_modules","dist","tmp","coverage","build","out","target"]),SRC_EXT=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".css",".scss",".sass",".less",".json",".html",".htm",".vue",".svelte",".mdx"]),SCAN_FILE_CAP=1e5;function fnv1a(t){let c=2166136261;for(let a=0;a<t.length;a+=1)c^=t.charCodeAt(a),c=Math.imul(c,16777619);return c>>>0}w(fnv1a,"fnv1a"),s(fnv1a,"fnv1a");const execFileP=(0,node_util_1.promisify)(node_child_process_1.execFile);async function gitIgnoredPaths(t){try{const{stdout:c}=await execFileP("git",["ls-files","--others","--directory","--ignored","--exclude-standard","-z"],{cwd:t,maxBuffer:33554432,timeout:1e4,windowsHide:!0});return new Set(c.split("\0").filter(Boolean))}catch{return new Set}}w(gitIgnoredPaths,"gitIgnoredPaths"),s(gitIgnoredPaths,"gitIgnoredPaths");function scaleTarget(t,c=fs.existsSync){if(t.decl.adminUrl)return{url:t.decl.adminUrl};const a=path.join(t.pool.dir,"parent.sock");return c(a)?{socketPath:a}:t.decl.port?{url:`http://localhost:${t.decl.port}`}:void 0}w(scaleTarget,"scaleTarget"),s(scaleTarget,"scaleTarget");function isLoopbackHostname(t){const c=t.toLowerCase().replace(/^\[(.*)\]$/,"$1").replace(/\.$/,"");return c==="localhost"||c.endsWith(".localhost")?!0:net.isIPv4(c)?c.split(".")[0]==="127":net.isIPv6(c)&&new URL(`http://[${c}]/`).hostname==="[::1]"}w(isLoopbackHostname,"isLoopbackHostname"),s(isLoopbackHostname,"isLoopbackHostname");function postScale(t,c,a){let i;if("socketPath"in t)i=http.request({socketPath:t.socketPath,path:"/webpack/scale",method:"POST",headers:{"content-type":"application/json"}},f=>f.resume());else{const f=t.url.startsWith("https"),C=f?require("node:https"):http,S={method:"POST"};f&&isLoopbackHostname(new URL(t.url).hostname)&&(S.rejectUnauthorized=!1),a&&(S.lookup=a),i=C.request(`${t.url}/webpack/scale`,S,_=>_.resume())}return i.on("error",()=>{}),i.end(JSON.stringify({pods:c})),i}w(postScale,"postScale"),s(postScale,"postScale");function isOwnRunnerCommand(t,c){return(0,serve_launch_1.isServeRunnerCommand)(t)?(0,serve_launch_1.isServeRunnerCommandOf)(t,c):t.includes("/node_modules/nx/")?t.includes(`${c.replace(/\/+$/,"")}/node_modules/nx/`):!0}w(isOwnRunnerCommand,"isOwnRunnerCommand"),s(isOwnRunnerCommand,"isOwnRunnerCommand");function isRunnerOf(t,c,a){const i=c.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");return!new RegExp(`(^|\\s)run ${i}(:|\\s|$)`).test(t)||!(0,serve_launch_1.isServeRunnerCommand)(t)&&!/\bnx(\.js)?\b/.test(t)?!1:isOwnRunnerCommand(t,a)}w(isRunnerOf,"isRunnerOf"),s(isRunnerOf,"isRunnerOf");const TEST_FILE_RE=/\.(spec|test|e2e|e2e-spec|stories)\.[cm]?[jt]sx?$/,TEST_DIR_SEGMENTS=new Set(["__tests__","__mocks__","__snapshots__"]);function isTestSourcePath(t){const c=t.split("/"),a=c[c.length-1]??"";if(TEST_FILE_RE.test(a))return!0;for(let i=0;i<c.length-1;i+=1)if(TEST_DIR_SEGMENTS.has(c[i]))return!0;return!1}w(isTestSourcePath,"isTestSourcePath"),s(isTestSourcePath,"isTestSourcePath");function sourceScanPass(t,c,a){const i=a?.ignoreTests??!0;let f;const C=new Map,S=`tree:${path.resolve(t)}`,_=s(h=>path.relative(t,h).split(path.sep).join("/"),"rel"),R=s(async(h,g)=>{const v=await(f??=gitIgnoredPaths(t)),x=[],F=new Set;let D=!1;const q=s(async(Q,y)=>{if(x.length>=SCAN_FILE_CAP){D=!0;return}let I;try{I=await fs.promises.readdir(Q,{withFileTypes:!0})}catch{return}y&&F.add(_(Q));for(const U of I){if(x.length>=SCAN_FILE_CAP){D=!0;return}const O=U.name,Z=path.join(Q,O);if(U.isDirectory()){if(!y||O.startsWith(".")||SRC_EXCLUDE.has(O)||c?.has(O)||O.endsWith("-devserver")||v.has(`${_(Z)}/`))continue;await q(Z,!0)}else if(U.isFile()){const ee=O.lastIndexOf(".");if(ee<=0||!SRC_EXT.has(O.slice(ee)))continue;const B=_(Z);if(v.has(B)||i&&isTestSourcePath(B))continue;x.push(B)}}},"walk");return await q(h,g),{paths:x,entered:F,truncated:D}},"walkDir"),b=s((h,g)=>{let v=C.get(h);return v||(v=g(),C.set(h,v)),v},"memo"),se=s(async()=>{const h=C.get(S);if(!h)return;const g=await h;return g.truncated?void 0:g},"wideWalk"),J=new Set,ie=s(()=>b(`files:${path.resolve(t)}`,async()=>{const h=await se();return h?{paths:h.paths.filter(g=>!g.includes("/")),entered:J,truncated:!1}:R(t,!1)}),"rootFiles"),ne=s(h=>b(`tree:${path.resolve(h)}`,async()=>{const g=await se(),v=_(h);if(g?.entered.has(v)){const x=`${v}/`;return{paths:v===""?g.paths:g.paths.filter(F=>F.startsWith(x)),entered:J,truncated:!1}}return R(h,!0)}),"subtree"),G=s(h=>{const g=new Set;let v=0,x=h.some(D=>D.truncated);const F=s(D=>g.has(D)?!0:g.size>=SCAN_FILE_CAP?(x=!0,!1):(g.add(D),v^=fnv1a(D),!0),"add");for(const D of h)if(!D.paths.every(F))break;return{signature:`${g.size}:${(v>>>0).toString(36)}`,paths:g,truncated:x}},"compose");return{async snapshot(h){if(!h||h.length===0)return G([await b(S,()=>R(t,!0))]);const g=[await ie()];for(const v of h)g.push(await ne(v));return G(g)}}}w(sourceScanPass,"sourceScanPass"),s(sourceScanPass,"sourceScanPass");async function sourceSetSnapshot(t,c,a,i){return sourceScanPass(t,a,i).snapshot(c)}w(sourceSetSnapshot,"sourceSetSnapshot"),s(sourceSetSnapshot,"sourceSetSnapshot");async function sourceSetSignature(t,c,a,i){return(await sourceSetSnapshot(t,c,a,i)).signature}w(sourceSetSignature,"sourceSetSignature"),s(sourceSetSignature,"sourceSetSignature");const SOURCE_DIFF_PATH_CAP=128;function diffSourceSets(t,c,a,i){if(t.paths.size===c.paths.size){let _=!0;for(const R of t.paths)if(!c.paths.has(R)){_=!1;break}if(_)return null}const f=[],C=[];let S=t.truncated||c.truncated;for(const _ of c.paths)t.paths.has(_)||(f.length+C.length<SOURCE_DIFF_PATH_CAP?f.push(_):S=!0);for(const _ of t.paths)c.paths.has(_)||(f.length+C.length<SOURCE_DIFF_PATH_CAP?C.push(_):S=!0);return{changedAt:a,added:f,removed:C,truncated:S,...i!==void 0?{windowMs:i}:{}}}w(diffSourceSets,"diffSourceSets"),s(diffSourceSets,"diffSourceSets");function sourceChangeWindowMs(t,c,a,i){const f=c&&c>0?t-c:a;return Math.max(f,a)+i}w(sourceChangeWindowMs,"sourceChangeWindowMs"),s(sourceChangeWindowMs,"sourceChangeWindowMs");function sourceScopeDirs(t,c,a,i,f){const C=s(R=>{const b=path.relative(t,R).split(path.sep).filter(Boolean);return b.length===0||b[0].startsWith("..")?"":b[0]},"bucketOf")(c);if(!C)return[];const S=new Set(a.filter(Boolean)),_=new Set([C]);for(const R of i)R.startsWith(".")||SRC_EXCLUDE.has(R)||f?.has(R)||R.endsWith("-devserver")||S.has(R)||_.add(R);return[..._].sort().map(R=>path.join(t,R))}w(sourceScopeDirs,"sourceScopeDirs"),s(sourceScopeDirs,"sourceScopeDirs");function podRecycleReason(t,c,a,i){return!t.proc||t.state!=="up"||t.spawnAt==null||t.inflight>0||(t.activeWork??0)>0||c-t.lastUsed<i.settleMs||t.lastStaleRecycleAt!=null&&c-t.lastStaleRecycleAt<i.cooldownMs?null:a>0&&a-t.spawnAt>i.graceMs?"stale-source-set":i.maxAgeMs>0&&c-t.spawnAt>i.maxAgeMs?"max-age":null}w(podRecycleReason,"podRecycleReason"),s(podRecycleReason,"podRecycleReason");function lensFrontendStale(t,c,a,i,f){return!(i-c<f.cooldownMs||a<=0||a-t<=f.graceMs||i-a<f.settleMs)}w(lensFrontendStale,"lensFrontendStale"),s(lensFrontendStale,"lensFrontendStale");function lensWatcherVerdict(t,c,a,i=[]){if(!t)return"unknown";if("schemaVersion"in t&&t.schemaVersion>=2&&i.length>0){const f=c-a,C=new Set(t.recentEvents.filter(S=>S.at>=f).map(S=>path.resolve(S.path)));return i.every(S=>C.has(path.resolve(S)))?"heard":"missed"}return t.lastFsSetEventAt>=c-a?"heard":"missed"}w(lensWatcherVerdict,"lensWatcherVerdict"),s(lensWatcherVerdict,"lensWatcherVerdict");function podWatcherVerdict(t,c,a){if(!t)return"unknown";if(t.degraded)return"degraded";const i=c-a;return t.lastWatchRunAt>=i||t.lastBuildAt>=i?"heard":"silent"}w(podWatcherVerdict,"podWatcherVerdict"),s(podWatcherVerdict,"podWatcherVerdict");function lensChildWedged(t,c,a,i){const f=t.hasListened?i.graceMs:Math.max(i.graceMs,i.neverListenedGraceMs);return a-t.spawnedAt<f||a-c<i.cooldownMs||t.probeFails<Math.max(2,i.failureThreshold)?null:"wedged-no-listener"}w(lensChildWedged,"lensChildWedged"),s(lensChildWedged,"lensChildWedged");function isPortConflictExit(t,c){return t.signal||t.code===null||t.code===0||t.ranMs>c.fastFailMs?!1:t.portHeld}w(isPortConflictExit,"isPortConflictExit"),s(isPortConflictExit,"isPortConflictExit");function createServiceLayer(t,c,a={}){const i=a.spawnProcess??node_child_process_1.spawn,f=a.probeAlive??pod_probe_1.probePodAlive,C=a.killTree??((e,r="SIGTERM")=>{try{process.kill(-e.pid,r)}catch{try{e.kill(r)}catch{}}}),S=a.sharedBuild??(0,shared_build_1.createSharedBuildRelay)(t),_=s((e,r="SIGTERM")=>{S.detach(e),C(e,r)},"killTree"),R=a.handoffDrainMs??(0,handoff_retire_1.handoffDrainBoundMs)(),b=a.treeAlive??(e=>{if(!e.pid)return!1;try{return process.kill(-e.pid,0),!0}catch(r){return r.code==="EPERM"}}),se=a.retirePollMs??1e3,J=a.rollReadyMs??types_1.POD_ROLL_READY_MS,ie=a.rollPollMs??750,ne=a.maxConcurrentRolls??types_1.POD_MAX_CONCURRENT_ROLLS,G=ne>0?ne:Number.POSITIVE_INFINITY,h=a.maxConcurrentColdStarts??types_1.MAX_COLD_STARTS,g=h>0?h:Number.POSITIVE_INFINITY;let v=0;const x=[],F=s(()=>v<g?(v+=1,Promise.resolve()):new Promise(e=>{x.push(()=>{v+=1,e()})}),"acquireColdSlot"),D=s(()=>{v-=1,x.shift()?.()},"releaseColdSlot"),{emit:q}=c,Q=s(e=>e.wsKey===t.wsKey?void 0:path.join(e.root,".lensmcp","events.jsonl"),"serviceBus"),y=s((e,r,n,o,d)=>q(r,n,o,{wsKey:e.wsKey,...d},void 0,Q(e)),"emitSvc"),I=s(e=>(e.scannedAt=0,(0,discovery_1.pickSock)(e,t.scanTtlMs)?e.socks.length:0),"scanNow"),U=s((e,r)=>{let n;try{n=(0,node_child_process_1.execFileSync)("ps",["ax","-ww","-o","pid=,pgid=,command="],{encoding:"utf8",timeout:5e3})}catch{return 0}const o=e.proc?.pid;let d=0;for(const l of n.split(`
2
+ `)){const p=l.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);if(!p)continue;const u=Number(p[1]),A=Number(p[2]);if(isRunnerOf(p[3],r,e.root)&&!(u===process.pid||u===o)&&!(o!==void 0&&A===o)){try{process.kill(-A,"SIGKILL")}catch{try{process.kill(u,"SIGKILL")}catch{}}d+=1}}return d>0&&(console.log(`[gateway] ${r}: reaped ${d} stale runner tree(s) \u2014 a foreign generation of this service (holding nx's task lock, or its pool)`),y(e,"warning",`stale-runner reap: ${e.project} (${d} foreign tree(s))`,`cluster-service:${e.project}`,{kind:"stale-runner-reap",project:e.project,reaped:d})),d},"reapForeignRunners"),O=s((e,r)=>({...(0,pod_env_1.podBaseEnv)(process.env,t.root,e.root),...e.instanceEnv?{...e.instanceEnv,LENSMCP_SOCKET_ONLY:"1"}:{},...e.frozen?{LENSMCP_FREEZE:"1",LENSMCP_TYPECHECK:"off"}:{},NX_DAEMON:"false",NX_ISOLATE_PLUGINS:"false",...e.wsKey?{LENSMCP_WS_KEY:e.wsKey}:{},...e.sockNs?{LENSMCP_SOCK_NS:e.sockNs}:{},LENSMCP_EVENT_FILE:e.wsKey===t.wsKey?t.eventFile:path.join(e.root,".lensmcp","events.jsonl"),LENSMCP_GATEWAY_PID:String(process.pid),LENSMCP_POD_GENERATION:String(r),...eastWestEnv(t,e)}),"serviceEnv"),Z=s((e,r,n,o)=>{const d=O(e,r),l=e.wsKey||path.basename(e.root),p=(0,serve_launch_1.planServeLaunch)({stores:t.graphStores,root:e.root,project:e.project,target:e.decl.serveTarget??"serve-hmr",childCount:o,baseEnv:d});if(p.kind==="runner"){const E=S.offeredTo(e),m=i((0,process_name_1.namedExecPath)((0,process_name_1.processName)(l,e.project,serve_launch_1.SERVE_RUNNER_ROLE)),[...p.script.execArgv,p.script.path,...(0,serve_launch_1.serveRunnerArgs)(n,o,e.root)],{cwd:e.root,detached:!0,env:E?{...p.env,[protocol_1.SHARED_BUILD_ENV]:"1"}:p.env,stdio:["ignore","pipe","pipe","ipc"]});return E&&S.attach(e,r,m),m.send?.(p.message,k=>{k&&console.error(`[gateway] ${n}: could not hand the task to its runner (${k.message})`)}),{proc:m,launcher:"runner"}}console.log(`[gateway] ${n}: starting through nx run \u2014 ${p.reason}`);const u=path.join(e.root,"node_modules","nx","dist","bin","nx.js"),A=(0,process_name_1.namedExecPath)((0,process_name_1.processName)(l,e.project,"nx")),[L,j]=fs.existsSync(u)?[A,[u,"run",n,`--childCount=${o}`]]:["yarn",["nx","run",n,`--childCount=${o}`]];return{proc:i(L,j,{cwd:e.root,detached:!0,env:d,stdio:["ignore","pipe","pipe"]}),launcher:"nx"}},"startServiceTree"),ee=new WeakMap,B=s((e,r,n)=>{const o=`${e.project}:${e.decl.serveTarget??"serve-hmr"}`,d=Math.max(1,e.decl.minPods??1),{proc:l,launcher:p}=Z(e,r,o,d);ee.set(l,p),n==="active"&&(e.launcher=p),n==="active"&&(e.lastErrors=[],e.buildFailed=!1,e.faults=new pod_output_1.PodFaults);const u=s(j=>{const E=j.toString();for(const m of E.split(`
3
+ `)){const k=(0,pod_output_1.classifyPodLine)(m),{clean:M,broken:N,healthy:$}=k,W=M;if(N){e.lastErrors.push(M),e.lastErrors.length>12&&e.lastErrors.shift(),e.buildFailed=!0;const P=Date.now();(M!==e.lastErrorEmitSig||P-(e.lastErrorEmitAt??0)>=types_1.SERVICE_ERROR_THROTTLE_MS)&&(e.lastErrorEmitSig=M,e.lastErrorEmitAt=P,y(e,"error",`service error: ${e.project}`,`cluster-service:${e.project}`,{kind:"service-error",project:e.project,line:M,fault:k.fault}))}$&&(e.buildFailed=!1);const T=(e.faults??=new pod_output_1.PodFaults).observe(k);if(T){e.lastErrorEmitSig=void 0;const P=T.open.length===0;y(e,"info",`${P?"service recovered":`${T.closed.join(" + ")} recovered`}: ${e.project}`,`cluster-service:${e.project}`,{kind:P?"service-recovered":"service-fault-closed",project:e.project,line:M,closed:T.closed,open:T.open})}!e.lockBlockedAt&&/Waiting for .+ in another nx process/.test(W)&&(e.lockBlockedAt=Date.now(),console.log(`[gateway] ${o}: spawn blocked by a stale nx runner \u2014 breaking the lock + respawning`),y(e,"warning",`nx task lock: ${e.project} blocked by a stale runner \u2014 auto-breaking`,`cluster-service:${e.project}`,{kind:"nx-lock-break",project:e.project}),setTimeout(()=>{t.stopped()||!e.lockBlockedAt||(V(e,"parked on a foreign nx task lock \u2014 recycling",{announce:!1}),U(e,o),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&X(e)},1500).unref?.())},250).unref?.())}},"sniff"),A=e.wsKey&&e.wsKey!==t.wsKey&&e.root?(0,workspace_log_1.workspaceLogWriter)(e.root):void 0,L=s(j=>E=>{if(t.stopped())return;const m=(0,inspector_secret_1.redactInspectorUrls)(E.toString());u(m);const k=`[${e.project}] ${m}`;j.write(k),A?.(k)},"relay");return l.stdout?.on("data",L(process.stdout)),l.stderr?.on("data",L(process.stderr)),l.on("exit",(j,E)=>{if(e.proc!==l||(e.proc=void 0,e.state="down",t.stopped()))return;const m=(0,exit_reason_1.describeExit)(j,E);if(console.log(`[gateway] ${o} exited (${j??"no code"}${E?`, ${E}`:""}${e.inflight?`, ${e.inflight} request(s) in flight`:""})${m?` \u2014 ${m}`:""}`),y(e,j===0?"info":"warning",`service down: ${e.project} (exit ${j})`,`cluster-service:${e.project}`,{kind:"service-down",project:e.project,code:j,...E?{signal:E}:{}}),j===0||e.buildFailed)return;const k=Date.now();if(e.restarts=(e.restarts??[]).filter(N=>k-N<types_1.SERVICE_RESTART_WINDOW_MS),e.restarts.length>=types_1.SERVICE_MAX_RESTARTS){y(e,"error",`service crash-looping: ${e.project} \u2014 ${types_1.SERVICE_MAX_RESTARTS}+ restarts/min, not auto-respawning (the next request retries)`,`cluster-service:${e.project}`,{kind:"service-crash-loop",project:e.project});return}e.restarts.push(k);const M=Math.min(8e3,500*2**(e.restarts.length-1));setTimeout(()=>{t.stopped()||e.proc||e.state!=="down"||(console.log(`[gateway] self-heal: respawning ${e.project} (restart ${e.restarts.length}/${types_1.SERVICE_MAX_RESTARTS})`),y(e,"info",`self-heal: respawning ${e.project}`,`cluster-service:${e.project}`,{kind:"service-respawn",project:e.project,attempt:e.restarts.length}),X(e))},M)}),l},"launchServiceProcess"),X=s(e=>{if(t.stopped())return;const r=`${e.project}:${e.decl.serveTarget??"serve-hmr"}`,n=Math.max(1,e.decl.minPods??1);a.reapForeign!==!1&&U(e,r),e.lockBlockedAt=void 0,console.log(`[gateway] starting ${r} (pods=${n})\u2026`),y(e,"info",`service starting: ${e.project} (${n} pod${n>1?"s":""})`,`cluster-service:${e.project}`,{kind:"service-starting",project:e.project,pods:n}),e.state="starting",e.spawnAt=Date.now(),e.generation=(e.generation??0)+1,e.proc=B(e,e.generation,"active")},"spawnService"),Ee=s(async e=>{const r=e.pool.socks[0];if(!r)return;const n=await(0,pod_probe_1.probePodAlive)(r,void 0,t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.wsKey,e.project)]);n&&(e.activeWork=n.activeWork,n.watcher&&(e.watcher=n.watcher))},"refreshActiveWork"),_e=s(e=>{try{const r=Number(fs.readFileSync(`${e}.owner`,"utf8").trim());return Number.isInteger(r)&&r>1?r:void 0}catch{return}},"readOwnerSidecar"),me=s(async e=>{const r=t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.wsKey,e.project)];if(!r)return[];const n=(0,node_crypto_1.createHash)("sha256").update(r).digest("hex").slice(0,8),o=[];for(const d of e.pool.socks){const l=await(0,pod_probe_1.probePodAlive)(d,void 0,r);l&&(!l.keyFingerprint||l.keyFingerprint!==n)&&o.push({sock:d,pid:l.pid})}return o},"staleAdoptedSocks"),$e=s((e,r)=>{const n=r.map(o=>o.sock);for(const{sock:o,pid:d}of r){const l=chooseStalePodKillTarget(d,_e(o),process.pid);if(l!==void 0)try{process.kill(l,"SIGTERM")}catch{}try{fs.unlinkSync(o)}catch{}try{fs.unlinkSync(`${o}.owner`)}catch{}}e.pool.socks=e.pool.socks.filter(o=>!n.includes(o)),e.pool.idx=-1,e.pool.scannedAt=0},"evictStaleAdopted"),le=s((e,r)=>{if(e.state==="up")return!1;e.state="up",e.wedgeRecycles=0;const n=e.pool.socks.length;return y(e,"info",`service up: ${e.project} (${n} pods, ${r})`,`cluster-service:${e.project}`,{kind:"service-up",project:e.project,pods:n,adopted:r==="adopted"}),!0},"markUp"),je=s(async e=>{if(I(e.pool)>0){const o=await me(e);if(o.length>0&&(console.error(`[gateway] ${e.project}: ${o.length} adopted pod(s) hold a foreign east-west key \u2014 evicting instead of adopting.`),y(e,"warning",`stale adoption: ${e.project} (foreign east-west key)`,`cluster-service:${e.project}`,{kind:"pod-recycle",project:e.project,reason:"stale-adoption",pods:o.length}),e.proc&&V(e,"stale adoption (foreign east-west key)"),$e(e,o)),e.pool.socks.length>0)return le(e,"adopted"),!0}if(t.stopped())return!1;let r=!1;const n=s(()=>e.state!=="starting"&&!e.proc,"notStarted");if(n())if(e.coldWait)await e.coldWait;else{v>=g&&(console.log(`[gateway] cold start queued: ${e.project}${e.wsKey&&e.wsKey!==t.wsKey?` (${e.wsKey})`:""} \u2014 ${v} starting, ${x.length} ahead (LENSMCP_MAX_COLD_STARTS=${g}).`),y(e,"info",`cold start queued: ${e.project}`,`cluster-service:${e.project}`,{kind:"cold-start-queued",project:e.project,inFlight:v,ahead:x.length}));const o=F();if(e.coldWait=o,await o,e.coldWait=void 0,r=!0,t.stopped())return D(),!1;n()&&X(e)}try{return await ke(e)}finally{r&&D()}},"ensureUp"),ke=s(async e=>{const r=Date.now()+t.startupTimeoutMs,n=Date.now()+12e3;for(;!t.stopped()&&Date.now()<r;){if(await sleep(Math.min(700,t.startupTimeoutMs/4)),e.buildFailed&&Date.now()>n)return!1;if(I(e.pool)>0){e.state="up",e.buildFailed=!1;const o=e.spawnAt?Date.now()-e.spawnAt:void 0;return console.log(`[gateway] ${e.project} up (${e.pool.socks.length} pods${o?`, cold start ${o}ms`:""})`),y(e,"info",`cold start: ${e.project} (${e.pool.socks.length} pods, ${o??"?"}ms)`,`cluster-service:${e.project}`,{kind:"cold-start",project:e.project,pods:e.pool.socks.length,durationMs:o}),!0}if(e.state==="down")return!1}return!1},"waitForPods"),Re=s(e=>{const r=e.decl.maxPods??e.decl.minPods??1,n=e.pool.socks.length,o=scaleTarget(e);!o||n===0||n>=r||e.inflight<=n||Date.now()-e.lastScaleAt<5e3||(e.lastScaleAt=Date.now(),postScale(o,n+1,a.scaleLookup),console.log(`[gateway] autoscale ${e.project}: ${n} \u2192 ${n+1} pods (inflight ${e.inflight})`),y(e,"info",`autoscale: ${e.project} ${n} \u2192 ${n+1} pods`,`cluster-service:${e.project}`,{kind:"autoscale",project:e.project,from:n,to:n+1,inflight:e.inflight}))},"scaleUp"),V=s((e,r,n)=>{if(!e.proc)return;console.log(`[gateway] ${e.project} ${r} \u2014 scaling to zero.`),n?.announce!==!1&&y(e,"info",`idle kill: ${e.project} (${r})`,`cluster-service:${e.project}`,{kind:"idle-kill",project:e.project,why:r});const o=e.proc;try{o.stdout?.removeAllListeners("data"),o.stderr?.removeAllListeners("data"),o.removeAllListeners("exit")}catch{}_(o,"SIGTERM"),e.proc=void 0,e.state="down",setTimeout(()=>{ce(e),e.pool.socks=[],e.pool.scannedAt=0},1200).unref?.()},"killSpawned"),ce=s(e=>{try{for(const r of fs.readdirSync(e.pool.dir)){if(!r.endsWith(".sock"))continue;const n=path.join(e.pool.dir,r);try{const o=Number(fs.readFileSync(`${n}.owner`,"utf8").trim());if(Number.isFinite(o)&&o>0)try{process.kill(o,0);continue}catch{}}catch{}try{fs.unlinkSync(n)}catch{}try{fs.unlinkSync(`${n}.owner`)}catch{}}}catch{}},"sweepDeadOwnerSocks");let H=0;const de=[],Ae=s(()=>H<G?(H+=1,Promise.resolve()):new Promise(e=>{de.push(()=>{H+=1,e()})}),"acquireRollSlot"),Ce=s(()=>{H-=1,de.shift()?.()},"releaseRollSlot"),Te=s(async(e,r,n)=>{I(e.pool);for(const o of e.pool.socks){const d=await f(o,void 0,n);if(d&&r(d.podGeneration))return!0}return!1},"anyPodAnswers"),pe=new Map,Me=s((e,r,n)=>{const o=R>0&&(0,handoff_retire_1.requestHandoffRetire)(e.pool.dir,n);if(_(r,"SIGTERM"),!o)return;const d=e.wsKey&&e.wsKey!==t.wsKey&&e.root?(0,workspace_log_1.workspaceLogWriter)(e.root):void 0,l=s(k=>M=>{if(t.stopped())return;const N=(0,inspector_secret_1.redactInspectorUrls)(M.toString()).replace(/^(?=.)/gm,`[${e.project} g${n} retiring] `);k.write(N),d?.(N)},"say"),p=l(process.stdout),u=l(process.stderr);r.stdout?.on("data",p),r.stderr?.on("data",u);let A=!1;const L=s(()=>{A||(A=!0,clearTimeout(j),clearInterval(m),r.stdout?.removeListener("data",p),r.stderr?.removeListener("data",u),pe.delete(r),(0,handoff_retire_1.clearHandoffRetire)(e.pool.dir,n),ce(e),e.pool.scannedAt=0)},"end"),j=setTimeout(()=>{console.error(`[gateway] pod handoff: ${e.project} g${n} was still running ${R}ms after it was retired \u2014 killing it.`),C(r,"SIGKILL"),L()},R);j.unref?.();const E=s(()=>{if(!A){if(t.stopped()){L();return}b(r)||(console.log(`[gateway] pod handoff: ${e.project} g${n} finished its in-flight work and exited.`),L())}},"look"),m=setInterval(E,se);m.unref?.(),r.once("exit",E),pe.set(r,()=>{C(r,"SIGTERM"),L()})},"retireReplacedTree"),Pe=s(()=>{for(const e of[...pe.values()])e()},"stopRetiringTrees"),we=s((e,r)=>{if(e.rolling)return e.rolling;const n=e.proc;if(!n||t.stopped())return Promise.resolve(!1);if(e.launcher==="nx")return console.log(`[gateway] pod handoff skipped: ${e.project} runs through nx run, which starts one instance of a target at a time \u2014 no standby can overlap it (reason=${r}).`),Promise.resolve(!1);H>=G&&console.log(`[gateway] pod handoff queued: ${e.project} reason=${r} \u2014 ${H} roll(s) in flight, ${de.length} ahead (LENSMCP_POD_MAX_CONCURRENT_ROLLS=${G}).`);const o=(async()=>{await Ae();try{return await xe(e,n,r)}finally{Ce()}})().finally(()=>{e.rolling=void 0});return e.rolling=o,o},"rollPod"),xe=s(async(e,r,n)=>{const o=e.proc;if(o!==r||t.stopped())return!1;const d=e.generation??0,l=d+1,p=Date.now();e.lastStaleRecycleAt=p,console.log(`[gateway] pod handoff start: ${e.project} g${d}\u2192g${l} reason=${n} \u2014 standby spawning, old generation keeps serving.`),y(e,"info",`pod handoff starting: ${e.project} (${n})`,`cluster-service:${e.project}`,{kind:"pod-handoff-start",project:e.project,reason:n,generation:l,fromGeneration:d});const u=B(e,l,"standby"),A=t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.wsKey,e.project)],L=p+J;let j;for(;!t.stopped()&&Date.now()<L&&!(u.exitCode!==null||u.signalCode!==null);){I(e.pool);for(const E of e.pool.socks)if((await f(E,void 0,A))?.podGeneration===l){j=E;break}if(j)break;await sleep(ie)}if(!j||t.stopped()){const E=t.stopped()?"gateway stopping":u.exitCode!==null||u.signalCode!==null?`standby exited (${u.exitCode??u.signalCode})`:`standby not ready within ${Math.round(J/1e3)}s`;try{u.stdout?.removeAllListeners("data"),u.stderr?.removeAllListeners("data")}catch{}_(u,"SIGTERM");const m=!t.stopped()&&e.proc===o&&o.exitCode===null&&o.signalCode===null&&await Te(e,M=>M!==l,A),k=m?`old generation g${d} kept serving`:`and the old generation g${d} is NOT serving (${e.proc!==o||o.exitCode!==null||o.signalCode!==null?"its tree is gone":"none of its pods answers"}) \u2014 the service is down until it recovers`;return console.error(`[gateway] pod handoff failed: ${e.project} g${l}: ${E} \u2014 ${k}.`),y(e,m?"warning":"error",`pod handoff failed: ${e.project}`,`cluster-service:${e.project}`,{kind:"pod-handoff-failed",project:e.project,reason:n,generation:l,error:E,durationMs:Date.now()-p,oldServing:m}),setTimeout(()=>ce(e),1200).unref?.(),!1}if(e.proc!==o)return _(u,"SIGTERM"),!1;e.proc=u,e.launcher=ee.get(u),e.generation=l,e.spawnAt=p,e.state="up",e.watcher=void 0,e.staleVerifiedAt=void 0,e.staleKeptAt=void 0,e.buildFailed=!1,e.lastErrors=[];try{o.stdout?.removeAllListeners("data"),o.stderr?.removeAllListeners("data"),o.removeAllListeners("exit")}catch{}return Me(e,o,d),setTimeout(()=>{ce(e),e.pool.scannedAt=0},1200).unref?.(),console.log(`[gateway] pod handoff ready: ${e.project} g${l} answered on ${path.basename(j)} in ${Date.now()-p}ms; retiring g${d}.`),y(e,"info",`pod handoff complete: ${e.project}`,`cluster-service:${e.project}`,{kind:"pod-handoff-complete",project:e.project,reason:n,generation:l,fromGeneration:d,durationMs:Date.now()-p}),!0},"runRoll"),Se=s(e=>{const r=Date.now();e.restarts=(e.restarts??[]).filter(o=>r-o<types_1.SERVICE_RESTART_WINDOW_MS);const n=(e.wedgeRecycles??0)+1;if(e.restarts.length>=types_1.SERVICE_MAX_RESTARTS||n>types_1.SERVICE_MAX_RESTARTS){const o=e.restarts.length>=types_1.SERVICE_MAX_RESTARTS?`wedged ${types_1.SERVICE_MAX_RESTARTS}+\xD7/min`:`recycled ${types_1.SERVICE_MAX_RESTARTS}\xD7 in a row and no pod came up`;y(e,"error",`service crash-looping: ${e.project} \u2014 ${o}, not auto-recycling (the next request retries)`,`cluster-service:${e.project}`,{kind:"service-crash-loop",project:e.project,recycles:n-1}),V(e,"wedged (crash-loop cap) \u2014 leaving down for the next request",{announce:!1});return}e.wedgeRecycles=n,e.restarts.push(r),console.log(`[gateway] self-heal: recycling WEDGED ${e.project} (live parent, 0 live pods; attempt ${n}/${types_1.SERVICE_MAX_RESTARTS} with no pod up since the first, ${e.restarts.length} in the last minute; ${e.inflight??0} request(s) in flight \u2014 jobs held by the dead pod keep their locks until they expire)`),y(e,"warning",`self-heal: recycling wedged ${e.project} (0 live pods)`,`cluster-service:${e.project}`,{kind:"service-recycle",project:e.project,attempt:n,inWindow:e.restarts.length}),V(e,"wedged: 0 live pods \u2014 recycling",{announce:!1}),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&X(e)},1500).unref?.()},"recycleWedged"),De={maxAgeMs:types_1.POD_MAX_AGE_MS,graceMs:types_1.POD_STALE_GRACE_MS,settleMs:types_1.POD_RECYCLE_SETTLE_MS,cooldownMs:types_1.POD_STALE_RECYCLE_COOLDOWN_MS};let ue;const te=new Map,ve=new Map;let fe=!1,he=0,ae=0;const Le=s(()=>{const e=t.projectRoots??{},r=new Set(t.services.map(o=>o.project));for(const o of t.routes)o.lens&&r.add(o.lens.project);const n=new Set;for(const o of r){const d=e[o];if(!d)continue;const l=path.relative(t.root,d).split(path.sep).filter(Boolean)[0];l&&!l.startsWith("..")&&n.add(l)}return[...n]},"managedBuckets"),be=s((e,r,n,o)=>{const d=t.projectSourceRoots?.[e];if(d?.length)return d;const l=t.projectRoots?.[e];if(!l)return;const p=sourceScopeDirs(t.root,l,n,r,o);return p.length>0?p:void 0},"scopeFor"),Ie=s(()=>{if(fe||Date.now()-he<types_1.POD_STALE_SCAN_MS)return;fe=!0,he=Date.now();const e=he;(async()=>{const r=(0,scope_1.readSourceSetOptions)(t.root),n=r.exclude,o={ignoreTests:r.ignoreTests},d=sourceScanPass(t.root,n,o),l=await d.snapshot(),p=Date.now(),u=sourceChangeWindowMs(p,ae,types_1.POD_STALE_SCAN_MS,types_1.POD_RECYCLE_SETTLE_MS),A=ue?diffSourceSets(ue,l,p,u):null;if(A&&(t.sourceSetChangedAt=p,t.sourceSetChange=A,q("info","source file set changed \u2014 stale pods recycle when idle","cluster-gateway",{kind:"source-set-changed",added:A.added,removed:A.removed,truncated:A.truncated})),ue=l,!t.projectRoots){ae=e;return}let L;try{L=(await fs.promises.readdir(t.root,{withFileTypes:!0})).filter($=>$.isDirectory()).map($=>$.name)}catch{return}const j=Le(),E=new Set(t.services.map($=>$.project));for(const $ of t.routes)$.lens&&E.add($.lens.project);const m=new Map;for(const $ of E){const W=be($,L,j,n);if(!W)continue;const T=W.join("|"),P=m.get(T)??{dirs:W,projects:[]};P.projects.push($),m.set(T,P)}const k=Date.now(),M=sourceChangeWindowMs(k,ae,types_1.POD_STALE_SCAN_MS,types_1.POD_RECYCLE_SETTLE_MS);ae=e;const N=s(($,W,T)=>{t.sourceSetChangedFor??=new Map,t.sourceSetChangesFor??=new Map;for(const P of $)t.sourceSetChangedFor.set(P,W),t.sourceSetChangesFor.set(P,T)},"stampScoped");for(const[$,{dirs:W,projects:T}]of m){const P=await d.snapshot(W),ye=te.get($);te.set($,P);const ge=new Map;for(const Y of T){const K=ve.get(Y);if(K===void 0||K===$)continue;const z=ge.get(K)??[];z.push(Y),ge.set(K,z)}for(const Y of T)ve.set(Y,$);for(const[Y,K]of ge){const z=te.get(Y),oe=z?diffSourceSets(z,P,k,M):A;oe&&(N(K,z?k:p,oe),q("info",`source file set changed \u2014 ${K.join(", ")}'s watch scope was re-keyed`,"cluster-gateway",{kind:"source-set-changed",scoped:!0,rescoped:!0,projects:K,added:oe.added,removed:oe.removed,truncated:oe.truncated}))}if(!ye)continue;const re=diffSourceSets(ye,P,k,M);re&&(N(T,k,re),q("info",`source file set changed in ${T.join(", ")}'s scope`,"cluster-gateway",{kind:"source-set-changed",scoped:!0,projects:T,added:re.added,removed:re.removed,truncated:re.truncated}))}for(const $ of[...te.keys()])m.has($)||te.delete($)})().catch(()=>{}).finally(()=>{fe=!1})},"maybeScanSourceSet"),Oe=s((e,r)=>{const n=(t.sourceSetChangesFor?.get(e.project)??t.sourceSetChange)?.windowMs??types_1.POD_STALE_SCAN_MS+types_1.POD_RECYCLE_SETTLE_MS,o=podWatcherVerdict(e.watcher,r,n),d=s(l=>(e.staleKeptAt!==r&&(e.staleKeptAt=r,console.log(`[gateway] stale source set: pod ${e.project} ${l} \u2014 no recycle.`)),!1),"keep");return o==="heard"?(e.staleVerifiedAt=r,y(e,"info",`stale check: ${e.project} watcher confirmed \u2014 no recycle`,`cluster-service:${e.project}`,{kind:"pod-stale-verified",project:e.project,watcher:o}),d(`watcher CONFIRMED alive across the change (last build ${new Date(e.watcher?.lastBuildAt??0).toISOString()})`)):o==="degraded"?(console.log(`[gateway] stale source set: pod ${e.project} watcher DEGRADED (${e.watcher?.degraded}) \u2014 recycling.`),!0):o==="silent"?types_1.POD_WATCHER_SILENT==="keep"?d("watcher alive but quiet across the change \u2014 keeping it (LENSMCP_POD_WATCHER_SILENT=keep; a live watcher cannot miss an add it will need)"):!0:types_1.POD_WATCHER_UNVERIFIED==="keep"?d("unverifiable (no watcher testimony \u2014 older devserver bundle?) \u2014 keeping it (LENSMCP_POD_WATCHER_UNVERIFIED=keep)"):!0},"podStaleRecycleAllowed"),Ne=s((e,r)=>{if(e.lastStaleRecycleAt=Date.now(),types_1.POD_RECYCLE_MODE==="rolling"&&e.proc&&e.launcher!=="nx"){we(e,r);return}console.log(`[gateway] zombie recycle: ${e.project} (${r}) \u2014 stale fs view; fresh pod incoming`),y(e,"warning",`zombie recycle: ${e.project} (${r})`,`cluster-service:${e.project}`,{kind:"pod-recycle",project:e.project,reason:r});const n=!!e.decl.eager;V(e,`${r} \u2014 recycling stale pod`,{announce:!1}),n&&setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&X(e)},1500).unref?.()},"recyclePod"),We=s(()=>{const e=setInterval(()=>{Fe().catch(r=>console.error("[gateway] sweep error:",r))},t.sweepMs);return e.unref(),e},"startSweeper"),Fe=s(async()=>{{if(t.stopped())return;for(const e of t.graphStores?.all()??[])e.refreshIfStale();types_1.POD_RECYCLE_ENABLED&&Ie();for(const e of t.registry?t.registry.allServices():t.services){if(t.stopped()||!e.proc){e.wedgedSince=void 0;continue}const r=path.resolve(e.root)===path.resolve(t.root),n=e.decl.idleKillSec;if(n&&e.inflight===0&&Date.now()-e.lastUsed>n*1e3){V(e,`idle ${n}s`),e.wedgedSince=void 0;continue}const o=e.state==="starting"&&e.spawnAt!==void 0&&Date.now()-e.spawnAt<t.startupTimeoutMs,d=I(e.pool)>0;if(d||o){if(e.wedgedSince=void 0,d&&le(e,"observed"),d&&!o&&types_1.POD_RECYCLE_ENABLED&&r){const l=t.sourceSetChangedFor?.get(e.project)??t.sourceSetChangedAt??0;await Ee(e);const p=(e.staleVerifiedAt??0)>=l&&l>0,u=podRecycleReason(e,Date.now(),p?0:l,De);if(u==="stale-source-set"&&!Oe(e,l))continue;u&&Ne(e,u)}continue}e.wedgedSince??=Date.now(),Date.now()-e.wedgedSince>types_1.WEDGE_RECYCLE_GRACE_MS&&(e.wedgedSince=void 0,Se(e))}}},"sweepOnce");return{scanNow:I,spawnService:X,ensureUp:je,scaleUp:Re,killSpawned:V,markUp:le,startSweeper:We,rollPod:we,recycleWedged:Se,stopRetiringTrees:Pe,stopBuildHost:s(e=>e===void 0?S.stop():S.release(e),"stopBuildHost")}}w(createServiceLayer,"createServiceLayer"),s(createServiceLayer,"createServiceLayer");function chooseStalePodKillTarget(t,c,a){if(!(t===void 0||!Number.isInteger(t)||t<=1)&&!(c!==void 0&&c!==t)&&t!==a)return t}w(chooseStalePodKillTarget,"chooseStalePodKillTarget"),s(chooseStalePodKillTarget,"chooseStalePodKillTarget");
@@ -19,6 +19,11 @@ export interface Observability {
19
19
  emit(severity: Severity, title: string, fingerprint: string, raw?: Record<string, unknown>, flowCtx?: Record<string, unknown>, busFile?: string): void;
20
20
  eid(): string;
21
21
  beginTrace(req: http.IncomingMessage): GwTrace | undefined;
22
+ /**
23
+ * Once the route is known: give a request that carries no flow one of its own, when it is a call from
24
+ * outside to a backend service. Returns the trace to go on with — a new one, `trace` itself, or `undefined`.
25
+ */
26
+ startHeadlessFlow(req: http.IncomingMessage, route: Route, trace: GwTrace | undefined, receivedAt: number): GwTrace | undefined;
22
27
  traceStep(t: GwTrace | undefined, stepName: string, info?: Record<string, unknown>): void;
23
28
  /** `route` identifies the OWNING workspace, so a guest's request narration goes to its own bus. */
24
29
  finishTrace(t: GwTrace | undefined, project: string, status: number, extra?: Record<string, unknown>, route?: Route): void;
@@ -1,2 +1,2 @@
1
- "use strict";var _=Object.defineProperty;var h=(t,d)=>_(t,"name",{value:d,configurable:!0});var S=Object.defineProperty,n=h((t,d)=>S(t,"name",{value:d,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EDGE_DURATION_SAMPLES=exports.LENS_DASHBOARD_PROJECT=void 0,exports.createObservability=createObservability;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),redact_1=require("@lensmcp/core/redact"),busHygiene=(()=>{try{return require("@lensmcp/node-instrumentation")}catch{return}})();exports.LENS_DASHBOARD_PROJECT="(lens-dashboard)",exports.EDGE_DURATION_SAMPLES=64;function ownerOf(t){return t?.svc?{root:t.svc.root,wsKey:t.svc.wsKey}:t?.lens?{...t.lens.workspaceRoot?{root:t.lens.workspaceRoot}:{},...t.lens.workspaceKey?{wsKey:t.lens.workspaceKey}:{}}:{}}h(ownerOf,"ownerOf"),n(ownerOf,"ownerOf");function busFileFor(t,d){const{root:u}=ownerOf(t);if(!u)return;const l=path.join(u,".lensmcp","events.jsonl");return l===d?void 0:l}h(busFileFor,"busFileFor"),n(busFileFor,"busFileFor");const hdr=n(t=>typeof t=="string"&&t!==""?t:Array.isArray(t)?t[0]:void 0,"hdr");function createObservability(t,d){const u=n(()=>Date.now().toString(36)+Math.random().toString(36).slice(2,12),"eid"),l=busHygiene?.createEventFloodGuard({burst:30,perSec:5}),w=n((e,r,s,a,i,c)=>{const o=c??t;try{const p=l?l.admit(s):{ok:!0,suppressed:0};if(!p.ok)return;fs.mkdirSync(path.dirname(o),{recursive:!0});const y=a&&(0,redact_1.redactDeep)(a),g=JSON.stringify({id:u(),sessionId:"pending",timestamp:Date.now(),source:"gateway",category:"cluster",severity:e,context:(0,redact_1.redactDeep)({sessionId:"pending",...i}),fingerprint:s,title:(0,redact_1.safeTitle)(r),raw:p.suppressed>0?{...y,floodSuppressed:p.suppressed}:y})+`
2
- `;busHygiene?.maybeRotateEventFile(o,g.length),fs.appendFileSync(o,g)}catch{}},"emit"),m=n(e=>{const r=hdr(e.headers["x-lensmcp-flow-id"]);let s=hdr(e.headers["x-request-id"]);if(!(!r&&!s&&!hdr(e.headers.traceparent)))return s||(s=u(),e.headers["x-request-id"]=s),{startedAt:Date.now(),method:(e.method??"GET").toUpperCase(),host:(e.headers.host||"").split(":")[0],url:e.url??"/",steps:[],...r?{flowId:r}:{},...s?{requestId:s}:{},...hdr(e.headers["x-lensmcp-parent-request-id"])?{parentRequestId:hdr(e.headers["x-lensmcp-parent-request-id"])}:{},...hdr(e.headers["x-lensmcp-origin-node-id"])?{originNodeId:hdr(e.headers["x-lensmcp-origin-node-id"])}:{}}},"beginTrace"),E=n((e,r,s)=>{e&&e.steps.push({step:r,atMs:Date.now()-e.startedAt,...s})},"traceStep"),D=n((e,r,s,a,i)=>{if(!e||e.done||(e.done=!0,r===exports.LENS_DASHBOARD_PROJECT&&s<500))return;const c=Date.now()-e.startedAt;w(s>=500?"error":"info",`gateway ${e.method} ${e.host}${e.url} \u2192 ${s} (${c}ms)`,`gateway-request:${r}`,{kind:"gateway-request",project:r,host:e.host,method:e.method,url:e.url,status:s,durationMs:c,startedAt:e.startedAt,steps:e.steps,...ownerOf(i).wsKey?{wsKey:ownerOf(i).wsKey}:{},...a},{...e.flowId?{flowId:e.flowId}:{},...e.requestId?{requestId:e.requestId}:{},...e.parentRequestId?{parentRequestId:e.parentRequestId}:{},...e.originNodeId?{originNodeId:e.originNodeId}:{}},busFileFor(i,t))},"finishTrace"),f=new Map;return{emit:w,eid:u,beginTrace:m,traceStep:E,finishTrace:D,recordEdge:n((e,r,s,a)=>{if(e.project===exports.LENS_DASHBOARD_PROJECT&&s<500&&s!==0)return;const i=e.host??"(default)",c=(a?a+"\u21D2":"")+i+"\u2192"+e.project,o=f.get(c)??{host:i,project:e.project,caller:a,count:0,totalMs:0,errors:0,durations:[],...busFileFor(e,t)?{busFile:busFileFor(e,t)}:{},...ownerOf(e).wsKey?{wsKey:ownerOf(e).wsKey}:{}};if(o.count+=1,o.totalMs+=r,o.durations.length<exports.EDGE_DURATION_SAMPLES)o.durations.push(Math.round(r));else{const p=Math.floor(Math.random()*o.count);p<exports.EDGE_DURATION_SAMPLES&&(o.durations[p]=Math.round(r))}(s>=500||s===0)&&(o.errors+=1),f.set(c,o)},"recordEdge"),startEdgeFlusher:n(()=>{const e=setInterval(()=>{for(const r of f.values()){const s=Math.round(r.totalMs/r.count);w(r.errors?"warning":"info",`traffic ${r.host} \u2192 ${r.project}: ${r.count} req, ~${s}ms${r.errors?`, ${r.errors} errors`:""}`,`gateway-traffic:${r.host}->${r.project}`,{kind:"gateway-traffic",host:r.host,project:r.project,caller:r.caller,count:r.count,avgMs:s,errors:r.errors,durationsMs:r.durations,...r.wsKey?{wsKey:r.wsKey}:{}},void 0,r.busFile)}f.clear()},d.trafficFlushMs);return e.unref(),e},"startEdgeFlusher")}}h(createObservability,"createObservability"),n(createObservability,"createObservability");
1
+ "use strict";var A=Object.defineProperty;var w=(s,c)=>A(s,"name",{value:c,configurable:!0});var _=Object.defineProperty,d=w((s,c)=>_(s,"name",{value:c,configurable:!0}),"d");Object.defineProperty(exports,"__esModule",{value:!0}),exports.EDGE_DURATION_SAMPLES=exports.LENS_DASHBOARD_PROJECT=void 0,exports.createObservability=createObservability;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),headless_flow_1=require("@lensmcp/core/headless-flow"),redact_1=require("@lensmcp/core/redact"),busHygiene=(()=>{try{return require("@lensmcp/node-instrumentation")}catch{return}})();exports.LENS_DASHBOARD_PROJECT="(lens-dashboard)",exports.EDGE_DURATION_SAMPLES=64;function ownerOf(s){return s?.svc?{root:s.svc.root,wsKey:s.svc.wsKey}:s?.lens?{...s.lens.workspaceRoot?{root:s.lens.workspaceRoot}:{},...s.lens.workspaceKey?{wsKey:s.lens.workspaceKey}:{}}:{}}w(ownerOf,"ownerOf"),d(ownerOf,"ownerOf");function busFileFor(s,c){const{root:l}=ownerOf(s);if(!l)return;const f=path.join(l,".lensmcp","events.jsonl");return f===c?void 0:f}w(busFileFor,"busFileFor"),d(busFileFor,"busFileFor");const hdr=d(s=>typeof s=="string"&&s!==""?s:Array.isArray(s)?s[0]:void 0,"hdr");function createObservability(s,c){const l=d(()=>Date.now().toString(36)+Math.random().toString(36).slice(2,12),"eid"),f=busHygiene?.createEventFloodGuard({burst:30,perSec:5}),y=d((e,t,r,u,n,a)=>{const o=a??s;try{const i=f?f.admit(r):{ok:!0,suppressed:0};if(!i.ok)return;fs.mkdirSync(path.dirname(o),{recursive:!0});const p=u&&(0,redact_1.redactDeep)(u),h=JSON.stringify({id:l(),sessionId:"pending",timestamp:Date.now(),source:"gateway",category:"cluster",severity:e,context:(0,redact_1.redactDeep)({sessionId:"pending",...n}),fingerprint:r,title:(0,redact_1.safeTitle)(t),raw:i.suppressed>0?{...p,floodSuppressed:i.suppressed}:p})+`
2
+ `;busHygiene?.maybeRotateEventFile(o,h.length),fs.appendFileSync(o,h)}catch{}},"emit"),I=d(e=>{const t=hdr(e.headers["x-lensmcp-flow-id"]);let r=hdr(e.headers["x-request-id"]);if(!(!t&&!r&&!hdr(e.headers.traceparent)))return r||(r=l(),e.headers["x-request-id"]=r),{startedAt:Date.now(),method:(e.method??"GET").toUpperCase(),host:(e.headers.host||"").split(":")[0],url:e.url??"/",steps:[],...t?{flowId:t}:{},...r?{requestId:r}:{},...hdr(e.headers["x-lensmcp-parent-request-id"])?{parentRequestId:hdr(e.headers["x-lensmcp-parent-request-id"])}:{},...hdr(e.headers["x-lensmcp-origin-node-id"])?{originNodeId:hdr(e.headers["x-lensmcp-origin-node-id"])}:{}}},"beginTrace"),E=d((e,t,r,u)=>{if(r?.flowId||!t.pool||t.internal||t.project===exports.LENS_DASHBOARD_PROJECT||!(0,headless_flow_1.headlessFlowsEnabled)())return r;const n=e.url??"/";if((0,headless_flow_1.isProbePath)(n))return r;const a=hdr(e.headers["x-request-id"]);if(!a)return r;const o=(0,headless_flow_1.headlessFlowId)(a);e.headers["x-lensmcp-flow-id"]=o;const i=(e.method??"GET").toUpperCase(),p=(e.headers.host||"").split(":")[0],h=`request:${i} ${n.split("?")[0]}`,m=r??{startedAt:u,method:i,host:p,url:n,steps:[{step:"received",atMs:0,host:p,method:i,url:n}],requestId:a};return m.flowId=o,m.originNodeId=h,y("info",`${i} ${n.split("?")[0]}`,`gateway-origin:${t.project}`,{kind:"flow-origin",originNodeId:h,project:t.project,host:p,method:i,url:n,...ownerOf(t).wsKey?{wsKey:ownerOf(t).wsKey}:{}},{flowId:o,requestId:a,originType:"request",originNodeId:h},busFileFor(t,s)),m},"startHeadlessFlow"),S=d((e,t,r)=>{e&&e.steps.push({step:t,atMs:Date.now()-e.startedAt,...r})},"traceStep"),q=d((e,t,r,u,n)=>{if(!e||e.done||(e.done=!0,t===exports.LENS_DASHBOARD_PROJECT&&r<500))return;const a=Date.now()-e.startedAt;y(r>=500?"error":"info",`gateway ${e.method} ${e.host}${e.url} \u2192 ${r} (${a}ms)`,`gateway-request:${t}`,{kind:"gateway-request",project:t,host:e.host,method:e.method,url:e.url,status:r,durationMs:a,startedAt:e.startedAt,steps:e.steps,...ownerOf(n).wsKey?{wsKey:ownerOf(n).wsKey}:{},...u},{...e.flowId?{flowId:e.flowId}:{},...e.requestId?{requestId:e.requestId}:{},...e.parentRequestId?{parentRequestId:e.parentRequestId}:{},...e.originNodeId?{originNodeId:e.originNodeId}:{}},busFileFor(n,s))},"finishTrace"),g=new Map;return{emit:y,eid:l,beginTrace:I,startHeadlessFlow:E,traceStep:S,finishTrace:q,recordEdge:d((e,t,r,u)=>{if(e.project===exports.LENS_DASHBOARD_PROJECT&&r<500&&r!==0)return;const n=e.host??"(default)",a=(u?u+"\u21D2":"")+n+"\u2192"+e.project,o=g.get(a)??{host:n,project:e.project,caller:u,count:0,totalMs:0,errors:0,durations:[],...busFileFor(e,s)?{busFile:busFileFor(e,s)}:{},...ownerOf(e).wsKey?{wsKey:ownerOf(e).wsKey}:{}};if(o.count+=1,o.totalMs+=t,o.durations.length<exports.EDGE_DURATION_SAMPLES)o.durations.push(Math.round(t));else{const i=Math.floor(Math.random()*o.count);i<exports.EDGE_DURATION_SAMPLES&&(o.durations[i]=Math.round(t))}(r>=500||r===0)&&(o.errors+=1),g.set(a,o)},"recordEdge"),startEdgeFlusher:d(()=>{const e=setInterval(()=>{for(const t of g.values()){const r=Math.round(t.totalMs/t.count);y(t.errors?"warning":"info",`traffic ${t.host} \u2192 ${t.project}: ${t.count} req, ~${r}ms${t.errors?`, ${t.errors} errors`:""}`,`gateway-traffic:${t.host}->${t.project}`,{kind:"gateway-traffic",host:t.host,project:t.project,caller:t.caller,count:t.count,avgMs:r,errors:t.errors,durationsMs:t.durations,...t.wsKey?{wsKey:t.wsKey}:{}},void 0,t.busFile)}g.clear()},c.trafficFlushMs);return e.unref(),e},"startEdgeFlusher")}}w(createObservability,"createObservability"),d(createObservability,"createObservability");
@@ -1 +1 @@
1
- "use strict";var K=Object.defineProperty;var x=(t,c)=>K(t,"name",{value:c,configurable:!0});var G=Object.defineProperty,s=x((t,c)=>G(t,"name",{value:c,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.isReplayableRequest=isReplayableRequest,exports.wedgedMessage=wedgedMessage,exports.createProxy=createProxy;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery"),edge_1=require("./edge"),gateway_errors_1=require("../gateway-errors"),body_stall_1=require("../body-stall"),types_1=require("./types"),pod_probe_1=require("./pod-probe"),service_keys_1=require("./service-keys");function isConnError(t){const c=t.code??"";return c==="ECONNREFUSED"||c==="ECONNRESET"||c==="ETIMEDOUT"||c==="EPIPE"||c==="EHOSTUNREACH"||c==="ENOENT"||/ECONNREFUSED|ECONNRESET|socket hang up|EPIPE/i.test(t.message)}x(isConnError,"isConnError"),s(isConnError,"isConnError");const IDEMPOTENT_METHODS=new Set(["GET","HEAD","OPTIONS","PUT","DELETE","TRACE"]);function isReplayableRequest(t){if(!IDEMPOTENT_METHODS.has((t.method??"GET").toUpperCase())||t.headers["transfer-encoding"]!==void 0)return!1;const c=t.headers["content-length"];return!(c!==void 0&&String(c).trim()!=="0")}x(isReplayableRequest,"isReplayableRequest"),s(isReplayableRequest,"isReplayableRequest");function edgeGone(t,c){return c.headersSent||c.writableEnded||c.destroyed===!0||t.aborted===!0}x(edgeGone,"edgeGone"),s(edgeGone,"edgeGone");const HOP_BY_HOP=new Set(["connection","keep-alive","proxy-connection","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),UPSTREAM_CONNECTION_HEADERS=["connection","keep-alive","proxy-connection"],httpProxy=require("http-proxy");function declaredFirstByteMs(t){const c=Number(t);return Number.isFinite(c)&&c>0?c:types_1.POD_RESPONSE_TIMEOUT_MS}x(declaredFirstByteMs,"declaredFirstByteMs"),s(declaredFirstByteMs,"declaredFirstByteMs");const secs=s(t=>`${(t/1e3).toFixed(1)}s`,"secs");function wedgedMessage(t,c,E){const U=E.failure==="gone"?"its process is gone":E.failure==="silent"?`it did not answer its liveness check for ${secs(E.silentMs)}`:"it gave no liveness answer";return`Pod ${t} of ${c} sent no response for ${secs(E.heldMs)} and ${U} \u2014 taken out of rotation, and the service is being restarted, so work it was still doing for this request may have been cut short. Retry if the call is safe to repeat.`}x(wedgedMessage,"wedgedMessage"),s(wedgedMessage,"wedgedMessage");function armFirstByteGuard(t){let c,E=!1,U=!1,q,M=0,P=(0,body_stall_1.requestBodyArriving)(t.req)?void 0:Date.now();const b=s(()=>{P??=Date.now()},"onBodyEnd");P===void 0&&t.req.once("end",b);const N=s(()=>(P===void 0&&!(0,body_stall_1.requestBodyArriving)(t.req)&&(P=Date.now()),P===void 0?0:Date.now()-P),"heldMs"),k=s(j=>{c=setTimeout(C,j),c.unref?.()},"arm"),C=s(()=>{if(E||!t.pending())return;const j=N();if(t.ceilingMs<=0){if(P!==void 0&&j>=t.firstByteMs){t.onWedged(`no response within ${j}ms (wedged socket) \u2014 evicted + respawning`,{heldMs:j,silentMs:0,probes:0,failure:"deadline"});return}k(P===void 0?t.firstByteMs:t.firstByteMs-j);return}(0,pod_probe_1.probePodAliveVerdict)(t.socketPath,void 0,t.serviceKey).then(({alive:D,failure:I})=>{if(E||!t.pending())return;const R=N();if(!D){if(M+=1,I==="silent"&&types_1.POD_WEDGE_CONFIRM_MS>0&&(q??=Date.now(),Date.now()-q<types_1.POD_WEDGE_CONFIRM_MS)){k(types_1.POD_ALIVE_PROBE_TIMEOUT_MS);return}const O=q===void 0?0:Date.now()-q,L=I==="gone"?"its socket no longer accepts (the pod process is gone)":I==="silent"?`nothing answered on ${types_1.POD_ALIVE_PATH} for ${O}ms (${M} probe${M===1?"":"s"})`:`no liveness answer on ${types_1.POD_ALIVE_PATH}`;t.onWedged(`no response within ${R}ms and ${L} (wedged socket) \u2014 evicted + respawning`,{heldMs:R,silentMs:O,probes:M,failure:I??"no-route"});return}if(q=void 0,R>=t.ceilingMs){t.onCeiling(`alive on ${types_1.POD_ALIVE_PATH} but no response within ${R}ms (ceiling ${t.ceilingMs}ms) \u2014 giving up on this request, pod kept`,D);return}!U&&P!==void 0&&(U=!0,t.onSlow(R,D)),k(Math.max(t.firstByteMs,types_1.POD_ALIVE_PROBE_TIMEOUT_MS))})},"onDeadline");return k(t.firstByteMs),{clear:s(()=>{E=!0,c&&clearTimeout(c),t.req.removeListener("end",b)},"clear")}}x(armFirstByteGuard,"armFirstByteGuard"),s(armFirstByteGuard,"armFirstByteGuard");function createProxy(t,c,E){const{recordEdge:U,finishTrace:q,traceStep:M,emit:P}=c,b=s((e,r,o,p)=>{const h=p.kind==="pod-evicted";P(h?"error":"warning",`${h?"pod evicted":"request given up"}: ${e.project} (${o}) \u2014 ${p.status}`,`cluster-pod-incident:${e.project}`,{kind:p.kind,project:e.project,pod:o,status:p.status,reason:p.reason,traceId:(0,gateway_errors_1.ensureRequestId)(r),method:r.method,path:(r.url??"").split("?")[0],...p.detail??{}})},"reportPodIncident"),N=s(async e=>{if(!e.lens||!t.lensSupervisor)return"unsupported";let r;const o=new Promise(g=>{r=setTimeout(()=>g("timeout"),types_1.LENS_REQUEST_RECOVERY_WAIT_MS),r.unref?.()}),p=t.lensSupervisor.recover(e.lens,"proxy-connection").then(g=>g?"ready":"failed").catch(()=>"failed"),h=await Promise.race([p,o]);r&&clearTimeout(r);const w=t.lensSupervisor.status(e.lens);return h==="failed"&&(!w||w.state==="ready")?"unsupported":h},"waitForManagedRecovery"),k=s((e,r,o,p,h,w)=>{if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}const g=e.lens?t.lensSupervisor?.status(e.lens):void 0,v=w==="timeout"||g?.state==="starting"||g?.state==="replacing",S=(0,gateway_errors_1.ensureRequestId)(r),l=e.lens?{project:e.project,reason:g?.reason??"connection-failure",generation:g?.generation??0,...v?{retryAfterMs:2e3}:{}}:void 0,n=v?503:502,i=v?"starting":"unavailable";console.error(`[gateway] ${new Date().toISOString()} upstream-error traceId=${S} project=${e.project} target=${String(p)} state=${g?.state??"unmanaged"} generation=${g?.generation??0}: ${h.message}`),P("warning",`upstream unavailable: ${e.project}`,`cluster-upstream:${e.project}`,{kind:"upstream-error",traceId:S,project:e.project,target:String(p),state:g?.state??"unmanaged",generation:g?.generation,error:h.message}),(0,edge_1.sendError)(o,r,n,i,v?`Upstream ${e.project} is restarting.`:`Upstream ${e.project} unavailable.`,l,v?{"retry-after":"2"}:void 0)},"sendTerminalUpstreamError"),C=(0,body_stall_1.createBodyStallGuard)(t.bodyStallMs??types_1.BODY_STALL_MS),j={keepAlive:!0,keepAliveMsecs:3e4,maxSockets:1024,maxFreeSockets:256},D=new http.Agent(j),I=new(require("node:https")).Agent(j),R=s(e=>typeof e=="string"&&e.startsWith("https:")?I:D,"agentForTarget"),O=httpProxy.createProxyServer({xfwd:!1,secure:!1,proxyTimeout:12e4,agent:D}),L=s((e,r,o)=>{const p=s(()=>{o()||e.destroyed||e.destroy()},"onEdgeClose");r.once("close",p),e.once("close",()=>r.removeListener("close",p))},"propagateEdgeCancel");O.on("proxyReq",(e,r,o)=>{for(const p of UPSTREAM_CONNECTION_HEADERS)e.removeHeader(p);e.setHeader("connection","keep-alive"),L(e,o,()=>o.writableEnded)}),O.on("proxyRes",(e,r,o)=>{const p=r.__lensmcpPod;p&&(e.headers["x-lensmcp-pod"]=p),delete e.headers["x-powered-by"],e.headers.server="LensMCP",o&&L(e,o,()=>o.writableEnded),o&&C.armResponse(o)});const W=s((e,r,o,p)=>{(0,edge_1.stampForwardingHeaders)(t,r),C.arm(r);const h={},w=r.method==="GET"||r.method==="HEAD"||r.method==="OPTIONS";for(const[a,f]of Object.entries(r.headers))f===void 0||a.startsWith(":")||HOP_BY_HOP.has(a)||w&&(a==="content-length"||a==="transfer-encoding")||(h[a]=f);let g=http,v;if(typeof e=="object")v={socketPath:e.socketPath,path:r.url,method:r.method,headers:h,agent:D};else{const a=new URL(e),f=a.protocol==="https:";g=f?require("node:https"):http,h.host=a.host,v={protocol:a.protocol,hostname:a.hostname,port:a.port||(f?443:80),path:r.url,method:r.method,headers:h,agent:R(e)}}const S=o;S.__lensmcpErrContained||(S.__lensmcpErrContained=!0,o.on("error",()=>{}));let l;const n=s(()=>{o.writableEnded||y.destroy()},"onReqGone"),i=s(()=>{l&&!l.destroyed&&l.destroy()},"onEdgeClose"),d=s(()=>{r.removeListener("error",n),r.removeListener("aborted",n),r.removeListener("close",n),o.removeListener("close",i)},"detach"),y=g.request(v,a=>{if(l=a,p.onResponse(),o.headersSent||o.writableEnded||o.destroyed){a.destroy();return}const f={};for(const[T,u]of Object.entries(a.headers))u===void 0||HOP_BY_HOP.has(T)||(f[T]=u);delete f["x-powered-by"],f.server="LensMCP";const B=r.__lensmcpPod;B&&(f["x-lensmcp-pod"]=B);try{o.writeHead(a.statusCode??502,f)}catch(T){console.error("[gateway] h2 relay: bad response header, resetting stream:",T.message),a.destroy(),o.destroyed||o.destroy(T);return}a.pipe(o),C.armResponse(o);const A=s(()=>{!o.writableEnded&&!o.destroyed&&o.destroy()},"abortEdge");a.once("error",A),a.once("close",A),o.on("close",i)});y.once("error",a=>p.onError(a)),y.once("close",d),r.on("error",n),r.on("aborted",n),r.on("close",n),w?y.end():r.pipe(y)},"pipeUpstream"),F=s(async(e,r,o)=>{const p=r.__lensmcpTrace;if(e.pool){const l=e.svc;l&&(l.lastUsed=Date.now(),l.inflight+=1,o.on("close",()=>{l.inflight-=1,l.lastUsed=Date.now()}));let n=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs);if(!n&&l&&(M(p,"cold-start",{project:e.project}),await E.ensureUp(l)&&(n=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs))),l&&n&&E.markUp(l,"observed"),l&&E.scaleUp(l),!n){const u=l?.lastErrors?.length?` Service errors: ${l.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,r,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${u} The watcher recovers and the next request retries automatically.`);return}const i=path.basename(n,".sock");r.__lensmcpPod=i,M(p,"pod-select",{pod:i,pods:e.pool.socks.length,strategy:"round-robin"});let d=!1;const y=s((u,$="unlink")=>{if(e.pool.socks=e.pool.socks.filter(m=>m!==n),e.pool.idx=-1,$==="unlink")try{fs.unlinkSync(n)}catch{}console.error(`[gateway] pod ${i} of ${e.project}: ${u}`)},"evictPod"),a=armFirstByteGuard({socketPath:n,serviceKey:t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],req:r,firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:s(()=>!d&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:s((u,$)=>{d=!0,y(u),b(e,r,i,{kind:"pod-evicted",status:502,reason:u,detail:$}),(0,edge_1.sendError)(o,r,502,"unavailable",wedgedMessage(i,e.project,$))},"onWedged"),onCeiling:s(u=>{d=!0,y(u,"keep"),b(e,r,i,{kind:"request-gave-up",status:504,reason:u}),(0,edge_1.sendError)(o,r,504,"unavailable",`Pod ${i} of ${e.project} did not answer in time \u2014 retry, or raise cluster.firstByteMs. The pod was kept: the request may still complete there.`)},"onCeiling"),onSlow:s((u,$)=>{console.log(`[gateway] pod ${i} of ${e.project}: slow first byte, pod alive (${u}ms, ${$.inflight} in-flight, ${$.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>a.clear());const f=n,B=isReplayableRequest(r);let A=0;const T=s(()=>{A+=1,W({socketPath:f},r,o,{onResponse:s(()=>{a.clear(),d=!0},"onResponse"),onError:s(u=>{if(!d){if(edgeGone(r,o)){a.clear(),d=!0;return}(0,pod_probe_1.podConnectionVerdict)(f,u).then($=>{if(!d){if($==="alive"&&A<2&&B&&!edgeGone(r,o)){console.log(`[gateway] pod ${i} of ${e.project}: ${u.message} on one connection, the pod still accepts \u2014 sending the ${r.method} again on a fresh one`),T();return}if(a.clear(),d=!0,$==="gone"){y(u.message),b(e,r,i,{kind:"pod-evicted",status:502,reason:`${u.message} \u2014 its socket no longer accepts (the pod process is gone)`}),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${i} of ${e.project} is gone (${u.message}) \u2014 retry.`);return}console.error(`[gateway] pod ${i} of ${e.project}: ${u.message} on one connection \u2014 the pod still accepts connections, so it stays in the pool`),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${i} of ${e.project} dropped the connection \u2014 retry.`)}})}},"onError")})},"sendToPod");T();return}const h=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let w=0,g=!1;const v=isReplayableRequest(r),S=s(()=>{if(edgeGone(r,o))return;const l=e.target;W(l,r,o,{onResponse:s(()=>{},"onResponse"),onError:s(n=>{if(isConnError(n)&&(w+=1),e.lens&&isConnError(n)&&v&&w>=2&&!g&&!edgeGone(r,o)){g=!0,N(e).then(i=>{if(!edgeGone(r,o)){if(i==="ready"){S();return}if(i==="unsupported"&&Date.now()<h){setTimeout(S,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}k(e,r,o,e.target,n,i)}});return}if(isConnError(n)&&v&&Date.now()<h&&!edgeGone(r,o)){setTimeout(S,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}k(e,r,o,l,n)},"onError")})},"tryTcp");S()},"forwardNative");return{proxy:O,forward:s(async(e,r,o)=>{const p=Date.now(),h=r.__lensmcpTrace;if(o.on("close",()=>{U(e,Date.now()-p,o.statusCode??0,r.__lensmcpCaller),q(h,e.project,o.statusCode??0,{...r.__lensmcpPod?{pod:r.__lensmcpPod}:{},...r.__lensmcpCaller?{caller:r.__lensmcpCaller}:{}},e)}),e.prependPrefix&&!r.url?.startsWith(e.prependPrefix)&&(r.url=e.prependPrefix+(r.url??"/")),(0,edge_1.stampForwardingHeaders)(t,r),C.arm(r),(r.httpVersionMajor??1)>=2){await F(e,r,o);return}if(e.pool){const n=e.svc;n&&(n.lastUsed=Date.now(),n.inflight+=1,o.on("close",()=>{n.inflight-=1,n.lastUsed=Date.now()}));let i=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs);if(!i&&n&&(M(h,"cold-start",{project:e.project}),await E.ensureUp(n)&&(i=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs))),n&&i&&E.markUp(n,"observed"),n&&E.scaleUp(n),!i){const m=n?.lastErrors?.length?` Service errors: ${n.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,r,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${m} The watcher recovers and the next request retries automatically.`);return}const d=path.basename(i,".sock");r.__lensmcpPod=d,M(h,"pod-select",{pod:d,pods:e.pool.socks.length,strategy:"round-robin"});let y=!1;const a=s((m,_="unlink")=>{if(e.pool.socks=e.pool.socks.filter(H=>H!==i),e.pool.idx=-1,_==="unlink")try{fs.unlinkSync(i)}catch{}console.error(`[gateway] pod ${d} of ${e.project}: ${m}`)},"evictPod"),f=armFirstByteGuard({socketPath:i,serviceKey:t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],req:r,firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:s(()=>!y&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:s((m,_)=>{y=!0,a(m),b(e,r,d,{kind:"pod-evicted",status:502,reason:m,detail:_}),(0,edge_1.sendError)(o,r,502,"unavailable",wedgedMessage(d,e.project,_))},"onWedged"),onCeiling:s(m=>{y=!0,a(m,"keep"),b(e,r,d,{kind:"request-gave-up",status:504,reason:m}),(0,edge_1.sendError)(o,r,504,"unavailable",`Pod ${d} of ${e.project} did not answer in time \u2014 retry, or raise cluster.firstByteMs. The pod was kept: the request may still complete there.`)},"onCeiling"),onSlow:s((m,_)=>{console.log(`[gateway] pod ${d} of ${e.project}: slow first byte, pod alive (${m}ms, ${_.inflight} in-flight, ${_.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>f.clear());const B=Math.max(12e4,types_1.POD_SLOW_FIRST_BYTE_MS+6e4),A=i,T=isReplayableRequest(r);let u=0;const $=s(()=>{u+=1;const m=Date.now();O.web(r,o,{target:{socketPath:A},agent:D,proxyTimeout:B},_=>{if(!y){if(edgeGone(r,o)){f.clear(),y=!0;return}(0,pod_probe_1.podConnectionVerdict)(A,_).then(H=>{if(y)return;const V=Date.now()-m>=B-250;if(H==="alive"&&u<2&&T&&!V&&!edgeGone(r,o)){console.log(`[gateway] pod ${d} of ${e.project}: ${_.message} on one connection, the pod still accepts \u2014 sending the ${r.method} again on a fresh one`),$();return}if(f.clear(),y=!0,H==="gone"){a(_.message),b(e,r,d,{kind:"pod-evicted",status:502,reason:`${_.message} \u2014 its socket no longer accepts (the pod process is gone)`}),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${d} of ${e.project} is gone (${_.message}) \u2014 retry.`);return}console.error(`[gateway] pod ${d} of ${e.project}: ${_.message} on one connection \u2014 the pod still accepts connections, so it stays in the pool`),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${d} of ${e.project} dropped the connection \u2014 retry.`)})}})},"sendToPod");$();return}const w=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let g=0,v=!1;const S=isReplayableRequest(r),l=s(()=>{if(edgeGone(r,o))return;const n=e.target,i={target:n,autoRewrite:typeof n=="string",changeOrigin:!0,agent:R(n)};O.web(r,o,i,d=>{if(isConnError(d)&&(g+=1),e.lens&&isConnError(d)&&S&&g>=2&&!v&&!edgeGone(r,o)){v=!0,N(e).then(y=>{if(!edgeGone(r,o)){if(y==="ready"){l();return}if(y==="unsupported"&&Date.now()<w){setTimeout(l,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}k(e,r,o,e.target,d,y)}});return}if(isConnError(d)&&S&&Date.now()<w&&!edgeGone(r,o)){setTimeout(l,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}o.destroyed||k(e,r,o,n,d)})},"tryTcp");l()},"forward"),agentForTarget:R,destroyAgents:s(()=>{C.stop(),D.destroy(),I.destroy()},"destroyAgents"),closeProxy:s(()=>{try{O.close?.()}catch{}},"closeProxy")}}x(createProxy,"createProxy"),s(createProxy,"createProxy");
1
+ "use strict";var K=Object.defineProperty;var b=(t,a)=>K(t,"name",{value:a,configurable:!0});var G=Object.defineProperty,s=b((t,a)=>G(t,"name",{value:a,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.isReplayableRequest=isReplayableRequest,exports.wedgedMessage=wedgedMessage,exports.createProxy=createProxy;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery"),edge_1=require("./edge"),gateway_errors_1=require("../gateway-errors"),body_stall_1=require("../body-stall"),types_1=require("./types"),pod_probe_1=require("./pod-probe"),service_keys_1=require("./service-keys");function isConnError(t){const a=t.code??"";return a==="ECONNREFUSED"||a==="ECONNRESET"||a==="ETIMEDOUT"||a==="EPIPE"||a==="EHOSTUNREACH"||a==="ENOENT"||/ECONNREFUSED|ECONNRESET|socket hang up|EPIPE/i.test(t.message)}b(isConnError,"isConnError"),s(isConnError,"isConnError");const IDEMPOTENT_METHODS=new Set(["GET","HEAD","OPTIONS","PUT","DELETE","TRACE"]);function isReplayableRequest(t){if(!IDEMPOTENT_METHODS.has((t.method??"GET").toUpperCase())||t.headers["transfer-encoding"]!==void 0)return!1;const a=t.headers["content-length"];return!(a!==void 0&&String(a).trim()!=="0")}b(isReplayableRequest,"isReplayableRequest"),s(isReplayableRequest,"isReplayableRequest");function edgeGone(t,a){return a.headersSent||a.writableEnded||a.destroyed===!0||t.aborted===!0}b(edgeGone,"edgeGone"),s(edgeGone,"edgeGone");const HOP_BY_HOP=new Set(["connection","keep-alive","proxy-connection","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding","upgrade"]),UPSTREAM_CONNECTION_HEADERS=["connection","keep-alive","proxy-connection"];function returnRequestId(t,a){if(t["x-request-id"]!==void 0)return;const y=a.headers["x-request-id"],T=Array.isArray(y)?y[0]:y;typeof T=="string"&&T!==""&&(t["x-request-id"]=T)}b(returnRequestId,"returnRequestId"),s(returnRequestId,"returnRequestId");const httpProxy=require("http-proxy");function declaredFirstByteMs(t){const a=Number(t);return Number.isFinite(a)&&a>0?a:types_1.POD_RESPONSE_TIMEOUT_MS}b(declaredFirstByteMs,"declaredFirstByteMs"),s(declaredFirstByteMs,"declaredFirstByteMs");const secs=s(t=>`${(t/1e3).toFixed(1)}s`,"secs");function wedgedMessage(t,a,y){const T=y.failure==="gone"?"its process is gone":y.failure==="silent"?`it did not answer its liveness check for ${secs(y.silentMs)}`:"it gave no liveness answer";return`Pod ${t} of ${a} sent no response for ${secs(y.heldMs)} and ${T} \u2014 taken out of rotation, and the service is being restarted, so work it was still doing for this request may have been cut short. Retry if the call is safe to repeat.`}b(wedgedMessage,"wedgedMessage"),s(wedgedMessage,"wedgedMessage");function armFirstByteGuard(t){let a,y=!1,T=!1,I,k=0,P=(0,body_stall_1.requestBodyArriving)(t.req)?void 0:Date.now();const R=s(()=>{P??=Date.now()},"onBodyEnd");P===void 0&&t.req.once("end",R);const N=s(()=>(P===void 0&&!(0,body_stall_1.requestBodyArriving)(t.req)&&(P=Date.now()),P===void 0?0:Date.now()-P),"heldMs"),O=s(D=>{a=setTimeout(U,D),a.unref?.()},"arm"),U=s(()=>{if(y||!t.pending())return;const D=N();if(t.ceilingMs<=0){if(P!==void 0&&D>=t.firstByteMs){t.onWedged(`no response within ${D}ms (wedged socket) \u2014 evicted + respawning`,{heldMs:D,silentMs:0,probes:0,failure:"deadline"});return}O(P===void 0?t.firstByteMs:t.firstByteMs-D);return}(0,pod_probe_1.probePodAliveVerdict)(t.socketPath,void 0,t.serviceKey).then(({alive:j,failure:C})=>{if(y||!t.pending())return;const q=N();if(!j){if(k+=1,C==="silent"&&types_1.POD_WEDGE_CONFIRM_MS>0&&(I??=Date.now(),Date.now()-I<types_1.POD_WEDGE_CONFIRM_MS)){O(types_1.POD_ALIVE_PROBE_TIMEOUT_MS);return}const x=I===void 0?0:Date.now()-I,L=C==="gone"?"its socket no longer accepts (the pod process is gone)":C==="silent"?`nothing answered on ${types_1.POD_ALIVE_PATH} for ${x}ms (${k} probe${k===1?"":"s"})`:`no liveness answer on ${types_1.POD_ALIVE_PATH}`;t.onWedged(`no response within ${q}ms and ${L} (wedged socket) \u2014 evicted + respawning`,{heldMs:q,silentMs:x,probes:k,failure:C??"no-route"});return}if(I=void 0,q>=t.ceilingMs){t.onCeiling(`alive on ${types_1.POD_ALIVE_PATH} but no response within ${q}ms (ceiling ${t.ceilingMs}ms) \u2014 giving up on this request, pod kept`,j);return}!T&&P!==void 0&&(T=!0,t.onSlow(q,j)),O(Math.max(t.firstByteMs,types_1.POD_ALIVE_PROBE_TIMEOUT_MS))})},"onDeadline");return O(t.firstByteMs),{clear:s(()=>{y=!0,a&&clearTimeout(a),t.req.removeListener("end",R)},"clear")}}b(armFirstByteGuard,"armFirstByteGuard"),s(armFirstByteGuard,"armFirstByteGuard");function createProxy(t,a,y){const{recordEdge:T,finishTrace:I,traceStep:k,emit:P}=a,R=s((e,r,o,p)=>{const f=p.kind==="pod-evicted";P(f?"error":"warning",`${f?"pod evicted":"request given up"}: ${e.project} (${o}) \u2014 ${p.status}`,`cluster-pod-incident:${e.project}`,{kind:p.kind,project:e.project,pod:o,status:p.status,reason:p.reason,traceId:(0,gateway_errors_1.ensureRequestId)(r),method:r.method,path:(r.url??"").split("?")[0],...p.detail??{}})},"reportPodIncident"),N=s(async e=>{if(!e.lens||!t.lensSupervisor)return"unsupported";let r;const o=new Promise(g=>{r=setTimeout(()=>g("timeout"),types_1.LENS_REQUEST_RECOVERY_WAIT_MS),r.unref?.()}),p=t.lensSupervisor.recover(e.lens,"proxy-connection").then(g=>g?"ready":"failed").catch(()=>"failed"),f=await Promise.race([p,o]);r&&clearTimeout(r);const w=t.lensSupervisor.status(e.lens);return f==="failed"&&(!w||w.state==="ready")?"unsupported":f},"waitForManagedRecovery"),O=s((e,r,o,p,f,w)=>{if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}const g=e.lens?t.lensSupervisor?.status(e.lens):void 0,m=w==="timeout"||g?.state==="starting"||g?.state==="replacing",S=(0,gateway_errors_1.ensureRequestId)(r),l=e.lens?{project:e.project,reason:g?.reason??"connection-failure",generation:g?.generation??0,...m?{retryAfterMs:2e3}:{}}:void 0,n=m?503:502,i=m?"starting":"unavailable";console.error(`[gateway] ${new Date().toISOString()} upstream-error traceId=${S} project=${e.project} target=${String(p)} state=${g?.state??"unmanaged"} generation=${g?.generation??0}: ${f.message}`),P("warning",`upstream unavailable: ${e.project}`,`cluster-upstream:${e.project}`,{kind:"upstream-error",traceId:S,project:e.project,target:String(p),state:g?.state??"unmanaged",generation:g?.generation,error:f.message}),(0,edge_1.sendError)(o,r,n,i,m?`Upstream ${e.project} is restarting.`:`Upstream ${e.project} unavailable.`,l,m?{"retry-after":"2"}:void 0)},"sendTerminalUpstreamError"),U=(0,body_stall_1.createBodyStallGuard)(t.bodyStallMs??types_1.BODY_STALL_MS),D={keepAlive:!0,keepAliveMsecs:3e4,maxSockets:1024,maxFreeSockets:256},j=new http.Agent(D),C=new(require("node:https")).Agent(D),q=s(e=>typeof e=="string"&&e.startsWith("https:")?C:j,"agentForTarget"),x=httpProxy.createProxyServer({xfwd:!1,secure:!1,proxyTimeout:12e4,agent:j}),L=s((e,r,o)=>{const p=s(()=>{o()||e.destroyed||e.destroy()},"onEdgeClose");r.once("close",p),e.once("close",()=>r.removeListener("close",p))},"propagateEdgeCancel");x.on("proxyReq",(e,r,o)=>{for(const p of UPSTREAM_CONNECTION_HEADERS)e.removeHeader(p);e.setHeader("connection","keep-alive"),L(e,o,()=>o.writableEnded)}),x.on("proxyRes",(e,r,o)=>{const p=r.__lensmcpPod;p&&(e.headers["x-lensmcp-pod"]=p),delete e.headers["x-powered-by"],e.headers.server="LensMCP",returnRequestId(e.headers,r),o&&L(e,o,()=>o.writableEnded),o&&U.armResponse(o)});const W=s((e,r,o,p)=>{(0,edge_1.stampForwardingHeaders)(t,r),U.arm(r);const f={},w=r.method==="GET"||r.method==="HEAD"||r.method==="OPTIONS";for(const[c,E]of Object.entries(r.headers))E===void 0||c.startsWith(":")||HOP_BY_HOP.has(c)||w&&(c==="content-length"||c==="transfer-encoding")||(f[c]=E);let g=http,m;if(typeof e=="object")m={socketPath:e.socketPath,path:r.url,method:r.method,headers:f,agent:j};else{const c=new URL(e),E=c.protocol==="https:";g=E?require("node:https"):http,f.host=c.host,m={protocol:c.protocol,hostname:c.hostname,port:c.port||(E?443:80),path:r.url,method:r.method,headers:f,agent:q(e)}}const S=o;S.__lensmcpErrContained||(S.__lensmcpErrContained=!0,o.on("error",()=>{}));let l;const n=s(()=>{o.writableEnded||h.destroy()},"onReqGone"),i=s(()=>{l&&!l.destroyed&&l.destroy()},"onEdgeClose"),d=s(()=>{r.removeListener("error",n),r.removeListener("aborted",n),r.removeListener("close",n),o.removeListener("close",i)},"detach"),h=g.request(m,c=>{if(l=c,p.onResponse(),o.headersSent||o.writableEnded||o.destroyed){c.destroy();return}const E={};for(const[M,u]of Object.entries(c.headers))u===void 0||HOP_BY_HOP.has(M)||(E[M]=u);delete E["x-powered-by"],E.server="LensMCP",returnRequestId(E,r);const B=r.__lensmcpPod;B&&(E["x-lensmcp-pod"]=B);try{o.writeHead(c.statusCode??502,E)}catch(M){console.error("[gateway] h2 relay: bad response header, resetting stream:",M.message),c.destroy(),o.destroyed||o.destroy(M);return}c.pipe(o),U.armResponse(o);const A=s(()=>{!o.writableEnded&&!o.destroyed&&o.destroy()},"abortEdge");c.once("error",A),c.once("close",A),o.on("close",i)});h.once("error",c=>p.onError(c)),h.once("close",d),r.on("error",n),r.on("aborted",n),r.on("close",n),w?h.end():r.pipe(h)},"pipeUpstream"),F=s(async(e,r,o)=>{const p=r.__lensmcpTrace;if(e.pool){const l=e.svc;l&&(l.lastUsed=Date.now(),l.inflight+=1,o.on("close",()=>{l.inflight-=1,l.lastUsed=Date.now()}));let n=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs);if(!n&&l&&(k(p,"cold-start",{project:e.project}),await y.ensureUp(l)&&(n=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs))),l&&n&&y.markUp(l,"observed"),l&&y.scaleUp(l),!n){const u=l?.lastErrors?.length?` Service errors: ${l.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,r,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${u} The watcher recovers and the next request retries automatically.`);return}const i=path.basename(n,".sock");r.__lensmcpPod=i,k(p,"pod-select",{pod:i,pods:e.pool.socks.length,strategy:"round-robin"});let d=!1;const h=s((u,$="unlink")=>{if(e.pool.socks=e.pool.socks.filter(v=>v!==n),e.pool.idx=-1,$==="unlink")try{fs.unlinkSync(n)}catch{}console.error(`[gateway] pod ${i} of ${e.project}: ${u}`)},"evictPod"),c=armFirstByteGuard({socketPath:n,serviceKey:t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],req:r,firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:s(()=>!d&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:s((u,$)=>{d=!0,h(u),R(e,r,i,{kind:"pod-evicted",status:502,reason:u,detail:$}),(0,edge_1.sendError)(o,r,502,"unavailable",wedgedMessage(i,e.project,$))},"onWedged"),onCeiling:s(u=>{d=!0,h(u,"keep"),R(e,r,i,{kind:"request-gave-up",status:504,reason:u}),(0,edge_1.sendError)(o,r,504,"unavailable",`Pod ${i} of ${e.project} did not answer in time \u2014 retry, or raise cluster.firstByteMs. The pod was kept: the request may still complete there.`)},"onCeiling"),onSlow:s((u,$)=>{console.log(`[gateway] pod ${i} of ${e.project}: slow first byte, pod alive (${u}ms, ${$.inflight} in-flight, ${$.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>c.clear());const E=n,B=isReplayableRequest(r);let A=0;const M=s(()=>{A+=1,W({socketPath:E},r,o,{onResponse:s(()=>{c.clear(),d=!0},"onResponse"),onError:s(u=>{if(!d){if(edgeGone(r,o)){c.clear(),d=!0;return}(0,pod_probe_1.podConnectionVerdict)(E,u).then($=>{if(!d){if($==="alive"&&A<2&&B&&!edgeGone(r,o)){console.log(`[gateway] pod ${i} of ${e.project}: ${u.message} on one connection, the pod still accepts \u2014 sending the ${r.method} again on a fresh one`),M();return}if(c.clear(),d=!0,$==="gone"){h(u.message),R(e,r,i,{kind:"pod-evicted",status:502,reason:`${u.message} \u2014 its socket no longer accepts (the pod process is gone)`}),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${i} of ${e.project} is gone (${u.message}) \u2014 retry.`);return}console.error(`[gateway] pod ${i} of ${e.project}: ${u.message} on one connection \u2014 the pod still accepts connections, so it stays in the pool`),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${i} of ${e.project} dropped the connection \u2014 retry.`)}})}},"onError")})},"sendToPod");M();return}const f=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let w=0,g=!1;const m=isReplayableRequest(r),S=s(()=>{if(edgeGone(r,o))return;const l=e.target;W(l,r,o,{onResponse:s(()=>{},"onResponse"),onError:s(n=>{if(isConnError(n)&&(w+=1),e.lens&&isConnError(n)&&m&&w>=2&&!g&&!edgeGone(r,o)){g=!0,N(e).then(i=>{if(!edgeGone(r,o)){if(i==="ready"){S();return}if(i==="unsupported"&&Date.now()<f){setTimeout(S,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}O(e,r,o,e.target,n,i)}});return}if(isConnError(n)&&m&&Date.now()<f&&!edgeGone(r,o)){setTimeout(S,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}O(e,r,o,l,n)},"onError")})},"tryTcp");S()},"forwardNative");return{proxy:x,forward:s(async(e,r,o)=>{const p=Date.now(),f=r.__lensmcpTrace;if(o.on("close",()=>{T(e,Date.now()-p,o.statusCode??0,r.__lensmcpCaller),I(f,e.project,o.statusCode??0,{...r.__lensmcpPod?{pod:r.__lensmcpPod}:{},...r.__lensmcpCaller?{caller:r.__lensmcpCaller}:{}},e)}),e.prependPrefix&&!r.url?.startsWith(e.prependPrefix)&&(r.url=e.prependPrefix+(r.url??"/")),(0,edge_1.stampForwardingHeaders)(t,r),U.arm(r),(r.httpVersionMajor??1)>=2){await F(e,r,o);return}if(e.pool){const n=e.svc;n&&(n.lastUsed=Date.now(),n.inflight+=1,o.on("close",()=>{n.inflight-=1,n.lastUsed=Date.now()}));let i=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs);if(!i&&n&&(k(f,"cold-start",{project:e.project}),await y.ensureUp(n)&&(i=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs))),n&&i&&y.markUp(n,"observed"),n&&y.scaleUp(n),!i){const v=n?.lastErrors?.length?` Service errors: ${n.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,r,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${v} The watcher recovers and the next request retries automatically.`);return}const d=path.basename(i,".sock");r.__lensmcpPod=d,k(f,"pod-select",{pod:d,pods:e.pool.socks.length,strategy:"round-robin"});let h=!1;const c=s((v,_="unlink")=>{if(e.pool.socks=e.pool.socks.filter(H=>H!==i),e.pool.idx=-1,_==="unlink")try{fs.unlinkSync(i)}catch{}console.error(`[gateway] pod ${d} of ${e.project}: ${v}`)},"evictPod"),E=armFirstByteGuard({socketPath:i,serviceKey:t.serviceKeys[(0,service_keys_1.serviceKeyId)(e.svc?.wsKey,e.project)],req:r,firstByteMs:declaredFirstByteMs(e.svc?.decl?.firstByteMs),ceilingMs:types_1.POD_SLOW_FIRST_BYTE_MS,pending:s(()=>!h&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:s((v,_)=>{h=!0,c(v),R(e,r,d,{kind:"pod-evicted",status:502,reason:v,detail:_}),(0,edge_1.sendError)(o,r,502,"unavailable",wedgedMessage(d,e.project,_))},"onWedged"),onCeiling:s(v=>{h=!0,c(v,"keep"),R(e,r,d,{kind:"request-gave-up",status:504,reason:v}),(0,edge_1.sendError)(o,r,504,"unavailable",`Pod ${d} of ${e.project} did not answer in time \u2014 retry, or raise cluster.firstByteMs. The pod was kept: the request may still complete there.`)},"onCeiling"),onSlow:s((v,_)=>{console.log(`[gateway] pod ${d} of ${e.project}: slow first byte, pod alive (${v}ms, ${_.inflight} in-flight, ${_.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>E.clear());const B=Math.max(12e4,types_1.POD_SLOW_FIRST_BYTE_MS+6e4),A=i,M=isReplayableRequest(r);let u=0;const $=s(()=>{u+=1;const v=Date.now();x.web(r,o,{target:{socketPath:A},agent:j,proxyTimeout:B},_=>{if(!h){if(edgeGone(r,o)){E.clear(),h=!0;return}(0,pod_probe_1.podConnectionVerdict)(A,_).then(H=>{if(h)return;const V=Date.now()-v>=B-250;if(H==="alive"&&u<2&&M&&!V&&!edgeGone(r,o)){console.log(`[gateway] pod ${d} of ${e.project}: ${_.message} on one connection, the pod still accepts \u2014 sending the ${r.method} again on a fresh one`),$();return}if(E.clear(),h=!0,H==="gone"){c(_.message),R(e,r,d,{kind:"pod-evicted",status:502,reason:`${_.message} \u2014 its socket no longer accepts (the pod process is gone)`}),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${d} of ${e.project} is gone (${_.message}) \u2014 retry.`);return}console.error(`[gateway] pod ${d} of ${e.project}: ${_.message} on one connection \u2014 the pod still accepts connections, so it stays in the pool`),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${d} of ${e.project} dropped the connection \u2014 retry.`)})}})},"sendToPod");$();return}const w=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let g=0,m=!1;const S=isReplayableRequest(r),l=s(()=>{if(edgeGone(r,o))return;const n=e.target,i={target:n,autoRewrite:typeof n=="string",changeOrigin:!0,agent:q(n)};x.web(r,o,i,d=>{if(isConnError(d)&&(g+=1),e.lens&&isConnError(d)&&S&&g>=2&&!m&&!edgeGone(r,o)){m=!0,N(e).then(h=>{if(!edgeGone(r,o)){if(h==="ready"){l();return}if(h==="unsupported"&&Date.now()<w){setTimeout(l,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}O(e,r,o,e.target,d,h)}});return}if(isConnError(d)&&S&&Date.now()<w&&!edgeGone(r,o)){setTimeout(l,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}o.destroyed||O(e,r,o,n,d)})},"tryTcp");l()},"forward"),agentForTarget:q,destroyAgents:s(()=>{U.stop(),j.destroy(),C.destroy()},"destroyAgents"),closeProxy:s(()=>{try{x.close?.()}catch{}},"closeProxy")}}b(createProxy,"createProxy"),s(createProxy,"createProxy");
@@ -1,3 +1,3 @@
1
- "use strict";var Se=Object.defineProperty;var T=(s,r)=>Se(s,"name",{value:r,configurable:!0});var _e=Object.defineProperty,n=T((s,r)=>_e(s,"name",{value:r,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.GATEWAY_H2_IDLE_SESSION_MS_DEFAULT=exports.GATEWAY_MAX_H2_STREAMS_DEFAULT=exports.GATEWAY_MAX_H2_SESSIONS_DEFAULT=exports.GATEWAY_MAX_CONNECTIONS_DEFAULT=exports.BENIGN_SOCKET_ERRNOS=void 0,exports.buildEdgeVerifierPool=buildEdgeVerifierPool,exports.classifyClientError=classifyClientError,exports.createClientErrorLogGate=createClientErrorLogGate,exports.retirePreviousWorkspace=retirePreviousWorkspace,exports.resolveMaxConnections=resolveMaxConnections,exports.h2BudgetFromEnv=h2BudgetFromEnv,exports.createH2Governor=createH2Governor,exports.startGateway=startGateway;const tslib_1=require("tslib"),http=tslib_1.__importStar(require("node:http")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery"),scope_1=require("./scope"),route_registry_1=require("./route-registry"),control_1=require("./control"),workspace_middleware_1=require("./workspace-middleware"),workspace_registry_1=require("./workspace-registry"),service_keys_1=require("./service-keys"),client_ip_1=require("../client-ip"),workspace_log_1=require("./workspace-log"),observability_1=require("./observability"),lifecycle_1=require("./lifecycle"),process_names_1=require("./process-names"),project_graph_store_1=require("./project-graph-store"),serve_launch_1=require("./serve-launch"),process_name_1=require("../../../process-name"),protocol_1=require("../../../shared-build/protocol"),lens_children_1=require("./lens-children"),proxy_1=require("./proxy"),auth_1=require("./auth"),hooks_1=require("./hooks"),handler_1=require("./handler"),upgrade_1=require("./upgrade"),jwks_verify_1=require("../jwks-verify"),types_1=require("./types");function insecureLocalFetch(s,r){return new Promise((d,h)=>{const a=(s.startsWith("https:")?require("node:https"):require("node:http")).request(s,{method:"GET",headers:r?.headers,rejectUnauthorized:!1},y=>{const _=[];y.on("data",S=>_.push(S)),y.on("end",()=>{const S=y.statusCode??0,p=Buffer.concat(_).toString("utf8");d({ok:S>=200&&S<300,status:S,json:n(async()=>JSON.parse(p),"json")})})});a.on("error",h),a.end()})}T(insecureLocalFetch,"insecureLocalFetch"),n(insecureLocalFetch,"insecureLocalFetch");function buildEdgeVerifierPool(s){const r=process.env.LENSMCP_GW_JWKS_URL,d=process.env.LENSMCP_GW_JWT_ISS,h=new Map;let a;const y=n((_,S)=>{console.log(`[gateway] edge JWT: verifying via JWKS ${_}${S}`);const p=(0,jwks_verify_1.createJwksVerifier)({jwksUrl:_,...d?{issuer:d}:{},fetchImpl:insecureLocalFetch});return p.refresh().catch(o=>console.warn("[gateway] edge JWKS warm failed (will retry):",o.message)),p},"make");return{for(_){if(r)return a??=y(r,"");const S=_.split(":")[0].toLowerCase();let p;for(const c of s()){const i=c.host;if(typeof i!="string"||!/^auth\./.test(i)||i.startsWith("internal."))continue;const A=i.slice(5);(S===A||S.endsWith(`.${A}`))&&(!p||A.length>p.length)&&(p=A)}if(!p)return;let o=h.get(p);return o||(o=y(`https://auth.${p}/jwks.json`,` (auto-derived for apex ${p})`),h.set(p,o)),o},stop(){a?.stop();for(const _ of h.values())_.stop()}}}T(buildEdgeVerifierPool,"buildEdgeVerifierPool"),n(buildEdgeVerifierPool,"buildEdgeVerifierPool"),exports.BENIGN_SOCKET_ERRNOS=new Set(["ECONNRESET","EPIPE","ETIMEDOUT","ECONNABORTED","ECANCELED"]);function classifyClientError(s,r){return s&&exports.BENIGN_SOCKET_ERRNOS.has(s)?{status:null,report:!1,cause:"client-abort"}:s==="HPE_INVALID_EOF_STATE"?{status:null,report:!1,cause:"truncated-at-eof"}:s==="ERR_HTTP_REQUEST_TIMEOUT"?{status:408,report:!0,cause:"request-timeout"}:s==="HPE_HEADER_OVERFLOW"?{status:431,report:!0,cause:"header-overflow"}:s?.startsWith("HPE_")?{status:400,report:!0,cause:"parse-error"}:{status:400,report:!0,cause:s??r??"unknown"}}T(classifyClientError,"classifyClientError"),n(classifyClientError,"classifyClientError");const CLIENT_ERROR_REASON={400:"Bad Request",408:"Request Timeout",431:"Request Header Fields Too Large"};function createClientErrorLogGate(s=1e4){const r=new Map;return d=>{const h=Date.now(),a=r.get(d)??0;return h-a<s?!1:(r.set(d,h),!0)}}T(createClientErrorLogGate,"createClientErrorLogGate"),n(createClientErrorLogGate,"createClientErrorLogGate");function guardInboundSocket(s){s.on("error",r=>{exports.BENIGN_SOCKET_ERRNOS.has(r.code??"")||console.warn(`[gateway] inbound socket error (${r.code??r.message}) \u2014 dropping this connection only`),s.destroy()})}T(guardInboundSocket,"guardInboundSocket"),n(guardInboundSocket,"guardInboundSocket");function retirePreviousWorkspace(s,r,d){const h=s.get(r);if(!h)return!1;s.delete(r),d==="re-registered"&&console.log(`[gateway] workspace '${r}' re-registered \u2014 retiring the children of its previous registration first.`);try{h()}catch(a){console.warn(`[gateway] could not fully retire the previous registration of '${r}' (${d}): ${a.message}`)}return!0}T(retirePreviousWorkspace,"retirePreviousWorkspace"),n(retirePreviousWorkspace,"retirePreviousWorkspace"),exports.GATEWAY_MAX_CONNECTIONS_DEFAULT=512,exports.GATEWAY_MAX_H2_SESSIONS_DEFAULT=64,exports.GATEWAY_MAX_H2_STREAMS_DEFAULT=4096,exports.GATEWAY_H2_IDLE_SESSION_MS_DEFAULT=3e5;function parseBudget(s,r){if(s===void 0||s.trim()==="")return r;const d=Number(s);return!Number.isFinite(d)||d<0?r:Math.floor(d)}T(parseBudget,"parseBudget"),n(parseBudget,"parseBudget");function resolveMaxConnections(s){return parseBudget(s,exports.GATEWAY_MAX_CONNECTIONS_DEFAULT)}T(resolveMaxConnections,"resolveMaxConnections"),n(resolveMaxConnections,"resolveMaxConnections");function h2BudgetFromEnv(s=process.env){return{maxSessions:parseBudget(s.LENSMCP_MAX_H2_SESSIONS,exports.GATEWAY_MAX_H2_SESSIONS_DEFAULT),maxStreams:parseBudget(s.LENSMCP_MAX_H2_STREAMS,exports.GATEWAY_MAX_H2_STREAMS_DEFAULT),idleSessionMs:parseBudget(s.LENSMCP_H2_IDLE_MS,exports.GATEWAY_H2_IDLE_SESSION_MS_DEFAULT)}}T(h2BudgetFromEnv,"h2BudgetFromEnv"),n(h2BudgetFromEnv,"h2BudgetFromEnv");function createH2Governor(s,r){const{NGHTTP2_ENHANCE_YOUR_CALM:d,NGHTTP2_REFUSED_STREAM:h}=require("node:http2").constants,a=new Map;let y=0;const _=n(o=>{o.idle&&(clearTimeout(o.idle),o.idle=void 0)},"disarm"),S=n((o,c)=>{_(c),!(s.idleSessionMs<=0||c.open>0)&&(c.idle=setTimeout(()=>{o.destroyed||o.closed||(r("session-reclaimed",{idleMs:s.idleSessionMs,sessions:a.size}),a.delete(o),o.close())},s.idleSessionMs),c.idle.unref?.())},"arm"),p=n(()=>{for(const[o,c]of a)if(!(c.open>0||o.destroyed))return _(c),a.delete(o),r("session-reclaimed",{reason:"cap",sessions:a.size+1,maxSessions:s.maxSessions}),o.close(),!0;return!1},"reclaimOldestIdle");return{admitSession(o){if(s.maxSessions>0&&a.size>=s.maxSessions&&!p()){r("session-refused",{sessions:a.size,maxSessions:s.maxSessions,streams:y});try{o.goaway(d)}catch{}try{o.close()}catch{}return!1}const c={open:0};return a.set(o,c),S(o,c),o.once("close",()=>{_(c),a.delete(o)}),!0},admitStream(o,c){const i=a.get(o);if(!i)return!0;if(s.maxStreams>0&&y>=s.maxStreams){r("stream-refused",{streams:y,maxStreams:s.maxStreams,sessions:a.size});try{c.close(h)}catch{}return!1}return y+=1,i.open+=1,_(i),c.once("close",()=>{y=Math.max(0,y-1),i.open=Math.max(0,i.open-1),i.open===0&&a.has(o)&&S(o,i)}),!0},dispose(){for(const o of a.values())_(o);a.clear()}}}T(createH2Governor,"createH2Governor"),n(createH2Governor,"createH2Governor");async function startGateway(s,r){const d=s.https!==!1,h=s.ports?.length?s.ports:[443],a=s.scanTtlMs??1500,y=s.startupTimeoutMs??12e4,_=s.sweepMs??1e4,S=s.trafficFlushMs??5e3;let p=!1;const o=process.env.LENSMCP_EVENT_FILE??path.join(r.root,".lensmcp","events.jsonl"),c=(0,scope_1.readLensScope)(r.root),i=c.key,A=c.sockNs!==i?c.sockNs:void 0,Y=r.projectsConfigurations?.projects??{},B=(0,discovery_1.projectSourceRoots)(r.root,Y,r.projectDependencies),{routes:H,services:v,instance:X}=(0,discovery_1.discoverRoutes)(r.root,Y,void 0,i,B,A,c.instance);if(H.length===0)throw new Error("[gateway] no project declares a `cluster` field \u2014 nothing to route.");const $=new route_registry_1.RouteRegistry;$.register({wsKey:i,...A?{sockNs:A}:{},root:r.root,routes:H,services:v,...X?{instance:X}:{}});const R=[];let J=n(()=>{},"refreshCert");const V=n(()=>{(0,route_registry_1.rebuildRoutesInPlace)(R,$),J()},"rebuildRoutes");V();const{serviceKeys:te,keyToProject:oe,keyToWorkspace:ne}=(0,service_keys_1.loadServiceKeys)(r.root,v,i),ae=(0,client_ip_1.createClientIpResolver)({...(0,client_ip_1.clientIpOptionsFromEnv)(),...s.trustProxyHops!==void 0?{trustProxyHops:s.trustProxyHops}:{},...s.clientIpHeader?{clientIpHeader:s.clientIpHeader}:{},...s.clientIpTrustedProxies?{clientIpTrustedProxies:s.clientIpTrustedProxies}:{}}),m={root:r.root,wsKey:i,...A?{sockNs:A}:{},eventFile:o,routes:R,registry:$,rebuildRoutes:V,services:v,serviceKeys:te,keyToProject:oe,keyToWorkspace:ne,clientIp:ae,bodyStallMs:s.bodyStallMs??types_1.BODY_STALL_MS,scanTtlMs:a,startupTimeoutMs:y,sweepMs:_,trafficFlushMs:S,https:d,projectRoots:Object.fromEntries(Object.entries(r.projectsConfigurations?.projects??{}).map(([e,t])=>[e,path.resolve(r.root,t.root)])),projectSourceRoots:B,...r.projectGraphStore?{graphStores:(0,project_graph_store_1.createProjectGraphStores)(r.projectGraphStore)}:{},...s.sharedCompiler!==void 0?{sharedCompiler:s.sharedCompiler}:{},stopped:n(()=>p,"stopped")};(0,process_name_1.prelinkProcessNames)((0,process_names_1.gatewayProcessNames)(v,{runner:(0,serve_launch_1.serveRunnerEnabled)()&&!!r.projectGraphStore,sharedBuild:(0,protocol_1.sharedCompilerEnabled)(process.env,s.sharedCompiler),label:i||path.basename(r.root),lens:{key:i,dashboard:s.dashboard!==!1,mcp:s.mcp!==!1&&H.some(e=>!!e.lens)}}));const w=(0,observability_1.createObservability)(o,{trafficFlushMs:S}),ie=createClientErrorLogGate(),x=h2BudgetFromEnv(),ce=createClientErrorLogGate(3e4),G=createH2Governor(x,(e,t)=>{ce(e)&&console.warn(`[gateway] h2 aggregate budget: ${e} (${JSON.stringify(t)}). These are the FRONT DOOR's ceilings, not an upstream's \u2014 raise/disable with LENSMCP_MAX_H2_SESSIONS / LENSMCP_MAX_H2_STREAMS / LENSMCP_H2_IDLE_MS / LENSMCP_MAX_CONNECTIONS (0 = unbounded).`),w.emit(e==="session-reclaimed"?"info":"warning",`h2 aggregate budget: ${e}`,`gateway-h2-budget:${e}`,{kind:"h2-aggregate-budget",event:e,project:"gateway",...t})}),N=(0,lifecycle_1.createServiceLayer)(m,w),k=(0,lens_children_1.createLensChildren)(m,w,s),j=new Map;let q;if(s.middleware){const e=require(path.resolve(r.root,s.middleware));q=e.default??e}const W=(0,workspace_middleware_1.createWorkspaceMiddleware)({ownerKey:i,...q?{owner:q}:{},policy:(0,workspace_middleware_1.guestMiddlewarePolicy)()});let O;try{O=(0,control_1.startControlServer)({rt:m,killService:n((e,t)=>N.killSpawned(e,t),"killService"),onRegister:n(async e=>{retirePreviousWorkspace(j,e.wsKey,"re-registered"),W.set(e.wsKey,e.root,e.middleware);const t=await k.hostWorkspaceDashboard(e),f=await k.hostWorkspaceApps(e);j.set(e.wsKey,()=>{try{t?.()}finally{f()}})},"onRegister"),onUnregister:n(e=>{retirePreviousWorkspace(j,e.wsKey,"unregistered"),W.drop(e.wsKey),N.stopBuildHost(e.root)},"onUnregister")})}catch(e){console.warn("[gateway] control plane failed to start (multi-workspace register disabled):",e.message)}const b=(0,proxy_1.createProxy)(m,w,N),z=buildEdgeVerifierPool(()=>m.routes),Q=(0,auth_1.createEdgeAuth)(m,w,z),le=n(e=>{const t=e.route?.wsKey,f=W.for(t);if(f)try{const l=t?m.registry.get(t):void 0;f(e.req,{...t?{wsKey:t}:{},...l?{root:l.root}:{},env:l?.instance?.env??{}})}catch(l){w.traceStep(e.trace,"auth",{mode:"middleware",ok:!1,reason:l.message}),e.route&&w.finishTrace(e.trace,e.route.project,401),e.sendError(401,"unauthorized",`Gateway middleware rejected the request: ${l.message}`)}},"builtinMiddleware"),Z=s.hooks??{},ee=(0,hooks_1.createHooks)({...Z,afterAuth:[le,...Z.afterAuth??[]]}),se=(0,handler_1.createHandler)({rt:m,obs:w,auth:Q,proxy:b,hooks:ee}),re=(0,upgrade_1.createUpgrade)({rt:m,auth:Q,proxy:b,hooks:ee,obs:w}),ue=w.startEdgeFlusher(),de=N.startSweeper();await k.bootSingletonsAndApps();for(const e of v)e.decl.eager&&N.ensureUp(e);let P,D,L;if(d){const e=require("../../../basic-ssl"),t=(0,service_keys_1.gatewayCacheDir)(r.root);L=n(()=>e.getCertificateSync(t,"gateway",m.routes.map(f=>f.host).filter(f=>!!f)),"mintCert"),P=L(),D=e.caCertPath()}const F=[],I=[],U=s.maxConnections??resolveMaxConnections(process.env.LENSMCP_MAX_CONNECTIONS);for(const e of h){const t=P?require("node:http2").createSecureServer({key:P,cert:P,allowHTTP1:!0,maxSessionMemory:512,settings:{maxConcurrentStreams:512},peerMaxConcurrentStreams:512,streamResetBurst:s.h2ResetBurst??types_1.GATEWAY_H2_RESET_BURST,streamResetRate:s.h2ResetRate??types_1.GATEWAY_H2_RESET_RATE},(l,E)=>se(l,E)):http.createServer(se);t.on("upgrade",re),t.on("error",l=>console.error(`[gateway] port ${e}: ${l.code}`)),t.on("connection",guardInboundSocket),t.on("secureConnection",guardInboundSocket),t.on("tlsClientError",(l,E)=>E.destroy()),t.on("clientError",(l,E)=>{const u=l,g=classifyClientError(u.code,u.message);if(g.report&&ie(g.cause)){const C=E.remoteAddress??"unknown",M=g.status===null?"dropped":String(g.status);console.warn(`[gateway] inbound H1 clientError from ${C}: ${g.cause} (${u.code??u.message}${u.bytesParsed!==void 0?`, bytesParsed=${u.bytesParsed}`:""}) \u2192 ${M}. This status is the GATEWAY's, not the upstream service's.`),w.emit("warning",`inbound clientError: ${g.cause} \u2192 ${M}`,`gateway-client-error:${g.cause}`,{kind:"gateway-client-error",cause:g.cause,code:u.code??null,status:g.status,peer:C,bytesParsed:u.bytesParsed??null})}g.status!==null&&E.writable&&E.end(`HTTP/1.1 ${g.status} ${CLIENT_ERROR_REASON[g.status]}\r
1
+ "use strict";var Se=Object.defineProperty;var T=(s,r)=>Se(s,"name",{value:r,configurable:!0});var _e=Object.defineProperty,n=T((s,r)=>_e(s,"name",{value:r,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.GATEWAY_H2_IDLE_SESSION_MS_DEFAULT=exports.GATEWAY_MAX_H2_STREAMS_DEFAULT=exports.GATEWAY_MAX_H2_SESSIONS_DEFAULT=exports.GATEWAY_MAX_CONNECTIONS_DEFAULT=exports.BENIGN_SOCKET_ERRNOS=void 0,exports.buildEdgeVerifierPool=buildEdgeVerifierPool,exports.classifyClientError=classifyClientError,exports.createClientErrorLogGate=createClientErrorLogGate,exports.retirePreviousWorkspace=retirePreviousWorkspace,exports.resolveMaxConnections=resolveMaxConnections,exports.h2BudgetFromEnv=h2BudgetFromEnv,exports.createH2Governor=createH2Governor,exports.startGateway=startGateway;const tslib_1=require("tslib"),http=tslib_1.__importStar(require("node:http")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery"),scope_1=require("./scope"),route_registry_1=require("./route-registry"),control_1=require("./control"),workspace_middleware_1=require("./workspace-middleware"),workspace_registry_1=require("./workspace-registry"),service_keys_1=require("./service-keys"),client_ip_1=require("../client-ip"),workspace_log_1=require("./workspace-log"),observability_1=require("./observability"),lifecycle_1=require("./lifecycle"),process_names_1=require("./process-names"),project_graph_store_1=require("./project-graph-store"),serve_launch_1=require("./serve-launch"),process_name_1=require("../../../process-name"),protocol_1=require("../../../shared-build/protocol"),lens_children_1=require("./lens-children"),proxy_1=require("./proxy"),auth_1=require("./auth"),hooks_1=require("./hooks"),handler_1=require("./handler"),upgrade_1=require("./upgrade"),jwks_verify_1=require("../jwks-verify"),types_1=require("./types");function insecureLocalFetch(s,r){return new Promise((d,h)=>{const a=(s.startsWith("https:")?require("node:https"):require("node:http")).request(s,{method:"GET",headers:r?.headers,rejectUnauthorized:!1},y=>{const _=[];y.on("data",S=>_.push(S)),y.on("end",()=>{const S=y.statusCode??0,p=Buffer.concat(_).toString("utf8");d({ok:S>=200&&S<300,status:S,json:n(async()=>JSON.parse(p),"json")})})});a.on("error",h),a.end()})}T(insecureLocalFetch,"insecureLocalFetch"),n(insecureLocalFetch,"insecureLocalFetch");function buildEdgeVerifierPool(s){const r=process.env.LENSMCP_GW_JWKS_URL,d=process.env.LENSMCP_GW_JWT_ISS,h=new Map;let a;const y=n((_,S)=>{console.log(`[gateway] edge JWT: verifying via JWKS ${_}${S}`);const p=(0,jwks_verify_1.createJwksVerifier)({jwksUrl:_,...d?{issuer:d}:{},fetchImpl:insecureLocalFetch});return p.refresh().catch(o=>console.warn("[gateway] edge JWKS warm failed (will retry):",o.message)),p},"make");return{for(_){if(r)return a??=y(r,"");const S=_.split(":")[0].toLowerCase();let p;for(const c of s()){const i=c.host;if(typeof i!="string"||!/^auth\./.test(i)||i.startsWith("internal."))continue;const A=i.slice(5);(S===A||S.endsWith(`.${A}`))&&(!p||A.length>p.length)&&(p=A)}if(!p)return;let o=h.get(p);return o||(o=y(`https://auth.${p}/jwks.json`,` (auto-derived for apex ${p})`),h.set(p,o)),o},stop(){a?.stop();for(const _ of h.values())_.stop()}}}T(buildEdgeVerifierPool,"buildEdgeVerifierPool"),n(buildEdgeVerifierPool,"buildEdgeVerifierPool"),exports.BENIGN_SOCKET_ERRNOS=new Set(["ECONNRESET","EPIPE","ETIMEDOUT","ECONNABORTED","ECANCELED"]);function classifyClientError(s,r){return s&&exports.BENIGN_SOCKET_ERRNOS.has(s)?{status:null,report:!1,cause:"client-abort"}:s==="HPE_INVALID_EOF_STATE"?{status:null,report:!1,cause:"truncated-at-eof"}:s==="ERR_HTTP_REQUEST_TIMEOUT"?{status:408,report:!0,cause:"request-timeout"}:s==="HPE_HEADER_OVERFLOW"?{status:431,report:!0,cause:"header-overflow"}:s?.startsWith("HPE_")?{status:400,report:!0,cause:"parse-error"}:{status:400,report:!0,cause:s??r??"unknown"}}T(classifyClientError,"classifyClientError"),n(classifyClientError,"classifyClientError");const CLIENT_ERROR_REASON={400:"Bad Request",408:"Request Timeout",431:"Request Header Fields Too Large"};function createClientErrorLogGate(s=1e4){const r=new Map;return d=>{const h=Date.now(),a=r.get(d)??0;return h-a<s?!1:(r.set(d,h),!0)}}T(createClientErrorLogGate,"createClientErrorLogGate"),n(createClientErrorLogGate,"createClientErrorLogGate");function guardInboundSocket(s){s.on("error",r=>{exports.BENIGN_SOCKET_ERRNOS.has(r.code??"")||console.warn(`[gateway] inbound socket error (${r.code??r.message}) \u2014 dropping this connection only`),s.destroy()})}T(guardInboundSocket,"guardInboundSocket"),n(guardInboundSocket,"guardInboundSocket");function retirePreviousWorkspace(s,r,d){const h=s.get(r);if(!h)return!1;s.delete(r),d==="re-registered"&&console.log(`[gateway] workspace '${r}' re-registered \u2014 retiring the children of its previous registration first.`);try{h()}catch(a){console.warn(`[gateway] could not fully retire the previous registration of '${r}' (${d}): ${a.message}`)}return!0}T(retirePreviousWorkspace,"retirePreviousWorkspace"),n(retirePreviousWorkspace,"retirePreviousWorkspace"),exports.GATEWAY_MAX_CONNECTIONS_DEFAULT=512,exports.GATEWAY_MAX_H2_SESSIONS_DEFAULT=64,exports.GATEWAY_MAX_H2_STREAMS_DEFAULT=4096,exports.GATEWAY_H2_IDLE_SESSION_MS_DEFAULT=3e5;function parseBudget(s,r){if(s===void 0||s.trim()==="")return r;const d=Number(s);return!Number.isFinite(d)||d<0?r:Math.floor(d)}T(parseBudget,"parseBudget"),n(parseBudget,"parseBudget");function resolveMaxConnections(s){return parseBudget(s,exports.GATEWAY_MAX_CONNECTIONS_DEFAULT)}T(resolveMaxConnections,"resolveMaxConnections"),n(resolveMaxConnections,"resolveMaxConnections");function h2BudgetFromEnv(s=process.env){return{maxSessions:parseBudget(s.LENSMCP_MAX_H2_SESSIONS,exports.GATEWAY_MAX_H2_SESSIONS_DEFAULT),maxStreams:parseBudget(s.LENSMCP_MAX_H2_STREAMS,exports.GATEWAY_MAX_H2_STREAMS_DEFAULT),idleSessionMs:parseBudget(s.LENSMCP_H2_IDLE_MS,exports.GATEWAY_H2_IDLE_SESSION_MS_DEFAULT)}}T(h2BudgetFromEnv,"h2BudgetFromEnv"),n(h2BudgetFromEnv,"h2BudgetFromEnv");function createH2Governor(s,r){const{NGHTTP2_ENHANCE_YOUR_CALM:d,NGHTTP2_REFUSED_STREAM:h}=require("node:http2").constants,a=new Map;let y=0;const _=n(o=>{o.idle&&(clearTimeout(o.idle),o.idle=void 0)},"disarm"),S=n((o,c)=>{_(c),!(s.idleSessionMs<=0||c.open>0)&&(c.idle=setTimeout(()=>{o.destroyed||o.closed||(r("session-reclaimed",{idleMs:s.idleSessionMs,sessions:a.size}),a.delete(o),o.close())},s.idleSessionMs),c.idle.unref?.())},"arm"),p=n(()=>{for(const[o,c]of a)if(!(c.open>0||o.destroyed))return _(c),a.delete(o),r("session-reclaimed",{reason:"cap",sessions:a.size+1,maxSessions:s.maxSessions}),o.close(),!0;return!1},"reclaimOldestIdle");return{admitSession(o){if(s.maxSessions>0&&a.size>=s.maxSessions&&!p()){r("session-refused",{sessions:a.size,maxSessions:s.maxSessions,streams:y});try{o.goaway(d)}catch{}try{o.close()}catch{}return!1}const c={open:0};return a.set(o,c),S(o,c),o.once("close",()=>{_(c),a.delete(o)}),!0},admitStream(o,c){const i=a.get(o);if(!i)return!0;if(s.maxStreams>0&&y>=s.maxStreams){r("stream-refused",{streams:y,maxStreams:s.maxStreams,sessions:a.size});try{c.close(h)}catch{}return!1}return y+=1,i.open+=1,_(i),c.once("close",()=>{y=Math.max(0,y-1),i.open=Math.max(0,i.open-1),i.open===0&&a.has(o)&&S(o,i)}),!0},dispose(){for(const o of a.values())_(o);a.clear()}}}T(createH2Governor,"createH2Governor"),n(createH2Governor,"createH2Governor");async function startGateway(s,r){const d=s.https!==!1,h=s.ports?.length?s.ports:[443],a=s.scanTtlMs??1500,y=s.startupTimeoutMs??12e4,_=s.sweepMs??1e4,S=s.trafficFlushMs??5e3;let p=!1;const o=process.env.LENSMCP_EVENT_FILE??path.join(r.root,".lensmcp","events.jsonl"),c=(0,scope_1.readLensScope)(r.root),i=c.key,A=c.sockNs!==i?c.sockNs:void 0,Y=r.projectsConfigurations?.projects??{},B=(0,discovery_1.projectSourceRoots)(r.root,Y,r.projectDependencies),{routes:H,services:N,instance:X}=(0,discovery_1.discoverRoutes)(r.root,Y,void 0,i,B,A,c.instance);if(H.length===0)throw new Error("[gateway] no project declares a `cluster` field \u2014 nothing to route.");const $=new route_registry_1.RouteRegistry;$.register({wsKey:i,...A?{sockNs:A}:{},root:r.root,routes:H,services:N,...X?{instance:X}:{}});const R=[];let J=n(()=>{},"refreshCert");const V=n(()=>{(0,route_registry_1.rebuildRoutesInPlace)(R,$),J()},"rebuildRoutes");V();const{serviceKeys:te,keyToProject:oe,keyToWorkspace:ne}=(0,service_keys_1.loadServiceKeys)(r.root,N,i),ae=(0,client_ip_1.createClientIpResolver)({...(0,client_ip_1.clientIpOptionsFromEnv)(),...s.trustProxyHops!==void 0?{trustProxyHops:s.trustProxyHops}:{},...s.clientIpHeader?{clientIpHeader:s.clientIpHeader}:{},...s.clientIpTrustedProxies?{clientIpTrustedProxies:s.clientIpTrustedProxies}:{}}),m={root:r.root,wsKey:i,...A?{sockNs:A}:{},eventFile:o,routes:R,registry:$,rebuildRoutes:V,services:N,serviceKeys:te,keyToProject:oe,keyToWorkspace:ne,clientIp:ae,bodyStallMs:s.bodyStallMs??types_1.BODY_STALL_MS,scanTtlMs:a,startupTimeoutMs:y,sweepMs:_,trafficFlushMs:S,https:d,projectRoots:Object.fromEntries(Object.entries(r.projectsConfigurations?.projects??{}).map(([e,t])=>[e,path.resolve(r.root,t.root)])),projectSourceRoots:B,...r.projectGraphStore?{graphStores:(0,project_graph_store_1.createProjectGraphStores)(r.projectGraphStore)}:{},...s.sharedCompiler!==void 0?{sharedCompiler:s.sharedCompiler}:{},stopped:n(()=>p,"stopped")};(0,process_name_1.prelinkProcessNames)((0,process_names_1.gatewayProcessNames)(N,{runner:(0,serve_launch_1.serveRunnerEnabled)()&&!!r.projectGraphStore,sharedBuild:(0,protocol_1.sharedCompilerEnabled)(process.env,s.sharedCompiler),label:i||path.basename(r.root),lens:{key:i,dashboard:s.dashboard!==!1,mcp:s.mcp!==!1&&H.some(e=>!!e.lens)}}));const w=(0,observability_1.createObservability)(o,{trafficFlushMs:S}),ie=createClientErrorLogGate(),x=h2BudgetFromEnv(),ce=createClientErrorLogGate(3e4),G=createH2Governor(x,(e,t)=>{ce(e)&&console.warn(`[gateway] h2 aggregate budget: ${e} (${JSON.stringify(t)}). These are the FRONT DOOR's ceilings, not an upstream's \u2014 raise/disable with LENSMCP_MAX_H2_SESSIONS / LENSMCP_MAX_H2_STREAMS / LENSMCP_H2_IDLE_MS / LENSMCP_MAX_CONNECTIONS (0 = unbounded).`),w.emit(e==="session-reclaimed"?"info":"warning",`h2 aggregate budget: ${e}`,`gateway-h2-budget:${e}`,{kind:"h2-aggregate-budget",event:e,project:"gateway",...t})}),v=(0,lifecycle_1.createServiceLayer)(m,w),k=(0,lens_children_1.createLensChildren)(m,w,s),j=new Map;let q;if(s.middleware){const e=require(path.resolve(r.root,s.middleware));q=e.default??e}const W=(0,workspace_middleware_1.createWorkspaceMiddleware)({ownerKey:i,...q?{owner:q}:{},policy:(0,workspace_middleware_1.guestMiddlewarePolicy)()});let O;try{O=(0,control_1.startControlServer)({rt:m,killService:n((e,t)=>v.killSpawned(e,t),"killService"),onRegister:n(async e=>{retirePreviousWorkspace(j,e.wsKey,"re-registered"),W.set(e.wsKey,e.root,e.middleware);const t=await k.hostWorkspaceDashboard(e),f=await k.hostWorkspaceApps(e);j.set(e.wsKey,()=>{try{t?.()}finally{f()}})},"onRegister"),onUnregister:n(e=>{retirePreviousWorkspace(j,e.wsKey,"unregistered"),W.drop(e.wsKey),v.stopBuildHost(e.root)},"onUnregister")})}catch(e){console.warn("[gateway] control plane failed to start (multi-workspace register disabled):",e.message)}const b=(0,proxy_1.createProxy)(m,w,v),z=buildEdgeVerifierPool(()=>m.routes),Q=(0,auth_1.createEdgeAuth)(m,w,z),le=n(e=>{const t=e.route?.wsKey,f=W.for(t);if(f)try{const l=t?m.registry.get(t):void 0;f(e.req,{...t?{wsKey:t}:{},...l?{root:l.root}:{},env:l?.instance?.env??{}})}catch(l){w.traceStep(e.trace,"auth",{mode:"middleware",ok:!1,reason:l.message}),e.route&&w.finishTrace(e.trace,e.route.project,401),e.sendError(401,"unauthorized",`Gateway middleware rejected the request: ${l.message}`)}},"builtinMiddleware"),Z=s.hooks??{},ee=(0,hooks_1.createHooks)({...Z,afterAuth:[le,...Z.afterAuth??[]]}),se=(0,handler_1.createHandler)({rt:m,obs:w,auth:Q,proxy:b,hooks:ee}),re=(0,upgrade_1.createUpgrade)({rt:m,auth:Q,proxy:b,hooks:ee,obs:w}),ue=w.startEdgeFlusher(),de=v.startSweeper();await k.bootSingletonsAndApps();for(const e of N)e.decl.eager&&v.ensureUp(e);let P,D,L;if(d){const e=require("../../../basic-ssl"),t=(0,service_keys_1.gatewayCacheDir)(r.root);L=n(()=>e.getCertificateSync(t,"gateway",m.routes.map(f=>f.host).filter(f=>!!f)),"mintCert"),P=L(),D=e.caCertPath()}const F=[],I=[],U=s.maxConnections??resolveMaxConnections(process.env.LENSMCP_MAX_CONNECTIONS);for(const e of h){const t=P?require("node:http2").createSecureServer({key:P,cert:P,allowHTTP1:!0,maxSessionMemory:512,settings:{maxConcurrentStreams:512},peerMaxConcurrentStreams:512,streamResetBurst:s.h2ResetBurst??types_1.GATEWAY_H2_RESET_BURST,streamResetRate:s.h2ResetRate??types_1.GATEWAY_H2_RESET_RATE},(l,E)=>se(l,E)):http.createServer(se);t.on("upgrade",re),t.on("error",l=>console.error(`[gateway] port ${e}: ${l.code}`)),t.on("connection",guardInboundSocket),t.on("secureConnection",guardInboundSocket),t.on("tlsClientError",(l,E)=>E.destroy()),t.on("clientError",(l,E)=>{const u=l,g=classifyClientError(u.code,u.message);if(g.report&&ie(g.cause)){const C=E.remoteAddress??"unknown",M=g.status===null?"dropped":String(g.status);console.warn(`[gateway] inbound H1 clientError from ${C}: ${g.cause} (${u.code??u.message}${u.bytesParsed!==void 0?`, bytesParsed=${u.bytesParsed}`:""}) \u2192 ${M}. This status is the GATEWAY's, not the upstream service's.`),w.emit("warning",`inbound clientError: ${g.cause} \u2192 ${M}`,`gateway-client-error:${g.cause}`,{kind:"gateway-client-error",cause:g.cause,code:u.code??null,status:g.status,peer:C,bytesParsed:u.bytesParsed??null})}g.status!==null&&E.writable&&E.end(`HTTP/1.1 ${g.status} ${CLIENT_ERROR_REASON[g.status]}\r
2
2
  \r
3
- `),E.destroy()}),t.keepAliveTimeout=types_1.GATEWAY_KEEPALIVE_TIMEOUT_MS,t.headersTimeout=s.headersTimeoutMs??types_1.GATEWAY_HEADERS_TIMEOUT_MS,U>0&&(t.maxConnections=U);const f=s.connectionsCheckingIntervalMs??types_1.GATEWAY_CONN_CHECK_MS;if(f>0&&(t.connectionsCheckingInterval=f),P!=null){const l=t,{NGHTTP2_CANCEL:E}=require("node:http2").constants;l.on("session",u=>{if(!G.admitSession(u)){u.on("error",()=>{});return}let g=0,C=0;u.on("stream",M=>{G.admitStream(u,M)&&M.once("close",()=>{u.destroyed?C+=1:M.rstCode===E&&(g+=1)})}),u.once("close",()=>{C!==0&&(console.warn(`[gateway] h2 session closed with ${C} stream(s) in flight after ${g} client reset(s) \u2014 every in-flight fetch on that connection failed at once (ERR_HTTP2_PROTOCOL_ERROR in the browser). Reset budget: LENSMCP_H2_RESET_BURST/LENSMCP_H2_RESET_RATE (issues/closed/foodguard-14).`),w.emit("warning",`h2 session lost with ${C} stream(s) in flight`,"gateway-h2-session-lost",{kind:"h2-session-lost",project:"gateway",inflight:C,clientResets:g}))}),u.on("error",M=>{const pe=M.code??M.message;console.warn(`[gateway] h2 session error (${pe}) \u2014 dropping this session only`),u.destroyed||u.destroy()})}),l.on("sessionError",u=>{console.warn(`[gateway] h2 sessionError: ${u.code??u.message}`)})}await new Promise(l=>{t.listen(e,()=>{const E=t.address()?.port??e;I.push(E),console.log(`[gateway] Listening on ${P?"https":"http"}://localhost:${E}`),l()})}),F.push(t)}L&&(J=n(()=>{const e=L();for(const t of F)t.setSecureContext?.({key:e,cert:e})},"refreshCert")),console.log("[gateway] Routes:");for(const e of R){const t=e.pool?`pods@${e.pool.dir}`:e.target;console.log(`[gateway] ${e.host??"(default)"} \u2192 ${t}${e.prependPrefix?` (+${e.prependPrefix})`:""}${e.internal?" [internal: no middleware]":""} [${e.project}]`)}D&&console.log(`[gateway] CA: ${D} \u2014 one-time setup via the trust executor.`);const K=n(e=>e>0?String(e):"unbounded","unbounded");return console.log(`[gateway] front-door budget: \u2264${K(U)} connections, \u2264${K(x.maxSessions)} h2 sessions (each \u2264512 MB session memory, \u2264512 streams), \u2264${K(x.maxStreams)} open streams in total, idle h2 session reclaimed after ${x.idleSessionMs>0?`${Math.round(x.idleSessionMs/1e3)}s`:"never"}. Override: LENSMCP_MAX_CONNECTIONS / LENSMCP_MAX_H2_SESSIONS / LENSMCP_MAX_H2_STREAMS / LENSMCP_H2_IDLE_MS (0 = unbounded).`),w.emit("info",`gateway up: ${R.length} routes on ${I.join("/")}`,"cluster-gateway",{kind:"gateway-up",project:"gateway",ports:I,routes:R.map(e=>({host:e.host??"(default)",project:e.project,mode:e.pool?"pods":"tcp",internal:!!e.internal,prependPrefix:e.prependPrefix}))}),O&&process.env.LENSMCP_GUEST_REPLAY!=="0"&&O.replay().catch(e=>console.warn("[gateway] guest replay failed:",e.message)),{ports:I,routes:R,services:v,stop:n(async()=>{if(!p){p=!0,clearInterval(ue),clearInterval(de),w.emit("info","gateway down","cluster-gateway",{kind:"gateway-down",project:"gateway"}),z.stop(),O?.close(),(0,workspace_registry_1.unregisterWorkspace)(i);for(const e of v)N.killSpawned(e,"gateway shutdown");await N.stopBuildHost(),await k.reapAll(),re.closeTunnels(),await Promise.all(F.map(e=>new Promise(t=>{let f=!1;const l=n(()=>{f||(f=!0,t())},"done"),E=e;e.close(()=>l());try{E.closeIdleConnections?.()}catch{}setTimeout(()=>{try{E.closeAllConnections?.()}catch{}l()},types_1.GATEWAY_STOP_GRACE_MS).unref?.()}))),b.closeProxy(),b.destroyAgents(),G.dispose(),(0,workspace_log_1.closeWorkspaceLogs)()}},"stop"),...m.lensSupervisor?{lensSupervisor:m.lensSupervisor}:{},...m.graphStores?{graphStores:m.graphStores}:{}}}T(startGateway,"startGateway"),n(startGateway,"startGateway");
3
+ `),E.destroy()}),t.keepAliveTimeout=types_1.GATEWAY_KEEPALIVE_TIMEOUT_MS,t.headersTimeout=s.headersTimeoutMs??types_1.GATEWAY_HEADERS_TIMEOUT_MS,U>0&&(t.maxConnections=U);const f=s.connectionsCheckingIntervalMs??types_1.GATEWAY_CONN_CHECK_MS;if(f>0&&(t.connectionsCheckingInterval=f),P!=null){const l=t,{NGHTTP2_CANCEL:E}=require("node:http2").constants;l.on("session",u=>{if(!G.admitSession(u)){u.on("error",()=>{});return}let g=0,C=0;u.on("stream",M=>{G.admitStream(u,M)&&M.once("close",()=>{u.destroyed?C+=1:M.rstCode===E&&(g+=1)})}),u.once("close",()=>{C!==0&&(console.warn(`[gateway] h2 session closed with ${C} stream(s) in flight after ${g} client reset(s) \u2014 every in-flight fetch on that connection failed at once (ERR_HTTP2_PROTOCOL_ERROR in the browser). Reset budget: LENSMCP_H2_RESET_BURST/LENSMCP_H2_RESET_RATE (issues/closed/foodguard-14).`),w.emit("warning",`h2 session lost with ${C} stream(s) in flight`,"gateway-h2-session-lost",{kind:"h2-session-lost",project:"gateway",inflight:C,clientResets:g}))}),u.on("error",M=>{const pe=M.code??M.message;console.warn(`[gateway] h2 session error (${pe}) \u2014 dropping this session only`),u.destroyed||u.destroy()})}),l.on("sessionError",u=>{console.warn(`[gateway] h2 sessionError: ${u.code??u.message}`)})}await new Promise(l=>{t.listen(e,()=>{const E=t.address()?.port??e;I.push(E),console.log(`[gateway] Listening on ${P?"https":"http"}://localhost:${E}`),l()})}),F.push(t)}L&&(J=n(()=>{const e=L();for(const t of F)t.setSecureContext?.({key:e,cert:e})},"refreshCert")),console.log("[gateway] Routes:");for(const e of R){const t=e.pool?`pods@${e.pool.dir}`:e.target;console.log(`[gateway] ${e.host??"(default)"} \u2192 ${t}${e.prependPrefix?` (+${e.prependPrefix})`:""}${e.internal?" [internal: no middleware]":""} [${e.project}]`)}D&&console.log(`[gateway] CA: ${D} \u2014 one-time setup via the trust executor.`);const K=n(e=>e>0?String(e):"unbounded","unbounded");return console.log(`[gateway] front-door budget: \u2264${K(U)} connections, \u2264${K(x.maxSessions)} h2 sessions (each \u2264512 MB session memory, \u2264512 streams), \u2264${K(x.maxStreams)} open streams in total, idle h2 session reclaimed after ${x.idleSessionMs>0?`${Math.round(x.idleSessionMs/1e3)}s`:"never"}. Override: LENSMCP_MAX_CONNECTIONS / LENSMCP_MAX_H2_SESSIONS / LENSMCP_MAX_H2_STREAMS / LENSMCP_H2_IDLE_MS (0 = unbounded).`),w.emit("info",`gateway up: ${R.length} routes on ${I.join("/")}`,"cluster-gateway",{kind:"gateway-up",project:"gateway",ports:I,routes:R.map(e=>({host:e.host??"(default)",project:e.project,mode:e.pool?"pods":"tcp",internal:!!e.internal,prependPrefix:e.prependPrefix}))}),O&&process.env.LENSMCP_GUEST_REPLAY!=="0"&&O.replay().catch(e=>console.warn("[gateway] guest replay failed:",e.message)),{ports:I,routes:R,services:N,stop:n(async()=>{if(!p){p=!0,clearInterval(ue),clearInterval(de),w.emit("info","gateway down","cluster-gateway",{kind:"gateway-down",project:"gateway"}),z.stop(),O?.close(),(0,workspace_registry_1.unregisterWorkspace)(i);for(const e of N)v.killSpawned(e,"gateway shutdown");v.stopRetiringTrees(),await v.stopBuildHost(),await k.reapAll(),re.closeTunnels(),await Promise.all(F.map(e=>new Promise(t=>{let f=!1;const l=n(()=>{f||(f=!0,t())},"done"),E=e;e.close(()=>l());try{E.closeIdleConnections?.()}catch{}setTimeout(()=>{try{E.closeAllConnections?.()}catch{}l()},types_1.GATEWAY_STOP_GRACE_MS).unref?.()}))),b.closeProxy(),b.destroyAgents(),G.dispose(),(0,workspace_log_1.closeWorkspaceLogs)()}},"stop"),...m.lensSupervisor?{lensSupervisor:m.lensSupervisor}:{},...m.graphStores?{graphStores:m.graphStores}:{}}}T(startGateway,"startGateway"),n(startGateway,"startGateway");
@@ -1 +1 @@
1
- "use strict";var ie=Object.defineProperty;var c=(e,r)=>ie(e,"name",{value:r,configurable:!0});var se=Object.defineProperty,o=c((e,r)=>se(e,"name",{value:r,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),node_child_process_1=require("node:child_process"),yaml=tslib_1.__importStar(require("js-yaml")),dev_devtool_1=require("../../dev-devtool"),devserver_notifier_1=require("../../devserver-notifier"),process_name_1=require("../../process-name"),attach_1=require("../../shared-build/attach");function definedEnv(e){const r={};for(const[_,i]of Object.entries(e))typeof i=="string"&&(r[_]=i);return r}c(definedEnv,"definedEnv"),o(definedEnv,"definedEnv");const frozen=o(()=>process.env.LENSMCP_FREEZE==="1","frozen");async function*serveExecutor(e,r){process.env.NODE_ENV="development";const _=r.projectsConfigurations.projects[r.projectName],i=r.root,g=path.join(i,_.root),U=e.outputPath?path.join(i,e.outputPath):path.join(i,"dist",_.root),B=e.configEnv||"development",R=path.join(i,"config",`config.${B}.yaml`),$=3050;let d=$,l=r.projectName,S={};fs.existsSync(R)?(S=yaml.load(fs.readFileSync(R,"utf8")),d=Number(S.port)||d,l=S.serviceName||l):console.warn(`[serve] ${r.projectName}: no config file at ${R} \u2014 public devserver port defaults to ${$}. If another service also lacks its config, both bind ${$} \u2192 EADDRINUSE. Add config/config.${B}.yaml with a distinct \`port:\` (matching this project's cluster.port).`),e.serviceName&&(l=e.serviceName);const O=e.servePrefix??"",v=e.debugPort??(Number(process.env.LENSMCP_DEBUG_PORT)||void 0),z=/^(1|true|yes|on)$/i.test((process.env.LENSMCP_DEBUG_FIXED_PORTS??"").trim()),p=e.logFile?path.resolve(i,e.logFile):process.env.LENSMCP_LOG_FILE||void 0;if(p){fs.mkdirSync(path.dirname(p),{recursive:!0}),fs.writeFileSync(p,"",{mode:384});try{fs.chmodSync(p,384)}catch{}}let T,D;e.gateway?.middleware&&(T=path.resolve(g,e.gateway.middleware),D=JSON.stringify(S));const K=e.entryFile||"./src/deployments/service/main.ts",Q=e.tsConfigFile||"./tsconfig.app.json",Z=path.resolve(g,K),W=path.resolve(g,Q),ee=(e.workers||[]).map(t=>({entryName:t.name,entryPath:path.resolve(g,t.entryPath)})),q={appName:r.projectName,appRoot:g,outputDir:U,main:Z,tsConfig:W,assets:e.assets||[],additionalEntryPoints:ee,port:d,serviceName:l,memoryLimit:e.memoryLimit||8192,buildLibsFromSource:e.buildLibsFromSource!==!1,workspaceRoot:i,orgScopes:e.orgScopes||[],bundlePackages:e.bundlePackages||[],nodeExternalsConfig:e.nodeExternalsConfig,webpackConfigPath:e.webpackConfigPath,httpsReload:e.gateway?.https===!0,...e.devtool!==void 0&&{devtool:e.devtool}};let u=null,k=!1,I=!1,m=!1;const V=path.join(__dirname,"../../main.devserver.js"),H=path.join(__dirname,"../../main.devserver.ts"),J=!fs.existsSync(V)&&fs.existsSync(H),x=J?H:V,oe=path.join(U,"main.js");function A(){u&&!u.killed&&(u.kill("SIGTERM"),u=null)}c(A,"H"),o(A,"cleanup");function N(){m=!0,A()}c(N,"k"),o(N,"stop");const te=["SIGINT","SIGTERM","SIGHUP","SIGQUIT"].map(t=>{const n=o(()=>N(),"handler");return process.on(t,n),{sig:t,handler:n}});process.on("exit",N);function E(){if(m)return;if(!fs.existsSync(x)){console.error(`[serve] devserver script not found at ${x}`);return}const t=["--enable-source-maps"];J&&t.unshift("--require","@swc-node/register");const n=(0,node_child_process_1.fork)(x,{cwd:i,env:{...process.env,PORT:String(d),SERVICE_NAME:l,SERVE_PREFIX:O,LENSMCP_PROJECT:process.env.LENSMCP_PROJECT||r.projectName||l,LENSMCP_EVENT_FILE:process.env.LENSMCP_EVENT_FILE||path.join(i,".lensmcp","events.jsonl"),CHILD_COUNT:String(e.childCount||process.env.CHILD_COUNT||1),BUNDLE_PATH:oe,...T&&{GATEWAY_MIDDLEWARE:T},...D&&{GATEWAY_CONFIG:D},...e.gateway?.routes?.length&&{GATEWAY_ROUTES:JSON.stringify(e.gateway.routes)},...e.gateway?.https&&{GATEWAY_HTTPS:"1"},...e.gateway?.extraPorts?.length&&{GATEWAY_EXTRA_PORTS:JSON.stringify(e.gateway.extraPorts)},...e.internalPort&&{INTERNAL_PORT:String(e.internalPort)},...v&&{LENSMCP_DEBUG_PORT:String(v)},...p&&{LENSMCP_LOG_FILE:p},...e.workers?.length&&{WORKERS:JSON.stringify(e.workers.map(a=>a.name))},[dev_devtool_1.DEV_SOURCE_MAPS_ENV]:I?"1":"0"},stdio:["inherit","inherit","inherit","ipc"],execArgv:t,execPath:(0,process_name_1.namedExecPath)((0,process_name_1.processName)(process.env.LENSMCP_WS_KEY||path.basename(i),l,"dev"))});u=n,n.on("exit",(a,f)=>{u===n&&(u=null),!(f||a===0||m)&&(console.error(`[serve] devserver exited unexpectedly (code=${a}, signal=${f}) \u2014 respawning in 1.5s`),setTimeout(()=>{!m&&!u&&k&&E()},1500).unref?.())}),console.log(`[serve] Devserver started on port ${d} (service: ${l}${O?`, prefix: /${O}`:""})`),v?(console.warn(`[serve] DEBUGGER ENABLED for ${l}. Node's inspector has NO authentication \u2014 the ws:// UUID is the whole credential, and any account on this host that obtains it runs code as you. Do not enable this on a shared machine.`),z?console.warn(`[serve] LENSMCP_DEBUG_FIXED_PORTS=1 \u2014 pod inspectors bind PREDICTABLE ports 127.0.0.1:${v}+slot (workers ${v}+20+i), reachable by any account on this host. Attach WebStorm (Attach to Node.js/Chrome) to ${v}. Unset it to bind ephemeral ports instead.`):console.log(`[serve] Debug: pod inspectors bind an EPHEMERAL 127.0.0.1 port. Get the ws:// URLs with \`lensmcp debug ${l}\` (the owner-only control socket); a devserver run in your own terminal also prints them there. Set LENSMCP_DEBUG_FIXED_PORTS=1 if your IDE needs a fixed attach port.`)):console.log(`[serve] Debug on demand: \`lensmcp debug ${l}\` opens pod inspectors without a restart`),p&&console.log(`[serve] Logs teed to ${p} \u2014 tail it in WebStorm via Run/Debug configuration \u2192 Logs tab`)}c(E,"S"),o(E,"startDevServer");function re(){if(!u||u.killed){console.log("[serve] Devserver not running, restarting..."),E();return}fetch(`http://localhost:${d}/webpack/reload`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"}).then(()=>console.log("[serve] Notified devserver to reload")).catch(t=>{console.warn("[serve] Failed to notify devserver, restarting...",t.message),A(),E()})}c(re,"ne"),o(re,"_notifyDevServerReload");let y=null;const M=[];function h(t){if(y){const n=y;y=null,n(t)}else M.push(t)}c(h,"h"),o(h,"pushResult");function Y(){return M.length>0?Promise.resolve(M.shift()):new Promise(t=>{y=t})}c(Y,"ee"),o(Y,"waitForNext");let w=o(()=>{},"closeBuild");function F(){return!k||!u?(k=!0,E(),!0):!1}c(F,"J"),o(F,"builtOk");function b(){if(m)return;const{createDevWebpackConfig:t}=require("../../create-webpack-dev"),n=require("webpack"),a=t(q),{watch:f,...P}=a,s=n(P);I=(0,dev_devtool_1.bundleCarriesSourceMaps)(s.options.devtool);const j=o((L,C)=>{if(L){console.error("[serve] Webpack error:",L),h({success:!1});return}const ne=C.hasErrors();if(console.info(C.toString({colors:!0,chunks:!1,assets:!0})),ne){h({success:!1});return}F(),h({success:!0,baseUrl:`http://localhost:${d}`})},"onBuild");if(frozen()){console.log(`[serve] ${r.projectName}: frozen \u2014 compiling once, no file watcher`),s.run((L,C)=>{j(L,C),s.close(()=>{})});return}const G=s.watch(a.watchOptions||{},j);w=o(()=>G.close(()=>{}),"closeBuild")}c(b,"A"),o(b,"compileOnItsOwn");function X(t){const n=t.accepted.projects.filter(s=>s!==r.projectName);console.log(`[serve] ${r.projectName}: compiled by the workspace's shared compilation ${t.accepted.group.slice(0,8)}${n.length?` with ${n.join(", ")}`:""} \u2014 no webpack in this process`);const a=new devserver_notifier_1.DevserverNotifier(d,e.gateway?.https===!0,(0,devserver_notifier_1.devserverControlSock)(l)),f=(0,attach_1.createAttachedTypecheck)({appName:r.projectName,tsConfig:W,workspaceRoot:i,memoryLimit:e.memoryLimit||8192});let P;w=o(()=>{t.close(),f.dispose()},"closeBuild"),t.listen(s=>{if(!m)switch(s.type){case"watch-run":a.watchRun(s.paths);return;case"watch-closed":a.degrade("watch closed");return;case"failed":console.error(`[serve] the shared compiler failed: ${s.message}`),a.degrade(`compiler failed: ${s.message}`),h({success:!1});return;case"refused":console.log(`[serve] ${r.projectName}: compiling on its own from now on \u2014 ${s.reason}`),w(),b();return;case"built":{if(console.info(s.text),I=s.sourceMaps,f.built(s),!s.ok){a.tellWatching(),h({success:!1});return}const j=s.hash!==P;P=s.hash;const G=F();a.built(j&&!G),h({success:!0,baseUrl:`http://localhost:${d}`});return}default:return}})}if(c(X,"re"),o(X,"attachToSharedBuild"),(0,attach_1.sharedBuildOffered)()&&!frozen()){const t=await(0,attach_1.joinSharedBuild)({project:r.projectName,target:r.targetName??"serve-hmr",...r.configurationName?{configuration:r.configurationName}:{},options:q,targetOptions:{...e},env:definedEnv(process.env)});t.kind==="attached"?X(t.session):(console.log(`[serve] ${r.projectName}: compiling on its own \u2014 ${t.reason}`),b())}else b();try{for(;;)yield await Y()}finally{N(),w(),te.forEach(({sig:t,handler:n})=>process.removeListener(t,n))}}c(serveExecutor,"serveExecutor"),o(serveExecutor,"serveExecutor"),exports.default=serveExecutor,module.exports=serveExecutor,module.exports.default=serveExecutor;
1
+ "use strict";var ie=Object.defineProperty;var c=(e,r)=>ie(e,"name",{value:r,configurable:!0});var se=Object.defineProperty,o=c((e,r)=>se(e,"name",{value:r,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),node_child_process_1=require("node:child_process"),yaml=tslib_1.__importStar(require("js-yaml")),dev_devtool_1=require("../../dev-devtool"),devserver_notifier_1=require("../../devserver-notifier"),process_name_1=require("../../process-name"),attach_1=require("../../shared-build/attach");function definedEnv(e){const r={};for(const[_,i]of Object.entries(e))typeof i=="string"&&(r[_]=i);return r}c(definedEnv,"definedEnv"),o(definedEnv,"definedEnv");const frozen=o(()=>process.env.LENSMCP_FREEZE==="1","frozen");async function*serveExecutor(e,r){process.env.NODE_ENV="development";const _=r.projectsConfigurations.projects[r.projectName],i=r.root,g=path.join(i,_.root),U=e.outputPath?path.join(i,e.outputPath):path.join(i,"dist",_.root),B=e.configEnv||"development",R=path.join(i,"config",`config.${B}.yaml`),$=3050;let u=$,l=r.projectName,S={};fs.existsSync(R)?(S=yaml.load(fs.readFileSync(R,"utf8")),u=Number(S.port)||u,l=S.serviceName||l):console.warn(`[serve] ${r.projectName}: no config file at ${R} \u2014 public devserver port defaults to ${$}. If another service also lacks its config, both bind ${$} \u2192 EADDRINUSE. Add config/config.${B}.yaml with a distinct \`port:\` (matching this project's cluster.port).`),e.serviceName&&(l=e.serviceName);const O=e.servePrefix??"",v=e.debugPort??(Number(process.env.LENSMCP_DEBUG_PORT)||void 0),z=/^(1|true|yes|on)$/i.test((process.env.LENSMCP_DEBUG_FIXED_PORTS??"").trim()),p=e.logFile?path.resolve(i,e.logFile):process.env.LENSMCP_LOG_FILE||void 0;if(p){fs.mkdirSync(path.dirname(p),{recursive:!0}),fs.writeFileSync(p,"",{mode:384});try{fs.chmodSync(p,384)}catch{}}let T,D;e.gateway?.middleware&&(T=path.resolve(g,e.gateway.middleware),D=JSON.stringify(S));const K=e.entryFile||"./src/deployments/service/main.ts",Q=e.tsConfigFile||"./tsconfig.app.json",Z=path.resolve(g,K),q=path.resolve(g,Q),ee=(e.workers||[]).map(t=>({entryName:t.name,entryPath:path.resolve(g,t.entryPath)})),W={appName:r.projectName,appRoot:g,outputDir:U,main:Z,tsConfig:q,assets:e.assets||[],additionalEntryPoints:ee,port:u,serviceName:l,memoryLimit:e.memoryLimit||8192,buildLibsFromSource:e.buildLibsFromSource!==!1,workspaceRoot:i,orgScopes:e.orgScopes||[],bundlePackages:e.bundlePackages||[],nodeExternalsConfig:e.nodeExternalsConfig,webpackConfigPath:e.webpackConfigPath,httpsReload:e.gateway?.https===!0,...e.devtool!==void 0&&{devtool:e.devtool}};let d=null,k=!1,I=!1,m=!1;const V=path.join(__dirname,"../../main.devserver.js"),H=path.join(__dirname,"../../main.devserver.ts"),J=!fs.existsSync(V)&&fs.existsSync(H),x=J?H:V,oe=path.join(U,"main.js");function A(){d&&!d.killed&&(d.kill("SIGTERM"),d=null)}c(A,"H"),o(A,"cleanup");function N(){m=!0,A()}c(N,"k"),o(N,"stop");const te=["SIGINT","SIGTERM","SIGHUP","SIGQUIT"].map(t=>{const n=o(()=>N(),"handler");return process.on(t,n),{sig:t,handler:n}});process.on("exit",N);function E(){if(m)return;if(!fs.existsSync(x)){console.error(`[serve] devserver script not found at ${x}`);return}const t=["--enable-source-maps"];J&&t.unshift("--require","@swc-node/register");const n=(0,node_child_process_1.fork)(x,{cwd:i,env:{...process.env,PORT:String(u),SERVICE_NAME:l,SERVE_PREFIX:O,LENSMCP_PROJECT:process.env.LENSMCP_PROJECT||r.projectName||l,LENSMCP_EVENT_FILE:process.env.LENSMCP_EVENT_FILE||path.join(i,".lensmcp","events.jsonl"),CHILD_COUNT:String(e.childCount||process.env.CHILD_COUNT||1),BUNDLE_PATH:oe,...T&&{GATEWAY_MIDDLEWARE:T},...D&&{GATEWAY_CONFIG:D},...e.gateway?.routes?.length&&{GATEWAY_ROUTES:JSON.stringify(e.gateway.routes)},...e.gateway?.https&&{GATEWAY_HTTPS:"1"},...e.gateway?.extraPorts?.length&&{GATEWAY_EXTRA_PORTS:JSON.stringify(e.gateway.extraPorts)},...e.internalPort&&{INTERNAL_PORT:String(e.internalPort)},...v&&{LENSMCP_DEBUG_PORT:String(v)},...p&&{LENSMCP_LOG_FILE:p},...e.workers?.length&&{WORKERS:JSON.stringify(e.workers.map(a=>a.name))},[dev_devtool_1.DEV_SOURCE_MAPS_ENV]:I?"1":"0"},stdio:["inherit","inherit","inherit","ipc"],execArgv:t,execPath:(0,process_name_1.namedExecPath)((0,process_name_1.processName)(process.env.LENSMCP_WS_KEY||path.basename(i),l,"dev"))});d=n,n.on("exit",(a,f)=>{d===n&&(d=null),!(f||a===0||m)&&(console.error(`[serve] devserver exited unexpectedly (code=${a}, signal=${f}) \u2014 respawning in 1.5s`),setTimeout(()=>{!m&&!d&&k&&E()},1500).unref?.())}),console.log(`[serve] Devserver started on port ${u} (service: ${l}${O?`, prefix: /${O}`:""})`),v?(console.warn(`[serve] DEBUGGER ENABLED for ${l}. Node's inspector has NO authentication \u2014 the ws:// UUID is the whole credential, and any account on this host that obtains it runs code as you. Do not enable this on a shared machine.`),z?console.warn(`[serve] LENSMCP_DEBUG_FIXED_PORTS=1 \u2014 pod inspectors bind PREDICTABLE ports 127.0.0.1:${v}+slot (workers ${v}+20+i), reachable by any account on this host. Attach WebStorm (Attach to Node.js/Chrome) to ${v}. Unset it to bind ephemeral ports instead.`):console.log(`[serve] Debug: pod inspectors bind an EPHEMERAL 127.0.0.1 port. Get the ws:// URLs with \`lensmcp debug ${l}\` (the owner-only control socket); a devserver run in your own terminal also prints them there. Set LENSMCP_DEBUG_FIXED_PORTS=1 if your IDE needs a fixed attach port.`)):console.log(`[serve] Debug on demand: \`lensmcp debug ${l}\` opens pod inspectors without a restart`),p&&console.log(`[serve] Logs teed to ${p} \u2014 tail it in WebStorm via Run/Debug configuration \u2192 Logs tab`)}c(E,"S"),o(E,"startDevServer");function re(){if(!d||d.killed){console.log("[serve] Devserver not running, restarting..."),E();return}fetch(`http://localhost:${u}/webpack/reload`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"}).then(()=>console.log("[serve] Notified devserver to reload")).catch(t=>{console.warn("[serve] Failed to notify devserver, restarting...",t.message),A(),E()})}c(re,"ne"),o(re,"_notifyDevServerReload");let y=null;const M=[];function h(t){if(y){const n=y;y=null,n(t)}else M.push(t)}c(h,"h"),o(h,"pushResult");function Y(){return M.length>0?Promise.resolve(M.shift()):new Promise(t=>{y=t})}c(Y,"ee"),o(Y,"waitForNext");let w=o(()=>{},"closeBuild");function F(){return!k||!d?(k=!0,E(),!0):!1}c(F,"J"),o(F,"builtOk");function b(){if(m)return;const{createDevWebpackConfig:t}=require("../../create-webpack-dev"),n=require("webpack"),a=t(W),{watch:f,...P}=a,s=n(P);I=(0,dev_devtool_1.bundleCarriesSourceMaps)(s.options.devtool);const j=o((L,C)=>{if(L){console.error("[serve] Webpack error:",L),h({success:!1});return}const ne=C.hasErrors();if(console.info(C.toString({colors:!0,chunks:!1,assets:!0})),ne){h({success:!1});return}F(),h({success:!0,baseUrl:`http://localhost:${u}`})},"onBuild");if(frozen()){console.log(`[serve] ${r.projectName}: frozen \u2014 compiling once, no file watcher`),s.run((L,C)=>{j(L,C),s.close(()=>{})});return}const G=s.watch(a.watchOptions||{},j);w=o(()=>G.close(()=>{}),"closeBuild")}c(b,"A"),o(b,"compileOnItsOwn");function X(t){const n=t.accepted.projects.filter(s=>s!==r.projectName);console.log(`[serve] ${r.projectName}: compiled by the workspace's shared compilation ${t.accepted.group.slice(0,8)}${n.length?` with ${n.join(", ")}`:""} \u2014 no webpack in this process`);const a=new devserver_notifier_1.DevserverNotifier(u,e.gateway?.https===!0,(0,devserver_notifier_1.devserverControlSock)(l)),f=(0,attach_1.createAttachedTypecheck)({appName:r.projectName,tsConfig:q,workspaceRoot:i,memoryLimit:e.memoryLimit||8192});let P;w=o(()=>{t.close(),f.dispose()},"closeBuild"),t.listen(s=>{if(!m)switch(s.type){case"watch-run":a.watchRun(s.paths);return;case"watch-closed":a.degrade("watch closed");return;case"failed":console.error(`[serve] the shared compiler failed: ${s.message}`),a.degrade(`compiler failed: ${s.message}`),h({success:!1});return;case"refused":console.log(`[serve] ${r.projectName}: compiling on its own from now on \u2014 ${s.reason}`),w(),b();return;case"built":{if(console.info(s.text),I=s.sourceMaps,f.built(s),!s.ok){a.failed(),h({success:!1});return}const j=s.hash!==P;P=s.hash;const G=F();a.built(j&&!G),h({success:!0,baseUrl:`http://localhost:${u}`});return}default:return}})}if(c(X,"re"),o(X,"attachToSharedBuild"),(0,attach_1.sharedBuildOffered)()&&!frozen()){const t=await(0,attach_1.joinSharedBuild)({project:r.projectName,target:r.targetName??"serve-hmr",...r.configurationName?{configuration:r.configurationName}:{},options:W,targetOptions:{...e},env:definedEnv(process.env)});t.kind==="attached"?X(t.session):(console.log(`[serve] ${r.projectName}: compiling on its own \u2014 ${t.reason}`),b())}else b();try{for(;;)yield await Y()}finally{N(),w(),te.forEach(({sig:t,handler:n})=>process.removeListener(t,n))}}c(serveExecutor,"serveExecutor"),o(serveExecutor,"serveExecutor"),exports.default=serveExecutor,module.exports=serveExecutor,module.exports.default=serveExecutor;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * How long a marker is a request. The gateway signals within milliseconds of writing it; a marker older than this
3
+ * is a leftover (a gateway that died between the write and the signal), and a leftover must not turn a later
4
+ * `gateway stop` into a fifteen-minute drain.
5
+ */
6
+ export declare const HANDOFF_MARKER_FRESH_MS = 60000;
7
+ /** `<pool dir>/retire-g<generation>` — named by generation, so the standby beside it is never addressed. */
8
+ export declare function handoffRetireMarker(poolDir: string, generation: number): string;
9
+ /** Gateway: ask generation `generation` to retire on the SIGTERM that follows. `false` when it could not be written. */
10
+ export declare function requestHandoffRetire(poolDir: string, generation: number, now?: number): boolean;
11
+ /** Either end: the request is spent (the tree is gone) or was never one (a tree that is just starting). */
12
+ export declare function clearHandoffRetire(poolDir: string, generation: number): void;
13
+ /** Devserver: was THIS generation asked to retire, just now? */
14
+ export declare function handoffRetireRequested(poolDir: string, generation: number, now?: number): boolean;
15
+ /**
16
+ * How long the gateway lets a retired tree run before it kills the group. The tree bounds itself — a pod drains for
17
+ * `LENSMCP_SWAP_DRAIN_MS` (15 min by default: a queue job holds a lock that outlives a killed worker by an hour),
18
+ * then closes — so this is the backstop behind that bound, 90 s later. `LENSMCP_POD_HANDOFF_DRAIN_MS` overrides it;
19
+ * `0` turns the graceful retire off (the old tree gets a plain SIGTERM, as before).
20
+ */
21
+ export declare function handoffDrainBoundMs(env?: NodeJS.ProcessEnv): number;
@@ -0,0 +1 @@
1
+ "use strict";var a=Object.defineProperty;var o=(e,r)=>a(e,"name",{value:r,configurable:!0});var i=Object.defineProperty,n=o((e,r)=>i(e,"name",{value:r,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.HANDOFF_MARKER_FRESH_MS=void 0,exports.handoffRetireMarker=handoffRetireMarker,exports.requestHandoffRetire=requestHandoffRetire,exports.clearHandoffRetire=clearHandoffRetire,exports.handoffRetireRequested=handoffRetireRequested,exports.handoffDrainBoundMs=handoffDrainBoundMs;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path"));exports.HANDOFF_MARKER_FRESH_MS=6e4;function handoffRetireMarker(e,r){return path.join(e,`retire-g${r}`)}o(handoffRetireMarker,"handoffRetireMarker"),n(handoffRetireMarker,"handoffRetireMarker");function requestHandoffRetire(e,r,t=Date.now()){try{return fs.writeFileSync(handoffRetireMarker(e,r),String(t),{mode:384}),!0}catch{return!1}}o(requestHandoffRetire,"requestHandoffRetire"),n(requestHandoffRetire,"requestHandoffRetire");function clearHandoffRetire(e,r){try{fs.unlinkSync(handoffRetireMarker(e,r))}catch{}}o(clearHandoffRetire,"clearHandoffRetire"),n(clearHandoffRetire,"clearHandoffRetire");function handoffRetireRequested(e,r,t=Date.now()){try{const f=Number(fs.readFileSync(handoffRetireMarker(e,r),"utf8").trim());return Number.isFinite(f)&&f>0&&t-f<exports.HANDOFF_MARKER_FRESH_MS}catch{return!1}}o(handoffRetireRequested,"handoffRetireRequested"),n(handoffRetireRequested,"handoffRetireRequested");function handoffDrainBoundMs(e=process.env){const r=Number(e.LENSMCP_POD_HANDOFF_DRAIN_MS);if(e.LENSMCP_POD_HANDOFF_DRAIN_MS!==void 0&&e.LENSMCP_POD_HANDOFF_DRAIN_MS!==""&&Number.isFinite(r)&&r>=0)return Math.floor(r);const t=Number(e.LENSMCP_SWAP_DRAIN_MS);return(e.LENSMCP_SWAP_DRAIN_MS!==void 0&&e.LENSMCP_SWAP_DRAIN_MS!==""&&Number.isFinite(t)&&t>=0?t:15*6e4)+9e4}o(handoffDrainBoundMs,"handoffDrainBoundMs"),n(handoffDrainBoundMs,"handoffDrainBoundMs");
package/main.devserver.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var Ve=Object.defineProperty;var f=(i,h)=>Ve(i,"name",{value:h,configurable:!0});var Ke=Object.defineProperty,n=f((i,h)=>Ke(i,"name",{value:h,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactInspectorUrls=exports.inspectorPortOf=exports.DEBUG_FIXED_PORTS_ENV=exports.mayDriveThePool=exports.isOwnerOnlyTransport=exports.gatewayManaged=void 0,exports.tell=tell,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor;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")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),inspector_secret_1=require("./inspector-secret");Object.defineProperty(exports,"inspectorPortOf",{enumerable:!0,get:n(function(){return inspector_secret_1.inspectorPortOf},"get")}),Object.defineProperty(exports,"redactInspectorUrls",{enumerable:!0,get:n(function(){return inspector_secret_1.redactInspectorUrls},"get")});const devserver_transport_1=require("./devserver-transport");Object.defineProperty(exports,"gatewayManaged",{enumerable:!0,get:n(function(){return devserver_transport_1.gatewayManaged},"get")}),Object.defineProperty(exports,"isOwnerOnlyTransport",{enumerable:!0,get:n(function(){return devserver_transport_1.isOwnerOnlyTransport},"get")}),Object.defineProperty(exports,"mayDriveThePool",{enumerable:!0,get:n(function(){return devserver_transport_1.mayDriveThePool},"get")});const 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"),bundle_parent_1=require("./bundle-parent"),checkout_cache_1=require("./checkout-cache"),dev_devtool_1=require("./dev-devtool"),pod_memory_1=require("./pod-memory"),pod_memory_events_1=require("./pod-memory-events"),heap_snapshot_1=require("./heap-snapshot"),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 i=process.env.GATEWAY_CONFIG;if(!i)return{};try{return JSON.parse(i)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const i=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof i=="function"?i:typeof i.default=="function"?i.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(i){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,i)}function applyGatewayMiddleware(i){if(gatewayMiddleware)try{gatewayMiddleware(i,GATEWAY_CONFIG)}catch(h){console.error("[gateway] Middleware error:",h)}}f(applyGatewayMiddleware,"applyGatewayMiddleware"),n(applyGatewayMiddleware,"applyGatewayMiddleware");const GATEWAY_ROUTES=(()=>{try{return JSON.parse(process.env.GATEWAY_ROUTES||"[]")}catch{return[]}})(),GATEWAY_HTTPS=process.env.GATEWAY_HTTPS==="1";function hostMatches(i,h){if(i.startsWith("*.")){const y=i.slice(1);return h.endsWith(y)||h===i.slice(2)}return h===i}f(hostMatches,"hostMatches"),n(hostMatches,"hostMatches");function matchGatewayRoute(i){if(GATEWAY_ROUTES.length===0)return;const h=(i.headers.host||"").split(":")[0],y=i.url||"/";for(const S of GATEWAY_ROUTES)if(!(S.host&&!hostMatches(S.host,h))&&!(S.prefix&&!y.startsWith(S.prefix)))return S}f(matchGatewayRoute,"matchGatewayRoute"),n(matchGatewayRoute,"matchGatewayRoute");const WORKER_NAMES=(()=>{try{return JSON.parse(process.env.WORKERS||"[]")}catch{return[]}})(),LENSMCP_WS_KEY=process.env.LENSMCP_WS_KEY||"",SOCK_NS=process.env.LENSMCP_SOCK_NS||LENSMCP_WS_KEY,SOCK_DIR=SOCK_NS?path.join(os.tmpdir(),SOCK_NS,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`),SOCKET_ONLY=process.env.LENSMCP_SOCKET_ONLY==="1",POD_GENERATION=(()=>{const i=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(i)&&i>1?Math.floor(i):1})();function childSockPath(i){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${i}-g${POD_GENERATION}.sock`:`child-${i}.sock`)}f(childSockPath,"childSockPath"),n(childSockPath,"childSockPath");function sockOwnerPath(i){return`${i}.owner`}f(sockOwnerPath,"sockOwnerPath"),n(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(i){return`${i}.retiring`}f(sockRetiringPath,"sockRetiringPath"),n(sockRetiringPath,"sockRetiringPath");function tell(i,h){if(!i.connected||typeof i.send!="function")return!1;try{return i.send(h,()=>{})}catch{return!1}}f(tell,"tell"),n(tell,"tell");function refusePoolCommand(i,h){console.warn(`[devserver] refused ${i.method} ${i.url} on the public port \u2014 under a gateway the pool is driven on the control socket only.`),h.writeHead(404,{"content-type":"application/json"}),h.end(JSON.stringify({error:"not found"}))}f(refusePoolCommand,"refusePoolCommand"),n(refusePoolCommand,"refusePoolCommand"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(i=process.env){const h=(i[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return h==="1"||h==="true"||h==="yes"||h==="on"}f(usesFixedInspectorPorts,"usesFixedInspectorPorts"),n(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(i,h,y=process.env){if(!usesFixedInspectorPorts(y))return 0;const S=Math.trunc(i)+Math.trunc(h);return Number.isInteger(S)&&S>1024&&S<=65535?S:0}f(inspectorPortFor,"inspectorPortFor"),n(inspectorPortFor,"inspectorPortFor");function ensureOwnerOnlyDir(i){try{fs.mkdirSync(i,{recursive:!0,mode:448})}catch{}try{const h=fs.lstatSync(i);h.isDirectory()&&(h.mode&63)!==0&&fs.chmodSync(i,448)}catch{}}f(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),n(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(i,h=process.pid){ensureOwnerOnlyDir(path.dirname(i));try{const y=sockOwnerPath(i);try{fs.unlinkSync(y)}catch{}fs.writeFileSync(y,String(h),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(i)}catch{}try{fs.unlinkSync(sockRetiringPath(i))}catch{}}f(claimSock,"claimSock"),n(claimSock,"claimSock");function sockOwnedByAnother(i,h=process.pid){try{const y=fs.readFileSync(sockOwnerPath(i),"utf8").trim();return!!y&&y!==String(h)}catch{return!1}}f(sockOwnedByAnother,"sockOwnedByAnother"),n(sockOwnedByAnother,"sockOwnedByAnother");function cleanupSock(i,h=process.pid){if(i){try{const y=fs.readFileSync(sockOwnerPath(i),"utf8").trim();if(y&&y!==String(h))return}catch{}try{fs.unlinkSync(i)}catch{}try{fs.unlinkSync(sockOwnerPath(i))}catch{}try{fs.unlinkSync(sockRetiringPath(i))}catch{}}}f(cleanupSock,"cleanupSock"),n(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const i=process.env.LENSMCP_SERVICE_KEY;if(!i)return;const{createHash:h}=require("node:crypto");return h("sha256").update(i).digest("hex").slice(0,8)}f(serviceKeyFingerprint,"serviceKeyFingerprint"),n(serviceKeyFingerprint,"serviceKeyFingerprint");function rootModuleOf(i){let h;for(const y of i.getModules?.()?.values()??[])typeof y?.metatype!="function"||typeof y.distance!="number"||!Number.isFinite(y.distance)||(!h||y.distance<h.distance)&&(h=y);return h?.metatype}f(rootModuleOf,"rootModuleOf"),n(rootModuleOf,"rootModuleOf");let lastImportedModule;function takeImportedModule(){const i=lastImportedModule;return lastImportedModule=void 0,i}f(takeImportedModule,"takeImportedModule"),n(takeImportedModule,"takeImportedModule");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{}eval(`(() => {
1
+ "use strict";var tt=Object.defineProperty;var f=(a,_)=>tt(a,"name",{value:_,configurable:!0});var Ze=Object.defineProperty,i=f((a,_)=>Ze(a,"name",{value:_,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactInspectorUrls=exports.inspectorPortOf=exports.DEBUG_FIXED_PORTS_ENV=exports.mayDriveThePool=exports.isOwnerOnlyTransport=exports.gatewayManaged=void 0,exports.tell=tell,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor;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")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),inspector_secret_1=require("./inspector-secret");Object.defineProperty(exports,"inspectorPortOf",{enumerable:!0,get:i(function(){return inspector_secret_1.inspectorPortOf},"get")}),Object.defineProperty(exports,"redactInspectorUrls",{enumerable:!0,get:i(function(){return inspector_secret_1.redactInspectorUrls},"get")});const devserver_transport_1=require("./devserver-transport");Object.defineProperty(exports,"gatewayManaged",{enumerable:!0,get:i(function(){return devserver_transport_1.gatewayManaged},"get")}),Object.defineProperty(exports,"isOwnerOnlyTransport",{enumerable:!0,get:i(function(){return devserver_transport_1.isOwnerOnlyTransport},"get")}),Object.defineProperty(exports,"mayDriveThePool",{enumerable:!0,get:i(function(){return devserver_transport_1.mayDriveThePool},"get")});const 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"),handoff_retire_1=require("./handoff-retire"),service_build_events_1=require("./service-build-events"),stdio_guard_1=require("./stdio-guard"),bundle_parent_1=require("./bundle-parent"),checkout_cache_1=require("./checkout-cache"),dev_devtool_1=require("./dev-devtool"),pod_memory_1=require("./pod-memory"),pod_memory_events_1=require("./pod-memory-events"),heap_snapshot_1=require("./heap-snapshot"),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 a=process.env.GATEWAY_CONFIG;if(!a)return{};try{return JSON.parse(a)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const a=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof a=="function"?a:typeof a.default=="function"?a.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(a){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,a)}function applyGatewayMiddleware(a){if(gatewayMiddleware)try{gatewayMiddleware(a,GATEWAY_CONFIG)}catch(_){console.error("[gateway] Middleware error:",_)}}f(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(a,_){if(a.startsWith("*.")){const g=a.slice(1);return _.endsWith(g)||_===a.slice(2)}return _===a}f(hostMatches,"hostMatches"),i(hostMatches,"hostMatches");function matchGatewayRoute(a){if(GATEWAY_ROUTES.length===0)return;const _=(a.headers.host||"").split(":")[0],g=a.url||"/";for(const S of GATEWAY_ROUTES)if(!(S.host&&!hostMatches(S.host,_))&&!(S.prefix&&!g.startsWith(S.prefix)))return S}f(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_NS=process.env.LENSMCP_SOCK_NS||LENSMCP_WS_KEY,SOCK_DIR=SOCK_NS?path.join(os.tmpdir(),SOCK_NS,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`),SOCKET_ONLY=process.env.LENSMCP_SOCKET_ONLY==="1",POD_GENERATION=(()=>{const a=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(a)&&a>1?Math.floor(a):1})();function childSockPath(a){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${a}-g${POD_GENERATION}.sock`:`child-${a}.sock`)}f(childSockPath,"childSockPath"),i(childSockPath,"childSockPath");function sockOwnerPath(a){return`${a}.owner`}f(sockOwnerPath,"sockOwnerPath"),i(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(a){return`${a}.retiring`}f(sockRetiringPath,"sockRetiringPath"),i(sockRetiringPath,"sockRetiringPath");function tell(a,_){if(!a.connected||typeof a.send!="function")return!1;try{return a.send(_,()=>{})}catch{return!1}}f(tell,"tell"),i(tell,"tell");function refusePoolCommand(a,_){console.warn(`[devserver] refused ${a.method} ${a.url} on the public port \u2014 under a gateway the pool is driven on the control socket only.`),_.writeHead(404,{"content-type":"application/json"}),_.end(JSON.stringify({error:"not found"}))}f(refusePoolCommand,"refusePoolCommand"),i(refusePoolCommand,"refusePoolCommand"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(a=process.env){const _=(a[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return _==="1"||_==="true"||_==="yes"||_==="on"}f(usesFixedInspectorPorts,"usesFixedInspectorPorts"),i(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(a,_,g=process.env){if(!usesFixedInspectorPorts(g))return 0;const S=Math.trunc(a)+Math.trunc(_);return Number.isInteger(S)&&S>1024&&S<=65535?S:0}f(inspectorPortFor,"inspectorPortFor"),i(inspectorPortFor,"inspectorPortFor");function ensureOwnerOnlyDir(a){try{fs.mkdirSync(a,{recursive:!0,mode:448})}catch{}try{const _=fs.lstatSync(a);_.isDirectory()&&(_.mode&63)!==0&&fs.chmodSync(a,448)}catch{}}f(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),i(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(a,_=process.pid){ensureOwnerOnlyDir(path.dirname(a));try{const g=sockOwnerPath(a);try{fs.unlinkSync(g)}catch{}fs.writeFileSync(g,String(_),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(a)}catch{}try{fs.unlinkSync(sockRetiringPath(a))}catch{}}f(claimSock,"claimSock"),i(claimSock,"claimSock");function sockOwnedByAnother(a,_=process.pid){try{const g=fs.readFileSync(sockOwnerPath(a),"utf8").trim();return!!g&&g!==String(_)}catch{return!1}}f(sockOwnedByAnother,"sockOwnedByAnother"),i(sockOwnedByAnother,"sockOwnedByAnother");function cleanupSock(a,_=process.pid){if(a){try{const g=fs.readFileSync(sockOwnerPath(a),"utf8").trim();if(g&&g!==String(_))return}catch{}try{fs.unlinkSync(a)}catch{}try{fs.unlinkSync(sockOwnerPath(a))}catch{}try{fs.unlinkSync(sockRetiringPath(a))}catch{}}}f(cleanupSock,"cleanupSock"),i(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const a=process.env.LENSMCP_SERVICE_KEY;if(!a)return;const{createHash:_}=require("node:crypto");return _("sha256").update(a).digest("hex").slice(0,8)}f(serviceKeyFingerprint,"serviceKeyFingerprint"),i(serviceKeyFingerprint,"serviceKeyFingerprint");function rootModuleOf(a){let _;for(const g of a.getModules?.()?.values()??[])typeof g?.metatype!="function"||typeof g.distance!="number"||!Number.isFinite(g.distance)||(!_||g.distance<_.distance)&&(_=g);return _?.metatype}f(rootModuleOf,"rootModuleOf"),i(rootModuleOf,"rootModuleOf");let lastImportedModule;function takeImportedModule(){const a=lastImportedModule;return lastImportedModule=void 0,a}f(takeImportedModule,"takeImportedModule"),i(takeImportedModule,"takeImportedModule");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{}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,15 +9,15 @@
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
- })()`),(0,bundle_parent_1.routeBundleRequires)(requested,resolved);const exports=eval(`require(${JSON.stringify(resolved)})`);try{const mod=eval("require.cache")[resolved];lastImportedModule=mod&&typeof mod=="object"?mod:void 0}catch{}return exports}f(importFresh,"importFresh"),n(importFresh,"importFresh");function debounce(i,h){let y=null;return(...S)=>{y&&clearTimeout(y),y=setTimeout(()=>i(...S),h)}}if(f(debounce,"debounce"),n(debounce,"debounce"),process.env.APP_RUNNER==="1"){(0,stdio_guard_1.guardBrokenStdio)();const i=n(y=>{if(!inspector.url())try{inspector.open(y,"127.0.0.1",!1)}catch(G){console.warn(`[child] inspector.open(${y}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,G.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const S=inspector.url();S&&tell(process,{type:"inspector-url",url:S})},"openInspector"),h=process.env.CHILD_DEBUG_PORT;if(h!==void 0&&h!==""){const y=Number(h);Number.isInteger(y)&&(y===0||y>1024)&&i(y)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let y=null,S=!1,G,J=!1;const Ce=(0,hot_swap_registries_1.registryPruningEnabled)(),te=n((t,s)=>{const p=(0,hot_swap_registries_1.describePrune)(t,s);p&&console.log(p)},"logPrune");let F={app:null,handler:n((t,s)=>{s.statusCode=503,s.end("starting")},"handler"),inflight:0};const re=(()=>{const t=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(t)&&t>=0?t:15*6e4})(),R=n(t=>{const s=globalThis.__lensmcpActiveWork;if(typeof s!="function")return{count:0};try{const p=s(t);return{count:Number(p?.count??0),...p?.oldestStartedAt?{oldestStartedAt:p.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),_e=n(t=>{const s=globalThis.__lensmcpStopWorkIntake;if(typeof s=="function")try{return s(t)}catch{return}},"stopWorkIntake"),oe=n(t=>{const s=globalThis.__lensmcpWorkIntakeReport;if(!(t===void 0||typeof s!="function"))try{return s(t)??void 0}catch{return}},"workIntakeReport"),be=(()=>{const t=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(t)&&t>=1?Math.floor(t):1})(),q=new Set,N=(0,swap_recycle_1.swapRecyclePolicy)();let L,j=0;const Ne=(()=>{const t=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(t)&&t>=0?t:3500})();let ne=!1,X=!1;const xe=(0,pod_memory_1.podMemoryPolicy)();let Ae=0;const ge=new Set;let u=!1;const P=n(()=>{if(!u)return;let t=0;for(const s of ge)s.deref()===void 0?ge.delete(s):t+=1;return t},"liveContainers"),m=(0,pod_memory_1.createPodMemoryMonitor)({policy:xe,state:n(()=>({gen:F.gen??0,swaps:j,...L===void 0?{}:{baseline:L},busy:S||J||A.size>0,lastBusyAt:T}),"state"),send:n(t=>{tell(process,t)},"send"),containers:P,counter:(0,pod_memory_1.createLiveObjectCounter)(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH)),onRetained:n(()=>ie(),"onRetained")}),W=n(()=>((0,pod_memory_1.fullGc)()&&m.tracker.noteGc(),process.memoryUsage().heapUsed),"liveHeap"),$=(()=>{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}`,p=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:n(_=>p.has(_)||_.startsWith(t)&&!_.slice(t.length).includes(s)&&!_.slice(t.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let se=!1;function x(){if(!(!$||se))try{const t=$();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),se=!0}}f(x,"D"),n(x,"releaseLibraryErrorFrames");const A=new Map;let T=Date.now();const D=n(()=>N.settleMs<=0||!S&&!J&&A.size===0&&Date.now()-T>=N.settleMs,"settled");let Y;function he(t){if(!(!N.enabled||ne||X)){if(N.settleMs<=0){z(t);return}Y&&clearTimeout(Y),Y=setTimeout(()=>{Y=void 0,D()?z(t):he(t)},N.settleMs),Y.unref?.()}}f(he,"_e"),n(he,"recycleCheckWhenSettled");const fe=n(()=>{if(N.stuckCloseMs<=0)return 0;const t=Date.now();let s=0;for(const p of A.values())t-p>=N.stuckCloseMs&&(s+=1);return s},"stuckCloses");function z(t){if(!N.enabled||ne||X||!process.send)return;const s=v8.getHeapStatistics().heap_size_limit,p=n(g=>{const E=m.oldestRetained();return{swaps:j,usedBytes:g,limitBytes:s,stuckCloses:fe(),settled:D(),...L===void 0?{}:{baselineBytes:L},...E?{retained:{generation:E.gen,ageMs:Date.now()-E.closedAt,gcs:E.gcs}}:{}}},"state");if(!(0,swap_recycle_1.recycleReason)(N,p(process.memoryUsage().heapUsed)))return;const _=(0,swap_recycle_1.recycleReason)(N,p(W()));_&&(ne=!0,console.log(`[child] asking for a fresh pod ${t} \u2014 ${_}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB / _RETAINED_MS, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),tell(process,{type:"recycle-request",reason:_}))}f(z,"ee"),n(z,"considerRecycle");let K;function ie(){if(!N.enabled||N.retainedMs<=0||ne||X)return;const t=m.oldestRetained();if(!t)return;const s=t.closedAt+N.retainedMs-Date.now();if(s<=0){z("with a closed generation still in memory");return}K||(K=setTimeout(()=>{K=void 0,m.measure("retention-check").catch(()=>{})},s),K.unref?.())}f(ie,"ae"),n(ie,"retainedRecycleCheck");function ae(t,s){const p={};s&&(p.module=s),t&&(p.app=t);try{const _=t?.app;if(_&&typeof _=="object"&&(p.nestApp=_,_.container&&typeof _.container=="object")){p.container=_.container;const g=rootModuleOf(_.container);g&&(p.rootModule=g)}}catch{}return p}f(ae,"ye"),n(ae,"generationTargets");async function ce(t,s){if(t.closed||(t.closed=!0,q.delete(t),s&&console.error(`[child] ${s}`),!t.app))return;A.set(t,Date.now());const p=N.enabled&&N.stuckCloseMs>0?setTimeout(()=>{A.has(t)&&(console.error(`[child] a previous app has been closing for ${Math.round(N.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.`),z("with a previous app stuck in close()"))},N.stuckCloseMs):void 0;p?.unref?.();const _=t.app;try{await t.app.close()}catch(g){console.error("[child] error closing a previous app:",g)}finally{A.delete(t),T=Date.now(),p&&clearTimeout(p),t.app=null,t.handler=(g,E)=>{E.statusCode=503,E.end("closed")},t.gen!==void 0&&m.closed(t.gen,j,ae(_,t.module)),t.module=void 0}if(t.superseded){try{te((0,hot_swap_registries_1.pruneRegistries)(t.superseded,"serve"),"after closing the previous app")}catch(g){console.error("[child] could not release the previous app's class registrations:",g)}t.superseded=void 0}he("after closing the previous app")}f(ce,"ge"),n(ce,"closeGeneration");async function Q(t){for(;q.size>=be;){const E=q.values().next().value;if(!E)break;await ce(E,`closing a still-draining generation early \u2014 ${q.size} were pending and the cap is ${be} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}q.add(t);const s=Date.now(),p=n(()=>({inflight:t.inflight,work:R(t.workGen).count}),"busy");for(;re>0&&Date.now()-s<re;){if(t.closed)return;const E=p();if(E.inflight===0&&E.work===0)break;await new Promise(H=>{setTimeout(H,250).unref?.()})}if(t.closed)return;const _=p(),g=oe(t.workGen);_.inflight>0||_.work>0?console.error(`[child] swap drain expired after ${Date.now()-s}ms \u2014 closing the previous app with ${_.inflight} in-flight request(s) and ${_.work} background job(s) still running. `+(g?`Previous app still processing ${g.running} queue job(s); ${g.takenAfterStop} were taken after the swap. `:"")+`A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${re}ms) if this is legitimate work.`):(Date.now()-s>250||g?.runningAtStop||g?.takenAfterStop)&&console.log(`[child] previous app drained in ${Date.now()-s}ms \u2014 closing it`+(g?` (it finished ${g.finishedAfterStop} queue job(s) after the swap; ${g.takenAfterStop} were taken after the swap)`:"")),await ce(t)}f(Q,"ce"),n(Q,"drainThenClose");async function Z(){if(S)return J=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),y;S=!0,T=Date.now(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(b,U)=>{console.error(`[child] uncaughtException (${U}):`,b)}),process.on("unhandledRejection",b=>{const U=b instanceof Error?b.stack||b.message:String(b);U.includes("FronteggContextScope")||U.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",b)});let t;if(Ce)try{t=(0,hot_swap_registries_1.captureRegistries)()}catch{t=void 0}const s=global.__lensmcpBeginWorkGeneration,p=typeof s=="function"?s():void 0;try{try{await importFresh(BUNDLE_PATH)}catch(b){if(!(b instanceof SyntaxError))throw b;console.warn(`[child] bundle was unreadable mid-write (${b.message}) \u2014 retrying the swap once`),await new Promise(U=>setTimeout(U,400)),await importFresh(BUNDLE_PATH)}}catch(b){throw S=!1,T=Date.now(),b}if(typeof global.createChildApp!="function")throw S=!1,T=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 _;if(t)try{_=(0,hot_swap_registries_1.supersededSince)(t),te((0,hot_swap_registries_1.pruneRegistries)(_,"build"),"at the swap")}catch(b){console.error("[child] could not release superseded class registrations:",b)}let g;try{g=await global.createChildApp()}catch(b){throw S=!1,T=Date.now(),takeImportedModule(),b}const E=F;_?.size&&(E.app?E.superseded=_:te((0,hot_swap_registries_1.pruneRegistries)(_,"serve"),"at the swap")),y=g;const H=takeImportedModule();F={app:g,handler:g.handler,inflight:0,gen:++Ae,...H?{module:H}:{},...p===void 0?{}:{workGen:p}};const de=ae(g,void 0).container;de&&(ge.add(new WeakRef(de)),u=!0);const V=E.app&&p!==void 0?_e(p):void 0;return console.log("[child] Swapped to fresh app (previous draining in background)"+(V?.workers?`; its ${V.workers} queue worker(s) stopped taking new jobs, previous app still processing ${V.runningAtStop} job(s)`:"")),x(),E.app&&(j+=1,Q(E).catch(b=>console.error("[child] background close error:",b)),setTimeout(()=>z("30s after a hot swap"),3e4).unref?.(),m.swapped(E.gen,()=>E.closed===!0&&!A.has(E))),S=!1,T=Date.now(),J?(J=!1,Z()):g}f(Z,"le"),n(Z,"swapNow");const ye=debounce(async()=>{try{console.log("[child] /webpack/reload"),await Z()}catch(t){console.error("[child] reload failed:",t)}},250),le=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})),ye();return}if(t.method==="GET"&&t.url==="/__lensmcp/alive"){const _=t.headers["x-internal-token"],g=process.env.LENSMCP_SERVICE_KEY;if(g&&_!==g){s.writeHead(404,{"content-type":"text/plain"}),s.end("not found");return}const E=R();s.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),s.end(JSON.stringify({ok:!0,pid:process.pid,swapping:S,inflight:F.inflight,oldestInflightAt:F.oldestAt,activeWork:E.count,oldestWorkAt:E.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...G?{watcher:G}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const p=F;p.inflight+=1,p.oldestAt===void 0&&(p.oldestAt=Date.now()),s.on("close",()=>{p.inflight-=1,p.inflight===0&&(p.oldestAt=void 0),m.activity()}),p.handler(t,s)});le.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 Z(),L=(await m.booted()).live,console.log(`[child] Using bundle: ${t}`),claimSock(I),le.listen(I,()=>{console.log(`[child] Listening on ${I}`),tell(process,{type:"ready",socketPath:I})})}catch(t){console.error("[child] Startup error:",t),tell(process,{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);i(Number.isInteger(s)&&s>1024?s:0);return}if(t.type==="watcher"){const s=t.testimony;s&&typeof s=="object"&&(G=s);return}if(t.type==="heap-snapshot"){me(t);return}if(t.type==="recycle-declined"){ne=!1;return}if(t.type==="retire"){ee();return}if(t.type==="reload"){if(X)return;try{await Z(),tell(process,{type:"reloaded",socketPath:I})}catch(s){console.error("[child] reload error:",s),tell(process,{type:"reload-error",error:String(s?.stack||s)})}}}});let pe=!1;async function me(t){const s=n(p=>{tell(process,{type:"heap-snapshot-result",id:t.id,...p})},"reply");if(pe)return s({ok:!1,refused:"a snapshot of this pod is already being written"});pe=!0;try{const p=W(),_=m.tracker.views().map(de=>de.gen),g=(0,heap_snapshot_1.heapSnapshotPolicy)();t.maxHeapBytes&&(g.maxHeapBytes=t.maxHeapBytes);const E=(0,heap_snapshot_1.heapSnapshotRefusal)(g,{live:p,...t.available?{available:t.available}:{}});if(E)return s({ok:!1,refused:E,live:p});console.log(`[child] writing a heap snapshot (${Math.round(p/1048576)} MB live) \u2014 the pod pauses while V8 serialises it`);const H=await(0,heap_snapshot_1.writeHeapSnapshot)(t.file);s({ok:!0,file:t.file,bytes:H.bytes,ms:H.ms,live:p,gen:F.gen??0,closedAlive:_})}catch(p){s({ok:!1,error:p?.message??String(p)})}finally{pe=!1}}f(me,"$e"),n(me,"takeHeapSnapshot");async function ee(){if(X)return;X=!0,m.stop(),console.log("[child] retiring \u2014 a fresh pod has taken over; finishing in-flight work, then exiting");const t=_e(Number.POSITIVE_INFINITY);t?.workers&&console.log(`[child] its ${t.workers} queue worker(s) stopped taking new jobs, still processing ${t.runningAtStop} job(s)`);try{fs.writeFileSync(sockRetiringPath(I),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(p=>{setTimeout(p,Ne).unref?.()}),cleanupSock(I),le.close();const s=Date.now();for(;Date.now()-s<re&&(le.closeIdleConnections?.(),!(F.inflight===0&&R().count===0));)await new Promise(p=>{setTimeout(p,250).unref?.()});setTimeout(()=>process.exit(0),3e4).unref?.();try{await y?.close()}catch(p){console.error("[child] close error while retiring:",p)}process.exit(0)}f(ee,"re"),n(ee,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const we=n(async t=>{console.log(`[child ${t}] shutting down\u2026`);const s=y?.close().catch(_=>console.error("[child] close error:",_)),p=new Promise(_=>le.close(()=>_()));cleanupSock(I),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([s,p]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(t=>{process.on(t,()=>{inspector?.close?.(),we(t)})})})()}else{let i=f(function(u){L.push(u),L.length>N&&L.splice(0,L.length-N),be?.write((0,inspector_secret_1.redactInspectorUrls)(u.replace(q,"")));for(const P of j)try{const m=P.plain?u.replace(q,""):u;P.res.write(P.redact?(0,inspector_secret_1.redactInspectorUrls)(m):m)}catch{j.delete(P)}},"z");n(i,"emitLogLine");const h=12,y=process.env.LENSMCP_PROJECT||SERVICE_NAME||"service",S=(0,pod_memory_events_1.createPodMemoryPublisher)(process.env.LENSMCP_EVENT_FILE),G=n(u=>({project:y,service:SERVICE_NAME||y,pod:u.id,pid:u.proc.pid??0,podGeneration:POD_GENERATION}),"podIdentity"),J=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],Ce="\x1B[0m",te=Math.max(1,Number(process.env.CHILD_COUNT||1)),F=LENSMCP_WS_KEY||path.basename(process.cwd()),re=n(u=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(F,SERVICE_NAME||"service",u)),"execPathFor");let R=(()=>{const u=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(u)&&u>1024?u:0})();const _e=20;R&&(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/closed/codex-security-06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${R}+slot, workers ${R}+${_e}+i). Any account on this host can connect to 127.0.0.1:${R} 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 oe=process.env.LENSMCP_LOG_FILE||"",be=oe?(()=>{try{fs.mkdirSync(path.dirname(oe),{recursive:!0});const u=fs.createWriteStream(oe,{flags:"a",mode:384});try{fs.chmodSync(oe,384)}catch{}return u}catch(u){return console.warn(`[parent] cannot open log file ${oe}:`,u.message),null}})():null,q=/\x1b\[[0-9;]*m/g,N=2e3,L=[],j=new Set,Ne=(0,devserver_transport_1.gatewayManaged)();for(const u of["log","info","warn","error"]){const P=console[u].bind(console),m=u==="log"||u==="info"?process.stdout:process.stderr;console[u]=(...W)=>{P(...W.map($=>typeof $=="string"?(0,inspector_secret_1.forStream)(m,Ne,$):$)),i(W.map($=>typeof $=="string"?$:util.inspect($)).join(" ")+`
13
- `)}}const ne=n(u=>J[(u-1)%J.length],"colorFor"),X=n(u=>{if(te===1)return"";const P=`[child#${u}] `;return`${ne(u)}${P}${Ce}`},"tagFor"),xe=n(u=>u.startsWith("{")&&u.endsWith("}"),"isJsonLog"),Ae=n((u,P,m)=>{const W=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:n(x=>`
14
- `+String(x),"stack")}}),$=n((x,A)=>{const T=x==="stdout"?process.stdout:process.stderr;try{m?.(A)}catch{}const D=xe(A)?W(A):`${A}
15
- `;T.write((0,inspector_secret_1.forStream)(T,Ne,`${P}${D}`)),i(`${P}${D}`)},"write"),se=n((x,A)=>{if(!x)return;const T=readline.createInterface({input:x});T.on("line",D=>$(A,D)),T.on("close",()=>{})},"attach");se(u.stdout,"stdout"),se(u.stderr,"stderr")},"wireProcLogging"),ge=n(u=>Ae(u.proc,X(u.id),P=>u.tail.push(P)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const u=httpProxy.createProxyServer({});let P=Number(process.env.PORT)||0;P||(P=9090);const m=[],W=new Set;function $(){let e=0;for(;W.has(e);)e++;return W.add(e),e}f($,"I"),n($,"allocDebugSlot");let se=1,x=0,A=!1,T=te,D=[],Y=0;const he=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),fe=n(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),z=n(e=>e.booting&&!fe(e)&&Date.now()-e.spawnedAt<he,"stillBooting");function K(){return m.filter(e=>e.healthy&&typeof e.socketPath=="string")}f(K,"J"),n(K,"healthyChildren");let ie;function ae(e){if(!e||typeof e!="object")return;const o=e;if(o.schemaVersion===1){ie=o;for(const l of m)tell(l.proc,{type:"watcher",testimony:o})}}f(ae,"ye"),n(ae,"recordWatcherTestimony");function ce(e,o=256*1024){return new Promise(l=>{let d="",a=!1;e.on("data",r=>{a||(d+=r,d.length>o&&(a=!0))}),e.on("end",()=>{if(a||!d)return l(void 0);try{l(JSON.parse(d))}catch{l(void 0)}}),e.on("error",()=>l(void 0))})}f(ce,"ge"),n(ce,"readJsonBody");function Q(){const e=K();if(e.length===0)return null;const o=x%e.length;return x=(x+1)%e.length,e[o]}f(Q,"ce"),n(Q,"pickChild");function Z(e,o){const l=`Child#${e.id}`,d=SERVICE_NAME||y,a=Date.now();if(o.type==="pod-memory"){const r=o.report;if(!r||typeof r.live!="number")return;e.memory.latest=r,e.memory.history.push(r),e.memory.history.length>12&&e.memory.history.splice(0,e.memory.history.length-12),console.log(`[parent] ${(0,pod_memory_1.describePodMemory)(l,r)}`),S.publish((0,pod_memory_events_1.buildPodMemoryEvent)(G(e),r))}else if(o.type==="pod-memory-retained"){const r=o.retained;if(!r||typeof r.gen!="number")return;e.memory.retained.set(r.gen,{...r,at:a}),console.warn(`[parent] ${(0,pod_memory_1.describeRetained)(l,d,r)}`),S.publish((0,pod_memory_events_1.buildPodRetainedEvent)(G(e),r,a))}else{const r=o.released;if(!r||typeof r.gen!="number")return;e.memory.retained.delete(r.gen),console.log(`[parent] ${(0,pod_memory_1.describeReleased)(l,d,r)}`),S.publish((0,pod_memory_events_1.buildPodReleasedEvent)(G(e),r,a))}}f(Z,"le"),n(Z,"recordPodMemory");const ye=new Map;let le=0;async function I(){const e=se++,o=childSockPath(e);cleanupSock(o);const l=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&l.unshift("--require","@swc-node/register"),l.push(...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const d=R?$():-1,a=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:o,...d>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(R,d))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:l,execPath:re("pod")}),r={id:e,proc:a,healthy:!1,lastError:null,debugSlot:d,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:Y,memory:{history:[],retained:new Map}};return a.on("error",w=>console.error(`[parent] Child#${e}: ${w.message}`)),a.on("message",w=>{if(!w||typeof w!="object")return;const c=w;c.type==="ready"?(r.socketPath=String(c.socketPath),r.healthy=!0,r.booting=!1,console.log(`[parent] Child#${r.id} ready on socket ${r.socketPath}`),ie&&tell(a,{type:"watcher",testimony:ie}),Y!==r.bootReloadSeq&&tell(a,{type:"reload"})):c.type==="recycle-request"?_(r,String(c.reason||"it has outgrown its bound")):c.type==="inspector-url"?(r.inspectorUrl=String(c.url),console.log(`[parent] Child#${r.id} Debugger listening on ${c.url}`)):c.type==="reloaded"?(r.socketPath=String(c.socketPath),r.healthy=!0,console.log(`[parent] Child#${r.id} hot-swapped on socket ${r.socketPath}`)):c.type==="reload-error"?(r.lastError=String(c.error||"unknown reload error"),console.error(`[parent] Child#${r.id} reload error: ${r.lastError}`),r.healthy=!1):c.type==="boot-error"?(r.lastError=String(c.error||"unknown boot error"),console.error(`[parent] Child#${r.id} boot error: ${r.lastError}`),r.healthy=!1,r.booting=!1):c.type==="pod-memory"||c.type==="pod-memory-retained"||c.type==="pod-memory-released"?Z(r,c):c.type==="heap-snapshot-result"&&ye.get(Number(c.id))?.(c)}),a.on("exit",(w,c)=>{r.booting=!1;for(const O of r.memory.retained.values()){const M={gen:O.gen,closedAt:O.closedAt,ageMs:Date.now()-O.closedAt,reason:"pod-exited"};console.log(`[parent] ${(0,pod_memory_1.describeReleased)(`Child#${r.id}`,SERVICE_NAME||y,M)}`),S.publish((0,pod_memory_events_1.buildPodReleasedEvent)(G(r),M,Date.now()))}r.memory.retained.clear(),r.memory.latest&&S.publish((0,pod_memory_events_1.buildPodExitedEvent)(G(r),{code:w,signal:c},Date.now())),r.debugSlot>=0&&(W.delete(r.debugSlot),r.debugSlot=-1);const v=c==="SIGINT"||c==="SIGTERM"||w===0;if(v?console.log(`[parent] Child#${r.id} exited cleanly (code=${w}, signal=${c??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${r.id} crashed (code=${w}, signal=${c??"none"}, pid=${r.proc.pid}, ${r.healthy?"was serving":"was not ready"})`,w,c,r.tail.snapshot())),r.lastError||(r.lastError=`Child exited abnormally (code=${w}, signal=${c??"none"})`)),r.healthy=!1,cleanupSock(r.socketPath,r.proc.pid),r.socketPath=void 0,!v&&!A){const O=m.indexOf(r);O>=0&&m.splice(O,1);const M=Date.now();if(D=D.filter(B=>M-B<3e4),D.length>=5){console.error(`[parent] crash-loop detected (${D.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}D.push(M),setTimeout(()=>{const B=m.filter(ve=>ve.proc.killed===!1).length;!A&&B<T&&(console.log(`[parent] respawning a crashed pod (${B}/${T} live)`),I().catch(()=>{}))},1500).unref?.()}}),m.push(r),ge(r),r}f(I,"O"),n(I,"spawnChild");async function pe(e){T=Math.max(T,e);const o=m.filter(d=>d.proc.killed===!1),l=e-o.length;for(let d=0;d<l;d++)await I()}f(pe,"pe"),n(pe,"ensurePoolSize");async function me(){const e=m.filter(o=>(!o.healthy||typeof o.socketPath!="string")&&!z(o));await Promise.all(e.map(async o=>{try{o.proc.kill("SIGTERM")}catch{}cleanupSock(o.socketPath,o.proc.pid),o.socketPath=void 0;const l=m.indexOf(o);l>=0&&m.splice(l,1),await I()}))}f(me,"$e"),n(me,"respawnUnhealthy");const ee=new Set;let we=!1,t=0;const s=3e4,p=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function _(e,o){const l=n(a=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${o}) \u2014 not now: ${a}`),tell(e.proc,{type:"recycle-declined"})},"decline");if(A||!m.includes(e))return;if(we)return l("another pod is already being replaced");const d=Date.now()-t;if(d<s)return l(`a pod was replaced ${Math.round(d/1e3)}s ago`);we=!0,console.log(`[parent] Child#${e.id} asked for a fresh pod \u2014 ${o}. Starting one; Child#${e.id} keeps serving until it is ready.`);try{const a=await I(),r=Date.now()+he;for(;!a.healthy&&!a.lastError&&!fe(a)&&Date.now()<r;)await new Promise(v=>{setTimeout(v,100).unref?.()});if(A)return;if(!a.healthy){const v=m.indexOf(a);v>=0&&m.splice(v,1);try{a.proc.kill("SIGTERM")}catch{}return l(`its successor Child#${a.id} did not come up (${a.lastError??"no answer in time"})`)}const w=m.indexOf(e);w>=0&&m.splice(w,1),e.healthy=!1,ee.add(e);const c=setTimeout(()=>{if(!fe(e)){console.error(`[parent] Child#${e.id} was still running ${p}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},p);if(c.unref?.(),e.proc.once("exit",()=>{clearTimeout(c),ee.delete(e)}),!tell(e.proc,{type:"retire"}))try{e.proc.kill("SIGTERM")}catch{}t=Date.now(),console.log(`[parent] Child#${a.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${a.proc.pid})`)}catch(a){l(`could not start a successor: ${a?.message??String(a)}`)}finally{we=!1}}f(_,"f"),n(_,"recyclePod");const g=[];function E(e){const o=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),l=path.join(o,`${e}.js`);if(!fs.existsSync(l))return console.error(`[parent] Worker "${e}" bundle not found: ${l}`),null;const d=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&d.unshift("--require","@swc-node/register");try{d.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const a=inspectorPortFor(R||9229,_e+Math.max(0,WORKER_NAMES.indexOf(e)));d.push(R?`--inspect=127.0.0.1:${a}`:`--inspect-port=127.0.0.1:${a}`,...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const r=(0,node_child_process_1.fork)(l,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:d,execPath:re(`w-${e}`)}),w={name:e,proc:r,debugPort:a};return g.push(w),r.on("error",c=>console.error(`[parent] Worker "${e}": ${c.message}`)),Ae(r,`\x1B[35m[worker:${e}] \x1B[0m`,c=>{const v=/Debugger listening on (wss?:\/\/\S+)/.exec(c);v&&(w.inspectorUrl=v[1])}),r.on("exit",(c,v)=>{v==="SIGINT"||v==="SIGTERM"||c===0||console.error(`[parent] Worker "${e}" crashed (code=${c}, signal=${v??"none"})`);const O=g.indexOf(w);O>=0&&g.splice(O,1)}),console.log(`[parent] Worker "${e}" started (pid=${r.pid})`),w}f(E,"E"),n(E,"spawnWorkerProcess");const H=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),de=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),V=new Set,b=n(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function U(e,o){if(V.delete(e),!b(e)){console.error(`[parent] Worker "${e.name}" (pid=${e.proc.pid}) ${o} \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{}}}f(U,"x"),n(U,"killDrainingWorker");function Ge(e){try{e.proc.kill("SIGTERM")}catch{}if(b(e))return;const o=[...V].filter(d=>d.name===e.name);for(;o.length>=de;)U(o.shift(),`was still running when a later reload replaced its successor (cap ${de})`);V.add(e);const l=setTimeout(()=>U(e,`was still running ${H}ms after SIGTERM`),H);l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),V.delete(e)})}f(Ge,"Ue"),n(Ge,"retireWorker");function Le(){for(const e of[...g])Ge(e);g.length=0;for(const e of WORKER_NAMES)E(e)}f(Le,"He"),n(Le,"restartWorkers");const Be=debounce(async()=>{Y+=1;const e=K();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const o of e)tell(o.proc,{type:"reload"})}await me(),WORKER_NAMES.length>0&&Le()},200),Ee=n(async(e,o)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){if(!(0,devserver_transport_1.mayDriveThePool)(e))return refusePoolCommand(e,o);o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0})),ce(e).then(a=>ae(a?.watcher)),Be();return}if(e.method==="POST"&&e.url==="/webpack/watching"){if(!(0,devserver_transport_1.mayDriveThePool)(e))return refusePoolCommand(e,o);const a=await ce(e);ae(a),o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,relayed:ie!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){if(!(0,devserver_transport_1.mayDriveThePool)(e))return refusePoolCommand(e,o);let a="";e.on("data",r=>{a+=r}),e.on("end",async()=>{let r=0;try{r=Number(JSON.parse(a||"{}").pods)||0}catch{}r=Math.max(1,Math.min(r,16)),await pe(r),o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,pods:r}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const a=new URL(e.url,"http://localhost").searchParams,r=Math.min(Math.max(Number(a.get("tail")??"100")||0,0),N),w=a.get("follow")!=="0",c=a.get("plain")==="1",v=!(0,devserver_transport_1.isOwnerOnlyTransport)(e);o.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const O=r===0?"":L.slice(-r).join(""),M=c?O.replace(q,""):O;if(o.write(v?(0,inspector_secret_1.redactInspectorUrls)(M):M),!w){o.end();return}const B={res:o,plain:c,redact:v};j.add(B),e.on("close",()=>j.delete(B));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!(0,devserver_transport_1.isOwnerOnlyTransport)(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(a)&&a>1024&&(R=a),R||(R=9229);const r=m.filter(c=>c.proc.exitCode===null&&c.proc.signalCode===null);for(const c of r)c.debugSlot<0&&(c.debugSlot=$()),tell(c.proc,{type:"debug-open",port:inspectorPortFor(R,c.debugSlot)});for(const c of g)try{c.proc.kill("SIGUSR1")}catch{}const w=Date.now()+2e3;for(;Date.now()<w&&r.some(c=>!c.inspectorUrl);)await new Promise(c=>setTimeout(c,50));o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,basePort:R,fixedPorts:usesFixedInspectorPorts(),pods:r.map(c=>({id:c.id,inspectorUrl:c.inspectorUrl??null})),workers:g.map(c=>({name:c.name,port:c.debugPort||(0,inspector_secret_1.inspectorPortOf)(c.inspectorUrl),inspectorUrl:c.inspectorUrl??null}))},null,2)+`
16
- `);return}if(e.method==="GET"&&e.url==="/webpack/memory"){if(!(0,devserver_transport_1.isOwnerOnlyTransport)(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),r=[...m,...ee].map(w=>({id:w.id,pid:w.proc.pid??0,podGeneration:POD_GENERATION,healthy:w.healthy,retiring:ee.has(w),latest:w.memory.latest??null,history:w.memory.history,retained:[...w.memory.retained.values()]}));o.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),o.end(JSON.stringify({ok:!0,service:SERVICE_NAME,project:y,bundle:{path:a},pods:r},null,2)+`
17
- `);return}if(e.method==="POST"&&e.url?.startsWith("/webpack/heap")){if(!(0,devserver_transport_1.isOwnerOnlyTransport)(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=n((C,ke)=>{o.writeHead(C,{"content-type":"application/json"}),o.end(JSON.stringify(ke,null,2)+`
18
- `)},"reply"),r=new URL(e.url,"http://localhost").searchParams,w=Number(r.get("pod")||""),c=r.has("maxMb")?Number(r.get("maxMb")):void 0;if(c!==void 0&&!(Number.isFinite(c)&&c>0)){a(400,{ok:!1,error:"maxMb takes a positive number of MB"});return}const v=Number.isInteger(w)&&w>0?m.find(C=>C.id===w):K()[0];if(!v||fe(v)){a(404,{ok:!1,error:w?`no pod #${w} \u2014 running: ${m.map(C=>`#${C.id}`).join(", ")||"none"}`:"no pod is serving"});return}if(ye.size>0){a(409,{ok:!1,refused:"a heap snapshot is already being written for this service"});return}const O=(0,heap_snapshot_1.heapSnapshotPolicy)(),M=(0,heap_snapshot_1.heapSnapshotDir)();let B;try{(0,heap_snapshot_1.ensureHeapDir)(M);const C=(0,heap_snapshot_1.pruneHeapSnapshots)(M,O);C.length&&console.log(`[parent] removed ${C.length} older heap snapshot(s) from ${M}`),B=path.join(M,(0,heap_snapshot_1.heapSnapshotFileName)(SERVICE_NAME||y,v.id,v.proc.pid??0,v.memory.latest?.gen??0,Date.now()))}catch(C){a(500,{ok:!1,error:`cannot prepare ${M}: ${C.message}`});return}const ve=++le,Me=(0,heap_snapshot_1.availableSystemMemory)();let Pe;const k=await new Promise(C=>{const ke=setTimeout(()=>C(void 0),6e5);ke.unref?.(),ye.set(ve,Ye=>{clearTimeout(ke),C(Ye)}),Pe=n(()=>{clearTimeout(ke),C(void 0)},"onExit"),v.proc.once("exit",Pe);const je={type:"heap-snapshot",id:ve,file:B,...Me?{available:Me}:{},...c!==void 0?{maxHeapBytes:Math.floor(c*1048576)}:{}};tell(v.proc,je)||Pe()}).finally(()=>{ye.delete(ve),Pe&&v.proc.off("exit",Pe)});if(!k){a(504,{ok:!1,pod:v.id,error:"the pod did not finish the snapshot (it exited, or took longer than 10 minutes)"});return}k.ok?console.log(`[parent] Child#${v.id} wrote a ${Math.round((k.bytes??0)/1048576)} MB heap snapshot in ${k.ms} ms \u2192 ${k.file}`):console.warn(`[parent] Child#${v.id} heap snapshot not taken: ${k.refused??k.error}`),a(k.ok?200:k.refused?409:500,{ok:k.ok,pod:v.id,pid:v.proc.pid??0,service:SERVICE_NAME,bundle:path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),...Me?{available:Me}:{},...k.file?{file:k.file,bytes:k.bytes,ms:k.ms}:{},...k.live!==void 0?{live:k.live}:{},...k.gen!==void 0?{gen:k.gen,closedAlive:k.closedAlive??[]}:{},...k.refused?{refused:k.refused}:{},...k.error?{error:k.error}:{}});return}const l=matchGatewayRoute(e);if(l?.prependPrefix&&!e.url?.startsWith(l.prependPrefix)&&(e.url=l.prependPrefix+(e.url??"/")),l?.target){applyGatewayMiddleware(e),u.web(e,o,{target:l.target,autoRewrite:typeof l.target=="string",changeOrigin:!0},a=>{console.error(`[parent] proxy error to ${l.target}:`,a),o.statusCode=502,o.setHeader("content-type","text/plain; charset=utf-8"),o.end(`Upstream error from ${l.target}.`)});return}if(SERVICE_PREFIX){if(!e.url?.startsWith(SERVICE_PREFIX)){o.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),o.end(`Not found. This service is mounted at ${SERVICE_PREFIX}/`);return}e.url=e.url.slice(SERVICE_PREFIX.length)||"/"}applyGatewayMiddleware(e);const d=Q();if(!d){o.statusCode=503,o.setHeader("content-type","text/plain; charset=utf-8");const a=m.map(r=>`#${r.id}: ${r.lastError??"no error recorded"}`).join(`
19
- `);o.end(`No healthy children available. POST /webpack/reload to recover.
12
+ })()`),(0,bundle_parent_1.routeBundleRequires)(requested,resolved);const exports=eval(`require(${JSON.stringify(resolved)})`);try{const mod=eval("require.cache")[resolved];lastImportedModule=mod&&typeof mod=="object"?mod:void 0}catch{}return exports}f(importFresh,"importFresh"),i(importFresh,"importFresh");function debounce(a,_){let g=null;return(...S)=>{g&&clearTimeout(g),g=setTimeout(()=>a(...S),_)}}if(f(debounce,"debounce"),i(debounce,"debounce"),process.env.APP_RUNNER==="1"){(0,stdio_guard_1.guardBrokenStdio)();const a=i(g=>{if(!inspector.url())try{inspector.open(g,"127.0.0.1",!1)}catch(F){console.warn(`[child] inspector.open(${g}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,F.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const S=inspector.url();S&&tell(process,{type:"inspector-url",url:S})},"openInspector"),_=process.env.CHILD_DEBUG_PORT;if(_!==void 0&&_!==""){const g=Number(_);Number.isInteger(g)&&(g===0||g>1024)&&a(g)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let g=null,S=!1,F,re=!1;const Le=(0,hot_swap_registries_1.registryPruningEnabled)(),ce=i((t,n)=>{const c=(0,hot_swap_registries_1.describePrune)(t,n);c&&console.log(c)},"logPrune");let H={app:null,handler:i((t,n)=>{n.statusCode=503,n.end("starting")},"handler"),inflight:0};const le=(()=>{const t=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(t)&&t>=0?t:15*6e4})(),T=i(t=>{const n=globalThis.__lensmcpActiveWork;if(typeof n!="function")return{count:0};try{const c=n(t);return{count:Number(c?.count??0),...c?.oldestStartedAt?{oldestStartedAt:c.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),ve=i(t=>{const n=globalThis.__lensmcpStopWorkIntake;if(typeof n=="function")try{return n(t)}catch{return}},"stopWorkIntake"),de=i(t=>{const n=globalThis.__lensmcpWorkIntakeReport;if(!(t===void 0||typeof n!="function"))try{return n(t)??void 0}catch{return}},"workIntakeReport"),ye=(()=>{const t=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(t)&&t>=1?Math.floor(t):1})(),x=new Set,Q=(()=>{const t=Number(process.env.LENSMCP_SWAP_EARLY_CLOSE_GRACE_MS);return process.env.LENSMCP_SWAP_EARLY_CLOSE_GRACE_MS!==void 0&&process.env.LENSMCP_SWAP_EARLY_CLOSE_GRACE_MS!==""&&Number.isFinite(t)&&t>=0?t:3e4})(),j=i(t=>{if(!t.abandoned?.size)return 0;const n=Date.now();for(const[c,p]of t.abandoned)(c.writableEnded||n-p>=Q)&&t.abandoned.delete(c);return t.abandoned.size},"abandonedRunning"),oe=i(t=>{let n=0;for(const c of t.open??[])c.headersSent||(n+=1);return{unanswered:n+j(t),streams:Math.max(0,t.inflight-n),work:T(t.workGen).count}},"owedBy"),ne=i((t,n,c=`${n}s`)=>`${t} ${t===1?n:c}`,"plural");async function Ie(){if(Q<=0)return;const t=Date.now();let n=!1;for(;!(x.size<ye);){const c=x.values().next().value;if(!c||c.closed)break;const p=oe(c);if(p.unanswered===0&&p.work===0)break;const m=(c.drainingSince??t)+Q-Date.now();if(m<=0)break;n||(n=!0,console.log(`[child] swap held \u2014 the app replaced ${Math.round((Date.now()-(c.drainingSince??t))/100)/10}s ago is still `+[p.unanswered?`answering ${ne(p.unanswered,"request")}`:"",p.work?`running ${ne(p.work,"background job")}`:""].filter(Boolean).join(" and ")+`, and this pod keeps at most ${ye} replaced app(s) alive (LENSMCP_SWAP_MAX_DRAINING). The new build is applied when it is done \u2014 in ${Math.ceil(m/1e3)}s at the latest (LENSMCP_SWAP_EARLY_CLOSE_GRACE_MS).`)),await new Promise(E=>{setTimeout(E,Math.min(100,m)).unref?.()})}n&&console.log(`[child] swap resumed after ${Date.now()-t}ms`)}f(Ie,"xe"),i(Ie,"holdForOwedWork");const R=(0,swap_recycle_1.swapRecyclePolicy)();let pe,se=0;const We=(()=>{const t=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(t)&&t>=0?t:3500})();let h=!1,b=!1;const v=(0,pod_memory_1.podMemoryPolicy)();let B=0;const O=new Set;let ue=!1;const U=i(()=>{if(!ue)return;let t=0;for(const n of O)n.deref()===void 0?O.delete(n):t+=1;return t},"liveContainers"),k=(0,pod_memory_1.createPodMemoryMonitor)({policy:v,state:i(()=>({gen:H.gen??0,swaps:se,...pe===void 0?{}:{baseline:pe},busy:S||re||I.size>0,lastBusyAt:Y}),"state"),send:i(t=>{tell(process,t)},"send"),containers:U,counter:(0,pod_memory_1.createLiveObjectCounter)(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH)),onRetained:i(()=>we(),"onRetained")}),G=i(()=>((0,pod_memory_1.fullGc)()&&k.tracker.noteGc(),process.memoryUsage().heapUsed),"liveHeap"),L=(()=>{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,n=`${path.sep}node_modules${path.sep}`,c=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:i(p=>c.has(p)||p.startsWith(t)&&!p.slice(t.length).includes(n)&&!p.slice(t.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let me=!1;function be(){if(!(!L||me))try{const t=L();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),me=!0}}f(be,"Ne"),i(be,"releaseLibraryErrorFrames");const I=new Map;let Y=Date.now();const he=i(()=>R.settleMs<=0||!S&&!re&&I.size===0&&Date.now()-Y>=R.settleMs,"settled");let K;function ie(t){if(!(!R.enabled||h||b)){if(R.settleMs<=0){Z(t);return}K&&clearTimeout(K),K=setTimeout(()=>{K=void 0,he()?Z(t):ie(t)},R.settleMs),K.unref?.()}}f(ie,"de"),i(ie,"recycleCheckWhenSettled");const Me=i(()=>{if(R.stuckCloseMs<=0)return 0;const t=Date.now();let n=0;for(const c of I.values())t-c>=R.stuckCloseMs&&(n+=1);return n},"stuckCloses");function Z(t){if(!R.enabled||h||b||!process.send)return;const n=v8.getHeapStatistics().heap_size_limit,c=i(m=>{const E=k.oldestRetained();return{swaps:se,usedBytes:m,limitBytes:n,stuckCloses:Me(),settled:he(),...pe===void 0?{}:{baselineBytes:pe},...E?{retained:{generation:E.gen,ageMs:Date.now()-E.closedAt,gcs:E.gcs}}:{}}},"state");if(!(0,swap_recycle_1.recycleReason)(R,c(process.memoryUsage().heapUsed)))return;const p=(0,swap_recycle_1.recycleReason)(R,c(G()));p&&(h=!0,console.log(`[child] asking for a fresh pod ${t} \u2014 ${p}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB / _RETAINED_MS, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),tell(process,{type:"recycle-request",reason:p}))}f(Z,"re"),i(Z,"considerRecycle");let fe;function we(){if(!R.enabled||R.retainedMs<=0||h||b)return;const t=k.oldestRetained();if(!t)return;const n=t.closedAt+R.retainedMs-Date.now();if(n<=0){Z("with a closed generation still in memory");return}fe||(fe=setTimeout(()=>{fe=void 0,k.measure("retention-check").catch(()=>{})},n),fe.unref?.())}f(we,"Oe"),i(we,"retainedRecycleCheck");function _e(t,n){const c={};n&&(c.module=n),t&&(c.app=t);try{const p=t?.app;if(p&&typeof p=="object"&&(c.nestApp=p,p.container&&typeof p.container=="object")){c.container=p.container;const m=rootModuleOf(p.container);m&&(c.rootModule=m)}}catch{}return c}f(_e,"we"),i(_e,"generationTargets");async function V(t,n){if(t.closed||(t.closed=!0,x.delete(t),n&&console.error(`[child] ${n}`),!t.app))return;I.set(t,Date.now());const c=R.enabled&&R.stuckCloseMs>0?setTimeout(()=>{I.has(t)&&(console.error(`[child] a previous app has been closing for ${Math.round(R.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.`),Z("with a previous app stuck in close()"))},R.stuckCloseMs):void 0;c?.unref?.();const p=t.app;try{await t.app.close()}catch(m){console.error("[child] error closing a previous app:",m)}finally{I.delete(t),Y=Date.now(),c&&clearTimeout(c),t.app=null,t.abandoned=void 0,t.handler=(m,E)=>{E.statusCode=503,E.end("closed")},t.gen!==void 0&&k.closed(t.gen,se,_e(p,t.module)),t.module=void 0}if(t.superseded){try{ce((0,hot_swap_registries_1.pruneRegistries)(t.superseded,"serve"),"after closing the previous app")}catch(m){console.error("[child] could not release the previous app's class registrations:",m)}t.superseded=void 0}ie("after closing the previous app")}f(V,"oe"),i(V,"closeGeneration");async function ke(t){const n=Date.now();t.drainingSince=n;const c=i(()=>({inflight:t.inflight+j(t),work:T(t.workGen).count}),"busy"),p=c();if(p.inflight>0||p.work>0){for(;x.size>=ye;){const $=x.values().next().value;if(!$)break;const P=oe($),te=Math.round((Date.now()-($.drainingSince??n))/1e3);if(P.unanswered===0&&P.streams===0&&P.work===0){await V($);continue}await V($,`closing a still-draining generation early \u2014 ${x.size} were pending and the cap is ${ye} (each holds a whole app: DI container, DB pool, queue workers). `+(P.unanswered>0||P.work>0?`It was replaced ${te}s ago and had still not answered ${ne(P.unanswered,"request")}`+(P.work?` or finished ${ne(P.work,"background job")}`:"")+` \u2014 those fail now (a queue job keeps its lock until the TTL lapses). Raise LENSMCP_SWAP_EARLY_CLOSE_GRACE_MS (currently ${Q}ms) to wait longer for them. `:`It held only ${ne(P.streams,"open stream")}; their clients reconnect to the new app. `)+"Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.")}x.add(t)}for(;le>0&&Date.now()-n<le;){if(t.closed)return;const $=c();if($.inflight===0&&$.work===0)break;await new Promise(P=>{setTimeout(P,250).unref?.()})}if(t.closed)return;const m=c(),E=de(t.workGen);m.inflight>0||m.work>0?console.error(`[child] swap drain expired after ${Date.now()-n}ms \u2014 closing the previous app with ${m.inflight} in-flight request(s) and ${m.work} background job(s) still running. `+(E?`Previous app still processing ${E.running} queue job(s); ${E.takenAfterStop} were taken after the swap. `:"")+`A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${le}ms) if this is legitimate work.`):(Date.now()-n>250||E?.runningAtStop||E?.takenAfterStop)&&console.log(`[child] previous app drained in ${Date.now()-n}ms \u2014 closing it`+(E?` (it finished ${E.finishedAfterStop} queue job(s) after the swap; ${E.takenAfterStop} were taken after the swap)`:"")),await V(t)}f(ke,"Ge"),i(ke,"drainThenClose");async function J(){if(S)return re=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),g;S=!0,Y=Date.now(),await Ie(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(A,X)=>{console.error(`[child] uncaughtException (${X}):`,A)}),process.on("unhandledRejection",A=>{const X=A instanceof Error?A.stack||A.message:String(A);X.includes("FronteggContextScope")||X.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",A)});let t;if(Le)try{t=(0,hot_swap_registries_1.captureRegistries)()}catch{t=void 0}const n=global.__lensmcpBeginWorkGeneration,c=typeof n=="function"?n():void 0;try{try{await importFresh(BUNDLE_PATH)}catch(A){if(!(A instanceof SyntaxError))throw A;console.warn(`[child] bundle was unreadable mid-write (${A.message}) \u2014 retrying the swap once`),await new Promise(X=>setTimeout(X,400)),await importFresh(BUNDLE_PATH)}}catch(A){throw S=!1,Y=Date.now(),A}if(typeof global.createChildApp!="function")throw S=!1,Y=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 p;if(t)try{p=(0,hot_swap_registries_1.supersededSince)(t),ce((0,hot_swap_registries_1.pruneRegistries)(p,"build"),"at the swap")}catch(A){console.error("[child] could not release superseded class registrations:",A)}let m;try{m=await global.createChildApp()}catch(A){throw S=!1,Y=Date.now(),takeImportedModule(),A}const E=H;p?.size&&(E.app?E.superseded=p:ce((0,hot_swap_registries_1.pruneRegistries)(p,"serve"),"at the swap")),g=m;const $=takeImportedModule();H={app:m,handler:m.handler,inflight:0,gen:++B,...$?{module:$}:{},...c===void 0?{}:{workGen:c}};const P=_e(m,void 0).container;P&&(O.add(new WeakRef(P)),ue=!0);const te=E.app&&c!==void 0?ve(c):void 0;return console.log("[child] Swapped to fresh app (previous draining in background)"+(te?.workers?`; its ${te.workers} queue worker(s) stopped taking new jobs, previous app still processing ${te.runningAtStop} job(s)`:"")),be(),E.app&&(se+=1,ke(E).catch(A=>console.error("[child] background close error:",A)),setTimeout(()=>Z("30s after a hot swap"),3e4).unref?.(),k.swapped(E.gen,()=>E.closed===!0&&!I.has(E))),S=!1,Y=Date.now(),re?(re=!1,J()):m}f(J,"q"),i(J,"swapNow");const Fe=debounce(async()=>{try{console.log("[child] /webpack/reload"),await J()}catch(t){console.error("[child] reload failed:",t)}},250),q=http.createServer((t,n)=>{if(t.method==="POST"&&t.url==="/webpack/reload"){n.writeHead(200,{"content-type":"application/json"}),n.end(JSON.stringify({ok:!0})),Fe();return}if(t.method==="GET"&&t.url==="/__lensmcp/alive"){const p=t.headers["x-internal-token"],m=process.env.LENSMCP_SERVICE_KEY;if(m&&p!==m){n.writeHead(404,{"content-type":"text/plain"}),n.end("not found");return}const E=T();n.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),n.end(JSON.stringify({ok:!0,pid:process.pid,swapping:S,inflight:H.inflight,oldestInflightAt:H.oldestAt,activeWork:E.count,oldestWorkAt:E.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...F?{watcher:F}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const c=H;c.inflight+=1,(c.open??=new Set).add(n),c.oldestAt===void 0&&(c.oldestAt=Date.now()),n.on("close",()=>{c.inflight-=1,c.open?.delete(n),Q>0&&!c.closed&&!n.headersSent&&!n.writableEnded&&((c.abandoned??=new Map).set(n,Date.now()),c.abandoned.size>256&&j(c)),c.inflight===0&&(c.oldestAt=void 0),k.activity()}),c.handler(t,n)});q.keepAliveTimeout=0;const W=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(W),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 n=(0,schema_first_instance_1.prewarmZodFor)(t);n?.created&&console.log(`[child] zod${n.version?` ${n.version}`:""}: built ${n.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 n=(0,redis_disconnect_fix_1.finishStrandedDisconnectsFor)(t);n?.patched&&console.log(`[child] ioredis${n.versions.length?` ${n.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 J(),pe=(await k.booted()).live,console.log(`[child] Using bundle: ${t}`),claimSock(W),q.listen(W,()=>{console.log(`[child] Listening on ${W}`),tell(process,{type:"ready",socketPath:W})})}catch(t){console.error("[child] Startup error:",t),tell(process,{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 n=Number(t.port);a(Number.isInteger(n)&&n>1024?n:0);return}if(t.type==="watcher"){const n=t.testimony;n&&typeof n=="object"&&(F=n);return}if(t.type==="heap-snapshot"){ee(t);return}if(t.type==="recycle-declined"){h=!1;return}if(t.type==="retire"){ge();return}if(t.type==="reload"){if(b)return;try{const n=Date.now();await J(),tell(process,{type:"reloaded",socketPath:W,swap:{generation:B,swaps:se,swapMs:Date.now()-n,draining:x.size}})}catch(n){console.error("[child] reload error:",n),tell(process,{type:"reload-error",error:String(n?.stack||n)})}}}});let Ee=!1;async function ee(t){const n=i(c=>{tell(process,{type:"heap-snapshot-result",id:t.id,...c})},"reply");if(Ee)return n({ok:!1,refused:"a snapshot of this pod is already being written"});Ee=!0;try{const c=G(),p=k.tracker.views().map(P=>P.gen),m=(0,heap_snapshot_1.heapSnapshotPolicy)();t.maxHeapBytes&&(m.maxHeapBytes=t.maxHeapBytes);const E=(0,heap_snapshot_1.heapSnapshotRefusal)(m,{live:c,...t.available?{available:t.available}:{}});if(E)return n({ok:!1,refused:E,live:c});console.log(`[child] writing a heap snapshot (${Math.round(c/1048576)} MB live) \u2014 the pod pauses while V8 serialises it`);const $=await(0,heap_snapshot_1.writeHeapSnapshot)(t.file);n({ok:!0,file:t.file,bytes:$.bytes,ms:$.ms,live:c,gen:H.gen??0,closedAlive:p})}catch(c){n({ok:!1,error:c?.message??String(c)})}finally{Ee=!1}}f(ee,"ne"),i(ee,"takeHeapSnapshot");async function ge(t="a fresh pod has taken over"){if(b)return;b=!0,k.stop(),console.log(`[child] retiring \u2014 ${t}; finishing in-flight work, then exiting`);const n=ve(Number.POSITIVE_INFINITY);n?.workers&&console.log(`[child] its ${n.workers} queue worker(s) stopped taking new jobs, still processing ${n.runningAtStop} job(s)`);try{fs.writeFileSync(sockRetiringPath(W),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(p=>{setTimeout(p,We).unref?.()}),cleanupSock(W),q.close();const c=Date.now();for(;Date.now()-c<le;){q.closeIdleConnections?.();let p=0;for(const m of x)p+=oe(m).unanswered;if(H.inflight+j(H)===0&&p===0&&T().count===0)break;await new Promise(m=>{setTimeout(m,250).unref?.()})}setTimeout(()=>process.exit(0),3e4).unref?.();try{await g?.close()}catch(p){console.error("[child] close error while retiring:",p)}process.exit(0)}f(ge,"_e"),i(ge,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const Ce=i(async t=>{console.log(`[child ${t}] shutting down\u2026`);const n=g?.close().catch(p=>console.error("[child] close error:",p)),c=new Promise(p=>q.close(()=>p()));cleanupSock(W),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([n,c]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(t=>{process.on(t,()=>{if(t==="SIGTERM"&&!b&&(0,handoff_retire_1.handoffRetireRequested)(SOCK_DIR,POD_GENERATION)){ge("this generation was handed over to a newer one");return}inspector?.close?.(),Ce(t)})})})()}else{let a=f(function(h){j.push(h),j.length>Q&&j.splice(0,j.length-Q),ye?.write((0,inspector_secret_1.redactInspectorUrls)(h.replace(x,"")));for(const b of oe)try{const v=b.plain?h.replace(x,""):h;b.res.write(b.redact?(0,inspector_secret_1.redactInspectorUrls)(v):v)}catch{oe.delete(b)}},"pe");i(a,"emitLogLine");const _=12,g=process.env.LENSMCP_PROJECT||SERVICE_NAME||"service",S=(0,pod_memory_events_1.createPodMemoryPublisher)(process.env.LENSMCP_EVENT_FILE),F=i(h=>({project:g,service:SERVICE_NAME||g,pod:h.id,pid:h.proc.pid??0,podGeneration:POD_GENERATION}),"podIdentity"),re=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],Le="\x1B[0m",ce=Math.max(1,Number(process.env.CHILD_COUNT||1)),H=LENSMCP_WS_KEY||path.basename(process.cwd()),le=i(h=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(H,SERVICE_NAME||"service",h)),"execPathFor");let T=(()=>{const h=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(h)&&h>1024?h:0})();const ve=20;T&&(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/closed/codex-security-06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${T}+slot, workers ${T}+${ve}+i). Any account on this host can connect to 127.0.0.1:${T} 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 de=process.env.LENSMCP_LOG_FILE||"",ye=de?(()=>{try{fs.mkdirSync(path.dirname(de),{recursive:!0});const h=fs.createWriteStream(de,{flags:"a",mode:384});try{fs.chmodSync(de,384)}catch{}return h}catch(h){return console.warn(`[parent] cannot open log file ${de}:`,h.message),null}})():null,x=/\x1b\[[0-9;]*m/g,Q=2e3,j=[],oe=new Set,ne=(0,devserver_transport_1.gatewayManaged)();for(const h of["log","info","warn","error"]){const b=console[h].bind(console),v=h==="log"||h==="info"?process.stdout:process.stderr;console[h]=(...B)=>{b(...B.map(O=>typeof O=="string"?(0,inspector_secret_1.forStream)(v,ne,O):O)),a(B.map(O=>typeof O=="string"?O:util.inspect(O)).join(" ")+`
13
+ `)}}const Ie=i(h=>re[(h-1)%re.length],"colorFor"),R=i(h=>{if(ce===1)return"";const b=`[child#${h}] `;return`${Ie(h)}${b}${Le}`},"tagFor"),pe=i(h=>h.startsWith("{")&&h.endsWith("}"),"isJsonLog"),se=i((h,b,v)=>{const B=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(U=>`
14
+ `+String(U),"stack")}}),O=i((U,k)=>{const G=U==="stdout"?process.stdout:process.stderr;try{v?.(k)}catch{}const L=pe(k)?B(k):`${k}
15
+ `;G.write((0,inspector_secret_1.forStream)(G,ne,`${b}${L}`)),a(`${b}${L}`)},"write"),ue=i((U,k)=>{if(!U)return;const G=readline.createInterface({input:U});G.on("line",L=>O(k,L)),G.on("close",()=>{})},"attach");ue(h.stdout,"stdout"),ue(h.stderr,"stderr")},"wireProcLogging"),We=i(h=>se(h.proc,R(h.id),b=>h.tail.push(b)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const h=httpProxy.createProxyServer({});let b=Number(process.env.PORT)||0;b||(b=9090);const v=[],B=new Set;function O(){let e=0;for(;B.has(e);)e++;return B.add(e),e}f(O,"I"),i(O,"allocDebugSlot");let ue=1,U=0,k=!1,G=ce,L=[],me=0;const be=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),I=i(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),Y=i(e=>e.booting&&!I(e)&&Date.now()-e.spawnedAt<be,"stillBooting");function he(){return v.filter(e=>e.healthy&&typeof e.socketPath=="string")}f(he,"he"),i(he,"healthyChildren");let K,ie=0,Me;const Z={project:g,service:SERVICE_NAME||g,podGeneration:POD_GENERATION};function fe(e){const r=(0,service_build_events_1.buildAccountOf)(e.building);r&&r.startedAt!==ie&&(ie=r.startedAt,S.publish((0,service_build_events_1.buildServiceBuildEvent)(Z,"started",r,{root:process.cwd()})));const l=(0,service_build_events_1.buildAccountOf)(e.failed);l&&(ie=0,S.publish((0,service_build_events_1.buildServiceBuildEvent)(Z,"failed",l,{root:process.cwd()})))}f(fe,"fe"),i(fe,"recordBuildProgress");function we(e){if(!e||typeof e!="object")return;const r=e;if(r.schemaVersion===1){fe(r),"failed"in r&&delete r.failed,K=r;for(const l of v)tell(l.proc,{type:"watcher",testimony:r})}}f(we,"Oe"),i(we,"recordWatcherTestimony");function _e(e,r=256*1024){return new Promise(l=>{let u="",s=!1;e.on("data",o=>{s||(u+=o,u.length>r&&(s=!0))}),e.on("end",()=>{if(s||!u)return l(void 0);try{l(JSON.parse(u))}catch{l(void 0)}}),e.on("error",()=>l(void 0))})}f(_e,"we"),i(_e,"readJsonBody");function V(){const e=he();if(e.length===0)return null;const r=U%e.length;return U=(U+1)%e.length,e[r]}f(V,"oe"),i(V,"pickChild");function ke(e,r){const l=`Child#${e.id}`,u=SERVICE_NAME||g,s=Date.now();if(r.type==="pod-memory"){const o=r.report;if(!o||typeof o.live!="number")return;e.memory.latest=o,e.memory.history.push(o),e.memory.history.length>12&&e.memory.history.splice(0,e.memory.history.length-12),console.log(`[parent] ${(0,pod_memory_1.describePodMemory)(l,o)}`),S.publish((0,pod_memory_events_1.buildPodMemoryEvent)(F(e),o))}else if(r.type==="pod-memory-retained"){const o=r.retained;if(!o||typeof o.gen!="number")return;e.memory.retained.set(o.gen,{...o,at:s}),console.warn(`[parent] ${(0,pod_memory_1.describeRetained)(l,u,o)}`),S.publish((0,pod_memory_events_1.buildPodRetainedEvent)(F(e),o,s))}else{const o=r.released;if(!o||typeof o.gen!="number")return;e.memory.retained.delete(o.gen),console.log(`[parent] ${(0,pod_memory_1.describeReleased)(l,u,o)}`),S.publish((0,pod_memory_events_1.buildPodReleasedEvent)(F(e),o,s))}}f(ke,"Ge"),i(ke,"recordPodMemory");const J=new Map;let Fe=0;async function q(){const e=ue++,r=childSockPath(e);cleanupSock(r);const l=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&l.unshift("--require","@swc-node/register"),l.push(...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const u=T?O():-1,s=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:r,...u>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(T,u))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:l,execPath:le("pod")}),o={id:e,proc:s,healthy:!1,lastError:null,debugSlot:u,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:me,memory:{history:[],retained:new Map}};return s.on("error",w=>console.error(`[parent] Child#${e}: ${w.message}`)),s.on("message",w=>{if(!w||typeof w!="object")return;const d=w;if(d.type==="ready")o.socketPath=String(d.socketPath),o.healthy=!0,o.booting=!1,console.log(`[parent] Child#${o.id} ready on socket ${o.socketPath}`),K&&tell(s,{type:"watcher",testimony:K}),me!==o.bootReloadSeq&&tell(s,{type:"reload"});else if(d.type==="recycle-request")c(o,String(d.reason||"it has outgrown its bound"));else if(d.type==="inspector-url")o.inspectorUrl=String(d.url),console.log(`[parent] Child#${o.id} Debugger listening on ${d.url}`);else if(d.type==="reloaded"){o.socketPath=String(d.socketPath),o.healthy=!0,console.log(`[parent] Child#${o.id} hot-swapped on socket ${o.socketPath}`);const y=d.swap;y&&typeof y.generation=="number"&&typeof y.swaps=="number"&&S.publish((0,service_build_events_1.buildServiceSwapEvent)(F(o),{generation:y.generation,swaps:y.swaps,swapMs:typeof y.swapMs=="number"?y.swapMs:0,draining:typeof y.draining=="number"?y.draining:0},Me,{root:process.cwd()}))}else d.type==="reload-error"?(o.lastError=String(d.error||"unknown reload error"),console.error(`[parent] Child#${o.id} reload error: ${o.lastError}`),o.healthy=!1):d.type==="boot-error"?(o.lastError=String(d.error||"unknown boot error"),console.error(`[parent] Child#${o.id} boot error: ${o.lastError}`),o.healthy=!1,o.booting=!1):d.type==="pod-memory"||d.type==="pod-memory-retained"||d.type==="pod-memory-released"?ke(o,d):d.type==="heap-snapshot-result"&&J.get(Number(d.id))?.(d)}),s.on("exit",(w,d)=>{o.booting=!1;for(const M of o.memory.retained.values()){const C={gen:M.gen,closedAt:M.closedAt,ageMs:Date.now()-M.closedAt,reason:"pod-exited"};console.log(`[parent] ${(0,pod_memory_1.describeReleased)(`Child#${o.id}`,SERVICE_NAME||g,C)}`),S.publish((0,pod_memory_events_1.buildPodReleasedEvent)(F(o),C,Date.now()))}o.memory.retained.clear(),o.memory.latest&&S.publish((0,pod_memory_events_1.buildPodExitedEvent)(F(o),{code:w,signal:d},Date.now())),o.debugSlot>=0&&(B.delete(o.debugSlot),o.debugSlot=-1);const y=d==="SIGINT"||d==="SIGTERM"||w===0;if(y?console.log(`[parent] Child#${o.id} exited cleanly (code=${w}, signal=${d??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${o.id} crashed (code=${w}, signal=${d??"none"}, pid=${o.proc.pid}, ${o.healthy?"was serving":"was not ready"})`,w,d,o.tail.snapshot())),o.lastError||(o.lastError=`Child exited abnormally (code=${w}, signal=${d??"none"})`)),o.healthy=!1,cleanupSock(o.socketPath,o.proc.pid),o.socketPath=void 0,!y&&!k){const M=v.indexOf(o);M>=0&&v.splice(M,1);const C=Date.now();if(L=L.filter(z=>C-z<3e4),L.length>=5){console.error(`[parent] crash-loop detected (${L.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}L.push(C),setTimeout(()=>{const z=v.filter(Te=>Te.proc.killed===!1).length;!k&&z<G&&(console.log(`[parent] respawning a crashed pod (${z}/${G} live)`),q().catch(()=>{}))},1500).unref?.()}}),v.push(o),We(o),o}f(q,"K"),i(q,"spawnChild");async function W(e){G=Math.max(G,e);const r=v.filter(u=>u.proc.killed===!1),l=e-r.length;for(let u=0;u<l;u++)await q()}f(W,"U"),i(W,"ensurePoolSize");async function Ee(){const e=v.filter(r=>(!r.healthy||typeof r.socketPath!="string")&&!Y(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 l=v.indexOf(r);l>=0&&v.splice(l,1),await q()}))}f(Ee,"Ee"),i(Ee,"respawnUnhealthy");const ee=new Set;let ge=!1,Ce=0;const t=3e4,n=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function c(e,r){const l=i(s=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${r}) \u2014 not now: ${s}`),tell(e.proc,{type:"recycle-declined"})},"decline");if(k||!v.includes(e))return;if(ge)return l("another pod is already being replaced");const u=Date.now()-Ce;if(u<t)return l(`a pod was replaced ${Math.round(u/1e3)}s ago`);ge=!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 s=await q(),o=Date.now()+be;for(;!s.healthy&&!s.lastError&&!I(s)&&Date.now()<o;)await new Promise(y=>{setTimeout(y,100).unref?.()});if(k)return;if(!s.healthy){const y=v.indexOf(s);y>=0&&v.splice(y,1);try{s.proc.kill("SIGTERM")}catch{}return l(`its successor Child#${s.id} did not come up (${s.lastError??"no answer in time"})`)}const w=v.indexOf(e);w>=0&&v.splice(w,1),e.healthy=!1,ee.add(e);const d=setTimeout(()=>{if(!I(e)){console.error(`[parent] Child#${e.id} was still running ${n}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},n);if(d.unref?.(),e.proc.once("exit",()=>{clearTimeout(d),ee.delete(e)}),!tell(e.proc,{type:"retire"}))try{e.proc.kill("SIGTERM")}catch{}Ce=Date.now(),console.log(`[parent] Child#${s.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${s.proc.pid})`)}catch(s){l(`could not start a successor: ${s?.message??String(s)}`)}finally{ge=!1}}f(c,"a"),i(c,"recyclePod");const p=[];function m(e){const r=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),l=path.join(r,`${e}.js`);if(!fs.existsSync(l))return console.error(`[parent] Worker "${e}" bundle not found: ${l}`),null;const u=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&u.unshift("--require","@swc-node/register");try{u.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const s=inspectorPortFor(T||9229,ve+Math.max(0,WORKER_NAMES.indexOf(e)));u.push(T?`--inspect=127.0.0.1:${s}`:`--inspect-port=127.0.0.1:${s}`,...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const o=(0,node_child_process_1.fork)(l,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:u,execPath:le(`w-${e}`)}),w={name:e,proc:o,debugPort:s};return p.push(w),o.on("error",d=>console.error(`[parent] Worker "${e}": ${d.message}`)),se(o,`\x1B[35m[worker:${e}] \x1B[0m`,d=>{const y=/Debugger listening on (wss?:\/\/\S+)/.exec(d);y&&(w.inspectorUrl=y[1])}),o.on("exit",(d,y)=>{y==="SIGINT"||y==="SIGTERM"||d===0||console.error(`[parent] Worker "${e}" crashed (code=${d}, signal=${y??"none"})`);const M=p.indexOf(w);M>=0&&p.splice(M,1)}),console.log(`[parent] Worker "${e}" started (pid=${o.pid})`),w}f(m,"y"),i(m,"spawnWorkerProcess");const E=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),$=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),P=new Set,te=i(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function A(e,r){if(P.delete(e),!te(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{}}}f(A,"R"),i(A,"killDrainingWorker");function X(e){try{e.proc.kill("SIGTERM")}catch{}if(te(e))return;const r=[...P].filter(u=>u.name===e.name);for(;r.length>=$;)A(r.shift(),`was still running when a later reload replaced its successor (cap ${$})`);P.add(e);const l=setTimeout(()=>A(e,`was still running ${E}ms after SIGTERM`),E);l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),P.delete(e)})}f(X,"B"),i(X,"retireWorker");function Be(){for(const e of[...p])X(e);p.length=0;for(const e of WORKER_NAMES)m(e)}f(Be,"Ke"),i(Be,"restartWorkers");const Je=debounce(async()=>{me+=1;const e=he();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const r of e)tell(r.proc,{type:"reload"})}await Ee(),WORKER_NAMES.length>0&&Be()},200),Pe=i(async(e,r)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){if(!(0,devserver_transport_1.mayDriveThePool)(e))return refusePoolCommand(e,r);r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0})),_e(e).then(s=>{const o=s;Me=(0,service_build_events_1.buildAccountOf)(o?.build),ie=0,we(o?.watcher)}),Je();return}if(e.method==="POST"&&e.url==="/webpack/watching"){if(!(0,devserver_transport_1.mayDriveThePool)(e))return refusePoolCommand(e,r);const s=await _e(e);we(s),r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,relayed:K!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){if(!(0,devserver_transport_1.mayDriveThePool)(e))return refusePoolCommand(e,r);let s="";e.on("data",o=>{s+=o}),e.on("end",async()=>{let o=0;try{o=Number(JSON.parse(s||"{}").pods)||0}catch{}o=Math.max(1,Math.min(o,16)),await W(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 s=new URL(e.url,"http://localhost").searchParams,o=Math.min(Math.max(Number(s.get("tail")??"100")||0,0),Q),w=s.get("follow")!=="0",d=s.get("plain")==="1",y=!(0,devserver_transport_1.isOwnerOnlyTransport)(e);r.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const M=o===0?"":j.slice(-o).join(""),C=d?M.replace(x,""):M;if(r.write(y?(0,inspector_secret_1.redactInspectorUrls)(C):C),!w){r.end();return}const z={res:r,plain:d,redact:y};oe.add(z),e.on("close",()=>oe.delete(z));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!(0,devserver_transport_1.isOwnerOnlyTransport)(e)){r.writeHead(404,{"content-type":"application/json"}),r.end(JSON.stringify({error:"not found"}));return}const s=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(s)&&s>1024&&(T=s),T||(T=9229);const o=v.filter(d=>d.proc.exitCode===null&&d.proc.signalCode===null);for(const d of o)d.debugSlot<0&&(d.debugSlot=O()),tell(d.proc,{type:"debug-open",port:inspectorPortFor(T,d.debugSlot)});for(const d of p)try{d.proc.kill("SIGUSR1")}catch{}const w=Date.now()+2e3;for(;Date.now()<w&&o.some(d=>!d.inspectorUrl);)await new Promise(d=>setTimeout(d,50));r.writeHead(200,{"content-type":"application/json"}),r.end(JSON.stringify({ok:!0,basePort:T,fixedPorts:usesFixedInspectorPorts(),pods:o.map(d=>({id:d.id,inspectorUrl:d.inspectorUrl??null})),workers:p.map(d=>({name:d.name,port:d.debugPort||(0,inspector_secret_1.inspectorPortOf)(d.inspectorUrl),inspectorUrl:d.inspectorUrl??null}))},null,2)+`
16
+ `);return}if(e.method==="GET"&&e.url==="/webpack/memory"){if(!(0,devserver_transport_1.isOwnerOnlyTransport)(e)){r.writeHead(404,{"content-type":"application/json"}),r.end(JSON.stringify({error:"not found"}));return}const s=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),o=[...v,...ee].map(w=>({id:w.id,pid:w.proc.pid??0,podGeneration:POD_GENERATION,healthy:w.healthy,retiring:ee.has(w),latest:w.memory.latest??null,history:w.memory.history,retained:[...w.memory.retained.values()]}));r.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),r.end(JSON.stringify({ok:!0,service:SERVICE_NAME,project:g,bundle:{path:s},pods:o},null,2)+`
17
+ `);return}if(e.method==="POST"&&e.url?.startsWith("/webpack/heap")){if(!(0,devserver_transport_1.isOwnerOnlyTransport)(e)){r.writeHead(404,{"content-type":"application/json"}),r.end(JSON.stringify({error:"not found"}));return}const s=i((D,$e)=>{r.writeHead(D,{"content-type":"application/json"}),r.end(JSON.stringify($e,null,2)+`
18
+ `)},"reply"),o=new URL(e.url,"http://localhost").searchParams,w=Number(o.get("pod")||""),d=o.has("maxMb")?Number(o.get("maxMb")):void 0;if(d!==void 0&&!(Number.isFinite(d)&&d>0)){s(400,{ok:!1,error:"maxMb takes a positive number of MB"});return}const y=Number.isInteger(w)&&w>0?v.find(D=>D.id===w):he()[0];if(!y||I(y)){s(404,{ok:!1,error:w?`no pod #${w} \u2014 running: ${v.map(D=>`#${D.id}`).join(", ")||"none"}`:"no pod is serving"});return}if(J.size>0){s(409,{ok:!1,refused:"a heap snapshot is already being written for this service"});return}const M=(0,heap_snapshot_1.heapSnapshotPolicy)(),C=(0,heap_snapshot_1.heapSnapshotDir)();let z;try{(0,heap_snapshot_1.ensureHeapDir)(C);const D=(0,heap_snapshot_1.pruneHeapSnapshots)(C,M);D.length&&console.log(`[parent] removed ${D.length} older heap snapshot(s) from ${C}`),z=path.join(C,(0,heap_snapshot_1.heapSnapshotFileName)(SERVICE_NAME||g,y.id,y.proc.pid??0,y.memory.latest?.gen??0,Date.now()))}catch(D){s(500,{ok:!1,error:`cannot prepare ${C}: ${D.message}`});return}const Te=++Fe,Ge=(0,heap_snapshot_1.availableSystemMemory)();let Oe;const N=await new Promise(D=>{const $e=setTimeout(()=>D(void 0),6e5);$e.unref?.(),J.set(Te,et=>{clearTimeout($e),D(et)}),Oe=i(()=>{clearTimeout($e),D(void 0)},"onExit"),y.proc.once("exit",Oe);const Qe={type:"heap-snapshot",id:Te,file:z,...Ge?{available:Ge}:{},...d!==void 0?{maxHeapBytes:Math.floor(d*1048576)}:{}};tell(y.proc,Qe)||Oe()}).finally(()=>{J.delete(Te),Oe&&y.proc.off("exit",Oe)});if(!N){s(504,{ok:!1,pod:y.id,error:"the pod did not finish the snapshot (it exited, or took longer than 10 minutes)"});return}N.ok?console.log(`[parent] Child#${y.id} wrote a ${Math.round((N.bytes??0)/1048576)} MB heap snapshot in ${N.ms} ms \u2192 ${N.file}`):console.warn(`[parent] Child#${y.id} heap snapshot not taken: ${N.refused??N.error}`),s(N.ok?200:N.refused?409:500,{ok:N.ok,pod:y.id,pid:y.proc.pid??0,service:SERVICE_NAME,bundle:path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),...Ge?{available:Ge}:{},...N.file?{file:N.file,bytes:N.bytes,ms:N.ms}:{},...N.live!==void 0?{live:N.live}:{},...N.gen!==void 0?{gen:N.gen,closedAlive:N.closedAlive??[]}:{},...N.refused?{refused:N.refused}:{},...N.error?{error:N.error}:{}});return}const l=matchGatewayRoute(e);if(l?.prependPrefix&&!e.url?.startsWith(l.prependPrefix)&&(e.url=l.prependPrefix+(e.url??"/")),l?.target){applyGatewayMiddleware(e),h.web(e,r,{target:l.target,autoRewrite:typeof l.target=="string",changeOrigin:!0},s=>{console.error(`[parent] proxy error to ${l.target}:`,s),r.statusCode=502,r.setHeader("content-type","text/plain; charset=utf-8"),r.end(`Upstream error from ${l.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 u=V();if(!u){r.statusCode=503,r.setHeader("content-type","text/plain; charset=utf-8");const s=v.map(o=>`#${o.id}: ${o.lastError??"no error recorded"}`).join(`
19
+ `);r.end(`No healthy children available. POST /webpack/reload to recover.
20
20
 
21
21
  Last errors:
22
- ${a}`);return}u.web(e,o,{target:{socketPath:d.socketPath},headers:{"x-proxy-child-id":String(d.id)}},a=>{console.error(`[parent] proxy error to Child#${d.id}:`,a),d.healthy=!1,d.lastError=String(a?.stack||a),o.statusCode=502,o.setHeader("content-type","text/plain; charset=utf-8"),o.end(`Upstream error from Child#${d.id}. Try /webpack/reload.
23
- ${d.lastError}`)})},"requestHandler");let ue,Se,De;const Te=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),o=GATEWAY_ROUTES.map(d=>d.host).filter(d=>!!d),l=(0,checkout_cache_1.checkoutCacheDir)(process.cwd(),"davnx-webpack");Se=e.getCertificateSync(l,SERVICE_NAME||"lensmcp.dev",o),De=e.caCertPath(),ue=require("node:https").createServer({key:Se,cert:Se},Ee)}else ue=http.createServer(Ee);const We=n((e,o,l)=>{const d=matchGatewayRoute(e);if(d?.prependPrefix&&!e.url?.startsWith(d.prependPrefix)&&(e.url=d.prependPrefix+(e.url??"/")),d?.target){u.ws(e,o,l,{target:d.target,changeOrigin:!0},()=>o.destroy());return}const a=Q();if(!a){o.destroy();return}u.ws(e,o,l,{target:{socketPath:a.socketPath}},()=>o.destroy())},"upgradeHandler");ue.on("upgrade",We);const Ue=2e3;let Fe=!1;ue.on("error",e=>{if(e.code==="EADDRINUSE"){Fe||(Fe=!0,console.warn(`[parent] public port ${P} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${Ue/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!A&&!ue.listening&&ue.listen(P)},Ue).unref?.();return}console.error("[parent] public server error:",e)}),await pe(te);for(const e of WORKER_NAMES)E(e);SOCKET_ONLY?console.log(`[parent] socket-only: no TCP listener (port ${P} is not bound \u2014 the gateway reaches this service through its socket pool)`):ue.listen(P,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${P}`),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(o=>`${o.host??"*"}${o.prefix??"/"}\u2192${o.target??"children"}`).join(" ")}`),De&&(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 ${De}`)),console.log(`POST ${e}://localhost:${P}/webpack/reload to trigger rolling swap/respawn`)});const Re=Number(process.env.INTERNAL_PORT||0);if(Re>0&&!SOCKET_ONLY){const e=n((l,d)=>{const a=Q();if(!a){d.statusCode=503,d.setHeader("content-type","text/plain; charset=utf-8"),d.end("No healthy children available.");return}u.web(l,d,{target:{socketPath:a.socketPath}},r=>{a.healthy=!1,a.lastError=String(r?.stack||r),d.statusCode=502,d.end(`Upstream error from Child#${a.id}.`)})},"internalHandler"),o=http.createServer(e);o.on("upgrade",(l,d,a)=>{const r=Q();if(!r){d.destroy();return}u.ws(l,d,a,{target:{socketPath:r.socketPath}},()=>d.destroy())}),o.on("error",l=>{console.warn(`[parent] internal port ${Re} unavailable (${l.code}).`)}),o.listen(Re,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${Re}`)}),Te.push(o)}const qe=SOCKET_ONLY?[]:(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of qe){if(!Number.isInteger(e)||e===Number(P))continue;const o=GATEWAY_HTTPS?require("node:https").createServer({key:Se,cert:Se},Ee):http.createServer(Ee);o.on("upgrade",We),o.on("error",l=>{console.warn(`[parent] extra port ${e} unavailable (${l.code}) \u2014 main port still up.`)}),o.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),Te.push(o)}const $e=path.join(SOCK_DIR,"parent.sock");claimSock($e);const Ie=http.createServer(Ee);Ie.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),Ie.listen($e),Te.push(Ie);const He=n(async e=>{A=!0,console.log(`[${e}] [parent] shutting down\u2026`);const o=sockOwnedByAnother($e);cleanupSock($e);for(const l of Te)if(!(l===Ie&&o))try{l.close()}catch{}for(const l of g)try{l.proc.kill("SIGTERM")}catch{}for(const l of[...V])U(l,"was still draining when its devserver shut down");for(const l of ee)try{l.proc.kill("SIGTERM")}catch{}for(const l of m){try{l.proc.kill("SIGTERM")}catch{}l.socketPath&&cleanupSock(l.socketPath,l.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,()=>{He(e)}));const Oe=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(Oe)&&Oe>0){const e=setInterval(()=>{try{process.kill(Oe,0)}catch(o){if(o.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${Oe}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),He("gateway-gone")}},5e3);e.unref()}})()}
22
+ ${s}`);return}h.web(e,r,{target:{socketPath:u.socketPath},headers:{"x-proxy-child-id":String(u.id)}},s=>{console.error(`[parent] proxy error to Child#${u.id}:`,s),u.healthy=!1,u.lastError=String(s?.stack||s),r.statusCode=502,r.setHeader("content-type","text/plain; charset=utf-8"),r.end(`Upstream error from Child#${u.id}. Try /webpack/reload.
23
+ ${u.lastError}`)})},"requestHandler");let ae,Ae,Ue;const Ne=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),r=GATEWAY_ROUTES.map(u=>u.host).filter(u=>!!u),l=(0,checkout_cache_1.checkoutCacheDir)(process.cwd(),"davnx-webpack");Ae=e.getCertificateSync(l,SERVICE_NAME||"lensmcp.dev",r),Ue=e.caCertPath(),ae=require("node:https").createServer({key:Ae,cert:Ae},Pe)}else ae=http.createServer(Pe);const qe=i((e,r,l)=>{const u=matchGatewayRoute(e);if(u?.prependPrefix&&!e.url?.startsWith(u.prependPrefix)&&(e.url=u.prependPrefix+(e.url??"/")),u?.target){h.ws(e,r,l,{target:u.target,changeOrigin:!0},()=>r.destroy());return}const s=V();if(!s){r.destroy();return}h.ws(e,r,l,{target:{socketPath:s.socketPath}},()=>r.destroy())},"upgradeHandler");ae.on("upgrade",qe);const je=2e3;let Ye=!1;ae.on("error",e=>{if(e.code==="EADDRINUSE"){Ye||(Ye=!0,console.warn(`[parent] public port ${b} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${je/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!k&&!ae.listening&&ae.listen(b)},je).unref?.();return}console.error("[parent] public server error:",e)}),await W(ce);for(const e of WORKER_NAMES)m(e);SOCKET_ONLY?console.log(`[parent] socket-only: no TCP listener (port ${b} is not bound \u2014 the gateway reaches this service through its socket pool)`):ae.listen(b,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${b}`),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(" ")}`),Ue&&(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 ${Ue}`)),console.log(`POST ${e}://localhost:${b}/webpack/reload to trigger rolling swap/respawn`)});const De=Number(process.env.INTERNAL_PORT||0);if(De>0&&!SOCKET_ONLY){const e=i((l,u)=>{const s=V();if(!s){u.statusCode=503,u.setHeader("content-type","text/plain; charset=utf-8"),u.end("No healthy children available.");return}h.web(l,u,{target:{socketPath:s.socketPath}},o=>{s.healthy=!1,s.lastError=String(o?.stack||o),u.statusCode=502,u.end(`Upstream error from Child#${s.id}.`)})},"internalHandler"),r=http.createServer(e);r.on("upgrade",(l,u,s)=>{const o=V();if(!o){u.destroy();return}h.ws(l,u,s,{target:{socketPath:o.socketPath}},()=>u.destroy())}),r.on("error",l=>{console.warn(`[parent] internal port ${De} unavailable (${l.code}).`)}),r.listen(De,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${De}`)}),Ne.push(r)}const Xe=SOCKET_ONLY?[]:(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of Xe){if(!Number.isInteger(e)||e===Number(b))continue;const r=GATEWAY_HTTPS?require("node:https").createServer({key:Ae,cert:Ae},Pe):http.createServer(Pe);r.on("upgrade",qe),r.on("error",l=>{console.warn(`[parent] extra port ${e} unavailable (${l.code}) \u2014 main port still up.`)}),r.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),Ne.push(r)}const Se=path.join(SOCK_DIR,"parent.sock");claimSock(Se),(0,handoff_retire_1.clearHandoffRetire)(SOCK_DIR,POD_GENERATION);const Re=http.createServer(Pe);Re.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),Re.listen(Se),Ne.push(Re);function Ke(){k=!0;const e=[...v,...ee];console.log(`[SIGTERM] [parent] generation g${POD_GENERATION} was handed over to a newer one \u2014 ${e.length} pod(s) finish their in-flight work, then it exits`);const r=sockOwnedByAnother(Se);cleanupSock(Se);for(const s of Ne)if(!(s===Re&&r))try{s.close()}catch{}try{ae.close()}catch{}for(const s of[...p])X(s);p.length=0;for(const s of v)if(!tell(s.proc,{type:"retire"}))try{s.proc.kill("SIGTERM")}catch{}const l=setTimeout(()=>{for(const s of e)if(!I(s)){console.error(`[parent] Child#${s.id} was still running ${n}ms after its generation was retired \u2014 killing it`);try{s.proc.kill("SIGKILL")}catch{}}},n);l.unref?.();const u=i(()=>{if(!(e.some(s=>!I(s))||[...P].some(s=>!te(s)))){clearTimeout(l);for(const s of e)s.socketPath&&cleanupSock(s.socketPath,s.proc.pid);(0,handoff_retire_1.clearHandoffRetire)(SOCK_DIR,POD_GENERATION);try{fs.rmdirSync(SOCK_DIR)}catch{}console.log(`[parent] generation g${POD_GENERATION} retired`),process.exit(0)}},"finish");for(const s of e)s.proc.once("exit",u);for(const s of P)s.proc.once("exit",u);u()}f(Ke,"Xe"),i(Ke,"retireGeneration");const Ve=i(async e=>{k=!0,console.log(`[${e}] [parent] shutting down\u2026`);const r=sockOwnedByAnother(Se);cleanupSock(Se);for(const l of Ne)if(!(l===Re&&r))try{l.close()}catch{}for(const l of p)try{l.proc.kill("SIGTERM")}catch{}for(const l of[...P])A(l,"was still draining when its devserver shut down");for(const l of ee)try{l.proc.kill("SIGTERM")}catch{}for(const l of v){try{l.proc.kill("SIGTERM")}catch{}l.socketPath&&cleanupSock(l.socketPath,l.proc.pid)}try{fs.rmdirSync(SOCK_DIR)}catch{}setTimeout(()=>process.exit(0),700).unref()},"shutdown");let He=0;const ze=2e3;["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(e=>process.on(e,()=>{if(e==="SIGTERM"){if(!k&&(0,handoff_retire_1.handoffRetireRequested)(SOCK_DIR,POD_GENERATION)){He=Date.now(),Ke();return}if(He>0&&Date.now()-He<ze)return}Ve(e)}));const xe=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(xe)&&xe>0){const e=setInterval(()=>{try{process.kill(xe,0)}catch(r){if(r.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${xe}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),Ve("gateway-gone")}},5e3);e.unref()}})()}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.24.3",
3
+ "version": "1.24.4",
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.24.3",
61
- "@lensmcp/node-instrumentation": "1.24.3",
62
- "@lensmcp/nx-plugin": "1.24.3",
60
+ "@lensmcp/core": "1.24.4",
61
+ "@lensmcp/node-instrumentation": "1.24.4",
62
+ "@lensmcp/nx-plugin": "1.24.4",
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",
@@ -59,9 +59,17 @@ interface BusHygiene {
59
59
  };
60
60
  }
61
61
  /** Appends pod-memory events to the bus. */
62
+ /** What the publisher needs of an event: its flood bucket, and a `raw` to note what the bucket held back in. */
63
+ export interface BusEventLike {
64
+ fingerprint: string;
65
+ raw: Record<string, unknown>;
66
+ }
62
67
  export interface PodMemoryPublisher {
63
- /** `false` when the flood guard dropped it, or there is no bus to write to. */
64
- publish(event: PodMemoryBusEvent): boolean;
68
+ /**
69
+ * Append one event to the bus file. `false`: held back by its flood bucket, or the bus could not be written.
70
+ * Any bus event of this devserver goes through here — pod memory, and what a build ends in (service-build-events.ts).
71
+ */
72
+ publish(event: BusEventLike): boolean;
65
73
  }
66
74
  /**
67
75
  * A publisher for `eventFile` (the devserver's `LENSMCP_EVENT_FILE`, which the serve executor always sets).
@@ -0,0 +1,50 @@
1
+ import type { PodIdentity } from './pod-memory-events';
2
+ /** Loose BaseEvent shape — cluster does not depend on `@lensmcp/protocol-types`. */
3
+ export interface ServiceBuildBusEvent {
4
+ id: string;
5
+ sessionId: string;
6
+ timestamp: number;
7
+ source: 'gateway';
8
+ category: 'cluster';
9
+ severity: 'info' | 'warning';
10
+ context: {
11
+ sessionId: string;
12
+ projectName: string;
13
+ };
14
+ fingerprint: string;
15
+ title: string;
16
+ raw: Record<string, unknown>;
17
+ }
18
+ /** One build's own account, as the build's notifier sends it (devserver-notifier.ts `BuildAccountWire`). */
19
+ export interface BuildAccount {
20
+ startedAt: number;
21
+ endedAt?: number;
22
+ changedFiles: string[];
23
+ moreChanged?: number;
24
+ }
25
+ /** What a pod says of the swap it just made (the `reloaded` IPC message). */
26
+ export interface SwapReport {
27
+ /** The app generation now serving: 1 at boot, then one per swap. */
28
+ generation: number;
29
+ swaps: number;
30
+ /** How long the swap itself took — building the new app, not compiling it. */
31
+ swapMs: number;
32
+ /** Replaced apps still finishing their work. */
33
+ draining: number;
34
+ }
35
+ /** Paths as a reader names them: relative to the workspace, forward slashes. One outside it stays as it is. */
36
+ export declare function workspaceRelative(files: readonly string[], root: string | undefined): string[];
37
+ /** Parse what arrived over HTTP into a build account, or nothing: never trust a body's shape. */
38
+ export declare function buildAccountOf(raw: unknown): BuildAccount | undefined;
39
+ /** A build of this service started (`phase: 'started'`) or ended with errors (`'failed'`). */
40
+ export declare function buildServiceBuildEvent(who: Pick<PodIdentity, 'project' | 'service' | 'podGeneration'>, phase: 'started' | 'failed', build: BuildAccount, opts?: {
41
+ root?: string;
42
+ at?: number;
43
+ id?: () => string;
44
+ }): ServiceBuildBusEvent;
45
+ /** A pod is now serving the app built from `build` — the event a backend edit ends in. */
46
+ export declare function buildServiceSwapEvent(who: PodIdentity, swap: SwapReport, build: BuildAccount | undefined, opts?: {
47
+ root?: string;
48
+ at?: number;
49
+ id?: () => string;
50
+ }): ServiceBuildBusEvent;
@@ -0,0 +1 @@
1
+ "use strict";var u=Object.defineProperty;var a=(d,e)=>u(d,"name",{value:e,configurable:!0});var p=Object.defineProperty,i=a((d,e)=>p(d,"name",{value:e,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.workspaceRelative=workspaceRelative,exports.buildAccountOf=buildAccountOf,exports.buildServiceBuildEvent=buildServiceBuildEvent,exports.buildServiceSwapEvent=buildServiceSwapEvent;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),MAX_FILES=50;let idCounter=0;function makeId(){return idCounter=(idCounter+1)%65535,`${Date.now().toString(36)}${idCounter.toString(36)}${Math.random().toString(36).slice(2,8)}`}a(makeId,"makeId"),i(makeId,"makeId");function workspaceRelative(d,e){return d.slice(0,MAX_FILES).map(t=>{if(!e||!path.isAbsolute(t))return t;const n=path.relative(e,t);return n&&!n.startsWith("..")&&!path.isAbsolute(n)?n.split(path.sep).join("/"):t})}a(workspaceRelative,"workspaceRelative"),i(workspaceRelative,"workspaceRelative");function buildAccountOf(d){if(!d||typeof d!="object")return;const e=d;if(typeof e.startedAt!="number"||!Number.isFinite(e.startedAt)||e.startedAt<=0)return;const t=Array.isArray(e.changedFiles)?e.changedFiles.filter(n=>typeof n=="string"&&n.length>0&&n.length<1024).slice(0,MAX_FILES):[];return{startedAt:e.startedAt,...typeof e.endedAt=="number"&&Number.isFinite(e.endedAt)?{endedAt:e.endedAt}:{},changedFiles:t,...typeof e.moreChanged=="number"&&e.moreChanged>0?{moreChanged:Math.floor(e.moreChanged)}:{}}}a(buildAccountOf,"buildAccountOf"),i(buildAccountOf,"buildAccountOf");function envelope(d,e,t,n,o,s,c){const r="pending";return{id:c(),sessionId:r,timestamp:e,source:"gateway",category:"cluster",severity:t,context:{sessionId:r,projectName:d.project},fingerprint:`cluster:${d.project}:${n}`,title:o,raw:{kind:n,...s,project:d.project,service:d.service,podGeneration:d.podGeneration}}}a(envelope,"envelope"),i(envelope,"envelope");const filesText=i((d,e)=>{const t=d+e;return t===0?"no source change":`${t} file${t===1?"":"s"}`},"filesText"),seconds=i(d=>d<1e3?`${d}ms`:`${(d/1e3).toFixed(1)}s`,"seconds");function buildServiceBuildEvent(d,e,t,n={}){const o=workspaceRelative(t.changedFiles,n.root),s=t.moreChanged??0,c=n.at??(e==="failed"?t.endedAt??Date.now():t.startedAt),r=t.endedAt!==void 0?Math.max(0,t.endedAt-t.startedAt):void 0,l=e==="started"?`build started: ${d.project} \u2014 ${filesText(o.length,s)}`:`build failed: ${d.project} \u2014 ${filesText(o.length,s)}${r!==void 0?` \xB7 ${seconds(r)}`:""} \xB7 nothing was swapped, the pod serves the last good build`;return envelope(d,c,e==="failed"?"warning":"info","service-build",l,{phase:e,startedAt:t.startedAt,...t.endedAt!==void 0?{endedAt:t.endedAt,compileMs:r}:{},changedFiles:o,...s>0?{moreChanged:s}:{}},n.id??makeId)}a(buildServiceBuildEvent,"buildServiceBuildEvent"),i(buildServiceBuildEvent,"buildServiceBuildEvent");function buildServiceSwapEvent(d,e,t,n={}){const o=n.at??Date.now(),s=workspaceRelative(t?.changedFiles??[],n.root),c=t?.moreChanged??0,r=t?.endedAt!==void 0?Math.max(0,t.endedAt-t.startedAt):void 0,l=`swap: ${d.project} \u2014 generation ${e.generation} (${e.swaps} swap${e.swaps===1?"":"s"})`+(t?` \xB7 ${filesText(s.length,c)}`:"")+(r!==void 0?` \xB7 compiled in ${seconds(r)}`:"")+` \xB7 swapped in ${seconds(e.swapMs)}`;return envelope(d,o,"info","service-swap",l,{pod:d.pod,pid:d.pid,generation:e.generation,swaps:e.swaps,swappedAt:o,swapMs:e.swapMs,draining:e.draining,...t?{compile:{startedAt:t.startedAt,...t.endedAt!==void 0?{endedAt:t.endedAt,ms:r}:{}},changedFiles:s,...c>0?{moreChanged:c}:{}}:{}},n.id??makeId)}a(buildServiceSwapEvent,"buildServiceSwapEvent"),i(buildServiceSwapEvent,"buildServiceSwapEvent");