@lensmcp/cluster 1.24.2 → 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");
@@ -35,6 +35,23 @@ export declare function probePodAlive(socketPath: string, timeoutMs?: number,
35
35
  /** The target service's east-west key. The pod refuses the route without it, so that a
36
36
  * client coming through the gateway cannot read its diagnostics. */
37
37
  serviceKey?: string): Promise<PodAliveness | undefined>;
38
+ /**
39
+ * Why a liveness probe found no evidence — the distinction a single `undefined` hid (issues/closed/foodguard-54):
40
+ *
41
+ * - `silent` — the socket took the connection and nothing answered in time. A dead event loop looks like this,
42
+ * and so does a LIVE one in the middle of a long synchronous stretch (a large parse, a GC pause on a
43
+ * big heap): the kernel accepts into the listen backlog either way. Only waiting tells them apart.
44
+ * - `gone` — nothing listens behind the file any more (`ECONNREFUSED`, `ENOENT`, a reset): the process is gone.
45
+ * - `no-route` — something answered, but not our liveness answer (an older pod with no such route, a refused key,
46
+ * another shape). No evidence either way; the caller keeps its evict-on-deadline behaviour.
47
+ */
48
+ export type PodProbeFailure = 'silent' | 'gone' | 'no-route';
49
+ export interface PodProbeVerdict {
50
+ alive?: PodAliveness;
51
+ failure?: PodProbeFailure;
52
+ }
53
+ /** {@link probePodAlive}, saying WHY when there is no answer. Never throws, never hangs past `timeoutMs`. */
54
+ export declare function probePodAliveVerdict(socketPath: string, timeoutMs?: number, serviceKey?: string): Promise<PodProbeVerdict>;
38
55
  /**
39
56
  * What one failed pod connection says about the POD.
40
57
  *
@@ -1 +1 @@
1
- "use strict";var h=Object.defineProperty;var p=(r,e)=>h(r,"name",{value:e,configurable:!0});var f=Object.defineProperty,u=p((r,e)=>f(r,"name",{value:e,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.parsePodWatcherTestimony=parsePodWatcherTestimony,exports.probePodAlive=probePodAlive,exports.podConnectionVerdict=podConnectionVerdict,exports.podSocketAccepts=podSocketAccepts;const tslib_1=require("tslib"),http=tslib_1.__importStar(require("node:http")),net=tslib_1.__importStar(require("node:net")),types_1=require("./types");function parsePodWatcherTestimony(r){if(!r||typeof r!="object")return;const e=r;if(e.schemaVersion!==1)return;const c=u(n=>typeof n=="number"&&Number.isFinite(n)?n:0,"num"),o=e.degraded;return{schemaVersion:1,lastWatchRunAt:c(e.lastWatchRunAt),lastBuildAt:c(e.lastBuildAt),buildsSinceSpawn:c(e.buildsSinceSpawn),degraded:typeof o=="string"&&o?o:o===!0?"degraded":!1,recentPaths:Array.isArray(e.recentPaths)?e.recentPaths.filter(n=>typeof n=="string").slice(-32):[]}}p(parsePodWatcherTestimony,"parsePodWatcherTestimony"),u(parsePodWatcherTestimony,"parsePodWatcherTestimony");async function probePodAlive(r,e=types_1.POD_ALIVE_PROBE_TIMEOUT_MS,c){return new Promise(o=>{let n=!1;const i=u(s=>{n||(n=!0,o(s))},"finish");let a;try{a=http.request({socketPath:r,path:types_1.POD_ALIVE_PATH,method:"GET",timeout:e,agent:!1,...c?{headers:{"x-internal-token":c}}:{}},s=>{if(s.statusCode!==200){s.resume(),i(void 0);return}let d="";s.setEncoding("utf8"),s.on("data",t=>{d+=t}),s.on("end",()=>{try{const t=JSON.parse(d);i(t.ok===!0?{inflight:Number(t.inflight??0),activeWork:Number(t.activeWork??0),swapping:t.swapping===!0,...typeof t.pid=="number"?{pid:t.pid}:{},...typeof t.keyFingerprint=="string"?{keyFingerprint:t.keyFingerprint}:{},...(()=>{const l=parsePodWatcherTestimony(t.watcher);return l?{watcher:l}:{}})(),...typeof t.podGeneration=="number"&&Number.isFinite(t.podGeneration)?{podGeneration:t.podGeneration}:{}}:void 0)}catch{i(void 0)}})})}catch{i(void 0);return}a.on("timeout",()=>{a.destroy(),i(void 0)}),a.on("error",()=>i(void 0)),a.end()})}p(probePodAlive,"probePodAlive"),u(probePodAlive,"probePodAlive");async function podConnectionVerdict(r,e,c=types_1.POD_ALIVE_PROBE_TIMEOUT_MS){const o=e.code??"";return o==="ECONNREFUSED"||o==="ENOENT"?"gone":await podSocketAccepts(r,c)?"alive":"gone"}p(podConnectionVerdict,"podConnectionVerdict"),u(podConnectionVerdict,"podConnectionVerdict");function podSocketAccepts(r,e=types_1.POD_ALIVE_PROBE_TIMEOUT_MS){return new Promise(c=>{let o=!1,n;const i=u(s=>{o||(o=!0,clearTimeout(a),n?.destroy(),c(s))},"finish"),a=setTimeout(()=>i(!1),e);a.unref?.();try{n=net.connect({path:r}),n.once("connect",()=>i(!0)),n.once("error",()=>i(!1))}catch{i(!1)}})}p(podSocketAccepts,"podSocketAccepts"),u(podSocketAccepts,"podSocketAccepts");
1
+ "use strict";var f=Object.defineProperty;var p=(t,e)=>f(t,"name",{value:e,configurable:!0});var l=Object.defineProperty,u=p((t,e)=>l(t,"name",{value:e,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.parsePodWatcherTestimony=parsePodWatcherTestimony,exports.probePodAlive=probePodAlive,exports.probePodAliveVerdict=probePodAliveVerdict,exports.podConnectionVerdict=podConnectionVerdict,exports.podSocketAccepts=podSocketAccepts;const tslib_1=require("tslib"),http=tslib_1.__importStar(require("node:http")),net=tslib_1.__importStar(require("node:net")),types_1=require("./types");function parsePodWatcherTestimony(t){if(!t||typeof t!="object")return;const e=t;if(e.schemaVersion!==1)return;const i=u(n=>typeof n=="number"&&Number.isFinite(n)?n:0,"num"),c=e.degraded;return{schemaVersion:1,lastWatchRunAt:i(e.lastWatchRunAt),lastBuildAt:i(e.lastBuildAt),buildsSinceSpawn:i(e.buildsSinceSpawn),degraded:typeof c=="string"&&c?c:c===!0?"degraded":!1,recentPaths:Array.isArray(e.recentPaths)?e.recentPaths.filter(n=>typeof n=="string").slice(-32):[]}}p(parsePodWatcherTestimony,"parsePodWatcherTestimony"),u(parsePodWatcherTestimony,"parsePodWatcherTestimony");async function probePodAlive(t,e=types_1.POD_ALIVE_PROBE_TIMEOUT_MS,i){return(await probePodAliveVerdict(t,e,i)).alive}p(probePodAlive,"probePodAlive"),u(probePodAlive,"probePodAlive");function probePodAliveVerdict(t,e=types_1.POD_ALIVE_PROBE_TIMEOUT_MS,i){return new Promise(c=>{let n=!1,s="no-route";const d=u(o=>{n||(n=!0,c(o?{alive:o}:{failure:s}))},"finish");let a;try{a=http.request({socketPath:t,path:types_1.POD_ALIVE_PATH,method:"GET",timeout:e,agent:!1,...i?{headers:{"x-internal-token":i}}:{}},o=>{if(o.statusCode!==200){o.resume(),d(void 0);return}let E="";o.setEncoding("utf8"),o.on("data",r=>{E+=r}),o.on("end",()=>{try{const r=JSON.parse(E);d(r.ok===!0?{inflight:Number(r.inflight??0),activeWork:Number(r.activeWork??0),swapping:r.swapping===!0,...typeof r.pid=="number"?{pid:r.pid}:{},...typeof r.keyFingerprint=="string"?{keyFingerprint:r.keyFingerprint}:{},...(()=>{const P=parsePodWatcherTestimony(r.watcher);return P?{watcher:P}:{}})(),...typeof r.podGeneration=="number"&&Number.isFinite(r.podGeneration)?{podGeneration:r.podGeneration}:{}}:void 0)}catch{d(void 0)}})})}catch{s="gone",d(void 0);return}a.on("timeout",()=>{s="silent",a.destroy(),d(void 0)}),a.on("error",o=>{s!=="silent"&&(s=o.code==="ECONNREFUSED"||o.code==="ENOENT"||o.code==="ECONNRESET"||o.code==="EPIPE"?"gone":"silent"),d(void 0)}),a.end()})}p(probePodAliveVerdict,"probePodAliveVerdict"),u(probePodAliveVerdict,"probePodAliveVerdict");async function podConnectionVerdict(t,e,i=types_1.POD_ALIVE_PROBE_TIMEOUT_MS){const c=e.code??"";return c==="ECONNREFUSED"||c==="ENOENT"?"gone":await podSocketAccepts(t,i)?"alive":"gone"}p(podConnectionVerdict,"podConnectionVerdict"),u(podConnectionVerdict,"podConnectionVerdict");function podSocketAccepts(t,e=types_1.POD_ALIVE_PROBE_TIMEOUT_MS){return new Promise(i=>{let c=!1,n;const s=u(a=>{c||(c=!0,clearTimeout(d),n?.destroy(),i(a))},"finish"),d=setTimeout(()=>s(!1),e);d.unref?.();try{n=net.connect({path:t}),n.once("connect",()=>s(!0)),n.once("error",()=>s(!1))}catch{s(!1)}})}p(podSocketAccepts,"podSocketAccepts"),u(podSocketAccepts,"podSocketAccepts");
@@ -1,6 +1,7 @@
1
1
  import * as http from 'node:http';
2
2
  import type * as stream from 'node:stream';
3
3
  import type { GatewayRuntime, Route } from './types';
4
+ import { type PodProbeFailure } from './pod-probe';
4
5
  import type { Observability } from './observability';
5
6
  import type { ServiceLayer } from './lifecycle';
6
7
  /**
@@ -44,4 +45,17 @@ export interface ProxyLayer {
44
45
  destroyAgents(): void;
45
46
  closeProxy(): void;
46
47
  }
48
+ /** What the guard knew when it condemned a pod — the incident's evidence (issues/closed/foodguard-54). */
49
+ export interface WedgeDetail {
50
+ /** How long the pod had held the whole request with nothing written back. */
51
+ heldMs: number;
52
+ /** How long its liveness route stayed silent before the verdict (0 when the verdict needed no confirming). */
53
+ silentMs: number;
54
+ /** Liveness probes that found no answer. */
55
+ probes: number;
56
+ /** Why the last probe found none — `deadline` when no probe is configured (`LENSMCP_POD_SLOW_FIRST_BYTE_MS=0`). */
57
+ failure: PodProbeFailure | 'deadline';
58
+ }
59
+ /** What the client reads in the 502 — what happened, and what it means for the call (issues/closed/foodguard-54). */
60
+ export declare function wedgedMessage(podName: string, project: string, d: WedgeDetail): string;
47
61
  export declare function createProxy(rt: GatewayRuntime, obs: Observability, svcLayer: ServiceLayer): ProxyLayer;
@@ -1 +1 @@
1
- "use strict";var G=Object.defineProperty;var j=(t,c)=>G(t,"name",{value:c,configurable:!0});var K=Object.defineProperty,s=j((t,c)=>K(t,"name",{value:c,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.isReplayableRequest=isReplayableRequest,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)}j(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")}j(isReplayableRequest,"isReplayableRequest"),s(isReplayableRequest,"isReplayableRequest");function edgeGone(t,c){return c.headersSent||c.writableEnded||c.destroyed===!0||t.aborted===!0}j(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}j(declaredFirstByteMs,"declaredFirstByteMs"),s(declaredFirstByteMs,"declaredFirstByteMs");function armFirstByteGuard(t){let c,$=!1,q=!1,w=(0,body_stall_1.requestBodyArriving)(t.req)?void 0:Date.now();const O=s(()=>{w??=Date.now()},"onBodyEnd");w===void 0&&t.req.once("end",O);const U=s(()=>(w===void 0&&!(0,body_stall_1.requestBodyArriving)(t.req)&&(w=Date.now()),w===void 0?0:Date.now()-w),"heldMs"),D=s(S=>{c=setTimeout(A,S),c.unref?.()},"arm"),A=s(()=>{if($||!t.pending())return;const S=U();if(t.ceilingMs<=0){if(w!==void 0&&S>=t.firstByteMs){t.onWedged(`no response within ${S}ms (wedged socket) \u2014 evicted + respawning`);return}D(w===void 0?t.firstByteMs:t.firstByteMs-S);return}(0,pod_probe_1.probePodAlive)(t.socketPath,void 0,t.serviceKey).then(x=>{if($||!t.pending())return;const T=U();if(!x){t.onWedged(`no response within ${T}ms and no answer on ${types_1.POD_ALIVE_PATH} (wedged socket) \u2014 evicted + respawning`);return}if(T>=t.ceilingMs){t.onCeiling(`alive on ${types_1.POD_ALIVE_PATH} but no response within ${T}ms (ceiling ${t.ceilingMs}ms) \u2014 giving up on this request, pod kept`,x);return}!q&&w!==void 0&&(q=!0,t.onSlow(T,x)),D(Math.max(t.firstByteMs,types_1.POD_ALIVE_PROBE_TIMEOUT_MS))})},"onDeadline");return D(t.firstByteMs),{clear:s(()=>{$=!0,c&&clearTimeout(c),t.req.removeListener("end",O)},"clear")}}j(armFirstByteGuard,"armFirstByteGuard"),s(armFirstByteGuard,"armFirstByteGuard");function createProxy(t,c,$){const{recordEdge:q,finishTrace:w,traceStep:O,emit:U}=c,D=s(async e=>{if(!e.lens||!t.lensSupervisor)return"unsupported";let r;const o=new Promise(p=>{r=setTimeout(()=>p("timeout"),types_1.LENS_REQUEST_RECOVERY_WAIT_MS),r.unref?.()}),g=t.lensSupervisor.recover(e.lens,"proxy-connection").then(p=>p?"ready":"failed").catch(()=>"failed"),h=await Promise.race([g,o]);r&&clearTimeout(r);const m=t.lensSupervisor.status(e.lens);return h==="failed"&&(!m||m.state==="ready")?"unsupported":h},"waitForManagedRecovery"),A=s((e,r,o,g,h,m)=>{if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}const p=e.lens?t.lensSupervisor?.status(e.lens):void 0,E=m==="timeout"||p?.state==="starting"||p?.state==="replacing",v=(0,gateway_errors_1.ensureRequestId)(r),d=e.lens?{project:e.project,reason:p?.reason??"connection-failure",generation:p?.generation??0,...E?{retryAfterMs:2e3}:{}}:void 0,n=E?503:502,a=E?"starting":"unavailable";console.error(`[gateway] ${new Date().toISOString()} upstream-error traceId=${v} project=${e.project} target=${String(g)} state=${p?.state??"unmanaged"} generation=${p?.generation??0}: ${h.message}`),U("warning",`upstream unavailable: ${e.project}`,`cluster-upstream:${e.project}`,{kind:"upstream-error",traceId:v,project:e.project,target:String(g),state:p?.state??"unmanaged",generation:p?.generation,error:h.message}),(0,edge_1.sendError)(o,r,n,a,E?`Upstream ${e.project} is restarting.`:`Upstream ${e.project} unavailable.`,d,E?{"retry-after":"2"}:void 0)},"sendTerminalUpstreamError"),S=(0,body_stall_1.createBodyStallGuard)(t.bodyStallMs??types_1.BODY_STALL_MS),x={keepAlive:!0,keepAliveMsecs:3e4,maxSockets:1024,maxFreeSockets:256},T=new http.Agent(x),I=new(require("node:https")).Agent(x),L=s(e=>typeof e=="string"&&e.startsWith("https:")?I:T,"agentForTarget"),C=httpProxy.createProxyServer({xfwd:!1,secure:!1,proxyTimeout:12e4,agent:T}),N=s((e,r,o)=>{const g=s(()=>{o()||e.destroyed||e.destroy()},"onEdgeClose");r.once("close",g),e.once("close",()=>r.removeListener("close",g))},"propagateEdgeCancel");C.on("proxyReq",(e,r,o)=>{for(const g of UPSTREAM_CONNECTION_HEADERS)e.removeHeader(g);e.setHeader("connection","keep-alive"),N(e,o,()=>o.writableEnded)}),C.on("proxyRes",(e,r,o)=>{const g=r.__lensmcpPod;g&&(e.headers["x-lensmcp-pod"]=g),delete e.headers["x-powered-by"],e.headers.server="LensMCP",o&&N(e,o,()=>o.writableEnded),o&&S.armResponse(o)});const H=s((e,r,o,g)=>{(0,edge_1.stampForwardingHeaders)(t,r),S.arm(r);const h={},m=r.method==="GET"||r.method==="HEAD"||r.method==="OPTIONS";for(const[i,f]of Object.entries(r.headers))f===void 0||i.startsWith(":")||HOP_BY_HOP.has(i)||m&&(i==="content-length"||i==="transfer-encoding")||(h[i]=f);let p=http,E;if(typeof e=="object")E={socketPath:e.socketPath,path:r.url,method:r.method,headers:h,agent:T};else{const i=new URL(e),f=i.protocol==="https:";p=f?require("node:https"):http,h.host=i.host,E={protocol:i.protocol,hostname:i.hostname,port:i.port||(f?443:80),path:r.url,method:r.method,headers:h,agent:L(e)}}const v=o;v.__lensmcpErrContained||(v.__lensmcpErrContained=!0,o.on("error",()=>{}));let d;const n=s(()=>{o.writableEnded||u.destroy()},"onReqGone"),a=s(()=>{d&&!d.destroyed&&d.destroy()},"onEdgeClose"),l=s(()=>{r.removeListener("error",n),r.removeListener("aborted",n),r.removeListener("close",n),o.removeListener("close",a)},"detach"),u=p.request(E,i=>{if(d=i,g.onResponse(),o.headersSent||o.writableEnded||o.destroyed){i.destroy();return}const f={};for(const[P,y]of Object.entries(i.headers))y===void 0||HOP_BY_HOP.has(P)||(f[P]=y);delete f["x-powered-by"],f.server="LensMCP";const R=r.__lensmcpPod;R&&(f["x-lensmcp-pod"]=R);try{o.writeHead(i.statusCode??502,f)}catch(P){console.error("[gateway] h2 relay: bad response header, resetting stream:",P.message),i.destroy(),o.destroyed||o.destroy(P);return}i.pipe(o),S.armResponse(o);const k=s(()=>{!o.writableEnded&&!o.destroyed&&o.destroy()},"abortEdge");i.once("error",k),i.once("close",k),o.on("close",a)});u.once("error",i=>g.onError(i)),u.once("close",l),r.on("error",n),r.on("aborted",n),r.on("close",n),m?u.end():r.pipe(u)},"pipeUpstream"),W=s(async(e,r,o)=>{const g=r.__lensmcpTrace;if(e.pool){const d=e.svc;d&&(d.lastUsed=Date.now(),d.inflight+=1,o.on("close",()=>{d.inflight-=1,d.lastUsed=Date.now()}));let n=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs);if(!n&&d&&(O(g,"cold-start",{project:e.project}),await $.ensureUp(d)&&(n=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs))),d&&n&&$.markUp(d,"observed"),d&&$.scaleUp(d),!n){const y=d?.lastErrors?.length?` Service errors: ${d.lastErrors.join("; ")}`:"";(0,edge_1.sendError)(o,r,503,"unavailable",`Service ${e.project} is not reachable (no pods in ${e.pool.dir}).${y} The watcher recovers and the next request retries automatically.`);return}const a=path.basename(n,".sock");r.__lensmcpPod=a,O(g,"pod-select",{pod:a,pods:e.pool.socks.length,strategy:"round-robin"});let l=!1;const u=s((y,b="unlink")=>{if(e.pool.socks=e.pool.socks.filter(_=>_!==n),e.pool.idx=-1,b==="unlink")try{fs.unlinkSync(n)}catch{}console.error(`[gateway] pod ${a} of ${e.project}: ${y}`)},"evictPod"),i=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(()=>!l&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:s(y=>{l=!0,u(y),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${a} of ${e.project} was unresponsive \u2014 evicted, retry.`)},"onWedged"),onCeiling:s(y=>{l=!0,u(y,"keep"),(0,edge_1.sendError)(o,r,504,"unavailable",`Pod ${a} of ${e.project} did not answer in time \u2014 retry, or raise cluster.firstByteMs.`)},"onCeiling"),onSlow:s((y,b)=>{console.log(`[gateway] pod ${a} of ${e.project}: slow first byte, pod alive (${y}ms, ${b.inflight} in-flight, ${b.activeWork} background) \u2014 waiting, not evicting. Set cluster.firstByteMs on ${e.project} to size this route's budget.`)},"onSlow")});o.on("close",()=>i.clear());const f=n,R=isReplayableRequest(r);let k=0;const P=s(()=>{k+=1,H({socketPath:f},r,o,{onResponse:s(()=>{i.clear(),l=!0},"onResponse"),onError:s(y=>{if(!l){if(edgeGone(r,o)){i.clear(),l=!0;return}(0,pod_probe_1.podConnectionVerdict)(f,y).then(b=>{if(!l){if(b==="alive"&&k<2&&R&&!edgeGone(r,o)){console.log(`[gateway] pod ${a} of ${e.project}: ${y.message} on one connection, the pod still accepts \u2014 sending the ${r.method} again on a fresh one`),P();return}if(i.clear(),l=!0,b==="gone"){u(y.message),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${a} of ${e.project} unavailable \u2014 retry.`);return}console.error(`[gateway] pod ${a} of ${e.project}: ${y.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 ${a} of ${e.project} dropped the connection \u2014 retry.`)}})}},"onError")})},"sendToPod");P();return}const h=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let m=0,p=!1;const E=isReplayableRequest(r),v=s(()=>{if(edgeGone(r,o))return;const d=e.target;H(d,r,o,{onResponse:s(()=>{},"onResponse"),onError:s(n=>{if(isConnError(n)&&(m+=1),e.lens&&isConnError(n)&&E&&m>=2&&!p&&!edgeGone(r,o)){p=!0,D(e).then(a=>{if(!edgeGone(r,o)){if(a==="ready"){v();return}if(a==="unsupported"&&Date.now()<h){setTimeout(v,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}A(e,r,o,e.target,n,a)}});return}if(isConnError(n)&&E&&Date.now()<h&&!edgeGone(r,o)){setTimeout(v,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}if(o.headersSent||o.writableEnded||o.destroyed){o.destroyed||o.destroy();return}A(e,r,o,d,n)},"onError")})},"tryTcp");v()},"forwardNative");return{proxy:C,forward:s(async(e,r,o)=>{const g=Date.now(),h=r.__lensmcpTrace;if(o.on("close",()=>{q(e,Date.now()-g,o.statusCode??0,r.__lensmcpCaller),w(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),S.arm(r),(r.httpVersionMajor??1)>=2){await W(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 a=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs);if(!a&&n&&(O(h,"cold-start",{project:e.project}),await $.ensureUp(n)&&(a=(0,discovery_1.pickSock)(e.pool,t.scanTtlMs))),n&&a&&$.markUp(n,"observed"),n&&$.scaleUp(n),!a){const _=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}).${_} The watcher recovers and the next request retries automatically.`);return}const l=path.basename(a,".sock");r.__lensmcpPod=l,O(h,"pod-select",{pod:l,pods:e.pool.socks.length,strategy:"round-robin"});let u=!1;const i=s((_,M="unlink")=>{if(e.pool.socks=e.pool.socks.filter(B=>B!==a),e.pool.idx=-1,M==="unlink")try{fs.unlinkSync(a)}catch{}console.error(`[gateway] pod ${l} of ${e.project}: ${_}`)},"evictPod"),f=armFirstByteGuard({socketPath:a,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(()=>!u&&!o.headersSent&&!o.writableEnded,"pending"),onWedged:s(_=>{u=!0,i(_),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${l} of ${e.project} was unresponsive \u2014 evicted, retry.`)},"onWedged"),onCeiling:s(_=>{u=!0,i(_,"keep"),(0,edge_1.sendError)(o,r,504,"unavailable",`Pod ${l} of ${e.project} did not answer in time \u2014 retry, or raise cluster.firstByteMs.`)},"onCeiling"),onSlow:s((_,M)=>{console.log(`[gateway] pod ${l} of ${e.project}: slow first byte, pod alive (${_}ms, ${M.inflight} in-flight, ${M.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 R=Math.max(12e4,types_1.POD_SLOW_FIRST_BYTE_MS+6e4),k=a,P=isReplayableRequest(r);let y=0;const b=s(()=>{y+=1;const _=Date.now();C.web(r,o,{target:{socketPath:k},agent:T,proxyTimeout:R},M=>{if(!u){if(edgeGone(r,o)){f.clear(),u=!0;return}(0,pod_probe_1.podConnectionVerdict)(k,M).then(B=>{if(u)return;const F=Date.now()-_>=R-250;if(B==="alive"&&y<2&&P&&!F&&!edgeGone(r,o)){console.log(`[gateway] pod ${l} of ${e.project}: ${M.message} on one connection, the pod still accepts \u2014 sending the ${r.method} again on a fresh one`),b();return}if(f.clear(),u=!0,B==="gone"){i(M.message),(0,edge_1.sendError)(o,r,502,"unavailable",`Pod ${l} of ${e.project} unavailable \u2014 retry.`);return}console.error(`[gateway] pod ${l} of ${e.project}: ${M.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 ${l} of ${e.project} dropped the connection \u2014 retry.`)})}})},"sendToPod");b();return}const m=Date.now()+types_1.UPSTREAM_HEAL_WINDOW_MS;let p=0,E=!1;const v=isReplayableRequest(r),d=s(()=>{if(edgeGone(r,o))return;const n=e.target,a={target:n,autoRewrite:typeof n=="string",changeOrigin:!0,agent:L(n)};C.web(r,o,a,l=>{if(isConnError(l)&&(p+=1),e.lens&&isConnError(l)&&v&&p>=2&&!E&&!edgeGone(r,o)){E=!0,D(e).then(u=>{if(!edgeGone(r,o)){if(u==="ready"){d();return}if(u==="unsupported"&&Date.now()<m){setTimeout(d,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}A(e,r,o,e.target,l,u)}});return}if(isConnError(l)&&v&&Date.now()<m&&!edgeGone(r,o)){setTimeout(d,types_1.UPSTREAM_HEAL_STEP_MS).unref?.();return}o.destroyed||A(e,r,o,n,l)})},"tryTcp");d()},"forward"),agentForTarget:L,destroyAgents:s(()=>{S.stop(),T.destroy(),I.destroy()},"destroyAgents"),closeProxy:s(()=>{try{C.close?.()}catch{}},"closeProxy")}}j(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");