@lensmcp/cluster 1.18.14 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/basic-ssl.js +1 -1
  2. package/build-scope-patterns.js +1 -1
  3. package/create-webpack-dev.js +1 -1
  4. package/create-webpack-prod.js +1 -1
  5. package/executors/build/build.impl.js +1 -1
  6. package/executors/gateway/gateway-errors.js +1 -1
  7. package/executors/gateway/gateway.impl.js +1 -1
  8. package/executors/gateway/gateway.lib.js +1 -1
  9. package/executors/gateway/health-check.js +1 -1
  10. package/executors/gateway/jwks-verify.js +1 -1
  11. package/executors/gateway/main.prod-gateway.js +2 -2
  12. package/executors/gateway/main.rollout.js +4 -4
  13. package/executors/gateway/manifest.js +1 -1
  14. package/executors/gateway/metrics.js +1 -1
  15. package/executors/gateway/otel-tracing.js +1 -1
  16. package/executors/gateway/prod-gateway.lib.js +1 -1
  17. package/executors/gateway/prod-runtime/access-log.js +1 -1
  18. package/executors/gateway/prod-runtime/app.js +1 -1
  19. package/executors/gateway/prod-runtime/auth.js +1 -1
  20. package/executors/gateway/prod-runtime/cors.js +1 -1
  21. package/executors/gateway/prod-runtime/edge.js +1 -1
  22. package/executors/gateway/prod-runtime/handler.js +1 -1
  23. package/executors/gateway/prod-runtime/hooks.js +1 -1
  24. package/executors/gateway/prod-runtime/observability.js +1 -1
  25. package/executors/gateway/prod-runtime/rollout.js +1 -1
  26. package/executors/gateway/prod-runtime/routing.js +1 -1
  27. package/executors/gateway/prod-runtime/server.js +1 -1
  28. package/executors/gateway/prod-runtime/trust.js +1 -1
  29. package/executors/gateway/prod-runtime/upgrade.js +2 -2
  30. package/executors/gateway/prod-runtime/upstream.js +1 -1
  31. package/executors/gateway/providers-prod.js +1 -1
  32. package/executors/gateway/rate-limit.js +2 -2
  33. package/executors/gateway/registry-source.js +1 -1
  34. package/executors/gateway/rollout-ops.js +2 -2
  35. package/executors/gateway/runtime/auth.js +1 -1
  36. package/executors/gateway/runtime/chooser.js +7 -7
  37. package/executors/gateway/runtime/control.js +1 -1
  38. package/executors/gateway/runtime/dev-auth.js +1 -1
  39. package/executors/gateway/runtime/discovery.js +1 -1
  40. package/executors/gateway/runtime/edge.js +1 -1
  41. package/executors/gateway/runtime/handler.js +1 -1
  42. package/executors/gateway/runtime/hmr-reload.js +1 -1
  43. package/executors/gateway/runtime/hooks.js +1 -1
  44. package/executors/gateway/runtime/lens-children.js +1 -1
  45. package/executors/gateway/runtime/lifecycle.js +3 -3
  46. package/executors/gateway/runtime/observability.js +2 -2
  47. package/executors/gateway/runtime/pod-env.js +2 -2
  48. package/executors/gateway/runtime/proxy.js +1 -1
  49. package/executors/gateway/runtime/route-registry.js +1 -1
  50. package/executors/gateway/runtime/scope.js +1 -1
  51. package/executors/gateway/runtime/server.js +2 -2
  52. package/executors/gateway/runtime/service-keys.js +1 -1
  53. package/executors/gateway/runtime/types.js +1 -1
  54. package/executors/gateway/runtime/upgrade.js +1 -1
  55. package/executors/gateway/runtime/workspace-registry.js +1 -1
  56. package/executors/gateway/ssrf-guard.js +1 -1
  57. package/executors/serve/serve.impl.js +1 -1
  58. package/executors/trust/trust.impl.js +3 -3
  59. package/gateway.js +1 -1
  60. package/index.js +1 -1
  61. package/main.devserver.js +5 -5
  62. package/package.json +4 -4
  63. package/tsgo-check-plugin.js +4 -4
  64. package/typecheck-bus.js +3 -3
@@ -1 +1 @@
1
- "use strict";var H=Object.defineProperty;var d=(e,a)=>H(e,"name",{value:a,configurable:!0});var B=Object.defineProperty,l=d((e,a)=>B(e,"name",{value:a,configurable:!0}),"l"),x=Object.defineProperty,n=l((e,a)=>x(e,"name",{value:a,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.startProdGateway=startProdGateway;const observability_1=require("./observability"),routing_1=require("./routing"),rollout_1=require("./rollout"),upstream_1=require("./upstream"),auth_1=require("./auth"),hooks_1=require("./hooks"),cors_1=require("./cors"),handler_1=require("./handler"),upgrade_1=require("./upgrade"),app_1=require("./app");async function startProdGateway(e){const a=e.ports?.length?e.ports:[e.tls?8443:8080],P=e.authenticate??(t=>{if(t==="jwt")throw new Error("jwt route but no authenticator configured")}),j=(e.versionHeader??"x-lensmcp-version").toLowerCase(),C=!!e.accessLog,O=new Set((e.corsCredentialOrigins??[]).map(t=>t.toLowerCase())),R=new Map(Object.entries(e.serviceKeys??{}).map(([t,i])=>[i,t])),h={},r=(0,observability_1.createObservability)(e,()=>h.current.routes),o=(0,routing_1.createRouteTable)(e,r);h.current=o;const s={opts:e,table:o,keyToProject:R,versionHeader:j,accessLog:C,corsCredentialOrigins:O},w=(0,rollout_1.createRollout)(e),y=(0,upstream_1.createUpstream)(e,r),b=(0,auth_1.createEdgeAuth)(s,r,w,P),u=(0,hooks_1.createHooks)(e.hooks??{}),c=new WeakMap,g=new WeakMap,m=new WeakMap,q=(0,cors_1.createCors)(s,c),L=(0,handler_1.createHandler)({rt:s,obs:r,cors:q,auth:b,upstream:y,rollout:w,hooks:u,reqState:c,spanByReq:g,ctxByReq:m}),M=(0,upgrade_1.createUpgrade)({rt:s,auth:b,upstream:y,hooks:u}),{buildApp:S}=(0,app_1.createApp)({rt:s,obs:r,cors:q,hooks:u,handler:L,upgrade:M,reqState:c,spanByReq:g,ctxByReq:m}),G=r.startFlusher();o.rebuild(o.manifests());const v=[],k=[];for(const t of a){const i=await S();await i.listen({port:t,host:"0.0.0.0"});const p=i.server.address(),_=typeof p=="object"&&p?p.port:t;k.push(_),console.log(`[gateway] listening on ${e.tls?"https":"http"}://0.0.0.0:${_}`),v.push(i)}e.healthChecker?.start();let f=!1;return{ports:k,routes:n(()=>o.routes,"routes"),status:r.buildStatusSnapshot,stop:n(async()=>{f||(f=!0,clearInterval(G),o.stop(),e.healthChecker?.stop(),e.rateLimiter?.stop(),e.metrics?.stop(),r.emit("info","gateway down","gateway-down",{kind:"gateway-down"}),await Promise.all(v.map(t=>t.close())))},"stop")}}d(startProdGateway,"startProdGateway"),l(startProdGateway,"startProdGateway"),n(startProdGateway,"startProdGateway");
1
+ "use strict";var B=Object.defineProperty;var l=(e,o)=>B(e,"name",{value:o,configurable:!0});var x=Object.defineProperty,n=l((e,o)=>x(e,"name",{value:o,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.startProdGateway=startProdGateway;const observability_1=require("./observability"),routing_1=require("./routing"),rollout_1=require("./rollout"),upstream_1=require("./upstream"),auth_1=require("./auth"),hooks_1=require("./hooks"),cors_1=require("./cors"),handler_1=require("./handler"),upgrade_1=require("./upgrade"),app_1=require("./app");async function startProdGateway(e){const o=e.ports?.length?e.ports:[e.tls?8443:8080],f=e.authenticate??(t=>{if(t==="jwt")throw new Error("jwt route but no authenticator configured")}),j=(e.versionHeader??"x-lensmcp-version").toLowerCase(),C=!!e.accessLog,P=new Set((e.corsCredentialOrigins??[]).map(t=>t.toLowerCase())),O=new Map(Object.entries(e.serviceKeys??{}).map(([t,i])=>[i,t])),h={},r=(0,observability_1.createObservability)(e,()=>h.current.routes),a=(0,routing_1.createRouteTable)(e,r);h.current=a;const s={opts:e,table:a,keyToProject:O,versionHeader:j,accessLog:C,corsCredentialOrigins:P},d=(0,rollout_1.createRollout)(e),w=(0,upstream_1.createUpstream)(e,r),y=(0,auth_1.createEdgeAuth)(s,r,d,f),u=(0,hooks_1.createHooks)(e.hooks??{}),c=new WeakMap,g=new WeakMap,b=new WeakMap,q=(0,cors_1.createCors)(s,c),R=(0,handler_1.createHandler)({rt:s,obs:r,cors:q,auth:y,upstream:w,rollout:d,hooks:u,reqState:c,spanByReq:g,ctxByReq:b}),L=(0,upgrade_1.createUpgrade)({rt:s,auth:y,upstream:w,hooks:u}),{buildApp:M}=(0,app_1.createApp)({rt:s,obs:r,cors:q,hooks:u,handler:R,upgrade:L,reqState:c,spanByReq:g,ctxByReq:b}),S=r.startFlusher();a.rebuild(a.manifests());const m=[],k=[];for(const t of o){const i=await M();await i.listen({port:t,host:"0.0.0.0"});const p=i.server.address(),_=typeof p=="object"&&p?p.port:t;k.push(_),console.log(`[gateway] listening on ${e.tls?"https":"http"}://0.0.0.0:${_}`),m.push(i)}e.healthChecker?.start();let v=!1;return{ports:k,routes:n(()=>a.routes,"routes"),status:r.buildStatusSnapshot,stop:n(async()=>{v||(v=!0,clearInterval(S),a.stop(),e.healthChecker?.stop(),e.rateLimiter?.stop(),e.metrics?.stop(),r.emit("info","gateway down","gateway-down",{kind:"gateway-down"}),await Promise.all(m.map(t=>t.close())))},"stop")}}l(startProdGateway,"startProdGateway"),n(startProdGateway,"startProdGateway");
@@ -1 +1 @@
1
- "use strict";var c=Object.defineProperty;var a=(t,e)=>c(t,"name",{value:e,configurable:!0});var d=Object.defineProperty,p=a((t,e)=>d(t,"name",{value:e,configurable:!0}),"p"),x=Object.defineProperty,r=p((t,e)=>x(t,"name",{value:e,configurable:!0}),"r");Object.defineProperty(exports,"__esModule",{value:!0}),exports.stampIdentity=exports.stripTrust=exports.HOP_BY_HOP=exports.TRUST_HEADERS=void 0,exports.TRUST_HEADERS=["x-internal-token","x-api-key-id","x-user-id","x-tenant-id","x-user-roles","x-user-permissions"],exports.HOP_BY_HOP=["connection","proxy-connection","keep-alive"];const stripTrust=r(t=>{for(const e of exports.TRUST_HEADERS)delete t.headers[e];for(const e of exports.HOP_BY_HOP)delete t.headers[e]},"stripTrust");exports.stripTrust=stripTrust;const stampIdentity=r((t,e)=>{const i=r((u,s)=>{(typeof s=="string"&&s!==""||typeof s=="number")&&(t.headers[u]=String(s))},"set");i("x-user-id",e.sub),i("x-tenant-id",e.tid);const o=e.roles;Array.isArray(o)&&o.length&&(t.headers["x-user-roles"]=o.map(String).join(","));const n=e.perms;Array.isArray(n)&&n.length&&(t.headers["x-user-permissions"]=n.map(String).join(","))},"stampIdentity");exports.stampIdentity=stampIdentity;
1
+ "use strict";var d=Object.defineProperty;var p=(t,e)=>d(t,"name",{value:e,configurable:!0});var x=Object.defineProperty,r=p((t,e)=>x(t,"name",{value:e,configurable:!0}),"r");Object.defineProperty(exports,"__esModule",{value:!0}),exports.stampIdentity=exports.stripTrust=exports.HOP_BY_HOP=exports.TRUST_HEADERS=void 0,exports.TRUST_HEADERS=["x-internal-token","x-api-key-id","x-user-id","x-tenant-id","x-user-roles","x-user-permissions"],exports.HOP_BY_HOP=["connection","proxy-connection","keep-alive"];const stripTrust=r(t=>{for(const e of exports.TRUST_HEADERS)delete t.headers[e];for(const e of exports.HOP_BY_HOP)delete t.headers[e]},"stripTrust");exports.stripTrust=stripTrust;const stampIdentity=r((t,e)=>{const i=r((a,s)=>{(typeof s=="string"&&s!==""||typeof s=="number")&&(t.headers[a]=String(s))},"set");i("x-user-id",e.sub),i("x-tenant-id",e.tid);const o=e.roles;Array.isArray(o)&&o.length&&(t.headers["x-user-roles"]=o.map(String).join(","));const n=e.perms;Array.isArray(n)&&n.length&&(t.headers["x-user-permissions"]=n.map(String).join(","))},"stampIdentity");exports.stampIdentity=stampIdentity;
@@ -1,4 +1,4 @@
1
- "use strict";var j=Object.defineProperty;var v=(d,s)=>j(d,"name",{value:s,configurable:!0});var U=Object.defineProperty,g=v((d,s)=>U(d,"name",{value:s,configurable:!0}),"g"),S=Object.defineProperty,m=g((d,s)=>S(d,"name",{value:s,configurable:!0}),"m");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createUpgrade=createUpgrade;const tslib_1=require("tslib"),net=tslib_1.__importStar(require("node:net")),manifest_1=require("../manifest"),edge_1=require("./edge"),trust_1=require("./trust");function createUpgrade(d){const{rt:s,auth:b,upstream:$,hooks:p}=d,_=m(async(e,r,h)=>{(0,trust_1.stripTrust)(e);const A=(e.headers.host||"").split(":")[0],w=(0,edge_1.canonicalPath)(e.url??"/");if(w===void 0){r.destroy();return}const o=(0,manifest_1.matchRoute)(s.table.routes,A,e.url??"/");if(!o){r.destroy();return}const t=p.any?p.upgradeContext(e,r,void 0):void 0;t&&(t.route=o);const i=o.internal?void 0:s.opts.identify?.(e);let f;if(o.internal){const a=b.internal(e,o,void 0);if(!a.ok){r.destroy();return}f=a.caller,t&&(t.caller=f)}else{if(!b.public(e,o,w,i,e.socket.remoteAddress??"",void 0).ok){r.destroy();return}if(t&&(t.claims=i),t&&p.has("afterAuth")&&(await p.run("afterAuth",t),t.responded()))return}if(o.prependPrefix){const a=e.url??"/",n=o.prependPrefix;a!==n&&!a.startsWith(n+"/")&&(e.url=n+a)}if(t&&p.has("onUpgrade")&&(await p.run("onUpgrade",t),t.responded()))return;const P=(0,edge_1.hdr)(e.headers[s.versionHeader]),y=await $.resolveUpstream(o,void 0,P);if(!y||!("url"in y)){r.destroy();return}const x=new URL(y.url),c=net.connect(Number(x.port)||(x.protocol==="https:"?443:80),x.hostname,()=>{const a=new Set(["x-internal-token","x-api-key-id","x-api-key","x-user-id","x-tenant-id","x-user-roles","x-user-permissions","connection","proxy-connection","keep-alive"]),n=[`${e.method} ${e.url} HTTP/1.1`,"Connection: Upgrade"];for(let u=0;u<e.rawHeaders.length;u+=2){const l=e.rawHeaders[u];a.has(l.toLowerCase())||n.push(`${l}: ${e.rawHeaders[u+1]}`)}f&&n.push(`x-api-key-id: ${f}`);const k=s.opts.serviceKeys?.[o.service];if(k&&n.push(`x-internal-token: ${k}`),i){i.sub&&n.push(`x-user-id: ${String(i.sub)}`),i.tid&&n.push(`x-tenant-id: ${String(i.tid)}`);const u=i.roles;Array.isArray(u)&&u.length&&n.push(`x-user-roles: ${u.map(String).join(",")}`);const l=i.perms;Array.isArray(l)&&l.length&&n.push(`x-user-permissions: ${l.map(String).join(",")}`)}c.write(n.join(`\r
1
+ "use strict";var U=Object.defineProperty;var g=(l,i)=>U(l,"name",{value:i,configurable:!0});var S=Object.defineProperty,m=g((l,i)=>S(l,"name",{value:i,configurable:!0}),"m");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createUpgrade=createUpgrade;const tslib_1=require("tslib"),net=tslib_1.__importStar(require("node:net")),manifest_1=require("../manifest"),edge_1=require("./edge"),trust_1=require("./trust");function createUpgrade(l){const{rt:i,auth:v,upstream:$,hooks:d}=l,k=m(async(e,r,h)=>{(0,trust_1.stripTrust)(e);const _=(e.headers.host||"").split(":")[0],w=(0,edge_1.canonicalPath)(e.url??"/");if(w===void 0){r.destroy();return}const s=(0,manifest_1.matchRoute)(i.table.routes,_,e.url??"/");if(!s){r.destroy();return}const t=d.any?d.upgradeContext(e,r,void 0):void 0;t&&(t.route=s);const o=s.internal?void 0:i.opts.identify?.(e);let f;if(s.internal){const a=v.internal(e,s,void 0);if(!a.ok){r.destroy();return}f=a.caller,t&&(t.caller=f)}else{if(!v.public(e,s,w,o,e.socket.remoteAddress??"",void 0).ok){r.destroy();return}if(t&&(t.claims=o),t&&d.has("afterAuth")&&(await d.run("afterAuth",t),t.responded()))return}if(s.prependPrefix){const a=e.url??"/",n=s.prependPrefix;a!==n&&!a.startsWith(n+"/")&&(e.url=n+a)}if(t&&d.has("onUpgrade")&&(await d.run("onUpgrade",t),t.responded()))return;const A=(0,edge_1.hdr)(e.headers[i.versionHeader]),y=await $.resolveUpstream(s,void 0,A);if(!y||!("url"in y)){r.destroy();return}const x=new URL(y.url),p=net.connect(Number(x.port)||(x.protocol==="https:"?443:80),x.hostname,()=>{const a=new Set(["x-internal-token","x-api-key-id","x-api-key","x-user-id","x-tenant-id","x-user-roles","x-user-permissions","connection","proxy-connection","keep-alive"]),n=[`${e.method} ${e.url} HTTP/1.1`,"Connection: Upgrade"];for(let u=0;u<e.rawHeaders.length;u+=2){const c=e.rawHeaders[u];a.has(c.toLowerCase())||n.push(`${c}: ${e.rawHeaders[u+1]}`)}f&&n.push(`x-api-key-id: ${f}`);const b=i.opts.serviceKeys?.[s.service];if(b&&n.push(`x-internal-token: ${b}`),o){o.sub&&n.push(`x-user-id: ${String(o.sub)}`),o.tid&&n.push(`x-tenant-id: ${String(o.tid)}`);const u=o.roles;Array.isArray(u)&&u.length&&n.push(`x-user-roles: ${u.map(String).join(",")}`);const c=o.perms;Array.isArray(c)&&c.length&&n.push(`x-user-permissions: ${c.map(String).join(",")}`)}p.write(n.join(`\r
2
2
  `)+`\r
3
3
  \r
4
- `),h?.length&&c.write(h),r.pipe(c),c.pipe(r)});c.on("error",()=>r.destroy()),r.on("error",()=>c.destroy())},"run");return(e,r,h)=>{_(e,r,h)}}v(createUpgrade,"createUpgrade"),g(createUpgrade,"createUpgrade"),m(createUpgrade,"createUpgrade");
4
+ `),h?.length&&p.write(h),r.pipe(p),p.pipe(r)});p.on("error",()=>r.destroy()),r.on("error",()=>p.destroy())},"run");return(e,r,h)=>{k(e,r,h)}}g(createUpgrade,"createUpgrade"),m(createUpgrade,"createUpgrade");
@@ -1 +1 @@
1
- "use strict";var m=Object.defineProperty;var c=(e,r)=>m(e,"name",{value:r,configurable:!0});var l=Object.defineProperty,p=c((e,r)=>l(e,"name",{value:r,configurable:!0}),"p"),u=Object.defineProperty,s=p((e,r)=>u(e,"name",{value:r,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createUpstream=createUpstream;function createUpstream(e,r){return{resolveUpstream:s(async(t,o,i)=>{if(t.upstream)return{url:t.upstream};const n={...i?{version:i}:{},...e.healthChecker?{isHealthy:s(v=>e.healthChecker.isHealthy(v),"isHealthy")}:{}};let a=e.pods?.pick(t.service,n);return!a&&e.pods?.ensureUp&&(r.step(o,"ensure-up",{project:t.service}),await e.pods.ensureUp(t.service)&&(a=e.pods.pick(t.service,n))),a},"resolveUpstream")}}c(createUpstream,"createUpstream"),p(createUpstream,"createUpstream"),s(createUpstream,"createUpstream");
1
+ "use strict";var l=Object.defineProperty;var p=(e,t)=>l(e,"name",{value:t,configurable:!0});var u=Object.defineProperty,s=p((e,t)=>u(e,"name",{value:t,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createUpstream=createUpstream;function createUpstream(e,t){return{resolveUpstream:s(async(r,o,c)=>{if(r.upstream)return{url:r.upstream};const i={...c?{version:c}:{},...e.healthChecker?{isHealthy:s(n=>e.healthChecker.isHealthy(n),"isHealthy")}:{}};let a=e.pods?.pick(r.service,i);return!a&&e.pods?.ensureUp&&(t.step(o,"ensure-up",{project:r.service}),await e.pods.ensureUp(r.service)&&(a=e.pods.pick(r.service,i))),a},"resolveUpstream")}}p(createUpstream,"createUpstream"),s(createUpstream,"createUpstream");
@@ -1 +1 @@
1
- "use strict";var S=Object.defineProperty;var y=(s,o)=>S(s,"name",{value:o,configurable:!0});var b=Object.defineProperty,p=y((s,o)=>b(s,"name",{value:o,configurable:!0}),"p"),w=Object.defineProperty,n=p((s,o)=>w(s,"name",{value:o,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.staticManifestProvider=staticManifestProvider,exports.fileManifestProvider=fileManifestProvider,exports.rolloutPodProvider=rolloutPodProvider,exports.endpointsPodProvider=endpointsPodProvider;const node_fs_1=require("node:fs"),manifest_1=require("./manifest"),toSpec=n(s=>Array.isArray(s)?{rules:[],cohorts:s}:{rules:s.rules??[],cohorts:s.cohorts??[]},"toSpec");function staticManifestProvider(s){return{list:n(()=>s,"list")}}y(staticManifestProvider,"staticManifestProvider"),p(staticManifestProvider,"staticManifestProvider"),n(staticManifestProvider,"staticManifestProvider");function parseManifests(s){let o;try{o=JSON.parse(s)}catch{return}const a=Array.isArray(o)?o:o?.manifests;if(!Array.isArray(a))return;const f=a.filter(i=>!!i&&typeof i=="object"&&typeof i.service=="string"&&Array.isArray(i.mounts));return f.length>0?f:void 0}y(parseManifests,"parseManifests"),p(parseManifests,"parseManifests"),n(parseManifests,"parseManifests");function fileManifestProvider(s,o={}){const a=o.onError??(()=>{});let f=[],i=-1;const t=new Set,e=n(()=>{if(!(0,node_fs_1.existsSync)(s))return;let l;try{l=(0,node_fs_1.statSync)(s).mtimeMs}catch{return}if(l===i)return;let h;try{h=(0,node_fs_1.readFileSync)(s,"utf8")}catch(v){a(v);return}const d=parseManifests(h);if(!d){a(new Error(`[gateway] rejecting invalid manifest file ${s} \u2014 keeping last-good`));return}i=l,f=d;for(const v of t)try{v(f)}catch(c){a(c)}},"reload");e();let r;try{r=(0,node_fs_1.watch)(s,{persistent:!1},()=>e())}catch{}const u=setInterval(e,o.pollMs??1e3);return u.unref?.(),{list:n(()=>f,"list"),watch:n(l=>(t.add(l),()=>t.delete(l)),"watch"),stop:n(()=>{try{r?.close()}catch{}clearInterval(u)},"stop")}}y(fileManifestProvider,"fileManifestProvider"),p(fileManifestProvider,"fileManifestProvider"),n(fileManifestProvider,"fileManifestProvider");function rolloutPodProvider(s){const o=new Map,a=n(t=>({cohorts:t.cohorts.map(e=>({...e,urls:e.urls.slice()})),select:(0,manifest_1.smoothWeightedSelector)(t.cohorts.map(e=>e.weight)),rr:t.cohorts.map(()=>-1),rules:t.rules??[]}),"build"),f=n(t=>{for(const[e,r]of Object.entries(t))o.set(e,a(toSpec(r)));for(const e of[...o.keys()])t[e]||o.delete(e)},"load");f(s);const i=n(t=>t.cohorts.findIndex(e=>e.urls.length>0),"firstNonEmpty");return{pick(t,e){const r=o.get(t);if(!r||r.cohorts.length===0)return;const u=e?.isHealthy,l=n((c,P)=>{const g=r.cohorts[c];if(!g||g.urls.length===0)return;const m=g.urls.length;for(let M=0;M<m;M++){r.rr[c]=(r.rr[c]+1)%m;const k=g.urls[r.rr[c]];if(!P||P(k))return k}},"pickUrl"),h=e?.version?r.cohorts.findIndex(c=>c.version===e.version&&c.urls.length>0):-1;if(h>=0){const c=l(h,u)??l(h);return c!==void 0?{url:c,version:r.cohorts[h].version}:void 0}let d=r.select();(!r.cohorts[d]||r.cohorts[d].urls.length===0)&&(d=i(r));let v=l(d,u);if(v===void 0&&u)for(let c=0;c<r.cohorts.length;c++){const P=l(c,u);if(P!==void 0){d=c,v=P;break}}return v===void 0&&(v=l(d)),v!==void 0?{url:v,version:r.cohorts[d].version}:void 0},endpoints(){const t=new Set;for(const e of o.values())for(const r of e.cohorts)for(const u of r.urls)t.add(u);return[...t]},resolveVersion(t,e){const r=o.get(t);return r&&r.rules.length?(0,manifest_1.pickTargetedVersion)(r.rules,e):void 0},bucket(t,e){const r=o.get(t);if(!r||r.cohorts.length===0)return;const u=r.cohorts[(0,manifest_1.bucketByWeight)(`${e}:${t}`,r.cohorts.map(l=>l.weight))];return u?{version:u.version}:void 0},ensureUp:n(async()=>!0,"ensureUp"),drop(t,e){const r=o.get(t);if(r&&"url"in e)for(const u of r.cohorts)u.urls=u.urls.filter(l=>l!==e.url)},update:f}}y(rolloutPodProvider,"rolloutPodProvider"),p(rolloutPodProvider,"rolloutPodProvider"),n(rolloutPodProvider,"rolloutPodProvider");function endpointsPodProvider(s){const o=n(i=>Object.fromEntries(Object.entries(i).map(([t,e])=>[t,[{version:"_flat",weight:1,urls:e}]])),"toCohorts"),a=rolloutPodProvider(o(s)),f=n(i=>i&&"url"in i?{url:i.url}:i,"unversion");return{...a,pick:n((i,t)=>f(a.pick(i,t)),"pick"),resolveVersion:n(()=>{},"resolveVersion"),bucket:n(()=>{},"bucket"),update:n(i=>a.update(o(i)),"update")}}y(endpointsPodProvider,"endpointsPodProvider"),p(endpointsPodProvider,"endpointsPodProvider"),n(endpointsPodProvider,"endpointsPodProvider");
1
+ "use strict";var b=Object.defineProperty;var p=(s,o)=>b(s,"name",{value:o,configurable:!0});var w=Object.defineProperty,n=p((s,o)=>w(s,"name",{value:o,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.staticManifestProvider=staticManifestProvider,exports.fileManifestProvider=fileManifestProvider,exports.rolloutPodProvider=rolloutPodProvider,exports.endpointsPodProvider=endpointsPodProvider;const node_fs_1=require("node:fs"),manifest_1=require("./manifest"),toSpec=n(s=>Array.isArray(s)?{rules:[],cohorts:s}:{rules:s.rules??[],cohorts:s.cohorts??[]},"toSpec");function staticManifestProvider(s){return{list:n(()=>s,"list")}}p(staticManifestProvider,"staticManifestProvider"),n(staticManifestProvider,"staticManifestProvider");function parseManifests(s){let o;try{o=JSON.parse(s)}catch{return}const a=Array.isArray(o)?o:o?.manifests;if(!Array.isArray(a))return;const f=a.filter(i=>!!i&&typeof i=="object"&&typeof i.service=="string"&&Array.isArray(i.mounts));return f.length>0?f:void 0}p(parseManifests,"parseManifests"),n(parseManifests,"parseManifests");function fileManifestProvider(s,o={}){const a=o.onError??(()=>{});let f=[],i=-1;const t=new Set,e=n(()=>{if(!(0,node_fs_1.existsSync)(s))return;let l;try{l=(0,node_fs_1.statSync)(s).mtimeMs}catch{return}if(l===i)return;let v;try{v=(0,node_fs_1.readFileSync)(s,"utf8")}catch(h){a(h);return}const d=parseManifests(v);if(!d){a(new Error(`[gateway] rejecting invalid manifest file ${s} \u2014 keeping last-good`));return}i=l,f=d;for(const h of t)try{h(f)}catch(c){a(c)}},"reload");e();let r;try{r=(0,node_fs_1.watch)(s,{persistent:!1},()=>e())}catch{}const u=setInterval(e,o.pollMs??1e3);return u.unref?.(),{list:n(()=>f,"list"),watch:n(l=>(t.add(l),()=>t.delete(l)),"watch"),stop:n(()=>{try{r?.close()}catch{}clearInterval(u)},"stop")}}p(fileManifestProvider,"fileManifestProvider"),n(fileManifestProvider,"fileManifestProvider");function rolloutPodProvider(s){const o=new Map,a=n(t=>({cohorts:t.cohorts.map(e=>({...e,urls:e.urls.slice()})),select:(0,manifest_1.smoothWeightedSelector)(t.cohorts.map(e=>e.weight)),rr:t.cohorts.map(()=>-1),rules:t.rules??[]}),"build"),f=n(t=>{for(const[e,r]of Object.entries(t))o.set(e,a(toSpec(r)));for(const e of[...o.keys()])t[e]||o.delete(e)},"load");f(s);const i=n(t=>t.cohorts.findIndex(e=>e.urls.length>0),"firstNonEmpty");return{pick(t,e){const r=o.get(t);if(!r||r.cohorts.length===0)return;const u=e?.isHealthy,l=n((c,g)=>{const y=r.cohorts[c];if(!y||y.urls.length===0)return;const P=y.urls.length;for(let m=0;m<P;m++){r.rr[c]=(r.rr[c]+1)%P;const k=y.urls[r.rr[c]];if(!g||g(k))return k}},"pickUrl"),v=e?.version?r.cohorts.findIndex(c=>c.version===e.version&&c.urls.length>0):-1;if(v>=0){const c=l(v,u)??l(v);return c!==void 0?{url:c,version:r.cohorts[v].version}:void 0}let d=r.select();(!r.cohorts[d]||r.cohorts[d].urls.length===0)&&(d=i(r));let h=l(d,u);if(h===void 0&&u)for(let c=0;c<r.cohorts.length;c++){const g=l(c,u);if(g!==void 0){d=c,h=g;break}}return h===void 0&&(h=l(d)),h!==void 0?{url:h,version:r.cohorts[d].version}:void 0},endpoints(){const t=new Set;for(const e of o.values())for(const r of e.cohorts)for(const u of r.urls)t.add(u);return[...t]},resolveVersion(t,e){const r=o.get(t);return r&&r.rules.length?(0,manifest_1.pickTargetedVersion)(r.rules,e):void 0},bucket(t,e){const r=o.get(t);if(!r||r.cohorts.length===0)return;const u=r.cohorts[(0,manifest_1.bucketByWeight)(`${e}:${t}`,r.cohorts.map(l=>l.weight))];return u?{version:u.version}:void 0},ensureUp:n(async()=>!0,"ensureUp"),drop(t,e){const r=o.get(t);if(r&&"url"in e)for(const u of r.cohorts)u.urls=u.urls.filter(l=>l!==e.url)},update:f}}p(rolloutPodProvider,"rolloutPodProvider"),n(rolloutPodProvider,"rolloutPodProvider");function endpointsPodProvider(s){const o=n(i=>Object.fromEntries(Object.entries(i).map(([t,e])=>[t,[{version:"_flat",weight:1,urls:e}]])),"toCohorts"),a=rolloutPodProvider(o(s)),f=n(i=>i&&"url"in i?{url:i.url}:i,"unversion");return{...a,pick:n((i,t)=>f(a.pick(i,t)),"pick"),resolveVersion:n(()=>{},"resolveVersion"),bucket:n(()=>{},"bucket"),update:n(i=>a.update(o(i)),"update")}}p(endpointsPodProvider,"endpointsPodProvider"),n(endpointsPodProvider,"endpointsPodProvider");
@@ -1,4 +1,4 @@
1
- "use strict";var g=Object.defineProperty;var d=(r,e)=>g(r,"name",{value:e,configurable:!0});var A=Object.defineProperty,u=d((r,e)=>A(r,"name",{value:e,configurable:!0}),"u"),R=Object.defineProperty,a=u((r,e)=>R(r,"name",{value:e,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createRateLimiter=createRateLimiter,exports.createRedisRateLimiter=createRedisRateLimiter;function createRateLimiter(r){const e=Math.max(1,r.limit),i=Math.max(1,r.windowMs),l=r.now??Date.now,c=e/i,s=new Map,m=a((o,n)=>Math.min(e,o.tokens+(n-o.last)*c),"refilled"),f=setInterval(()=>{const o=l();for(const[n,t]of s)m(t,o)>=e&&s.delete(n)},i);return f.unref?.(),{check(o){const n=l();let t=s.get(o);return t||(t={tokens:e,last:n},s.set(o,t)),t.tokens=m(t,n),t.last=n,t.tokens>=1?(t.tokens-=1,{allowed:!0,remaining:Math.floor(t.tokens),retryAfterMs:0}):{allowed:!1,remaining:0,retryAfterMs:Math.ceil((1-t.tokens)/c)}},stop(){clearInterval(f)}}}d(createRateLimiter,"createRateLimiter"),u(createRateLimiter,"createRateLimiter"),a(createRateLimiter,"createRateLimiter");const BUCKET_LUA=`
1
+ "use strict";var A=Object.defineProperty;var u=(r,e)=>A(r,"name",{value:e,configurable:!0});var R=Object.defineProperty,a=u((r,e)=>R(r,"name",{value:e,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createRateLimiter=createRateLimiter,exports.createRedisRateLimiter=createRedisRateLimiter;function createRateLimiter(r){const e=Math.max(1,r.limit),i=Math.max(1,r.windowMs),n=r.now??Date.now,c=e/i,s=new Map,m=a((l,o)=>Math.min(e,l.tokens+(o-l.last)*c),"refilled"),f=setInterval(()=>{const l=n();for(const[o,t]of s)m(t,l)>=e&&s.delete(o)},i);return f.unref?.(),{check(l){const o=n();let t=s.get(l);return t||(t={tokens:e,last:o},s.set(l,t)),t.tokens=m(t,o),t.last=o,t.tokens>=1?(t.tokens-=1,{allowed:!0,remaining:Math.floor(t.tokens),retryAfterMs:0}):{allowed:!1,remaining:0,retryAfterMs:Math.ceil((1-t.tokens)/c)}},stop(){clearInterval(f)}}}u(createRateLimiter,"createRateLimiter"),a(createRateLimiter,"createRateLimiter");const BUCKET_LUA=`
2
2
  local b = redis.call('HMGET', KEYS[1], 't', 's')
3
3
  local cap = tonumber(ARGV[1])
4
4
  local refill = tonumber(ARGV[2])
@@ -15,4 +15,4 @@ redis.call('HSET', KEYS[1], 't', tokens, 's', now)
15
15
  redis.call('PEXPIRE', KEYS[1], ttl)
16
16
  local retry = 0
17
17
  if ok == 0 then retry = math.ceil((1 - tokens) / refill) end
18
- return {ok, math.floor(tokens), retry}`,RL_TIMEOUT=Symbol("rl-timeout"),RL_FAIL=Symbol("rl-fail");function createRedisRateLimiter(r,e){const i=Math.max(1,e.limit),l=Math.max(1,e.windowMs),c=e.prefix??"lensmcp:gw:rl:",s=Math.max(1,e.timeoutMs??100),m=e.failOpen!==!1,f=i/l,o=Math.ceil(l)+1e3,n=a(()=>m?{allowed:!0,remaining:i,retryAfterMs:0}:{allowed:!1,remaining:0,retryAfterMs:l},"onFail");return{async check(t){let w;const h=Promise.resolve(r.eval(BUCKET_LUA,1,`${c}${t}`,i,f,o)).then(M=>M,()=>RL_FAIL),b=new Promise(M=>{w=setTimeout(()=>M(RL_TIMEOUT),s)}),k=await Promise.race([h,b]);if(clearTimeout(w),k===RL_TIMEOUT||k===RL_FAIL)return n();const[p,y,L]=k;return p===1?{allowed:!0,remaining:Number(y)||0,retryAfterMs:0}:{allowed:!1,remaining:0,retryAfterMs:Number(L)||l}},stop(){}}}d(createRedisRateLimiter,"createRedisRateLimiter"),u(createRedisRateLimiter,"createRedisRateLimiter"),a(createRedisRateLimiter,"createRedisRateLimiter");
18
+ return {ok, math.floor(tokens), retry}`,RL_TIMEOUT=Symbol("rl-timeout"),RL_FAIL=Symbol("rl-fail");function createRedisRateLimiter(r,e){const i=Math.max(1,e.limit),n=Math.max(1,e.windowMs),c=e.prefix??"lensmcp:gw:rl:",s=Math.max(1,e.timeoutMs??100),m=e.failOpen!==!1,f=i/n,l=Math.ceil(n)+1e3,o=a(()=>m?{allowed:!0,remaining:i,retryAfterMs:0}:{allowed:!1,remaining:0,retryAfterMs:n},"onFail");return{async check(t){let M;const w=Promise.resolve(r.eval(BUCKET_LUA,1,`${c}${t}`,i,f,l)).then(d=>d,()=>RL_FAIL),h=new Promise(d=>{M=setTimeout(()=>d(RL_TIMEOUT),s)}),k=await Promise.race([w,h]);if(clearTimeout(M),k===RL_TIMEOUT||k===RL_FAIL)return o();const[b,y,p]=k;return b===1?{allowed:!0,remaining:Number(y)||0,retryAfterMs:0}:{allowed:!1,remaining:0,retryAfterMs:Number(p)||n}},stop(){}}}u(createRedisRateLimiter,"createRedisRateLimiter"),a(createRedisRateLimiter,"createRedisRateLimiter");
@@ -1 +1 @@
1
- "use strict";var m=Object.defineProperty;var d=(e,t)=>m(e,"name",{value:t,configurable:!0});var g=Object.defineProperty,u=d((e,t)=>g(e,"name",{value:t,configurable:!0}),"u"),y=Object.defineProperty,s=u((e,t)=>y(e,"name",{value:t,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.changeChannel=exports.rolloutKey=exports.manifestKey=exports.DEFAULT_REGISTRY_PREFIX=void 0,exports.redisRegistrySource=redisRegistrySource,exports.createRegistryProviders=createRegistryProviders;const providers_prod_1=require("./providers-prod");exports.DEFAULT_REGISTRY_PREFIX="lensmcp:gw";const manifestKey=s((e,t)=>`${e}:manifest:${t}`,"manifestKey");exports.manifestKey=manifestKey;const rolloutKey=s((e,t)=>`${e}:rollout:${t}`,"rolloutKey");exports.rolloutKey=rolloutKey;const changeChannel=s(e=>`${e}:changed`,"changeChannel");exports.changeChannel=changeChannel;function parseManifest(e){try{const t=JSON.parse(e);return t&&typeof t.service=="string"&&Array.isArray(t.mounts)?t:void 0}catch{return}}d(parseManifest,"parseManifest"),u(parseManifest,"parseManifest"),s(parseManifest,"parseManifest");function redisRegistrySource(e,t={}){const o=t.prefix??exports.DEFAULT_REGISTRY_PREFIX;let a;const c=`${o}:rollout:`;return{async read(){const i=await e.keys(`${o}:manifest:*`),f=i.length?(await e.mget(...i)).filter(r=>!!r).map(parseManifest).filter(r=>!!r):[],n=await e.keys(`${c}*`),l={};if(n.length){const r=await e.mget(...n);n.forEach((p,h)=>{const v=r[h];if(v)try{l[p.slice(c.length)]=JSON.parse(v)}catch{}})}return{manifests:f,endpoints:l}},async watch(i){return a=e.duplicate(),await a.subscribe((0,exports.changeChannel)(o)),a.on("message",()=>i()),async()=>{try{await a?.quit()}catch{}}},stop(){a?.quit().catch(()=>{})}}}d(redisRegistrySource,"redisRegistrySource"),u(redisRegistrySource,"redisRegistrySource"),s(redisRegistrySource,"redisRegistrySource");function createRegistryProviders(e,t={}){const o=t.onError??(()=>{});let a=[];const c=new Set,i=(0,providers_prod_1.rolloutPodProvider)({});let f,n;const l=s(async()=>{let r;try{r=await e.read()}catch(p){o(p);return}a=r.manifests,i.update(r.endpoints);for(const p of c)try{p(a)}catch(h){o(h)}},"refresh");return{manifests:{list:s(()=>a,"list"),watch:s(r=>(c.add(r),()=>c.delete(r)),"watch")},pods:i,async start(){if(await l(),e.watch)try{f=await e.watch(()=>{l()})}catch(r){o(r)}n=setInterval(()=>{l()},t.pollMs??5e3),n.unref?.()},stop(){f?.(),n&&clearInterval(n),e.stop?.()}}}d(createRegistryProviders,"createRegistryProviders"),u(createRegistryProviders,"createRegistryProviders"),s(createRegistryProviders,"createRegistryProviders");
1
+ "use strict";var g=Object.defineProperty;var u=(e,t)=>g(e,"name",{value:t,configurable:!0});var y=Object.defineProperty,s=u((e,t)=>y(e,"name",{value:t,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.changeChannel=exports.rolloutKey=exports.manifestKey=exports.DEFAULT_REGISTRY_PREFIX=void 0,exports.redisRegistrySource=redisRegistrySource,exports.createRegistryProviders=createRegistryProviders;const providers_prod_1=require("./providers-prod");exports.DEFAULT_REGISTRY_PREFIX="lensmcp:gw";const manifestKey=s((e,t)=>`${e}:manifest:${t}`,"manifestKey");exports.manifestKey=manifestKey;const rolloutKey=s((e,t)=>`${e}:rollout:${t}`,"rolloutKey");exports.rolloutKey=rolloutKey;const changeChannel=s(e=>`${e}:changed`,"changeChannel");exports.changeChannel=changeChannel;function parseManifest(e){try{const t=JSON.parse(e);return t&&typeof t.service=="string"&&Array.isArray(t.mounts)?t:void 0}catch{return}}u(parseManifest,"parseManifest"),s(parseManifest,"parseManifest");function redisRegistrySource(e,t={}){const o=t.prefix??exports.DEFAULT_REGISTRY_PREFIX;let a;const i=`${o}:rollout:`;return{async read(){const c=await e.keys(`${o}:manifest:*`),d=c.length?(await e.mget(...c)).filter(r=>!!r).map(parseManifest).filter(r=>!!r):[],n=await e.keys(`${i}*`),l={};if(n.length){const r=await e.mget(...n);n.forEach((p,f)=>{const h=r[f];if(h)try{l[p.slice(i.length)]=JSON.parse(h)}catch{}})}return{manifests:d,endpoints:l}},async watch(c){return a=e.duplicate(),await a.subscribe((0,exports.changeChannel)(o)),a.on("message",()=>c()),async()=>{try{await a?.quit()}catch{}}},stop(){a?.quit().catch(()=>{})}}}u(redisRegistrySource,"redisRegistrySource"),s(redisRegistrySource,"redisRegistrySource");function createRegistryProviders(e,t={}){const o=t.onError??(()=>{});let a=[];const i=new Set,c=(0,providers_prod_1.rolloutPodProvider)({});let d,n;const l=s(async()=>{let r;try{r=await e.read()}catch(p){o(p);return}a=r.manifests,c.update(r.endpoints);for(const p of i)try{p(a)}catch(f){o(f)}},"refresh");return{manifests:{list:s(()=>a,"list"),watch:s(r=>(i.add(r),()=>i.delete(r)),"watch")},pods:c,async start(){if(await l(),e.watch)try{d=await e.watch(()=>{l()})}catch(r){o(r)}n=setInterval(()=>{l()},t.pollMs??5e3),n.unref?.()},stop(){d?.(),n&&clearInterval(n),e.stop?.()}}}u(createRegistryProviders,"createRegistryProviders"),s(createRegistryProviders,"createRegistryProviders");
@@ -1,2 +1,2 @@
1
- "use strict";var m=Object.defineProperty;var c=(n,e)=>m(n,"name",{value:e,configurable:!0});var d=Object.defineProperty,u=c((n,e)=>d(n,"name",{value:e,configurable:!0}),"u"),h=Object.defineProperty,s=u((n,e)=>h(n,"name",{value:e,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.applyRolloutOp=applyRolloutOp,exports.summarizeRollout=summarizeRollout,exports.parseRolloutCommand=parseRolloutCommand;const RULE_OPS=new Set(["eq","ne","in","nin","prefix","suffix","contains","regex","exists","gt","lt","cidr"]),clone=s(n=>JSON.parse(JSON.stringify(n)),"clone");function asSpec(n,e){if(e===void 0)return{rules:[],cohorts:[]};if(Array.isArray(e)){if(e.length&&typeof e[0]=="string")throw new Error(`service "${n}" is a flat endpoint list; convert it to cohorts first (e.g. add-cohort)`);return{rules:[],cohorts:e}}return{rules:e.rules??[],cohorts:e.cohorts??[]}}c(asSpec,"asSpec"),u(asSpec,"asSpec"),s(asSpec,"asSpec");const findCohort=s((n,e)=>n.cohorts.find(o=>o.version===e),"findCohort");function reqWeight(n){if(!Number.isFinite(n)||n<0)throw new Error(`weight must be a number \u2265 0 (got ${n})`);return n}c(reqWeight,"reqWeight"),u(reqWeight,"reqWeight"),s(reqWeight,"reqWeight");function applyRolloutOp(n,e){const o=clone(n),r=asSpec(e.service,o[e.service]);switch(e.kind){case"add-cohort":{if(findCohort(r,e.version))throw new Error(`cohort "${e.version}" already exists on "${e.service}" \u2014 use set-weight`);if(!e.urls.length)throw new Error("add-cohort needs at least one upstream url");r.cohorts.push({version:e.version,weight:reqWeight(e.weight),urls:e.urls.slice()});break}case"set-weight":{const t=findCohort(r,e.version);if(!t)throw new Error(`no cohort "${e.version}" on "${e.service}"`);t.weight=reqWeight(e.weight);break}case"remove-cohort":{if(!findCohort(r,e.version))throw new Error(`no cohort "${e.version}" on "${e.service}"`);r.cohorts=r.cohorts.filter(t=>t.version!==e.version);break}case"promote":{if(!findCohort(r,e.version))throw new Error(`no cohort "${e.version}" on "${e.service}"`);for(const t of r.cohorts)t.weight=t.version===e.version?100:0;break}case"abort":{const t=findCohort(r,e.version);if(!t)throw new Error(`no cohort "${e.version}" on "${e.service}"`);t.weight=0;break}case"add-rule":{validateRule(e.rule),r.rules=[...(r.rules??[]).filter(t=>!e.rule.name||t.name!==e.rule.name),e.rule];break}case"remove-rule":{const t=(r.rules??[]).length;if(r.rules=(r.rules??[]).filter(i=>i.name!==e.name),r.rules.length===t)throw new Error(`no rule named "${e.name}" on "${e.service}"`);break}}return o[e.service]=r.rules&&r.rules.length?{rules:r.rules,cohorts:r.cohorts}:r.cohorts,o}c(applyRolloutOp,"applyRolloutOp"),u(applyRolloutOp,"applyRolloutOp"),s(applyRolloutOp,"applyRolloutOp");function validateRule(n){if(!n||typeof n.version!="string"||!n.version)throw new Error("rule needs a non-empty version");if(!n.when||typeof n.when!="object")throw new Error("rule needs a `when` condition");const e=s(o=>{const r=o;if("all"in r||"any"in r||"not"in r){for(const t of["all","any"])Array.isArray(r[t])&&r[t].forEach(e);r.not&&e(r.not);return}if(typeof r.attr!="string"||!RULE_OPS.has(r.op))throw new Error(`invalid rule condition (attr/op): ${JSON.stringify(r)}`)},"walk");e(n.when)}c(validateRule,"validateRule"),u(validateRule,"validateRule"),s(validateRule,"validateRule");function summarizeRollout(n,e){const o=e?[e]:Object.keys(n);if(!o.length)return"(no services in rollout config)";const r=[];for(const t of o){const i=n[t];if(i===void 0){r.push(`${t}: (not present)`);continue}if(Array.isArray(i)&&i.length&&typeof i[0]=="string"){r.push(`${t}: flat LB \u2192 ${i.join(", ")}`);continue}const l=asSpec(t,i),v=l.cohorts.reduce((a,f)=>a+Math.max(0,f.weight),0)||1;r.push(`${t}:`);for(const a of l.cohorts)r.push(` \u2022 ${a.version.padEnd(16)} weight ${String(a.weight).padStart(3)} (${(Math.max(0,a.weight)/v*100).toFixed(0)}%) ${a.urls.length} url(s)`);for(const a of l.rules??[])r.push(` \u25B8 rule ${(a.name??"(unnamed)").padEnd(16)} \u2192 ${a.version} ${ruleSummary(a.when)}`)}return r.join(`
2
- `)}c(summarizeRollout,"summarizeRollout"),u(summarizeRollout,"summarizeRollout"),s(summarizeRollout,"summarizeRollout");function ruleSummary(n){const e=n;return"all"in e?"("+e.all.map(ruleSummary).join(" AND ")+")":"any"in e?"("+e.any.map(ruleSummary).join(" OR ")+")":"not"in e?"NOT "+ruleSummary(e.not):`${e.attr} ${e.op}${e.value!==void 0?" "+JSON.stringify(e.value):""}`}c(ruleSummary,"ruleSummary"),u(ruleSummary,"ruleSummary"),s(ruleSummary,"ruleSummary");function parseRolloutCommand(n){const[e,...o]=n,r=s((t,i)=>{if(o.length<t)throw new Error(`usage: ${e} ${i}`)},"need");switch(e){case"status":return{status:o[0]};case"add-cohort":return r(4,"<service> <version> <weight> <url> [url...]"),{op:{kind:"add-cohort",service:o[0],version:o[1],weight:Number(o[2]),urls:o.slice(3)}};case"set-weight":return r(3,"<service> <version> <weight>"),{op:{kind:"set-weight",service:o[0],version:o[1],weight:Number(o[2])}};case"remove-cohort":return r(2,"<service> <version>"),{op:{kind:"remove-cohort",service:o[0],version:o[1]}};case"promote":return r(2,"<service> <version>"),{op:{kind:"promote",service:o[0],version:o[1]}};case"abort":return r(2,"<service> <version>"),{op:{kind:"abort",service:o[0],version:o[1]}};case"add-rule":return r(6,"<service> <name> <attr> <op> <value> <version>"),{op:{kind:"add-rule",service:o[0],rule:{name:o[1],when:{attr:o[2],op:o[3],value:o[4]},version:o[5]}}};case"remove-rule":return r(2,"<service> <name>"),{op:{kind:"remove-rule",service:o[0],name:o[1]}};default:throw new Error(`unknown command "${e??""}". commands: status, add-cohort, set-weight, remove-cohort, promote, abort, add-rule, remove-rule`)}}c(parseRolloutCommand,"parseRolloutCommand"),u(parseRolloutCommand,"parseRolloutCommand"),s(parseRolloutCommand,"parseRolloutCommand");
1
+ "use strict";var d=Object.defineProperty;var u=(t,r)=>d(t,"name",{value:r,configurable:!0});var h=Object.defineProperty,s=u((t,r)=>h(t,"name",{value:r,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.applyRolloutOp=applyRolloutOp,exports.summarizeRollout=summarizeRollout,exports.parseRolloutCommand=parseRolloutCommand;const RULE_OPS=new Set(["eq","ne","in","nin","prefix","suffix","contains","regex","exists","gt","lt","cidr"]),clone=s(t=>JSON.parse(JSON.stringify(t)),"clone");function asSpec(t,r){if(r===void 0)return{rules:[],cohorts:[]};if(Array.isArray(r)){if(r.length&&typeof r[0]=="string")throw new Error(`service "${t}" is a flat endpoint list; convert it to cohorts first (e.g. add-cohort)`);return{rules:[],cohorts:r}}return{rules:r.rules??[],cohorts:r.cohorts??[]}}u(asSpec,"asSpec"),s(asSpec,"asSpec");const findCohort=s((t,r)=>t.cohorts.find(o=>o.version===r),"findCohort");function reqWeight(t){if(!Number.isFinite(t)||t<0)throw new Error(`weight must be a number \u2265 0 (got ${t})`);return t}u(reqWeight,"reqWeight"),s(reqWeight,"reqWeight");function applyRolloutOp(t,r){const o=clone(t),e=asSpec(r.service,o[r.service]);switch(r.kind){case"add-cohort":{if(findCohort(e,r.version))throw new Error(`cohort "${r.version}" already exists on "${r.service}" \u2014 use set-weight`);if(!r.urls.length)throw new Error("add-cohort needs at least one upstream url");e.cohorts.push({version:r.version,weight:reqWeight(r.weight),urls:r.urls.slice()});break}case"set-weight":{const n=findCohort(e,r.version);if(!n)throw new Error(`no cohort "${r.version}" on "${r.service}"`);n.weight=reqWeight(r.weight);break}case"remove-cohort":{if(!findCohort(e,r.version))throw new Error(`no cohort "${r.version}" on "${r.service}"`);e.cohorts=e.cohorts.filter(n=>n.version!==r.version);break}case"promote":{if(!findCohort(e,r.version))throw new Error(`no cohort "${r.version}" on "${r.service}"`);for(const n of e.cohorts)n.weight=n.version===r.version?100:0;break}case"abort":{const n=findCohort(e,r.version);if(!n)throw new Error(`no cohort "${r.version}" on "${r.service}"`);n.weight=0;break}case"add-rule":{validateRule(r.rule),e.rules=[...(e.rules??[]).filter(n=>!r.rule.name||n.name!==r.rule.name),r.rule];break}case"remove-rule":{const n=(e.rules??[]).length;if(e.rules=(e.rules??[]).filter(i=>i.name!==r.name),e.rules.length===n)throw new Error(`no rule named "${r.name}" on "${r.service}"`);break}}return o[r.service]=e.rules&&e.rules.length?{rules:e.rules,cohorts:e.cohorts}:e.cohorts,o}u(applyRolloutOp,"applyRolloutOp"),s(applyRolloutOp,"applyRolloutOp");function validateRule(t){if(!t||typeof t.version!="string"||!t.version)throw new Error("rule needs a non-empty version");if(!t.when||typeof t.when!="object")throw new Error("rule needs a `when` condition");const r=s(o=>{const e=o;if("all"in e||"any"in e||"not"in e){for(const n of["all","any"])Array.isArray(e[n])&&e[n].forEach(r);e.not&&r(e.not);return}if(typeof e.attr!="string"||!RULE_OPS.has(e.op))throw new Error(`invalid rule condition (attr/op): ${JSON.stringify(e)}`)},"walk");r(t.when)}u(validateRule,"validateRule"),s(validateRule,"validateRule");function summarizeRollout(t,r){const o=r?[r]:Object.keys(t);if(!o.length)return"(no services in rollout config)";const e=[];for(const n of o){const i=t[n];if(i===void 0){e.push(`${n}: (not present)`);continue}if(Array.isArray(i)&&i.length&&typeof i[0]=="string"){e.push(`${n}: flat LB \u2192 ${i.join(", ")}`);continue}const c=asSpec(n,i),l=c.cohorts.reduce((a,v)=>a+Math.max(0,v.weight),0)||1;e.push(`${n}:`);for(const a of c.cohorts)e.push(` \u2022 ${a.version.padEnd(16)} weight ${String(a.weight).padStart(3)} (${(Math.max(0,a.weight)/l*100).toFixed(0)}%) ${a.urls.length} url(s)`);for(const a of c.rules??[])e.push(` \u25B8 rule ${(a.name??"(unnamed)").padEnd(16)} \u2192 ${a.version} ${ruleSummary(a.when)}`)}return e.join(`
2
+ `)}u(summarizeRollout,"summarizeRollout"),s(summarizeRollout,"summarizeRollout");function ruleSummary(t){const r=t;return"all"in r?"("+r.all.map(ruleSummary).join(" AND ")+")":"any"in r?"("+r.any.map(ruleSummary).join(" OR ")+")":"not"in r?"NOT "+ruleSummary(r.not):`${r.attr} ${r.op}${r.value!==void 0?" "+JSON.stringify(r.value):""}`}u(ruleSummary,"ruleSummary"),s(ruleSummary,"ruleSummary");function parseRolloutCommand(t){const[r,...o]=t,e=s((n,i)=>{if(o.length<n)throw new Error(`usage: ${r} ${i}`)},"need");switch(r){case"status":return{status:o[0]};case"add-cohort":return e(4,"<service> <version> <weight> <url> [url...]"),{op:{kind:"add-cohort",service:o[0],version:o[1],weight:Number(o[2]),urls:o.slice(3)}};case"set-weight":return e(3,"<service> <version> <weight>"),{op:{kind:"set-weight",service:o[0],version:o[1],weight:Number(o[2])}};case"remove-cohort":return e(2,"<service> <version>"),{op:{kind:"remove-cohort",service:o[0],version:o[1]}};case"promote":return e(2,"<service> <version>"),{op:{kind:"promote",service:o[0],version:o[1]}};case"abort":return e(2,"<service> <version>"),{op:{kind:"abort",service:o[0],version:o[1]}};case"add-rule":return e(6,"<service> <name> <attr> <op> <value> <version>"),{op:{kind:"add-rule",service:o[0],rule:{name:o[1],when:{attr:o[2],op:o[3],value:o[4]},version:o[5]}}};case"remove-rule":return e(2,"<service> <name>"),{op:{kind:"remove-rule",service:o[0],name:o[1]}};default:throw new Error(`unknown command "${r??""}". commands: status, add-cohort, set-weight, remove-cohort, promote, abort, add-rule, remove-rule`)}}u(parseRolloutCommand,"parseRolloutCommand"),s(parseRolloutCommand,"parseRolloutCommand");
@@ -1 +1 @@
1
- "use strict";var g=Object.defineProperty;var c=(r,a)=>g(r,"name",{value:a,configurable:!0});var m=Object.defineProperty,l=c((r,a)=>m(r,"name",{value:a,configurable:!0}),"l"),f=Object.defineProperty,n=l((r,a)=>f(r,"name",{value:a,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createEdgeAuth=createEdgeAuth;const manifest_1=require("../manifest"),dev_auth_1=require("./dev-auth");function createEdgeAuth(r,a,p){const{traceStep:o}=a;return{internal:n((e,h,t)=>{const u=String(e.headers["x-api-key"]??""),i=r.keyToProject.get(u);return i?(e.headers["x-api-key-id"]=i,e.__lensmcpCaller=i,delete e.headers["x-api-key"],o(t,"auth",{mode:"x-api-key",ok:!0,caller:i}),{ok:!0,caller:i}):(o(t,"auth",{mode:"x-api-key",ok:!1}),{ok:!1,status:401,reason:"unauthorized",detail:"Invalid or missing x-api-key for internal route."})},"internal"),public:n((e,h,t)=>{const u=h.auth;if(u){const i=(e.url??"/").split("?")[0],k=(e.method??"GET").toUpperCase(),s=(0,manifest_1.authRuleFor)(u,i,k);if(s.mode==="jwt"){const v=String(e.headers.host??"").split(":")[0].toLowerCase(),d=(0,dev_auth_1.verifyDevJwt)(e,p?.for(v));return d?s.rule&&!(0,manifest_1.evalRule)(s.rule,(0,dev_auth_1.devAttrs)(e,d))?(o(t,"authz",{ok:!1}),{ok:!1,status:403,reason:"forbidden",detail:"Forbidden: insufficient permission"}):((0,dev_auth_1.stampIdentity)(e,d),o(t,"auth",{mode:"jwt",ok:!0}),{ok:!0,claims:d}):(o(t,"auth",{mode:"jwt",ok:!1}),{ok:!1,status:401,reason:"unauthorized",detail:"Unauthorized: invalid or missing token"})}return o(t,"auth",{mode:s.mode}),{ok:!0}}return o(t,"auth",{mode:"public",unconfigured:!0}),{ok:!0}},"publicAuth")}}c(createEdgeAuth,"createEdgeAuth"),l(createEdgeAuth,"createEdgeAuth"),n(createEdgeAuth,"createEdgeAuth");
1
+ "use strict";var m=Object.defineProperty;var l=(o,i)=>m(o,"name",{value:i,configurable:!0});var f=Object.defineProperty,n=l((o,i)=>f(o,"name",{value:i,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createEdgeAuth=createEdgeAuth;const manifest_1=require("../manifest"),dev_auth_1=require("./dev-auth");function createEdgeAuth(o,i,h){const{traceStep:r}=i;return{internal:n((e,c,t)=>{const u=String(e.headers["x-api-key"]??""),a=o.keyToProject.get(u);return a?(e.headers["x-api-key-id"]=a,e.__lensmcpCaller=a,delete e.headers["x-api-key"],r(t,"auth",{mode:"x-api-key",ok:!0,caller:a}),{ok:!0,caller:a}):(r(t,"auth",{mode:"x-api-key",ok:!1}),{ok:!1,status:401,reason:"unauthorized",detail:"Invalid or missing x-api-key for internal route."})},"internal"),public:n((e,c,t)=>{const u=c.auth;if(u){const a=(e.url??"/").split("?")[0],p=(e.method??"GET").toUpperCase(),s=(0,manifest_1.authRuleFor)(u,a,p);if(s.mode==="jwt"){const k=String(e.headers.host??"").split(":")[0].toLowerCase(),d=(0,dev_auth_1.verifyDevJwt)(e,h?.for(k));return d?s.rule&&!(0,manifest_1.evalRule)(s.rule,(0,dev_auth_1.devAttrs)(e,d))?(r(t,"authz",{ok:!1}),{ok:!1,status:403,reason:"forbidden",detail:"Forbidden: insufficient permission"}):((0,dev_auth_1.stampIdentity)(e,d),r(t,"auth",{mode:"jwt",ok:!0}),{ok:!0,claims:d}):(r(t,"auth",{mode:"jwt",ok:!1}),{ok:!1,status:401,reason:"unauthorized",detail:"Unauthorized: invalid or missing token"})}return r(t,"auth",{mode:s.mode}),{ok:!0}}return r(t,"auth",{mode:"public",unconfigured:!0}),{ok:!0}},"publicAuth")}}l(createEdgeAuth,"createEdgeAuth"),n(createEdgeAuth,"createEdgeAuth");
@@ -1,11 +1,11 @@
1
- "use strict";var f=Object.defineProperty;var i=(a,r)=>f(a,"name",{value:r,configurable:!0});var l=Object.defineProperty,n=i((a,r)=>l(a,"name",{value:r,configurable:!0}),"n"),p=Object.defineProperty,t=n((a,r)=>p(a,"name",{value:r,configurable:!0}),"t");Object.defineProperty(exports,"__esModule",{value:!0}),exports.renderChooserPage=renderChooserPage,exports.serveChooser=serveChooser;const workspace_registry_1=require("./workspace-registry"),esc=t(a=>a.replace(/[&<>"']/g,r=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[r]),"esc");function renderChooserPage(a,r){const o=a.map(e=>({...e,live:e.key===r||(0,workspace_registry_1.isWorkspaceLive)(e)})).sort((e,s)=>Number(s.live)-Number(e.live)||e.key.localeCompare(s.key)),d=o.length===0?'<p class="empty">No workspaces registered yet. Start a gateway in a workspace (<code>lensmcp gateway start</code>) and it will appear here.</p>':o.map(e=>{const s=e.key===r?'<span class="badge here">running here</span>':e.live?'<span class="badge live">live</span>':'<span class="badge off">offline</span>',g=e.live?'<span class="open">Open dashboard \u2192</span>':'<span class="hint">start its gateway to open</span>',c=`<div class="card-main">
1
+ "use strict";var l=Object.defineProperty;var n=(a,o)=>l(a,"name",{value:o,configurable:!0});var p=Object.defineProperty,t=n((a,o)=>p(a,"name",{value:o,configurable:!0}),"t");Object.defineProperty(exports,"__esModule",{value:!0}),exports.renderChooserPage=renderChooserPage,exports.serveChooser=serveChooser;const workspace_registry_1=require("./workspace-registry"),esc=t(a=>a.replace(/[&<>"']/g,o=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[o]),"esc");function renderChooserPage(a,o){const r=a.map(e=>({...e,live:e.key===o||(0,workspace_registry_1.isWorkspaceLive)(e)})).sort((e,s)=>Number(s.live)-Number(e.live)||e.key.localeCompare(s.key)),c=r.length===0?'<p class="empty">No workspaces registered yet. Start a gateway in a workspace (<code>lensmcp gateway start</code>) and it will appear here.</p>':r.map(e=>{const s=e.key===o?'<span class="badge here">running here</span>':e.live?'<span class="badge live">live</span>':'<span class="badge off">offline</span>',d=e.live?'<span class="open">Open dashboard \u2192</span>':'<span class="hint">start its gateway to open</span>',i=`<div class="card-main">
2
2
  <div class="key">${esc(e.key)} ${s}</div>
3
3
  <div class="root">${esc(e.root)}</div>
4
4
  </div>
5
- <div class="card-action">${g}</div>`;return e.live?`<a class="card is-live" href="${esc(e.base)}/">
6
- ${c}
5
+ <div class="card-action">${d}</div>`;return e.live?`<a class="card is-live" href="${esc(e.base)}/">
6
+ ${i}
7
7
  </a>`:`<div class="card is-off">
8
- ${c}
8
+ ${i}
9
9
  </div>`}).join(`
10
10
  `);return`<!doctype html>
11
11
  <html lang="en"><head>
@@ -42,10 +42,10 @@
42
42
  <body><div class="wrap">
43
43
  <header>
44
44
  <h1>LensMCP workspaces</h1>
45
- <p class="sub">Pick a workspace to open its dashboard. ${o.filter(e=>e.live).length} live \xB7 ${o.length} known.</p>
45
+ <p class="sub">Pick a workspace to open its dashboard. ${r.filter(e=>e.live).length} live \xB7 ${r.length} known.</p>
46
46
  </header>
47
47
  <div class="list">
48
- ${d}
48
+ ${c}
49
49
  </div>
50
50
  <footer>Served by the gateway on <code>lensmcp.local</code>. This list updates as gateways start and stop.</footer>
51
- </div></body></html>`}i(renderChooserPage,"renderChooserPage"),n(renderChooserPage,"renderChooserPage"),t(renderChooserPage,"renderChooserPage");function serveChooser(a,r){const o=renderChooserPage((0,workspace_registry_1.readWorkspaces)(),r);a.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),a.end(o)}i(serveChooser,"serveChooser"),n(serveChooser,"serveChooser"),t(serveChooser,"serveChooser");
51
+ </div></body></html>`}n(renderChooserPage,"renderChooserPage"),t(renderChooserPage,"renderChooserPage");function serveChooser(a,o){const r=renderChooserPage((0,workspace_registry_1.readWorkspaces)(),o);a.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),a.end(r)}n(serveChooser,"serveChooser"),t(serveChooser,"serveChooser");
@@ -1 +1 @@
1
- "use strict";var w=Object.defineProperty;var l=(t,r)=>w(t,"name",{value:r,configurable:!0});var h=Object.defineProperty,d=l((t,r)=>h(t,"name",{value:r,configurable:!0}),"d"),p=Object.defineProperty,y=d((t,r)=>p(t,"name",{value:r,configurable:!0}),"y");Object.defineProperty(exports,"__esModule",{value:!0}),exports.controlSocketPath=controlSocketPath,exports.startControlServer=startControlServer;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery");function controlSocketPath(){const t=process.env.LENSMCP_HOME||os.homedir();return path.join(t,".lensmcp","control.sock")}l(controlSocketPath,"controlSocketPath"),d(controlSocketPath,"controlSocketPath"),y(controlSocketPath,"controlSocketPath");function readJsonBody(t){return new Promise((r,c)=>{let s="";t.on("data",u=>{s+=u,s.length>1e6&&c(new Error("body too large"))}),t.on("end",()=>{try{r(s?JSON.parse(s):{})}catch(u){c(u)}}),t.on("error",c)})}l(readJsonBody,"readJsonBody"),d(readJsonBody,"readJsonBody"),y(readJsonBody,"readJsonBody");function startControlServer(t){const{rt:r}=t,c=controlSocketPath();fs.mkdirSync(path.dirname(c),{recursive:!0});try{fs.unlinkSync(c)}catch{}const s=y((n,i,a)=>{n.writeHead(i,{"content-type":"application/json"}),n.end(JSON.stringify(a))},"send"),u=http.createServer((n,i)=>{(async()=>{try{const a=(n.url??"").split("?")[0];if(n.method==="GET"&&a==="/list")return s(i,200,{workspaces:r.registry.keys().map(e=>{const o=r.registry.get(e);return{key:e,root:o?.root,routes:o?.routes.length??0,services:o?.services.length??0}})});if(n.method==="GET"&&a==="/status")return s(i,200,{daemon:{pid:process.pid,wsKey:r.wsKey},workspaces:r.registry.keys().map(e=>{const o=r.registry.get(e);return{key:e,root:o?.root,routes:o?.routes.length??0,services:o?.services.length??0,isDaemon:e===r.wsKey}}),services:r.registry.allServices().map(e=>({project:e.project,wsKey:e.wsKey,host:e.decl.host,state:e.state}))});if(n.method==="POST"&&a==="/register"){const e=await readJsonBody(n);if(!e.wsKey||!e.root)return s(i,400,{error:"wsKey and root are required"});const{routes:o,services:g}=(0,discovery_1.discoverRoutes)(e.root,e.projects??{},void 0,e.wsKey),v={wsKey:e.wsKey,root:e.root,routes:o,services:g};return r.registry.register(v),await t.onRegister?.(v),r.rebuildRoutes(),s(i,200,{ok:!0,wsKey:e.wsKey,routes:o.length,services:g.length,workspaces:r.registry.keys()})}if(n.method==="POST"&&a==="/unregister"){const e=await readJsonBody(n);if(!e.wsKey)return s(i,400,{error:"wsKey is required"});const o=r.registry.unregister(e.wsKey);if(o){for(const g of o.services)t.killService(g,"workspace unregistered");t.onUnregister?.(o)}return r.rebuildRoutes(),s(i,200,{ok:!0,removed:!!o,workspaces:r.registry.keys()})}s(i,404,{error:"not found"})}catch(a){s(i,500,{error:a.message})}})()});return u.listen(c),{socketPath:c,close:y(()=>{u.close();try{fs.unlinkSync(c)}catch{}},"close")}}l(startControlServer,"startControlServer"),d(startControlServer,"startControlServer"),y(startControlServer,"startControlServer");
1
+ "use strict";var h=Object.defineProperty;var d=(o,r)=>h(o,"name",{value:r,configurable:!0});var p=Object.defineProperty,y=d((o,r)=>p(o,"name",{value:r,configurable:!0}),"y");Object.defineProperty(exports,"__esModule",{value:!0}),exports.controlSocketPath=controlSocketPath,exports.startControlServer=startControlServer;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path")),discovery_1=require("./discovery");function controlSocketPath(){const o=process.env.LENSMCP_HOME||os.homedir();return path.join(o,".lensmcp","control.sock")}d(controlSocketPath,"controlSocketPath"),y(controlSocketPath,"controlSocketPath");function readJsonBody(o){return new Promise((r,c)=>{let s="";o.on("data",u=>{s+=u,s.length>1e6&&c(new Error("body too large"))}),o.on("end",()=>{try{r(s?JSON.parse(s):{})}catch(u){c(u)}}),o.on("error",c)})}d(readJsonBody,"readJsonBody"),y(readJsonBody,"readJsonBody");function startControlServer(o){const{rt:r}=o,c=controlSocketPath();fs.mkdirSync(path.dirname(c),{recursive:!0});try{fs.unlinkSync(c)}catch{}const s=y((n,i,a)=>{n.writeHead(i,{"content-type":"application/json"}),n.end(JSON.stringify(a))},"send"),u=http.createServer((n,i)=>{(async()=>{try{const a=(n.url??"").split("?")[0];if(n.method==="GET"&&a==="/list")return s(i,200,{workspaces:r.registry.keys().map(e=>{const t=r.registry.get(e);return{key:e,root:t?.root,routes:t?.routes.length??0,services:t?.services.length??0}})});if(n.method==="GET"&&a==="/status")return s(i,200,{daemon:{pid:process.pid,wsKey:r.wsKey},workspaces:r.registry.keys().map(e=>{const t=r.registry.get(e);return{key:e,root:t?.root,routes:t?.routes.length??0,services:t?.services.length??0,isDaemon:e===r.wsKey}}),services:r.registry.allServices().map(e=>({project:e.project,wsKey:e.wsKey,host:e.decl.host,state:e.state}))});if(n.method==="POST"&&a==="/register"){const e=await readJsonBody(n);if(!e.wsKey||!e.root)return s(i,400,{error:"wsKey and root are required"});const{routes:t,services:l}=(0,discovery_1.discoverRoutes)(e.root,e.projects??{},void 0,e.wsKey),g={wsKey:e.wsKey,root:e.root,routes:t,services:l};return r.registry.register(g),await o.onRegister?.(g),r.rebuildRoutes(),s(i,200,{ok:!0,wsKey:e.wsKey,routes:t.length,services:l.length,workspaces:r.registry.keys()})}if(n.method==="POST"&&a==="/unregister"){const e=await readJsonBody(n);if(!e.wsKey)return s(i,400,{error:"wsKey is required"});const t=r.registry.unregister(e.wsKey);if(t){for(const l of t.services)o.killService(l,"workspace unregistered");o.onUnregister?.(t)}return r.rebuildRoutes(),s(i,200,{ok:!0,removed:!!t,workspaces:r.registry.keys()})}s(i,404,{error:"not found"})}catch(a){s(i,500,{error:a.message})}})()});return u.listen(c),{socketPath:c,close:y(()=>{u.close();try{fs.unlinkSync(c)}catch{}},"close")}}d(startControlServer,"startControlServer"),y(startControlServer,"startControlServer");
@@ -1 +1 @@
1
- "use strict";var m=Object.defineProperty;var f=(e,r)=>m(e,"name",{value:r,configurable:!0});var y=Object.defineProperty,a=f((e,r)=>y(e,"name",{value:r,configurable:!0}),"a"),h=Object.defineProperty,u=a((e,r)=>h(e,"name",{value:r,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.devAttrs=void 0,exports.verifyDevJwt=verifyDevJwt,exports.stampIdentity=stampIdentity;const node_crypto_1=require("node:crypto");function bearerToken(e){const r=Array.isArray(e.headers.authorization)?e.headers.authorization[0]:e.headers.authorization,t=String(r??"");return t.startsWith("Bearer ")?t.slice(7):void 0}f(bearerToken,"bearerToken"),a(bearerToken,"bearerToken"),u(bearerToken,"bearerToken");function verifyDevJwt(e,r){const t=bearerToken(e);if(!t)return;const[n,s,p]=t.split(".");if(!n||!s||!p)return;if(r)return r.verify(t);let o;try{o=JSON.parse(Buffer.from(n,"base64url").toString()).alg}catch{return}if(typeof o=="string"&&o!=="HS256"){if(process.env.LENSMCP_GW_DEV_JWT_INSECURE_DECODE!=="1")return;try{const i=JSON.parse(Buffer.from(s,"base64url").toString());return typeof i.exp=="number"&&Date.now()/1e3>i.exp?void 0:i}catch{return}}const l=process.env.AUTH_JWT_SECRET??"tetros-dev-secret",v=(0,node_crypto_1.createHmac)("sha256",l).update(`${n}.${s}`).digest("base64url"),c=Buffer.from(p),d=Buffer.from(v);if(!(c.length!==d.length||!(0,node_crypto_1.timingSafeEqual)(c,d)))try{const i=JSON.parse(Buffer.from(s,"base64url").toString());return typeof i.exp=="number"&&Date.now()/1e3>i.exp?void 0:i}catch{return}}f(verifyDevJwt,"verifyDevJwt"),a(verifyDevJwt,"verifyDevJwt"),u(verifyDevJwt,"verifyDevJwt");function stampIdentity(e,r){const t=u((p,o)=>{o!=null&&o!==""&&(e.headers[p]=String(o))},"set");t("x-user-id",r.sub),t("x-tenant-id",r.tid);const n=r.roles;Array.isArray(n)&&n.length&&(e.headers["x-user-roles"]=n.map(String).join(","));const s=r.perms;Array.isArray(s)&&s.length&&(e.headers["x-user-permissions"]=s.map(String).join(","))}f(stampIdentity,"stampIdentity"),a(stampIdentity,"stampIdentity"),u(stampIdentity,"stampIdentity");const devAttrs=u((e,r)=>({...r,ip:e.socket.remoteAddress??"",method:(e.method??"GET").toUpperCase(),path:(e.url??"/").split("?")[0]}),"devAttrs");exports.devAttrs=devAttrs;
1
+ "use strict";var y=Object.defineProperty;var a=(e,r)=>y(e,"name",{value:r,configurable:!0});var h=Object.defineProperty,u=a((e,r)=>h(e,"name",{value:r,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.devAttrs=void 0,exports.verifyDevJwt=verifyDevJwt,exports.stampIdentity=stampIdentity;const node_crypto_1=require("node:crypto");function bearerToken(e){const r=Array.isArray(e.headers.authorization)?e.headers.authorization[0]:e.headers.authorization,t=String(r??"");return t.startsWith("Bearer ")?t.slice(7):void 0}a(bearerToken,"bearerToken"),u(bearerToken,"bearerToken");function verifyDevJwt(e,r){const t=bearerToken(e);if(!t)return;const[n,s,f]=t.split(".");if(!n||!s||!f)return;if(r)return r.verify(t);let o;try{o=JSON.parse(Buffer.from(n,"base64url").toString()).alg}catch{return}if(typeof o=="string"&&o!=="HS256"){if(process.env.LENSMCP_GW_DEV_JWT_INSECURE_DECODE!=="1")return;try{const i=JSON.parse(Buffer.from(s,"base64url").toString());return typeof i.exp=="number"&&Date.now()/1e3>i.exp?void 0:i}catch{return}}const d=process.env.AUTH_JWT_SECRET??"tetros-dev-secret",l=(0,node_crypto_1.createHmac)("sha256",d).update(`${n}.${s}`).digest("base64url"),p=Buffer.from(f),c=Buffer.from(l);if(!(p.length!==c.length||!(0,node_crypto_1.timingSafeEqual)(p,c)))try{const i=JSON.parse(Buffer.from(s,"base64url").toString());return typeof i.exp=="number"&&Date.now()/1e3>i.exp?void 0:i}catch{return}}a(verifyDevJwt,"verifyDevJwt"),u(verifyDevJwt,"verifyDevJwt");function stampIdentity(e,r){const t=u((f,o)=>{o!=null&&o!==""&&(e.headers[f]=String(o))},"set");t("x-user-id",r.sub),t("x-tenant-id",r.tid);const n=r.roles;Array.isArray(n)&&n.length&&(e.headers["x-user-roles"]=n.map(String).join(","));const s=r.perms;Array.isArray(s)&&s.length&&(e.headers["x-user-permissions"]=s.map(String).join(","))}a(stampIdentity,"stampIdentity"),u(stampIdentity,"stampIdentity");const devAttrs=u((e,r)=>({...r,ip:e.socket.remoteAddress??"",method:(e.method??"GET").toUpperCase(),path:(e.url??"/").split("?")[0]}),"devAttrs");exports.devAttrs=devAttrs;
@@ -1 +1 @@
1
- "use strict";var _=Object.defineProperty;var l=(e,o)=>_(e,"name",{value:o,configurable:!0});var S=Object.defineProperty,p=l((e,o)=>S(e,"name",{value:o,configurable:!0}),"p"),k=Object.defineProperty,a=p((e,o)=>k(e,"name",{value:o,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.pickSock=pickSock,exports.lensPortFromUpstream=lensPortFromUpstream,exports.discoverRoutes=discoverRoutes;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")),manifest_1=require("../manifest"),scope_1=require("./scope");function pickSock(e,o=1500){const n=Date.now();if(n-e.scannedAt>o){try{e.socks=fs.readdirSync(e.dir).filter(s=>/^child-\d+\.sock$/.test(s)).map(s=>path.join(e.dir,s))}catch{e.socks=[]}e.scannedAt=n}if(e.socks.length!==0)return e.idx=(e.idx+1)%e.socks.length,e.socks[e.idx]}l(pickSock,"pickSock"),p(pickSock,"pickSock"),a(pickSock,"pickSock");function lensPortFromUpstream(e){if(!e)return;const o=/:(\d+)/.exec(e);return o?Number(o[1]):void 0}l(lensPortFromUpstream,"lensPortFromUpstream"),p(lensPortFromUpstream,"lensPortFromUpstream"),a(lensPortFromUpstream,"lensPortFromUpstream");function discoverRoutes(e,o,n=os.tmpdir(),s=""){const b=a(r=>s?path.join(n,s,`${r}-devserver`):path.join(n,`${r}-devserver`),"sockDirFor"),i=[],m=[],j=[];let d;for(const[r,x]of Object.entries(o)){let t;try{const c=JSON.parse(fs.readFileSync(path.join(e,x.root,"project.json"),"utf8"));t=c.cluster??c.davnx}catch{continue}if(!t)continue;let u,h,f,y;if(t.service)t.lens&&console.warn(`[gateway] ${r}: cluster.lens is ignored for pod (service) decls \u2014 backends are instrumented by serve-hmr.`),u={dir:b(t.service),socks:[],idx:-1,scannedAt:0},h={project:r,decl:t,root:e,wsKey:s,pool:u,state:"down",lastUsed:Date.now(),inflight:0,lastScaleAt:0},m.push(h);else if(t.lens){const c=t.port??lensPortFromUpstream(t.upstream)??5173;f=`http://localhost:${c}`,y={project:r,projectRoot:path.join(e,x.root),port:c}}else if(f=t.upstream??(t.port?`http://localhost:${t.port}`:void 0),!f){console.warn(`[gateway] ${r}: no service/upstream/port \u2014 skipped.`);continue}const v={project:r,prependPrefix:t.prependPrefix,target:f,pool:u,svc:h,prefix:t.path,auth:t.auth,lens:y};t.host&&(i.push({...v,host:t.host}),j.push(t.host),u&&t.internalHost!==!1&&i.push({...v,host:typeof t.internalHost=="string"?t.internalHost:`internal.${t.host}`,internal:!0})),t.default&&(d={...v,prefix:void 0})}return d&&(d.apex=(0,scope_1.baseDomainOf)(j),i.push(d)),(0,manifest_1.sortBySpecificity)(i),{routes:i,services:m}}l(discoverRoutes,"discoverRoutes"),p(discoverRoutes,"discoverRoutes"),a(discoverRoutes,"discoverRoutes");
1
+ "use strict";var S=Object.defineProperty;var p=(e,r)=>S(e,"name",{value:r,configurable:!0});var k=Object.defineProperty,a=p((e,r)=>k(e,"name",{value:r,configurable:!0}),"a");Object.defineProperty(exports,"__esModule",{value:!0}),exports.pickSock=pickSock,exports.lensPortFromUpstream=lensPortFromUpstream,exports.discoverRoutes=discoverRoutes;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")),manifest_1=require("../manifest"),scope_1=require("./scope");function pickSock(e,r=1500){const i=Date.now();if(i-e.scannedAt>r){try{e.socks=fs.readdirSync(e.dir).filter(s=>/^child-\d+\.sock$/.test(s)).map(s=>path.join(e.dir,s))}catch{e.socks=[]}e.scannedAt=i}if(e.socks.length!==0)return e.idx=(e.idx+1)%e.socks.length,e.socks[e.idx]}p(pickSock,"pickSock"),a(pickSock,"pickSock");function lensPortFromUpstream(e){if(!e)return;const r=/:(\d+)/.exec(e);return r?Number(r[1]):void 0}p(lensPortFromUpstream,"lensPortFromUpstream"),a(lensPortFromUpstream,"lensPortFromUpstream");function discoverRoutes(e,r,i=os.tmpdir(),s=""){const y=a(t=>s?path.join(i,s,`${t}-devserver`):path.join(i,`${t}-devserver`),"sockDirFor"),n=[],v=[],m=[];let l;for(const[t,x]of Object.entries(r)){let o;try{const c=JSON.parse(fs.readFileSync(path.join(e,x.root,"project.json"),"utf8"));o=c.cluster??c.davnx}catch{continue}if(!o)continue;let d,f,u,j;if(o.service)o.lens&&console.warn(`[gateway] ${t}: cluster.lens is ignored for pod (service) decls \u2014 backends are instrumented by serve-hmr.`),d={dir:y(o.service),socks:[],idx:-1,scannedAt:0},f={project:t,decl:o,root:e,wsKey:s,pool:d,state:"down",lastUsed:Date.now(),inflight:0,lastScaleAt:0},v.push(f);else if(o.lens){const c=o.port??lensPortFromUpstream(o.upstream)??5173;u=`http://localhost:${c}`,j={project:t,projectRoot:path.join(e,x.root),port:c}}else if(u=o.upstream??(o.port?`http://localhost:${o.port}`:void 0),!u){console.warn(`[gateway] ${t}: no service/upstream/port \u2014 skipped.`);continue}const h={project:t,prependPrefix:o.prependPrefix,target:u,pool:d,svc:f,prefix:o.path,auth:o.auth,lens:j};o.host&&(n.push({...h,host:o.host}),m.push(o.host),d&&o.internalHost!==!1&&n.push({...h,host:typeof o.internalHost=="string"?o.internalHost:`internal.${o.host}`,internal:!0})),o.default&&(l={...h,prefix:void 0})}return l&&(l.apex=(0,scope_1.baseDomainOf)(m),n.push(l)),(0,manifest_1.sortBySpecificity)(n),{routes:n,services:v}}p(discoverRoutes,"discoverRoutes"),a(discoverRoutes,"discoverRoutes");
@@ -1 +1 @@
1
- "use strict";var l=Object.defineProperty;var n=(r,e)=>l(r,"name",{value:e,configurable:!0});var i=Object.defineProperty,a=n((r,e)=>i(r,"name",{value:e,configurable:!0}),"a"),c=Object.defineProperty,s=a((r,e)=>c(r,"name",{value:e,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.PREFLIGHT_METHODS_ROUTED=exports.PREFLIGHT_METHODS_NOROUTE=exports.corsPreflight=exports.cors=exports.GATEWAY_OWNED_HEADERS=exports.sendError=void 0;const gateway_errors_1=require("../gateway-errors"),sendError=s((r,e,o,E,d)=>{const t=(0,gateway_errors_1.ensureRequestId)(e);r.headersSent||r.writeHead(o,{"content-type":gateway_errors_1.GATEWAY_ERROR_CONTENT_TYPE,"x-request-id":t}),r.end((0,gateway_errors_1.gatewayErrorBody)(o,E,t,d))},"sendError");exports.sendError=sendError,exports.GATEWAY_OWNED_HEADERS=["x-internal-token","x-api-key-id","x-user-id","x-tenant-id","x-user-roles","x-user-permissions"];const cors=s((r,e)=>{const o=String(r.headers.origin??"*");e.setHeader("access-control-allow-origin",o),e.setHeader("access-control-allow-credentials","true"),e.setHeader("vary","Origin")},"cors");exports.cors=cors;const corsPreflight=s((r,e,o)=>{(0,exports.cors)(r,e),e.writeHead(204,{"access-control-allow-methods":o,"access-control-allow-headers":String(r.headers["access-control-request-headers"]??"*")}),e.end()},"corsPreflight");exports.corsPreflight=corsPreflight,exports.PREFLIGHT_METHODS_NOROUTE="GET,HEAD,OPTIONS",exports.PREFLIGHT_METHODS_ROUTED="GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS";
1
+ "use strict";var i=Object.defineProperty;var a=(r,e)=>i(r,"name",{value:e,configurable:!0});var c=Object.defineProperty,s=a((r,e)=>c(r,"name",{value:e,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.PREFLIGHT_METHODS_ROUTED=exports.PREFLIGHT_METHODS_NOROUTE=exports.corsPreflight=exports.cors=exports.GATEWAY_OWNED_HEADERS=exports.sendError=void 0;const gateway_errors_1=require("../gateway-errors"),sendError=s((r,e,o,n,E)=>{const t=(0,gateway_errors_1.ensureRequestId)(e);r.headersSent||r.writeHead(o,{"content-type":gateway_errors_1.GATEWAY_ERROR_CONTENT_TYPE,"x-request-id":t}),r.end((0,gateway_errors_1.gatewayErrorBody)(o,n,t,E))},"sendError");exports.sendError=sendError,exports.GATEWAY_OWNED_HEADERS=["x-internal-token","x-api-key-id","x-user-id","x-tenant-id","x-user-roles","x-user-permissions"];const cors=s((r,e)=>{const o=String(r.headers.origin??"*");e.setHeader("access-control-allow-origin",o),e.setHeader("access-control-allow-credentials","true"),e.setHeader("vary","Origin")},"cors");exports.cors=cors;const corsPreflight=s((r,e,o)=>{(0,exports.cors)(r,e),e.writeHead(204,{"access-control-allow-methods":o,"access-control-allow-headers":String(r.headers["access-control-request-headers"]??"*")}),e.end()},"corsPreflight");exports.corsPreflight=corsPreflight,exports.PREFLIGHT_METHODS_NOROUTE="GET,HEAD,OPTIONS",exports.PREFLIGHT_METHODS_ROUTED="GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS";
@@ -1 +1 @@
1
- "use strict";var j=Object.defineProperty;var p=(l,i)=>j(l,"name",{value:i,configurable:!0});var E=Object.defineProperty,d=p((l,i)=>E(l,"name",{value:i,configurable:!0}),"d"),v=Object.defineProperty,h=d((l,i)=>v(l,"name",{value:i,configurable:!0}),"h");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHandler=createHandler;const manifest_1=require("../manifest"),gateway_errors_1=require("../gateway-errors"),discovery_1=require("./discovery"),edge_1=require("./edge"),observability_1=require("./observability"),chooser_1=require("./chooser");function createHandler(l){const{rt:i,obs:u,auth:f,proxy:w,hooks:a}=l,m=h(e=>(0,manifest_1.matchRoute)(i.routes,(e.headers.host||"").split(":")[0],e.url??"/"),"match"),y=h((e,s)=>{if((0,edge_1.cors)(e,s),e.method==="OPTIONS"){s.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_NOROUTE,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),s.end();return}const n=(e.headers.host||"").split(":")[0],o=i.routes.filter(t=>t.host&&(0,manifest_1.hostMatches)(t.host,n)&&t.prefix),r=(e.url??"/").split("?")[0];if((r==="/"||r==="/index.html"||r==="")&&i.routes.some(t=>t.host&&(0,manifest_1.hostMatches)(t.host,n)&&t.project===observability_1.LENS_DASHBOARD_PROJECT)){(0,chooser_1.serveChooser)(s,i.wsKey);return}if(o.length>0&&(r==="/health"||r==="/healthz")){const t=o.map(c=>({project:c.project,prefix:c.prefix,pods:c.pool?((0,discovery_1.pickSock)(c.pool,0),c.pool.socks.length):void 0,state:c.svc?.state??(c.target?"up":"unknown")})),g=t.every(c=>c.state!=="down"||!0);s.writeHead(g?200:503,{"content-type":"application/json"}),s.end(JSON.stringify({status:"ok",host:n,services:t},null,2));return}if(o.length>0&&r==="/.well-known/services"){s.writeHead(200,{"content-type":"application/json"}),s.end(JSON.stringify({host:n,services:o.map(t=>({prefix:t.prefix,project:t.project,internal:!!t.internal}))},null,2));return}(0,edge_1.sendError)(s,e,404,"not_found",o.length>0?`No service mounted at ${r}. Mounted on ${n}: ${[...new Set(o.map(t=>t.prefix))].join(", ")} (+ /health, /.well-known/services)`:"No gateway route for this hostname.")},"handleNoRoute"),_=h(async(e,s)=>{if(!e.headers.host){const r=e.authority??e.headers[":authority"];r&&(e.headers.host=r)}for(const r of edge_1.GATEWAY_OWNED_HEADERS)delete e.headers[r];(0,gateway_errors_1.ensureRequestId)(e);const n=u.beginTrace(e);n&&(u.traceStep(n,"received",{host:n.host,method:n.method,url:n.url}),e.__lensmcpTrace=n);const o=a.httpContext(e,s,n);try{if(a.has("onRequest")&&(await a.run("onRequest",o),o.responded()))return;const r=m(e);if(!r){y(e,s);return}if(o.route=r,u.traceStep(n,"route-match",{project:r.project,...r.prefix?{prefix:r.prefix}:{},...r.host?{routeHost:r.host}:{},...r.internal?{internal:!0}:{}}),e.method==="OPTIONS"){(0,edge_1.cors)(e,s),s.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_ROUTED,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),s.end();return}if((0,edge_1.cors)(e,s),a.has("beforeAuth")&&(await a.run("beforeAuth",o),o.responded()))return;if(r.internal){const t=f.internal(e,r,n);if(!t.ok){u.finishTrace(n,r.project,t.status),(0,edge_1.sendError)(s,e,t.status,t.reason,t.detail);return}o.caller=t.caller}else{const t=f.public(e,r,n);if(!t.ok){u.finishTrace(n,r.project,t.status),(0,edge_1.sendError)(s,e,t.status,t.reason,t.detail);return}if(o.claims=t.claims,a.has("afterAuth")&&(await a.run("afterAuth",o),o.responded()))return}if(r.pool&&i.serviceKeys[r.project]&&(e.headers["x-internal-token"]=i.serviceKeys[r.project]),a.has("beforeForward")&&(await a.run("beforeForward",o),o.responded()))return;a.has("onResponse")&&s.on("close",()=>{o.status=s.statusCode??0,a.run("onResponse",o)}),w.forward(r,e,s)}catch(r){o.error=r,a.has("onError")&&await a.run("onError",o),o.responded()||(0,edge_1.sendError)(s,e,500,"internal",r.message)}},"runPipeline");return(e,s)=>{_(e,s)}}p(createHandler,"createHandler"),d(createHandler,"createHandler"),h(createHandler,"createHandler");
1
+ "use strict";var E=Object.defineProperty;var d=(l,i)=>E(l,"name",{value:i,configurable:!0});var v=Object.defineProperty,h=d((l,i)=>v(l,"name",{value:i,configurable:!0}),"h");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHandler=createHandler;const manifest_1=require("../manifest"),gateway_errors_1=require("../gateway-errors"),discovery_1=require("./discovery"),edge_1=require("./edge"),observability_1=require("./observability"),chooser_1=require("./chooser");function createHandler(l){const{rt:i,obs:u,auth:p,proxy:f,hooks:a}=l,w=h(e=>(0,manifest_1.matchRoute)(i.routes,(e.headers.host||"").split(":")[0],e.url??"/"),"match"),m=h((e,o)=>{if((0,edge_1.cors)(e,o),e.method==="OPTIONS"){o.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_NOROUTE,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),o.end();return}const n=(e.headers.host||"").split(":")[0],s=i.routes.filter(r=>r.host&&(0,manifest_1.hostMatches)(r.host,n)&&r.prefix),t=(e.url??"/").split("?")[0];if((t==="/"||t==="/index.html"||t==="")&&i.routes.some(r=>r.host&&(0,manifest_1.hostMatches)(r.host,n)&&r.project===observability_1.LENS_DASHBOARD_PROJECT)){(0,chooser_1.serveChooser)(o,i.wsKey);return}if(s.length>0&&(t==="/health"||t==="/healthz")){const r=s.map(c=>({project:c.project,prefix:c.prefix,pods:c.pool?((0,discovery_1.pickSock)(c.pool,0),c.pool.socks.length):void 0,state:c.svc?.state??(c.target?"up":"unknown")})),_=r.every(c=>c.state!=="down"||!0);o.writeHead(_?200:503,{"content-type":"application/json"}),o.end(JSON.stringify({status:"ok",host:n,services:r},null,2));return}if(s.length>0&&t==="/.well-known/services"){o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({host:n,services:s.map(r=>({prefix:r.prefix,project:r.project,internal:!!r.internal}))},null,2));return}(0,edge_1.sendError)(o,e,404,"not_found",s.length>0?`No service mounted at ${t}. Mounted on ${n}: ${[...new Set(s.map(r=>r.prefix))].join(", ")} (+ /health, /.well-known/services)`:"No gateway route for this hostname.")},"handleNoRoute"),y=h(async(e,o)=>{if(!e.headers.host){const t=e.authority??e.headers[":authority"];t&&(e.headers.host=t)}for(const t of edge_1.GATEWAY_OWNED_HEADERS)delete e.headers[t];(0,gateway_errors_1.ensureRequestId)(e);const n=u.beginTrace(e);n&&(u.traceStep(n,"received",{host:n.host,method:n.method,url:n.url}),e.__lensmcpTrace=n);const s=a.httpContext(e,o,n);try{if(a.has("onRequest")&&(await a.run("onRequest",s),s.responded()))return;const t=w(e);if(!t){m(e,o);return}if(s.route=t,u.traceStep(n,"route-match",{project:t.project,...t.prefix?{prefix:t.prefix}:{},...t.host?{routeHost:t.host}:{},...t.internal?{internal:!0}:{}}),e.method==="OPTIONS"){(0,edge_1.cors)(e,o),o.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_ROUTED,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*")}),o.end();return}if((0,edge_1.cors)(e,o),a.has("beforeAuth")&&(await a.run("beforeAuth",s),s.responded()))return;if(t.internal){const r=p.internal(e,t,n);if(!r.ok){u.finishTrace(n,t.project,r.status),(0,edge_1.sendError)(o,e,r.status,r.reason,r.detail);return}s.caller=r.caller}else{const r=p.public(e,t,n);if(!r.ok){u.finishTrace(n,t.project,r.status),(0,edge_1.sendError)(o,e,r.status,r.reason,r.detail);return}if(s.claims=r.claims,a.has("afterAuth")&&(await a.run("afterAuth",s),s.responded()))return}if(t.pool&&i.serviceKeys[t.project]&&(e.headers["x-internal-token"]=i.serviceKeys[t.project]),a.has("beforeForward")&&(await a.run("beforeForward",s),s.responded()))return;a.has("onResponse")&&o.on("close",()=>{s.status=o.statusCode??0,a.run("onResponse",s)}),f.forward(t,e,o)}catch(t){s.error=t,a.has("onError")&&await a.run("onError",s),s.responded()||(0,edge_1.sendError)(o,e,500,"internal",t.message)}},"runPipeline");return(e,o)=>{y(e,o)}}d(createHandler,"createHandler"),h(createHandler,"createHandler");
@@ -1 +1 @@
1
- "use strict";var u=Object.defineProperty;var i=(o,e)=>u(o,"name",{value:e,configurable:!0});var a=Object.defineProperty,l=i((o,e)=>a(o,"name",{value:e,configurable:!0}),"l"),c=Object.defineProperty,s=l((o,e)=>c(o,"name",{value:e,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.VITE_FULL_RELOAD_PAYLOAD=void 0,exports.encodeWsTextFrame=encodeWsTextFrame,exports.registerHmrClient=registerHmrClient,exports.broadcastFullReload=broadcastFullReload,exports.VITE_FULL_RELOAD_PAYLOAD='{"type":"full-reload"}';function encodeWsTextFrame(o){const e=Buffer.from(o,"utf8"),r=e.length;if(r<126)return Buffer.concat([Buffer.from([129,r]),e]);if(r<=65535){const t=Buffer.alloc(4);return t[0]=129,t[1]=126,t.writeUInt16BE(r,2),Buffer.concat([t,e])}const n=Buffer.alloc(10);return n[0]=129,n[1]=127,n.writeBigUInt64BE(BigInt(r),2),Buffer.concat([n,e])}i(encodeWsTextFrame,"encodeWsTextFrame"),l(encodeWsTextFrame,"encodeWsTextFrame"),s(encodeWsTextFrame,"encodeWsTextFrame");function registerHmrClient(o,e,r){const n=o.hmrClients??=new Map;let t=n.get(e);t||n.set(e,t=new Set),t.add(r);const f=s(()=>{t.delete(r),t.size===0&&n.delete(e)},"drop");r.once("close",f),r.once("error",f)}i(registerHmrClient,"registerHmrClient"),l(registerHmrClient,"registerHmrClient"),s(registerHmrClient,"registerHmrClient");function broadcastFullReload(o,e){const r=o.hmrClients?.get(e);if(!r||r.size===0)return 0;const n=encodeWsTextFrame(exports.VITE_FULL_RELOAD_PAYLOAD);let t=0;for(const f of r)if(!f.destroyed)try{f.write(n),t+=1}catch{}return t}i(broadcastFullReload,"broadcastFullReload"),l(broadcastFullReload,"broadcastFullReload"),s(broadcastFullReload,"broadcastFullReload");
1
+ "use strict";var a=Object.defineProperty;var l=(n,e)=>a(n,"name",{value:e,configurable:!0});var c=Object.defineProperty,s=l((n,e)=>c(n,"name",{value:e,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.VITE_FULL_RELOAD_PAYLOAD=void 0,exports.encodeWsTextFrame=encodeWsTextFrame,exports.registerHmrClient=registerHmrClient,exports.broadcastFullReload=broadcastFullReload,exports.VITE_FULL_RELOAD_PAYLOAD='{"type":"full-reload"}';function encodeWsTextFrame(n){const e=Buffer.from(n,"utf8"),t=e.length;if(t<126)return Buffer.concat([Buffer.from([129,t]),e]);if(t<=65535){const r=Buffer.alloc(4);return r[0]=129,r[1]=126,r.writeUInt16BE(t,2),Buffer.concat([r,e])}const o=Buffer.alloc(10);return o[0]=129,o[1]=127,o.writeBigUInt64BE(BigInt(t),2),Buffer.concat([o,e])}l(encodeWsTextFrame,"encodeWsTextFrame"),s(encodeWsTextFrame,"encodeWsTextFrame");function registerHmrClient(n,e,t){const o=n.hmrClients??=new Map;let r=o.get(e);r||o.set(e,r=new Set),r.add(t);const f=s(()=>{r.delete(t),r.size===0&&o.delete(e)},"drop");t.once("close",f),t.once("error",f)}l(registerHmrClient,"registerHmrClient"),s(registerHmrClient,"registerHmrClient");function broadcastFullReload(n,e){const t=n.hmrClients?.get(e);if(!t||t.size===0)return 0;const o=encodeWsTextFrame(exports.VITE_FULL_RELOAD_PAYLOAD);let r=0;for(const f of t)if(!f.destroyed)try{f.write(o),r+=1}catch{}return r}l(broadcastFullReload,"broadcastFullReload"),s(broadcastFullReload,"broadcastFullReload");
@@ -1 +1 @@
1
- "use strict";var p=Object.defineProperty;var c=(o,e)=>p(o,"name",{value:e,configurable:!0});var u=Object.defineProperty,i=c((o,e)=>u(o,"name",{value:e,configurable:!0}),"i"),a=Object.defineProperty,n=i((o,e)=>a(o,"name",{value:e,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHooks=createHooks;const edge_1=require("./edge"),TERMINAL_PHASES=new Set(["onResponse","onError"]);function createHooks(o){return{run:n(async(e,r)=>{const t=o[e];if(!t||t.length===0)return;const d=TERMINAL_PHASES.has(e);for(const s of t){if(!d&&r.responded())return;await s(r)}},"run"),has:n(e=>(o[e]?.length??0)>0,"has"),httpContext:n((e,r,t)=>({req:e,res:r,socket:void 0,trace:t,route:void 0,claims:void 0,caller:void 0,pod:void 0,error:void 0,status:void 0,sendError:n((d,s,v)=>(0,edge_1.sendError)(r,e,d,s,v),"sendError"),responded:n(()=>r.writableEnded||r.headersSent,"responded")}),"httpContext"),upgradeContext:n((e,r,t)=>({req:e,res:void 0,socket:r,trace:t,route:void 0,claims:void 0,caller:void 0,pod:void 0,error:void 0,status:void 0,sendError:n(()=>{try{r.destroy()}catch{}},"sendError"),responded:n(()=>r.destroyed,"responded")}),"upgradeContext")}}c(createHooks,"createHooks"),i(createHooks,"createHooks"),n(createHooks,"createHooks");
1
+ "use strict";var u=Object.defineProperty;var i=(t,e)=>u(t,"name",{value:e,configurable:!0});var a=Object.defineProperty,n=i((t,e)=>a(t,"name",{value:e,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHooks=createHooks;const edge_1=require("./edge"),TERMINAL_PHASES=new Set(["onResponse","onError"]);function createHooks(t){return{run:n(async(e,r)=>{const o=t[e];if(!o||o.length===0)return;const d=TERMINAL_PHASES.has(e);for(const s of o){if(!d&&r.responded())return;await s(r)}},"run"),has:n(e=>(t[e]?.length??0)>0,"has"),httpContext:n((e,r,o)=>({req:e,res:r,socket:void 0,trace:o,route:void 0,claims:void 0,caller:void 0,pod:void 0,error:void 0,status:void 0,sendError:n((d,s,c)=>(0,edge_1.sendError)(r,e,d,s,c),"sendError"),responded:n(()=>r.writableEnded||r.headersSent,"responded")}),"httpContext"),upgradeContext:n((e,r,o)=>({req:e,res:void 0,socket:r,trace:o,route:void 0,claims:void 0,caller:void 0,pod:void 0,error:void 0,status:void 0,sendError:n(()=>{try{r.destroy()}catch{}},"sendError"),responded:n(()=>r.destroyed,"responded")}),"upgradeContext")}}i(createHooks,"createHooks"),n(createHooks,"createHooks");
@@ -1 +1 @@
1
- "use strict";var te=Object.defineProperty;var P=(s,c)=>te(s,"name",{value:c,configurable:!0});var Z=Object.defineProperty,L=P((s,c)=>Z(s,"name",{value:c,configurable:!0}),"L"),X=Object.defineProperty,l=L((s,c)=>X(s,"name",{value:c,configurable:!0}),"l");Object.defineProperty(exports,"__esModule",{value:!0}),exports.probePortFree=probePortFree,exports.probePortListening=probePortListening,exports.probeWatcherHealth=probeWatcherHealth,exports.waitForPortFree=waitForPortFree,exports.viteArgPort=viteArgPort,exports.createLensChildren=createLensChildren;const tslib_1=require("tslib"),net=tslib_1.__importStar(require("node:net")),path=tslib_1.__importStar(require("node:path")),node_child_process_1=require("node:child_process"),lens_frontend_1=require("@lensmcp/nx-plugin/lens-frontend"),hmr_reload_1=require("./hmr-reload"),pod_env_1=require("./pod-env"),scope_1=require("./scope"),workspace_registry_1=require("./workspace-registry"),lifecycle_1=require("./lifecycle"),types_1=require("./types"),observability_1=require("./observability"),HEAL_BASE_MS=1500,HEAL_MAX_MS=3e4,HEAL_HEALTHY_MS=2e4;function tryBind(s,c){return new Promise(p=>{const h=net.createServer();h.unref(),h.once("error",d=>{const E=d.code??"";p(E==="EADDRINUSE"||E==="EACCES"?"in-use":"unavailable")}),h.listen(c===void 0?{port:s}:{port:s,host:c},()=>h.close(()=>p("free")))})}P(tryBind,"tryBind"),L(tryBind,"tryBind"),l(tryBind,"tryBind");async function probePortFree(s){for(const c of[void 0,"0.0.0.0","127.0.0.1","::1"])if(await tryBind(s,c)==="in-use")return!1;return!0}P(probePortFree,"probePortFree"),L(probePortFree,"probePortFree"),l(probePortFree,"probePortFree");function probeConnect(s,c,p){return new Promise(h=>{let d=!1;const E=net.createConnection({host:s,port:c});E.unref();const R=setTimeout(()=>y(!1),p);R.unref?.();function y($){d||(d=!0,clearTimeout(R),E.destroy(),h($))}P(y,"P"),L(y,"y"),l(y,"finish"),E.once("connect",()=>y(!0)),E.once("error",()=>y(!1))})}P(probeConnect,"probeConnect"),L(probeConnect,"probeConnect"),l(probeConnect,"probeConnect");async function probePortListening(s,c=1e3){return await probeConnect("127.0.0.1",s,c)?!0:probeConnect("::1",s,c)}P(probePortListening,"probePortListening"),L(probePortListening,"probePortListening"),l(probePortListening,"probePortListening");async function probeWatcherHealth(s,c=1500){try{const p=new AbortController,h=setTimeout(()=>p.abort(),c);h.unref?.();try{const d=await fetch(`http://127.0.0.1:${s}/__lensmcp/watcher-health`,{signal:p.signal});if(!d.ok)return null;const E=await d.json();return typeof E?.lastFsSetEventAt=="number"?{lastFsSetEventAt:E.lastFsSetEventAt}:null}finally{clearTimeout(h)}}catch{return null}}P(probeWatcherHealth,"probeWatcherHealth"),L(probeWatcherHealth,"probeWatcherHealth"),l(probeWatcherHealth,"probeWatcherHealth");const sleepUnref=l(s=>new Promise(c=>{setTimeout(c,s).unref?.()}),"sleepUnref");async function waitForPortFree(s,c,p={}){const h=p.isPortFree??probePortFree,d=Math.max(1,p.intervalMs??types_1.PORT_FREE_POLL_MS),E=Date.now()+Math.max(0,c);for(;;){if(await h(s))return!0;const R=E-Date.now();if(R<=0)return!1;await sleepUnref(Math.min(d,R))}}P(waitForPortFree,"waitForPortFree"),L(waitForPortFree,"waitForPortFree"),l(waitForPortFree,"waitForPortFree");function viteArgPort(s){const c=l(p=>Number.isInteger(p)&&p>0&&p<65536?p:void 0,"valid");for(let p=0;p<s.length;p+=1){const h=s[p];if(h==="--port")return c(Number(s[p+1]));if(h.startsWith("--port="))return c(Number(h.slice(7)))}}P(viteArgPort,"viteArgPort"),L(viteArgPort,"viteArgPort"),l(viteArgPort,"viteArgPort");function createLensChildren(s,c,p,h){const{emit:d}=c,E=h?.isPortFree??probePortFree,R=h?.probeWatcherHealth??probeWatcherHealth,y=[],$=new Map,A=new Map,T=new Map,W=new Map,k=new Set;let v;const m=new Map,b=new Set,x=new Set,B=new Set,N=new Set,G=l((t,a,n)=>{if(N.has(t))return;N.add(t);const e=setInterval(()=>{(async()=>{if(s.stopped()||b.has(t)){clearInterval(e),N.delete(t);return}await E(a)&&(clearInterval(e),N.delete(t),console.log(`[gateway] lens child ${t}: :${a} freed \u2014 respawning.`),n())})().catch(()=>{})},types_1.LENS_PARKED_PORT_REPROBE_MS);e.unref?.()},"parkUntilPortFree"),j=l(async(t,a)=>{let n=t;for(;a.has(n)||!await E(n);)n+=1;return a.add(n),n!==t&&console.warn(`[gateway] port ${t} is unavailable (another process holds it \u2014 an orphan from a crashed gateway?) \u2014 using ${n} instead.`),n},"claimFreePort"),M=l((t,a,n,e,r=!1,o,i,u)=>{const f=Date.now(),S=(0,node_child_process_1.spawn)(a,n,{cwd:i??s.root,env:{...(0,pod_env_1.podBaseEnv)(process.env,s.root,i??s.root),...e??{},LENSMCP_PARENT_PID:String(process.pid)},stdio:"inherit"});y.push(S),m.set(t,S);const O=t==="vite"||t.endsWith(":vite"),_=O?viteArgPort(n):u;if(O){const C=typeof e?.LENSMCP_EVENT_FILE=="string"?e.LENSMCP_EVENT_FILE:void 0;A.set(S,{project:e?.LENSMCP_PROJECT??"app",spawnedAt:f,port:_,probeFails:0,listening:!1,...C&&C!==s.eventFile?{bus:C}:{}})}return S.on("exit",(C,g)=>{const D=y.indexOf(S);if(D>=0&&y.splice(D,1),A.delete(S),s.stopped()||b.has(t))return;const F=Date.now()-f,w=$.get(t)??{attempts:0,portConflicts:0};(async()=>{const H=_!==void 0&&!await E(_),I=(0,lifecycle_1.isPortConflictExit)({code:C,signal:g,ranMs:F,portHeld:H},{fastFailMs:types_1.CHILD_START_FAIL_MS});if(I&&w.portConflicts<types_1.MAX_PORT_CONFLICT_RETRIES)w.portConflicts+=1,$.set(t,w),console.warn(`[gateway] lens child ${t} lost the :${_} bind race (EADDRINUSE, code=${C}); waiting for the port, then retrying (port-retry ${w.portConflicts}/${types_1.MAX_PORT_CONFLICT_RETRIES}; crash budget untouched).`);else if(I){w.portConflicts=0,$.set(t,w),console.warn(`[gateway] lens child ${t}: :${_} persistently held after ${types_1.MAX_PORT_CONFLICT_RETRIES} bind races \u2014 another server owns it (an external/shared daemon?). Parking; re-probing every ${Math.round(types_1.LENS_PARKED_PORT_REPROBE_MS/1e3)}s, will respawn if it frees.`),d("warning",`lens child parked: ${t} (:${_} held externally)`,`cluster-lens-child:${t}`,{kind:"lens-child-parked",label:t,port:_}),G(t,_,()=>M(t,a,n,e,r,o,i,u));return}else{w.portConflicts=0,F>=HEAL_HEALTHY_MS&&(w.attempts=0);const Y=Math.min(HEAL_MAX_MS,HEAL_BASE_MS*2**w.attempts);w.attempts+=1,$.set(t,w),console.warn(`[gateway] lens child ${t} exited (code=${C} sig=${g}); auto-healing in ${Math.round(Y/1e3)}s (attempt ${w.attempts}).`),await sleepUnref(Y)}s.stopped()||b.has(t)||(_!==void 0&&!await waitForPortFree(_,types_1.PORT_FREE_WAIT_MS,{isPortFree:E,intervalMs:types_1.PORT_FREE_POLL_MS})&&console.warn(`[gateway] lens child ${t}: :${_} still held after ${Math.round(types_1.PORT_FREE_WAIT_MS/1e3)}s \u2014 spawning anyway.`),!(s.stopped()||b.has(t))&&M(t,a,n,e,r,o,i,u))})().catch(()=>{})}),S},"spawnManagedChild");let K=!1;const V=l(async(t,a)=>{if(!K){K=!0;try{for(const[n,e]of[...A]){if(s.stopped())return;if(e.port===void 0||n.exitCode!==null||n.signalCode!==null)continue;if(await probePortListening(e.port,types_1.LENS_PROBE_TIMEOUT_MS)){e.probeFails=0,e.listening||(e.listening=!0,d("info",`lens app up: ${e.project} (vite :${e.port} listening)`,`cluster-lens-app:${e.project}`,{kind:"service-up",project:e.project,port:e.port},void 0,e.bus));continue}const r=e.listening?t.graceMs:Math.max(t.graceMs,t.neverListenedGraceMs);if(Date.now()-e.spawnedAt<r){e.probeFails=0;continue}if(!a)continue;e.probeFails+=1;const o=(0,lifecycle_1.lensChildWedged)({spawnedAt:e.spawnedAt,probeFails:e.probeFails,hasListened:e.listening},T.get(e.project)??0,Date.now(),t);if(!o)continue;const i=e.listening?"its listener VANISHED (was up, now refusing)":`it NEVER bound within the ${Math.round(r/1e3)}s boot allowance`;T.set(e.project,Date.now()),e.probeFails=0,e.listening=!1,console.error(`[gateway] wedge recycle: lens app ${e.project} is ALIVE but NOT LISTENING on :${e.port} after ${Math.max(2,t.failureThreshold)} probes \u2014 ${i} \u2014 SIGKILL; a fresh vite is coming.`),d("warning",`wedge recycle: ${e.project} (${o})`,`cluster-lens-app:${e.project}`,{kind:"pod-recycle",project:e.project,reason:o,port:e.port},void 0,e.bus);try{n.kill("SIGKILL")}catch{}}}finally{K=!1}}},"probeLensChildren"),z=l(()=>{if(v||!types_1.POD_RECYCLE_ENABLED&&!types_1.LENS_PROBE_ENABLED)return;const t={graceMs:types_1.POD_STALE_GRACE_MS,settleMs:types_1.POD_RECYCLE_SETTLE_MS,cooldownMs:types_1.POD_STALE_RECYCLE_COOLDOWN_MS};v=setInterval(()=>{if(s.stopped()||A.size===0||(q(),!types_1.POD_RECYCLE_ENABLED))return;const a=s.sourceSetChangedAt??0;if(a<=0)return;const n=Date.now();for(const[e,r]of A){const o=s.sourceSetChangedFor?.get(r.project)??a,i=T.get(r.project)??0;(0,lifecycle_1.lensFrontendStale)(r.spawnedAt,i,o,n,t)&&((W.get(r.project)??0)>=o||k.has(r.project)||(k.add(r.project),(async()=>{try{const u=types_1.LENS_WATCHER_PROBE_ENABLED&&r.port!==void 0?(0,lifecycle_1.lensWatcherVerdict)(await R(r.port),o,Math.min(types_1.POD_STALE_SCAN_MS,15e3)+types_1.POD_RECYCLE_SETTLE_MS):"unknown";if(u==="heard"){W.set(r.project,o),console.log(`[gateway] stale source set: lens app ${r.project} watcher CONFIRMED the change \u2014 no recycle, HMR intact.`),d("info",`stale check: ${r.project} watcher confirmed \u2014 no recycle`,`cluster-lens-app:${r.project}`,{kind:"pod-stale-verified",project:r.project},void 0,r.bus);return}if(e.exitCode!==null||e.signalCode!==null)return;T.set(r.project,Date.now()),r.listening=!1,console.log(`[gateway] zombie recycle: lens app ${r.project} (stale-source-set) \u2014 watcher ${u==="missed"?"SILENT across the change":"unverifiable"}; restarting it`),d("warning",`zombie recycle: ${r.project} (stale-source-set)`,`cluster-lens-app:${r.project}`,{kind:"pod-recycle",project:r.project,reason:"stale-source-set",watcher:u},void 0,r.bus);const f=(0,hmr_reload_1.broadcastFullReload)(s,r.project);f>0&&console.log(`[gateway] \u2192 told ${f} open tab(s) to reload (stale optimize-dep generation)`);try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null){console.log(`[gateway] zombie recycle: lens app ${r.project} ignored SIGTERM for ${Math.round(types_1.LENS_RECYCLE_KILL_GRACE_MS/1e3)}s \u2014 escalating to SIGKILL.`);try{e.kill("SIGKILL")}catch{}}},types_1.LENS_RECYCLE_KILL_GRACE_MS).unref?.()}finally{k.delete(r.project)}})()))}},Math.min(types_1.POD_STALE_SCAN_MS,15e3)),v.unref?.()},"startLensSweeper"),U=l(async()=>{const t=[...new Map(s.routes.filter(e=>e.lens).map(e=>[e.project,e.lens])).values()],a=p.dashboard!==!1,n=p.mcp!==!1&&t.length>0;if(a){const e=(0,lens_frontend_1.findDashboardBundle)(s.root);if(!e)console.warn("[gateway] lens dashboard bundle not found \u2014 skipping. Install the self-contained CLI: `npm i -D lensmcp` (it ships `bundled/dashboard.js`), or `yarn nx build @lensmcp/lensmcp-mcp` in-repo.");else{const r=(0,scope_1.readLensScope)(s.root),o=await j(p.dashboardPort??r.dashboardPort,x),i=p.lensHost??"lensmcp.local",u=r.basePath;s.registry.appendRoutes(s.wsKey,[{host:i,prefix:u,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${o}`}]),s.rebuildRoutes(),(0,workspace_registry_1.registerWorkspace)({key:r.key,root:s.root,base:u,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] starting lens dashboard \u2192 http://localhost:${o} (routed at https://${i}${u}/)`),d("info",`lens dashboard up: ${i}${u} \u2192 :${o}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:i,port:o,base:u}),M("lens-dashboard",process.execPath,[e],{LENSMCP_EVENT_FILE:s.eventFile,LENSMCP_DASHBOARD_PORT:String(o),LENSMCP_DASHBOARD_BASE:u},!1,void 0,void 0,o)}}if(n){const e=(0,lens_frontend_1.findMcpBundle)(s.root);if(!e)console.warn("[gateway] MCP server bundle not found \u2014 skipping (lens apps will still publish to the bus; an external `lensmcp mcp` can read it). Install: `npm i -D lensmcp`.");else{const r=(0,scope_1.readLensScope)(s.root),o=Number(process.env.LENSMCP_PORT??r.mcpHttpPort),i=l(()=>{console.log(`[gateway] starting lens MCP server on 127.0.0.1:${o} (${e})`),M("lens-mcp",process.execPath,[e],{LENSMCP_EVENT_FILE:s.eventFile,LENSMCP_TRANSPORT:process.env.LENSMCP_TRANSPORT??"http",LENSMCP_PORT:String(o),LENSMCP_WORKSPACE_KEY:r.key,LENSMCP_WORKSPACE_ROOT:s.root},!1,void 0,void 0,o)},"spawnMcp");await E(o)?i():(console.warn(`[gateway] lens MCP port :${o} is already served \u2014 an external lensmcp server (a shared daemon?) owns this workspace's MCP; not spawning a second. Re-probing every ${Math.round(types_1.LENS_PARKED_PORT_REPROBE_MS/1e3)}s; will take over if it frees.`),d("info",`lens MCP served externally on :${o} \u2014 gateway parked its own`,"cluster-lens-child:lens-mcp",{kind:"lens-mcp-external",port:o}),G("lens-mcp",o,i))}}for(const e of t){if(e.started)continue;e.started=!0;const r=await j(e.port,B);if(r!==e.port){for(const o of s.routes)o.lens?.project===e.project&&(o.lens.port=r,o.target=`http://localhost:${r}`);e.port=r}try{(0,lens_frontend_1.spawnLensFrontend)({projectRoot:e.projectRoot,workspaceRoot:s.root,project:e.project,eventFile:s.eventFile,port:e.port,chrome:!0,headless:!0,viteEnv:{LENSMCP_PROJECT:e.project},log:l(o=>console.log(o),"log")},M),d("info",`lens app started: ${e.project} (vite :${e.port})`,`cluster-lens-app:${e.project}`,{kind:"lens-app-up",project:e.project,port:e.port})}catch(o){console.error(`[gateway] lens app ${e.project}: ${o.message}`)}}z()},"bootSingletonsAndApps"),J=l(async t=>{const a=(0,lens_frontend_1.findDashboardBundle)(s.root);if(!a)return;const n=(0,scope_1.readLensScope)(t.root),e=p.lensHost??"lensmcp.local",r=n.basePath,o=await j(n.dashboardPort,x),i=`lens-dashboard:${t.wsKey}`,u=path.join(t.root,".lensmcp","events.jsonl");return b.delete(i),s.registry.appendRoutes(t.wsKey,[{host:e,prefix:r,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${o}`}]),(0,workspace_registry_1.registerWorkspace)({key:t.wsKey,root:t.root,base:r,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] hosting dashboard for workspace '${t.wsKey}' \u2192 http://localhost:${o} (routed at https://${e}${r}/)`),d("info",`lens dashboard up: ${e}${r} \u2192 :${o}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:e,port:o,base:r}),M(i,process.execPath,[a],{LENSMCP_EVENT_FILE:u,LENSMCP_DASHBOARD_PORT:String(o),LENSMCP_DASHBOARD_BASE:r,LENSMCP_WS_KEY:t.wsKey},!1,void 0,t.root,o),()=>{b.add(i),x.delete(o),(0,workspace_registry_1.unregisterWorkspace)(t.wsKey);try{m.get(i)?.kill("SIGTERM")}catch{}m.delete(i)}},"hostWorkspaceDashboard"),Q=l(async t=>{const a=new Map;for(const o of t.routes){if(!o.lens)continue;const i=a.get(o.lens.project)??[];i.push(o),a.set(o.lens.project,i)}if(a.size===0)return()=>{};const n=path.join(t.root,".lensmcp","events.jsonl"),e=[],r=[];for(const[o,i]of a){const u=i[0].lens;if(u.started)continue;const f=await j(u.port,B);r.push(f);for(const g of i)g.lens.started=!0,g.lens.port=f,g.target=`http://localhost:${f}`;const S=`${t.wsKey}:${o}:`,O=`${S}vite`,_=`${S}browser-capture`;e.push(O,_),b.delete(O),b.delete(_);const C=l((g,D,F,w,H,I)=>M(g,D,F,w,H,I,t.root),"spawnInRoot");console.log(`[gateway] hosting lens app '${o}' of '${t.wsKey}' \u2192 vite :${f}`);try{(0,lens_frontend_1.spawnLensFrontend)({projectRoot:u.projectRoot,workspaceRoot:t.root,project:o,eventFile:n,port:f,chrome:!0,headless:!0,viteEnv:{LENSMCP_PROJECT:o},labelPrefix:S,log:l(g=>console.log(g),"log")},C),d("info",`lens app started: ${t.wsKey}/${o} (vite :${f})`,`cluster-lens-app:${t.wsKey}:${o}`,{kind:"lens-app-up",project:o,port:f},void 0,n)}catch(g){console.error(`[gateway] lens app ${t.wsKey}/${o}: ${g.message}`)}}return()=>{for(const o of e){b.add(o);try{m.get(o)?.kill("SIGTERM")}catch{}m.delete(o)}for(const o of r)B.delete(o)}},"hostWorkspaceApps"),ee=l(async(t=2500)=>{v&&(clearInterval(v),v=void 0);const a=[...y];for(const r of a)try{r.kill("SIGTERM")}catch{}const n=l(r=>r.exitCode===null&&r.signalCode===null,"alive"),e=Date.now()+t;for(;Date.now()<e&&a.some(n);)await new Promise(r=>setTimeout(r,100));for(const r of a)if(n(r))try{r.kill("SIGKILL")}catch{}},"reapAll"),q=l(()=>V({graceMs:types_1.LENS_PROBE_GRACE_MS,failureThreshold:types_1.LENS_PROBE_FAILS,cooldownMs:types_1.LENS_PROBE_COOLDOWN_MS,neverListenedGraceMs:types_1.LENS_PROBE_BOOT_GRACE_MS},types_1.LENS_PROBE_ENABLED),"probeLensFrontends");return{spawnManagedChild:M,bootSingletonsAndApps:U,hostWorkspaceDashboard:J,hostWorkspaceApps:Q,probeLensFrontends:q,reapAll:ee}}P(createLensChildren,"createLensChildren"),L(createLensChildren,"createLensChildren"),l(createLensChildren,"createLensChildren");
1
+ "use strict";var Z=Object.defineProperty;var L=(n,p)=>Z(n,"name",{value:p,configurable:!0});var X=Object.defineProperty,l=L((n,p)=>X(n,"name",{value:p,configurable:!0}),"l");Object.defineProperty(exports,"__esModule",{value:!0}),exports.probePortFree=probePortFree,exports.probePortListening=probePortListening,exports.probeWatcherHealth=probeWatcherHealth,exports.waitForPortFree=waitForPortFree,exports.viteArgPort=viteArgPort,exports.createLensChildren=createLensChildren;const tslib_1=require("tslib"),net=tslib_1.__importStar(require("node:net")),path=tslib_1.__importStar(require("node:path")),node_child_process_1=require("node:child_process"),lens_frontend_1=require("@lensmcp/nx-plugin/lens-frontend"),hmr_reload_1=require("./hmr-reload"),pod_env_1=require("./pod-env"),scope_1=require("./scope"),workspace_registry_1=require("./workspace-registry"),lifecycle_1=require("./lifecycle"),types_1=require("./types"),observability_1=require("./observability"),HEAL_BASE_MS=1500,HEAL_MAX_MS=3e4,HEAL_HEALTHY_MS=2e4;function tryBind(n,p){return new Promise(c=>{const h=net.createServer();h.unref(),h.once("error",d=>{const E=d.code??"";c(E==="EADDRINUSE"||E==="EACCES"?"in-use":"unavailable")}),h.listen(p===void 0?{port:n}:{port:n,host:p},()=>h.close(()=>c("free")))})}L(tryBind,"tryBind"),l(tryBind,"tryBind");async function probePortFree(n){for(const p of[void 0,"0.0.0.0","127.0.0.1","::1"])if(await tryBind(n,p)==="in-use")return!1;return!0}L(probePortFree,"probePortFree"),l(probePortFree,"probePortFree");function probeConnect(n,p,c){return new Promise(h=>{let d=!1;const E=net.createConnection({host:n,port:p});E.unref();const C=setTimeout(()=>P(!1),c);C.unref?.();function P(R){d||(d=!0,clearTimeout(C),E.destroy(),h(R))}L(P,"y"),l(P,"finish"),E.once("connect",()=>P(!0)),E.once("error",()=>P(!1))})}L(probeConnect,"probeConnect"),l(probeConnect,"probeConnect");async function probePortListening(n,p=1e3){return await probeConnect("127.0.0.1",n,p)?!0:probeConnect("::1",n,p)}L(probePortListening,"probePortListening"),l(probePortListening,"probePortListening");async function probeWatcherHealth(n,p=1500){try{const c=new AbortController,h=setTimeout(()=>c.abort(),p);h.unref?.();try{const d=await fetch(`http://127.0.0.1:${n}/__lensmcp/watcher-health`,{signal:c.signal});if(!d.ok)return null;const E=await d.json();return typeof E?.lastFsSetEventAt=="number"?{lastFsSetEventAt:E.lastFsSetEventAt}:null}finally{clearTimeout(h)}}catch{return null}}L(probeWatcherHealth,"probeWatcherHealth"),l(probeWatcherHealth,"probeWatcherHealth");const sleepUnref=l(n=>new Promise(p=>{setTimeout(p,n).unref?.()}),"sleepUnref");async function waitForPortFree(n,p,c={}){const h=c.isPortFree??probePortFree,d=Math.max(1,c.intervalMs??types_1.PORT_FREE_POLL_MS),E=Date.now()+Math.max(0,p);for(;;){if(await h(n))return!0;const C=E-Date.now();if(C<=0)return!1;await sleepUnref(Math.min(d,C))}}L(waitForPortFree,"waitForPortFree"),l(waitForPortFree,"waitForPortFree");function viteArgPort(n){const p=l(c=>Number.isInteger(c)&&c>0&&c<65536?c:void 0,"valid");for(let c=0;c<n.length;c+=1){const h=n[c];if(h==="--port")return p(Number(n[c+1]));if(h.startsWith("--port="))return p(Number(h.slice(7)))}}L(viteArgPort,"viteArgPort"),l(viteArgPort,"viteArgPort");function createLensChildren(n,p,c,h){const{emit:d}=p,E=h?.isPortFree??probePortFree,C=h?.probeWatcherHealth??probeWatcherHealth,P=[],R=new Map,v=new Map,O=new Map,H=new Map,F=new Set;let $;const A=new Map,y=new Set,k=new Set,x=new Set,T=new Set,W=l((t,a,s)=>{if(T.has(t))return;T.add(t);const e=setInterval(()=>{(async()=>{if(n.stopped()||y.has(t)){clearInterval(e),T.delete(t);return}await E(a)&&(clearInterval(e),T.delete(t),console.log(`[gateway] lens child ${t}: :${a} freed \u2014 respawning.`),s())})().catch(()=>{})},types_1.LENS_PARKED_PORT_REPROBE_MS);e.unref?.()},"parkUntilPortFree"),N=l(async(t,a)=>{let s=t;for(;a.has(s)||!await E(s);)s+=1;return a.add(s),s!==t&&console.warn(`[gateway] port ${t} is unavailable (another process holds it \u2014 an orphan from a crashed gateway?) \u2014 using ${s} instead.`),s},"claimFreePort"),b=l((t,a,s,e,r=!1,o,i,u)=>{const w=Date.now(),g=(0,node_child_process_1.spawn)(a,s,{cwd:i??n.root,env:{...(0,pod_env_1.podBaseEnv)(process.env,n.root,i??n.root),...e??{},LENSMCP_PARENT_PID:String(process.pid)},stdio:"inherit"});P.push(g),A.set(t,g);const m=t==="vite"||t.endsWith(":vite"),_=m?viteArgPort(s):u;if(m){const M=typeof e?.LENSMCP_EVENT_FILE=="string"?e.LENSMCP_EVENT_FILE:void 0;v.set(g,{project:e?.LENSMCP_PROJECT??"app",spawnedAt:w,port:_,probeFails:0,listening:!1,...M&&M!==n.eventFile?{bus:M}:{}})}return g.on("exit",(M,S)=>{const j=P.indexOf(g);if(j>=0&&P.splice(j,1),v.delete(g),n.stopped()||y.has(t))return;const D=Date.now()-w,f=R.get(t)??{attempts:0,portConflicts:0};(async()=>{const K=_!==void 0&&!await E(_),I=(0,lifecycle_1.isPortConflictExit)({code:M,signal:S,ranMs:D,portHeld:K},{fastFailMs:types_1.CHILD_START_FAIL_MS});if(I&&f.portConflicts<types_1.MAX_PORT_CONFLICT_RETRIES)f.portConflicts+=1,R.set(t,f),console.warn(`[gateway] lens child ${t} lost the :${_} bind race (EADDRINUSE, code=${M}); waiting for the port, then retrying (port-retry ${f.portConflicts}/${types_1.MAX_PORT_CONFLICT_RETRIES}; crash budget untouched).`);else if(I){f.portConflicts=0,R.set(t,f),console.warn(`[gateway] lens child ${t}: :${_} persistently held after ${types_1.MAX_PORT_CONFLICT_RETRIES} bind races \u2014 another server owns it (an external/shared daemon?). Parking; re-probing every ${Math.round(types_1.LENS_PARKED_PORT_REPROBE_MS/1e3)}s, will respawn if it frees.`),d("warning",`lens child parked: ${t} (:${_} held externally)`,`cluster-lens-child:${t}`,{kind:"lens-child-parked",label:t,port:_}),W(t,_,()=>b(t,a,s,e,r,o,i,u));return}else{f.portConflicts=0,D>=HEAL_HEALTHY_MS&&(f.attempts=0);const q=Math.min(HEAL_MAX_MS,HEAL_BASE_MS*2**f.attempts);f.attempts+=1,R.set(t,f),console.warn(`[gateway] lens child ${t} exited (code=${M} sig=${S}); auto-healing in ${Math.round(q/1e3)}s (attempt ${f.attempts}).`),await sleepUnref(q)}n.stopped()||y.has(t)||(_!==void 0&&!await waitForPortFree(_,types_1.PORT_FREE_WAIT_MS,{isPortFree:E,intervalMs:types_1.PORT_FREE_POLL_MS})&&console.warn(`[gateway] lens child ${t}: :${_} still held after ${Math.round(types_1.PORT_FREE_WAIT_MS/1e3)}s \u2014 spawning anyway.`),!(n.stopped()||y.has(t))&&b(t,a,s,e,r,o,i,u))})().catch(()=>{})}),g},"spawnManagedChild");let B=!1;const Y=l(async(t,a)=>{if(!B){B=!0;try{for(const[s,e]of[...v]){if(n.stopped())return;if(e.port===void 0||s.exitCode!==null||s.signalCode!==null)continue;if(await probePortListening(e.port,types_1.LENS_PROBE_TIMEOUT_MS)){e.probeFails=0,e.listening||(e.listening=!0,d("info",`lens app up: ${e.project} (vite :${e.port} listening)`,`cluster-lens-app:${e.project}`,{kind:"service-up",project:e.project,port:e.port},void 0,e.bus));continue}const r=e.listening?t.graceMs:Math.max(t.graceMs,t.neverListenedGraceMs);if(Date.now()-e.spawnedAt<r){e.probeFails=0;continue}if(!a)continue;e.probeFails+=1;const o=(0,lifecycle_1.lensChildWedged)({spawnedAt:e.spawnedAt,probeFails:e.probeFails,hasListened:e.listening},O.get(e.project)??0,Date.now(),t);if(!o)continue;const i=e.listening?"its listener VANISHED (was up, now refusing)":`it NEVER bound within the ${Math.round(r/1e3)}s boot allowance`;O.set(e.project,Date.now()),e.probeFails=0,e.listening=!1,console.error(`[gateway] wedge recycle: lens app ${e.project} is ALIVE but NOT LISTENING on :${e.port} after ${Math.max(2,t.failureThreshold)} probes \u2014 ${i} \u2014 SIGKILL; a fresh vite is coming.`),d("warning",`wedge recycle: ${e.project} (${o})`,`cluster-lens-app:${e.project}`,{kind:"pod-recycle",project:e.project,reason:o,port:e.port},void 0,e.bus);try{s.kill("SIGKILL")}catch{}}}finally{B=!1}}},"probeLensChildren"),V=l(()=>{if($||!types_1.POD_RECYCLE_ENABLED&&!types_1.LENS_PROBE_ENABLED)return;const t={graceMs:types_1.POD_STALE_GRACE_MS,settleMs:types_1.POD_RECYCLE_SETTLE_MS,cooldownMs:types_1.POD_STALE_RECYCLE_COOLDOWN_MS};$=setInterval(()=>{if(n.stopped()||v.size===0||(G(),!types_1.POD_RECYCLE_ENABLED))return;const a=n.sourceSetChangedAt??0;if(a<=0)return;const s=Date.now();for(const[e,r]of v){const o=n.sourceSetChangedFor?.get(r.project)??a,i=O.get(r.project)??0;(0,lifecycle_1.lensFrontendStale)(r.spawnedAt,i,o,s,t)&&((H.get(r.project)??0)>=o||F.has(r.project)||(F.add(r.project),(async()=>{try{const u=types_1.LENS_WATCHER_PROBE_ENABLED&&r.port!==void 0?(0,lifecycle_1.lensWatcherVerdict)(await C(r.port),o,Math.min(types_1.POD_STALE_SCAN_MS,15e3)+types_1.POD_RECYCLE_SETTLE_MS):"unknown";if(u==="heard"){H.set(r.project,o),console.log(`[gateway] stale source set: lens app ${r.project} watcher CONFIRMED the change \u2014 no recycle, HMR intact.`),d("info",`stale check: ${r.project} watcher confirmed \u2014 no recycle`,`cluster-lens-app:${r.project}`,{kind:"pod-stale-verified",project:r.project},void 0,r.bus);return}if(e.exitCode!==null||e.signalCode!==null)return;O.set(r.project,Date.now()),r.listening=!1,console.log(`[gateway] zombie recycle: lens app ${r.project} (stale-source-set) \u2014 watcher ${u==="missed"?"SILENT across the change":"unverifiable"}; restarting it`),d("warning",`zombie recycle: ${r.project} (stale-source-set)`,`cluster-lens-app:${r.project}`,{kind:"pod-recycle",project:r.project,reason:"stale-source-set",watcher:u},void 0,r.bus);const w=(0,hmr_reload_1.broadcastFullReload)(n,r.project);w>0&&console.log(`[gateway] \u2192 told ${w} open tab(s) to reload (stale optimize-dep generation)`);try{e.kill("SIGTERM")}catch{}setTimeout(()=>{if(e.exitCode===null&&e.signalCode===null){console.log(`[gateway] zombie recycle: lens app ${r.project} ignored SIGTERM for ${Math.round(types_1.LENS_RECYCLE_KILL_GRACE_MS/1e3)}s \u2014 escalating to SIGKILL.`);try{e.kill("SIGKILL")}catch{}}},types_1.LENS_RECYCLE_KILL_GRACE_MS).unref?.()}finally{F.delete(r.project)}})()))}},Math.min(types_1.POD_STALE_SCAN_MS,15e3)),$.unref?.()},"startLensSweeper"),z=l(async()=>{const t=[...new Map(n.routes.filter(e=>e.lens).map(e=>[e.project,e.lens])).values()],a=c.dashboard!==!1,s=c.mcp!==!1&&t.length>0;if(a){const e=(0,lens_frontend_1.findDashboardBundle)(n.root);if(!e)console.warn("[gateway] lens dashboard bundle not found \u2014 skipping. Install the self-contained CLI: `npm i -D lensmcp` (it ships `bundled/dashboard.js`), or `yarn nx build @lensmcp/lensmcp-mcp` in-repo.");else{const r=(0,scope_1.readLensScope)(n.root),o=await N(c.dashboardPort??r.dashboardPort,k),i=c.lensHost??"lensmcp.local",u=r.basePath;n.registry.appendRoutes(n.wsKey,[{host:i,prefix:u,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${o}`}]),n.rebuildRoutes(),(0,workspace_registry_1.registerWorkspace)({key:r.key,root:n.root,base:u,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] starting lens dashboard \u2192 http://localhost:${o} (routed at https://${i}${u}/)`),d("info",`lens dashboard up: ${i}${u} \u2192 :${o}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:i,port:o,base:u}),b("lens-dashboard",process.execPath,[e],{LENSMCP_EVENT_FILE:n.eventFile,LENSMCP_DASHBOARD_PORT:String(o),LENSMCP_DASHBOARD_BASE:u},!1,void 0,void 0,o)}}if(s){const e=(0,lens_frontend_1.findMcpBundle)(n.root);if(!e)console.warn("[gateway] MCP server bundle not found \u2014 skipping (lens apps will still publish to the bus; an external `lensmcp mcp` can read it). Install: `npm i -D lensmcp`.");else{const r=(0,scope_1.readLensScope)(n.root),o=Number(process.env.LENSMCP_PORT??r.mcpHttpPort),i=l(()=>{console.log(`[gateway] starting lens MCP server on 127.0.0.1:${o} (${e})`),b("lens-mcp",process.execPath,[e],{LENSMCP_EVENT_FILE:n.eventFile,LENSMCP_TRANSPORT:process.env.LENSMCP_TRANSPORT??"http",LENSMCP_PORT:String(o),LENSMCP_WORKSPACE_KEY:r.key,LENSMCP_WORKSPACE_ROOT:n.root},!1,void 0,void 0,o)},"spawnMcp");await E(o)?i():(console.warn(`[gateway] lens MCP port :${o} is already served \u2014 an external lensmcp server (a shared daemon?) owns this workspace's MCP; not spawning a second. Re-probing every ${Math.round(types_1.LENS_PARKED_PORT_REPROBE_MS/1e3)}s; will take over if it frees.`),d("info",`lens MCP served externally on :${o} \u2014 gateway parked its own`,"cluster-lens-child:lens-mcp",{kind:"lens-mcp-external",port:o}),W("lens-mcp",o,i))}}for(const e of t){if(e.started)continue;e.started=!0;const r=await N(e.port,x);if(r!==e.port){for(const o of n.routes)o.lens?.project===e.project&&(o.lens.port=r,o.target=`http://localhost:${r}`);e.port=r}try{(0,lens_frontend_1.spawnLensFrontend)({projectRoot:e.projectRoot,workspaceRoot:n.root,project:e.project,eventFile:n.eventFile,port:e.port,chrome:!0,headless:!0,viteEnv:{LENSMCP_PROJECT:e.project},log:l(o=>console.log(o),"log")},b),d("info",`lens app started: ${e.project} (vite :${e.port})`,`cluster-lens-app:${e.project}`,{kind:"lens-app-up",project:e.project,port:e.port})}catch(o){console.error(`[gateway] lens app ${e.project}: ${o.message}`)}}V()},"bootSingletonsAndApps"),U=l(async t=>{const a=(0,lens_frontend_1.findDashboardBundle)(n.root);if(!a)return;const s=(0,scope_1.readLensScope)(t.root),e=c.lensHost??"lensmcp.local",r=s.basePath,o=await N(s.dashboardPort,k),i=`lens-dashboard:${t.wsKey}`,u=path.join(t.root,".lensmcp","events.jsonl");return y.delete(i),n.registry.appendRoutes(t.wsKey,[{host:e,prefix:r,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${o}`}]),(0,workspace_registry_1.registerWorkspace)({key:t.wsKey,root:t.root,base:r,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] hosting dashboard for workspace '${t.wsKey}' \u2192 http://localhost:${o} (routed at https://${e}${r}/)`),d("info",`lens dashboard up: ${e}${r} \u2192 :${o}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:e,port:o,base:r}),b(i,process.execPath,[a],{LENSMCP_EVENT_FILE:u,LENSMCP_DASHBOARD_PORT:String(o),LENSMCP_DASHBOARD_BASE:r,LENSMCP_WS_KEY:t.wsKey},!1,void 0,t.root,o),()=>{y.add(i),k.delete(o),(0,workspace_registry_1.unregisterWorkspace)(t.wsKey);try{A.get(i)?.kill("SIGTERM")}catch{}A.delete(i)}},"hostWorkspaceDashboard"),J=l(async t=>{const a=new Map;for(const o of t.routes){if(!o.lens)continue;const i=a.get(o.lens.project)??[];i.push(o),a.set(o.lens.project,i)}if(a.size===0)return()=>{};const s=path.join(t.root,".lensmcp","events.jsonl"),e=[],r=[];for(const[o,i]of a){const u=i[0].lens;if(u.started)continue;const w=await N(u.port,x);r.push(w);for(const S of i)S.lens.started=!0,S.lens.port=w,S.target=`http://localhost:${w}`;const g=`${t.wsKey}:${o}:`,m=`${g}vite`,_=`${g}browser-capture`;e.push(m,_),y.delete(m),y.delete(_);const M=l((S,j,D,f,K,I)=>b(S,j,D,f,K,I,t.root),"spawnInRoot");console.log(`[gateway] hosting lens app '${o}' of '${t.wsKey}' \u2192 vite :${w}`);try{(0,lens_frontend_1.spawnLensFrontend)({projectRoot:u.projectRoot,workspaceRoot:t.root,project:o,eventFile:s,port:w,chrome:!0,headless:!0,viteEnv:{LENSMCP_PROJECT:o},labelPrefix:g,log:l(S=>console.log(S),"log")},M),d("info",`lens app started: ${t.wsKey}/${o} (vite :${w})`,`cluster-lens-app:${t.wsKey}:${o}`,{kind:"lens-app-up",project:o,port:w},void 0,s)}catch(S){console.error(`[gateway] lens app ${t.wsKey}/${o}: ${S.message}`)}}return()=>{for(const o of e){y.add(o);try{A.get(o)?.kill("SIGTERM")}catch{}A.delete(o)}for(const o of r)x.delete(o)}},"hostWorkspaceApps"),Q=l(async(t=2500)=>{$&&(clearInterval($),$=void 0);const a=[...P];for(const r of a)try{r.kill("SIGTERM")}catch{}const s=l(r=>r.exitCode===null&&r.signalCode===null,"alive"),e=Date.now()+t;for(;Date.now()<e&&a.some(s);)await new Promise(r=>setTimeout(r,100));for(const r of a)if(s(r))try{r.kill("SIGKILL")}catch{}},"reapAll"),G=l(()=>Y({graceMs:types_1.LENS_PROBE_GRACE_MS,failureThreshold:types_1.LENS_PROBE_FAILS,cooldownMs:types_1.LENS_PROBE_COOLDOWN_MS,neverListenedGraceMs:types_1.LENS_PROBE_BOOT_GRACE_MS},types_1.LENS_PROBE_ENABLED),"probeLensFrontends");return{spawnManagedChild:b,bootSingletonsAndApps:z,hostWorkspaceDashboard:U,hostWorkspaceApps:J,probeLensFrontends:G,reapAll:Q}}L(createLensChildren,"createLensChildren"),l(createLensChildren,"createLensChildren");
@@ -1,3 +1,3 @@
1
- "use strict";var O=Object.defineProperty;var v=(t,n)=>O(t,"name",{value:n,configurable:!0});var W=Object.defineProperty,E=v((t,n)=>W(t,"name",{value:n,configurable:!0}),"E"),I=Object.defineProperty,i=E((t,n)=>I(t,"name",{value:n,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.sourceSetSignature=sourceSetSignature,exports.sourceScopeDirs=sourceScopeDirs,exports.podRecycleReason=podRecycleReason,exports.lensFrontendStale=lensFrontendStale,exports.lensWatcherVerdict=lensWatcherVerdict,exports.lensChildWedged=lensChildWedged,exports.isPortConflictExit=isPortConflictExit,exports.createServiceLayer=createServiceLayer;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),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"),types_1=require("./types");function eastWestEnv(t,n){const l={},d=t.serviceKeys[n];if(d&&(l.LENSMCP_SERVICE_KEY=d),t.https){const a=require("../../../basic-ssl").caCertPath();fs.existsSync(a)&&(l.NODE_EXTRA_CA_CERTS=a)}return l}v(eastWestEnv,"eastWestEnv"),E(eastWestEnv,"eastWestEnv"),i(eastWestEnv,"eastWestEnv");const sleep=i(t=>new Promise(n=>setTimeout(n,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 n=2166136261;for(let l=0;l<t.length;l+=1)n^=t.charCodeAt(l),n=Math.imul(n,16777619);return n>>>0}v(fnv1a,"fnv1a"),E(fnv1a,"fnv1a"),i(fnv1a,"fnv1a");const execFileP=(0,node_util_1.promisify)(node_child_process_1.execFile);async function gitIgnoredPaths(t){try{const{stdout:n}=await execFileP("git",["ls-files","--others","--directory","--ignored","--exclude-standard","-z"],{cwd:t,maxBuffer:33554432,timeout:1e4,windowsHide:!0});return new Set(n.split("\0").filter(Boolean))}catch{return new Set}}v(gitIgnoredPaths,"gitIgnoredPaths"),E(gitIgnoredPaths,"gitIgnoredPaths"),i(gitIgnoredPaths,"gitIgnoredPaths");async function sourceSetSignature(t,n,l){let d=0,a=0;const y=await gitIgnoredPaths(t),k=i(f=>path.relative(t,f).split(path.sep).join("/"),"rel"),S=i(async(f,A=!0)=>{if(d>SCAN_FILE_CAP)return;let x;try{x=await fs.promises.readdir(f,{withFileTypes:!0})}catch{return}for(const m of x){if(d>SCAN_FILE_CAP)return;const $=m.name,R=path.join(f,$);if(m.isDirectory()){if(!A||$.startsWith(".")||SRC_EXCLUDE.has($)||l?.has($)||$.endsWith("-devserver")||y.has(`${k(R)}/`))continue;await S(R)}else if(m.isFile()){const M=$.lastIndexOf(".");if(M<=0||!SRC_EXT.has($.slice(M))||y.has(k(R)))continue;d+=1,a^=fnv1a(R)}}},"walk");if(n&&n.length>0){await S(t,!1);for(const f of n)await S(f)}else await S(t);return`${d}:${(a>>>0).toString(36)}`}v(sourceSetSignature,"sourceSetSignature"),E(sourceSetSignature,"sourceSetSignature"),i(sourceSetSignature,"sourceSetSignature");function sourceScopeDirs(t,n,l,d,a){const y=i(f=>{const A=path.relative(t,f).split(path.sep).filter(Boolean);return A.length===0||A[0].startsWith("..")?"":A[0]},"bucketOf")(n);if(!y)return[];const k=new Set(l.filter(Boolean)),S=new Set([y]);for(const f of d)f.startsWith(".")||SRC_EXCLUDE.has(f)||a?.has(f)||f.endsWith("-devserver")||k.has(f)||S.add(f);return[...S].sort().map(f=>path.join(t,f))}v(sourceScopeDirs,"sourceScopeDirs"),E(sourceScopeDirs,"sourceScopeDirs"),i(sourceScopeDirs,"sourceScopeDirs");function podRecycleReason(t,n,l,d){return!t.proc||t.state!=="up"||t.spawnAt==null||t.inflight>0||n-t.lastUsed<d.settleMs||t.lastStaleRecycleAt!=null&&n-t.lastStaleRecycleAt<d.cooldownMs?null:l>0&&l-t.spawnAt>d.graceMs?"stale-source-set":d.maxAgeMs>0&&n-t.spawnAt>d.maxAgeMs?"max-age":null}v(podRecycleReason,"podRecycleReason"),E(podRecycleReason,"podRecycleReason"),i(podRecycleReason,"podRecycleReason");function lensFrontendStale(t,n,l,d,a){return!(d-n<a.cooldownMs||l<=0||l-t<=a.graceMs||d-l<a.settleMs)}v(lensFrontendStale,"lensFrontendStale"),E(lensFrontendStale,"lensFrontendStale"),i(lensFrontendStale,"lensFrontendStale");function lensWatcherVerdict(t,n,l){return t?t.lastFsSetEventAt>=n-l?"heard":"missed":"unknown"}v(lensWatcherVerdict,"lensWatcherVerdict"),E(lensWatcherVerdict,"lensWatcherVerdict"),i(lensWatcherVerdict,"lensWatcherVerdict");function lensChildWedged(t,n,l,d){const a=t.hasListened?d.graceMs:Math.max(d.graceMs,d.neverListenedGraceMs);return l-t.spawnedAt<a||l-n<d.cooldownMs||t.probeFails<Math.max(2,d.failureThreshold)?null:"wedged-no-listener"}v(lensChildWedged,"lensChildWedged"),E(lensChildWedged,"lensChildWedged"),i(lensChildWedged,"lensChildWedged");function isPortConflictExit(t,n){return t.signal||t.code===null||t.code===0||t.ranMs>n.fastFailMs?!1:t.portHeld}v(isPortConflictExit,"isPortConflictExit"),E(isPortConflictExit,"isPortConflictExit"),i(isPortConflictExit,"isPortConflictExit");function createServiceLayer(t,n){const{emit:l}=n,d=i(e=>e.wsKey===t.wsKey?void 0:path.join(e.root,".lensmcp","events.jsonl"),"serviceBus"),a=i((e,r,o,s,c)=>l(r,o,s,{wsKey:e.wsKey,...c},void 0,d(e)),"emitSvc"),y=i(e=>(e.scannedAt=0,(0,discovery_1.pickSock)(e,t.scanTtlMs)?e.socks.length:0),"scanNow"),k=i((e,r)=>{let o;try{o=(0,node_child_process_1.execFileSync)("ps",["ax","-ww","-o","pid=,pgid=,command="],{encoding:"utf8",timeout:5e3})}catch{return 0}const s=e.proc?.pid;let c=0;for(const p of o.split(`
2
- `)){const w=p.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);if(!w)continue;const g=Number(w[1]),u=Number(w[2]),h=w[3];if(h.includes(`run ${r}`)&&/\bnx(\.js)?\b/.test(h)&&!(h.includes("/node_modules/nx/")&&!h.includes(e.root))&&!(g===process.pid||g===s)&&!(s!==void 0&&u===s)){try{process.kill(-u,"SIGKILL")}catch{try{process.kill(g,"SIGKILL")}catch{}}c+=1}}return c>0&&(console.log(`[gateway] ${r}: reaped ${c} stale runner tree(s) \u2014 a foreign generation held the nx task lock`),a(e,"warning",`stale-runner reap: ${e.project} (${c} foreign tree(s))`,`cluster-service:${e.project}`,{kind:"stale-runner-reap",project:e.project,reaped:c})),c},"reapForeignRunners"),S=i(e=>{if(t.stopped())return;const r=`${e.project}:${e.decl.serveTarget??"serve-hmr"}`,o=Math.max(1,e.decl.minPods??1);k(e,r),e.lockBlockedAt=void 0,console.log(`[gateway] starting ${r} (pods=${o})\u2026`),a(e,"info",`service starting: ${e.project} (${o} pod${o>1?"s":""})`,`cluster-service:${e.project}`,{kind:"service-starting",project:e.project,pods:o}),e.state="starting",e.spawnAt=Date.now();const s=path.join(e.root,"node_modules","nx","dist","bin","nx.js"),[c,p]=fs.existsSync(s)?[process.execPath,[s,"run",r,`--childCount=${o}`]]:["yarn",["nx","run",r,`--childCount=${o}`]],w=(0,node_child_process_1.spawn)(c,p,{cwd:e.root,detached:!0,env:{...(0,pod_env_1.podBaseEnv)(process.env,t.root,e.root),NX_DAEMON:"false",NX_ISOLATE_PLUGINS:"false",...e.wsKey?{LENSMCP_WS_KEY:e.wsKey}:{},LENSMCP_EVENT_FILE:e.wsKey===t.wsKey?t.eventFile:path.join(e.root,".lensmcp","events.jsonl"),LENSMCP_GATEWAY_PID:String(process.pid),...eastWestEnv(t,e.project)},stdio:["ignore","pipe","pipe"]});e.lastErrors=[],e.buildFailed=!1;const g=i(u=>{const h=u.toString();for(const j of h.split(`
3
- `)){if(/ERROR in |error TS\d+|Found \d+ error|Unhandled 'error' event|EADDRINUSE/.test(j)){const _=j.replace(/\x1b\[[0-9;]*m/g,"").trim();e.lastErrors.push(_),e.lastErrors.length>12&&e.lastErrors.shift(),e.buildFailed=!0;const C=Date.now();(_!==e.lastErrorEmitSig||C-(e.lastErrorEmitAt??0)>=types_1.SERVICE_ERROR_THROTTLE_MS)&&(e.lastErrorEmitSig=_,e.lastErrorEmitAt=C,a(e,"error",`service error: ${e.project}`,`cluster-service:${e.project}`,{kind:"service-error",project:e.project,line:_}))}/No typescript errors found|webpack compiled successfully|Child#\d+ ready/.test(j)&&(e.buildFailed=!1),!e.lockBlockedAt&&/Waiting for .+ in another nx process/.test(j)&&(e.lockBlockedAt=Date.now(),console.log(`[gateway] ${r}: spawn blocked by a stale nx runner \u2014 breaking the lock + respawning`),a(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||(m(e,"parked on a foreign nx task lock \u2014 recycling",{announce:!1}),k(e,r),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&S(e)},1500).unref?.())},250).unref?.())}},"sniff");w.stdout?.on("data",u=>{t.stopped()||(g(u),process.stdout.write(`[${e.project}] ${u}`))}),w.stderr?.on("data",u=>{t.stopped()||(g(u),process.stderr.write(`[${e.project}] ${u}`))}),w.on("exit",u=>{if(e.proc=void 0,e.state="down",t.stopped()||(console.log(`[gateway] ${r} exited (${u})`),a(e,u===0?"info":"warning",`service down: ${e.project} (exit ${u})`,`cluster-service:${e.project}`,{kind:"service-down",project:e.project,code:u}),u===0||e.buildFailed))return;const h=Date.now();if(e.restarts=(e.restarts??[]).filter(_=>h-_<types_1.SERVICE_RESTART_WINDOW_MS),e.restarts.length>=types_1.SERVICE_MAX_RESTARTS){a(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(h);const j=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})`),a(e,"info",`self-heal: respawning ${e.project}`,`cluster-service:${e.project}`,{kind:"service-respawn",project:e.project,attempt:e.restarts.length}),S(e))},j)}),e.proc=w},"spawnService"),f=i((e,r)=>{if(e.state==="up")return!1;e.state="up";const o=e.pool.socks.length;return a(e,"info",`service up: ${e.project} (${o} pods, ${r})`,`cluster-service:${e.project}`,{kind:"service-up",project:e.project,pods:o,adopted:r==="adopted"}),!0},"markUp"),A=i(async e=>{if(y(e.pool)>0)return f(e,"adopted"),!0;if(t.stopped())return!1;e.state!=="starting"&&!e.proc&&S(e);const r=Date.now()+t.startupTimeoutMs,o=Date.now()+12e3;for(;!t.stopped()&&Date.now()<r;){if(await sleep(Math.min(700,t.startupTimeoutMs/4)),e.buildFailed&&Date.now()>o)return!1;if(y(e.pool)>0){e.state="up",e.buildFailed=!1;const s=e.spawnAt?Date.now()-e.spawnAt:void 0;return console.log(`[gateway] ${e.project} up (${e.pool.socks.length} pods${s?`, cold start ${s}ms`:""})`),a(e,"info",`cold start: ${e.project} (${e.pool.socks.length} pods, ${s??"?"}ms)`,`cluster-service:${e.project}`,{kind:"cold-start",project:e.project,pods:e.pool.socks.length,durationMs:s}),!0}if(e.state==="down")return!1}return!1},"ensureUp"),x=i(e=>{const r=e.decl.maxPods??e.decl.minPods??1,o=e.pool.socks.length,s=e.decl.adminUrl??(e.decl.port?`http://localhost:${e.decl.port}`:void 0);if(!s||o===0||o>=r||e.inflight<=o||Date.now()-e.lastScaleAt<5e3)return;e.lastScaleAt=Date.now();const c=(s.startsWith("https")?require("node:https"):http).request(`${s}/webpack/scale`,{method:"POST",rejectUnauthorized:!1},p=>p.resume());c.on("error",()=>{}),c.end(JSON.stringify({pods:o+1})),console.log(`[gateway] autoscale ${e.project}: ${o} \u2192 ${o+1} pods (inflight ${e.inflight})`),a(e,"info",`autoscale: ${e.project} ${o} \u2192 ${o+1} pods`,`cluster-service:${e.project}`,{kind:"autoscale",project:e.project,from:o,to:o+1,inflight:e.inflight})},"scaleUp"),m=i((e,r,o)=>{if(!e.proc)return;console.log(`[gateway] ${e.project} ${r} \u2014 scaling to zero.`),o?.announce!==!1&&a(e,"info",`idle kill: ${e.project} (${r})`,`cluster-service:${e.project}`,{kind:"idle-kill",project:e.project,why:r});const s=e.proc;try{s.stdout?.removeAllListeners("data"),s.stderr?.removeAllListeners("data"),s.removeAllListeners("exit")}catch{}try{process.kill(-s.pid,"SIGTERM")}catch{try{s.kill("SIGTERM")}catch{}}e.proc=void 0,e.state="down",setTimeout(()=>{try{for(const c of fs.readdirSync(e.pool.dir)){if(!c.endsWith(".sock"))continue;const p=path.join(e.pool.dir,c);try{const w=Number(fs.readFileSync(`${p}.owner`,"utf8").trim());if(Number.isFinite(w)&&w>0)try{process.kill(w,0);continue}catch{}}catch{}try{fs.unlinkSync(p)}catch{}try{fs.unlinkSync(`${p}.owner`)}catch{}}}catch{}e.pool.socks=[],e.pool.scannedAt=0},1200).unref?.()},"killSpawned"),$=i(e=>{const r=Date.now();if(e.restarts=(e.restarts??[]).filter(o=>r-o<types_1.SERVICE_RESTART_WINDOW_MS),e.restarts.length>=types_1.SERVICE_MAX_RESTARTS){a(e,"error",`service crash-looping: ${e.project} \u2014 wedged ${types_1.SERVICE_MAX_RESTARTS}+\xD7/min, not auto-recycling (the next request retries)`,`cluster-service:${e.project}`,{kind:"service-crash-loop",project:e.project}),m(e,"wedged (crash-loop cap) \u2014 leaving down for the next request",{announce:!1});return}e.restarts.push(r),console.log(`[gateway] self-heal: recycling WEDGED ${e.project} (live parent, 0 live pods; ${e.restarts.length}/${types_1.SERVICE_MAX_RESTARTS})`),a(e,"warning",`self-heal: recycling wedged ${e.project} (0 live pods)`,`cluster-service:${e.project}`,{kind:"service-recycle",project:e.project,attempt:e.restarts.length}),m(e,"wedged: 0 live pods \u2014 recycling",{announce:!1}),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&S(e)},1500).unref?.()},"recycleWedged"),R={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 M;const T=new Map;let D=!1,b=0;const L=i(()=>{const e=t.projectRoots??{},r=new Set(t.services.map(s=>s.project));for(const s of t.routes)s.lens&&r.add(s.lens.project);const o=new Set;for(const s of r){const c=e[s];if(!c)continue;const p=path.relative(t.root,c).split(path.sep).filter(Boolean)[0];p&&!p.startsWith("..")&&o.add(p)}return[...o]},"managedBuckets"),P=i((e,r,o,s)=>{const c=t.projectRoots?.[e];if(!c)return;const p=sourceScopeDirs(t.root,c,o,r,s);return p.length>0?p:void 0},"scopeFor"),F=i(()=>{D||Date.now()-b<types_1.POD_STALE_SCAN_MS||(D=!0,b=Date.now(),(async()=>{const e=(0,scope_1.readSourceSetExclude)(t.root),r=await sourceSetSignature(t.root,void 0,e);if(M!==void 0&&r!==M&&(t.sourceSetChangedAt=Date.now(),l("info","source file set changed \u2014 stale pods recycle when idle","cluster-gateway",{kind:"source-set-changed"})),M=r,!t.projectRoots)return;let o;try{o=(await fs.promises.readdir(t.root,{withFileTypes:!0})).filter(g=>g.isDirectory()).map(g=>g.name)}catch{return}const s=L(),c=new Set(t.services.map(g=>g.project));for(const g of t.routes)g.lens&&c.add(g.lens.project);const p=new Map;for(const g of c){const u=P(g,o,s,e);if(!u)continue;const h=u.join("|"),j=p.get(h)??{dirs:u,projects:[]};j.projects.push(g),p.set(h,j)}const w=Date.now();for(const[g,{dirs:u,projects:h}]of p){const j=await sourceSetSignature(t.root,u,e),_=T.get(g);if(T.set(g,j),!(_===void 0||j===_)){t.sourceSetChangedFor??=new Map;for(const C of h)t.sourceSetChangedFor.set(C,w);l("info",`source file set changed in ${h.join(", ")}'s scope`,"cluster-gateway",{kind:"source-set-changed",scoped:!0,projects:h})}}})().catch(()=>{}).finally(()=>{D=!1}))},"maybeScanSourceSet"),N=i((e,r)=>{e.lastStaleRecycleAt=Date.now(),console.log(`[gateway] zombie recycle: ${e.project} (${r}) \u2014 stale fs view; fresh pod incoming`),a(e,"warning",`zombie recycle: ${e.project} (${r})`,`cluster-service:${e.project}`,{kind:"pod-recycle",project:e.project,reason:r});const o=!!e.decl.eager;m(e,`${r} \u2014 recycling stale pod`,{announce:!1}),o&&setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&S(e)},1500).unref?.()},"recyclePod");return{scanNow:y,spawnService:S,ensureUp:A,scaleUp:x,killSpawned:m,markUp:f,startSweeper:i(()=>{const e=setInterval(()=>{if(!t.stopped()){types_1.POD_RECYCLE_ENABLED&&F();for(const r of t.services){if(t.stopped()||!r.proc){r.wedgedSince=void 0;continue}const o=r.decl.idleKillSec;if(o&&r.inflight===0&&Date.now()-r.lastUsed>o*1e3){m(r,`idle ${o}s`),r.wedgedSince=void 0;continue}const s=r.state==="starting"&&r.spawnAt!==void 0&&Date.now()-r.spawnAt<t.startupTimeoutMs,c=y(r.pool)>0;if(c||s){if(r.wedgedSince=void 0,c&&f(r,"observed"),c&&!s&&types_1.POD_RECYCLE_ENABLED){const p=t.sourceSetChangedFor?.get(r.project)??t.sourceSetChangedAt??0,w=podRecycleReason(r,Date.now(),p,R);w&&N(r,w)}continue}r.wedgedSince??=Date.now(),Date.now()-r.wedgedSince>types_1.WEDGE_RECYCLE_GRACE_MS&&(r.wedgedSince=void 0,$(r))}}},t.sweepMs);return e.unref(),e},"startSweeper")}}v(createServiceLayer,"createServiceLayer"),E(createServiceLayer,"createServiceLayer"),i(createServiceLayer,"createServiceLayer");
1
+ "use strict";var W=Object.defineProperty;var E=(t,n)=>W(t,"name",{value:n,configurable:!0});var I=Object.defineProperty,i=E((t,n)=>I(t,"name",{value:n,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.sourceSetSignature=sourceSetSignature,exports.sourceScopeDirs=sourceScopeDirs,exports.podRecycleReason=podRecycleReason,exports.lensFrontendStale=lensFrontendStale,exports.lensWatcherVerdict=lensWatcherVerdict,exports.lensChildWedged=lensChildWedged,exports.isPortConflictExit=isPortConflictExit,exports.createServiceLayer=createServiceLayer;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),fs=tslib_1.__importStar(require("node:fs")),http=tslib_1.__importStar(require("node:http")),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"),types_1=require("./types");function eastWestEnv(t,n){const l={},p=t.serviceKeys[n];if(p&&(l.LENSMCP_SERVICE_KEY=p),t.https){const a=require("../../../basic-ssl").caCertPath();fs.existsSync(a)&&(l.NODE_EXTRA_CA_CERTS=a)}return l}E(eastWestEnv,"eastWestEnv"),i(eastWestEnv,"eastWestEnv");const sleep=i(t=>new Promise(n=>setTimeout(n,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 n=2166136261;for(let l=0;l<t.length;l+=1)n^=t.charCodeAt(l),n=Math.imul(n,16777619);return n>>>0}E(fnv1a,"fnv1a"),i(fnv1a,"fnv1a");const execFileP=(0,node_util_1.promisify)(node_child_process_1.execFile);async function gitIgnoredPaths(t){try{const{stdout:n}=await execFileP("git",["ls-files","--others","--directory","--ignored","--exclude-standard","-z"],{cwd:t,maxBuffer:33554432,timeout:1e4,windowsHide:!0});return new Set(n.split("\0").filter(Boolean))}catch{return new Set}}E(gitIgnoredPaths,"gitIgnoredPaths"),i(gitIgnoredPaths,"gitIgnoredPaths");async function sourceSetSignature(t,n,l){let p=0,a=0;const v=await gitIgnoredPaths(t),_=i(f=>path.relative(t,f).split(path.sep).join("/"),"rel"),S=i(async(f,k=!0)=>{if(p>SCAN_FILE_CAP)return;let M;try{M=await fs.promises.readdir(f,{withFileTypes:!0})}catch{return}for(const y of M){if(p>SCAN_FILE_CAP)return;const m=y.name,A=path.join(f,m);if(y.isDirectory()){if(!k||m.startsWith(".")||SRC_EXCLUDE.has(m)||l?.has(m)||m.endsWith("-devserver")||v.has(`${_(A)}/`))continue;await S(A)}else if(y.isFile()){const R=m.lastIndexOf(".");if(R<=0||!SRC_EXT.has(m.slice(R))||v.has(_(A)))continue;p+=1,a^=fnv1a(A)}}},"walk");if(n&&n.length>0){await S(t,!1);for(const f of n)await S(f)}else await S(t);return`${p}:${(a>>>0).toString(36)}`}E(sourceSetSignature,"sourceSetSignature"),i(sourceSetSignature,"sourceSetSignature");function sourceScopeDirs(t,n,l,p,a){const v=i(f=>{const k=path.relative(t,f).split(path.sep).filter(Boolean);return k.length===0||k[0].startsWith("..")?"":k[0]},"bucketOf")(n);if(!v)return[];const _=new Set(l.filter(Boolean)),S=new Set([v]);for(const f of p)f.startsWith(".")||SRC_EXCLUDE.has(f)||a?.has(f)||f.endsWith("-devserver")||_.has(f)||S.add(f);return[...S].sort().map(f=>path.join(t,f))}E(sourceScopeDirs,"sourceScopeDirs"),i(sourceScopeDirs,"sourceScopeDirs");function podRecycleReason(t,n,l,p){return!t.proc||t.state!=="up"||t.spawnAt==null||t.inflight>0||n-t.lastUsed<p.settleMs||t.lastStaleRecycleAt!=null&&n-t.lastStaleRecycleAt<p.cooldownMs?null:l>0&&l-t.spawnAt>p.graceMs?"stale-source-set":p.maxAgeMs>0&&n-t.spawnAt>p.maxAgeMs?"max-age":null}E(podRecycleReason,"podRecycleReason"),i(podRecycleReason,"podRecycleReason");function lensFrontendStale(t,n,l,p,a){return!(p-n<a.cooldownMs||l<=0||l-t<=a.graceMs||p-l<a.settleMs)}E(lensFrontendStale,"lensFrontendStale"),i(lensFrontendStale,"lensFrontendStale");function lensWatcherVerdict(t,n,l){return t?t.lastFsSetEventAt>=n-l?"heard":"missed":"unknown"}E(lensWatcherVerdict,"lensWatcherVerdict"),i(lensWatcherVerdict,"lensWatcherVerdict");function lensChildWedged(t,n,l,p){const a=t.hasListened?p.graceMs:Math.max(p.graceMs,p.neverListenedGraceMs);return l-t.spawnedAt<a||l-n<p.cooldownMs||t.probeFails<Math.max(2,p.failureThreshold)?null:"wedged-no-listener"}E(lensChildWedged,"lensChildWedged"),i(lensChildWedged,"lensChildWedged");function isPortConflictExit(t,n){return t.signal||t.code===null||t.code===0||t.ranMs>n.fastFailMs?!1:t.portHeld}E(isPortConflictExit,"isPortConflictExit"),i(isPortConflictExit,"isPortConflictExit");function createServiceLayer(t,n){const{emit:l}=n,p=i(e=>e.wsKey===t.wsKey?void 0:path.join(e.root,".lensmcp","events.jsonl"),"serviceBus"),a=i((e,o,r,s,c)=>l(o,r,s,{wsKey:e.wsKey,...c},void 0,p(e)),"emitSvc"),v=i(e=>(e.scannedAt=0,(0,discovery_1.pickSock)(e,t.scanTtlMs)?e.socks.length:0),"scanNow"),_=i((e,o)=>{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 s=e.proc?.pid;let c=0;for(const d of r.split(`
2
+ `)){const w=d.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);if(!w)continue;const g=Number(w[1]),u=Number(w[2]),h=w[3];if(h.includes(`run ${o}`)&&/\bnx(\.js)?\b/.test(h)&&!(h.includes("/node_modules/nx/")&&!h.includes(e.root))&&!(g===process.pid||g===s)&&!(s!==void 0&&u===s)){try{process.kill(-u,"SIGKILL")}catch{try{process.kill(g,"SIGKILL")}catch{}}c+=1}}return c>0&&(console.log(`[gateway] ${o}: reaped ${c} stale runner tree(s) \u2014 a foreign generation held the nx task lock`),a(e,"warning",`stale-runner reap: ${e.project} (${c} foreign tree(s))`,`cluster-service:${e.project}`,{kind:"stale-runner-reap",project:e.project,reaped:c})),c},"reapForeignRunners"),S=i(e=>{if(t.stopped())return;const o=`${e.project}:${e.decl.serveTarget??"serve-hmr"}`,r=Math.max(1,e.decl.minPods??1);_(e,o),e.lockBlockedAt=void 0,console.log(`[gateway] starting ${o} (pods=${r})\u2026`),a(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();const s=path.join(e.root,"node_modules","nx","dist","bin","nx.js"),[c,d]=fs.existsSync(s)?[process.execPath,[s,"run",o,`--childCount=${r}`]]:["yarn",["nx","run",o,`--childCount=${r}`]],w=(0,node_child_process_1.spawn)(c,d,{cwd:e.root,detached:!0,env:{...(0,pod_env_1.podBaseEnv)(process.env,t.root,e.root),NX_DAEMON:"false",NX_ISOLATE_PLUGINS:"false",...e.wsKey?{LENSMCP_WS_KEY:e.wsKey}:{},LENSMCP_EVENT_FILE:e.wsKey===t.wsKey?t.eventFile:path.join(e.root,".lensmcp","events.jsonl"),LENSMCP_GATEWAY_PID:String(process.pid),...eastWestEnv(t,e.project)},stdio:["ignore","pipe","pipe"]});e.lastErrors=[],e.buildFailed=!1;const g=i(u=>{const h=u.toString();for(const j of h.split(`
3
+ `)){if(/ERROR in |error TS\d+|Found \d+ error|Unhandled 'error' event|EADDRINUSE/.test(j)){const $=j.replace(/\x1b\[[0-9;]*m/g,"").trim();e.lastErrors.push($),e.lastErrors.length>12&&e.lastErrors.shift(),e.buildFailed=!0;const x=Date.now();($!==e.lastErrorEmitSig||x-(e.lastErrorEmitAt??0)>=types_1.SERVICE_ERROR_THROTTLE_MS)&&(e.lastErrorEmitSig=$,e.lastErrorEmitAt=x,a(e,"error",`service error: ${e.project}`,`cluster-service:${e.project}`,{kind:"service-error",project:e.project,line:$}))}/No typescript errors found|webpack compiled successfully|Child#\d+ ready/.test(j)&&(e.buildFailed=!1),!e.lockBlockedAt&&/Waiting for .+ in another nx process/.test(j)&&(e.lockBlockedAt=Date.now(),console.log(`[gateway] ${o}: spawn blocked by a stale nx runner \u2014 breaking the lock + respawning`),a(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||(y(e,"parked on a foreign nx task lock \u2014 recycling",{announce:!1}),_(e,o),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&S(e)},1500).unref?.())},250).unref?.())}},"sniff");w.stdout?.on("data",u=>{t.stopped()||(g(u),process.stdout.write(`[${e.project}] ${u}`))}),w.stderr?.on("data",u=>{t.stopped()||(g(u),process.stderr.write(`[${e.project}] ${u}`))}),w.on("exit",u=>{if(e.proc=void 0,e.state="down",t.stopped()||(console.log(`[gateway] ${o} exited (${u})`),a(e,u===0?"info":"warning",`service down: ${e.project} (exit ${u})`,`cluster-service:${e.project}`,{kind:"service-down",project:e.project,code:u}),u===0||e.buildFailed))return;const h=Date.now();if(e.restarts=(e.restarts??[]).filter($=>h-$<types_1.SERVICE_RESTART_WINDOW_MS),e.restarts.length>=types_1.SERVICE_MAX_RESTARTS){a(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(h);const j=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})`),a(e,"info",`self-heal: respawning ${e.project}`,`cluster-service:${e.project}`,{kind:"service-respawn",project:e.project,attempt:e.restarts.length}),S(e))},j)}),e.proc=w},"spawnService"),f=i((e,o)=>{if(e.state==="up")return!1;e.state="up";const r=e.pool.socks.length;return a(e,"info",`service up: ${e.project} (${r} pods, ${o})`,`cluster-service:${e.project}`,{kind:"service-up",project:e.project,pods:r,adopted:o==="adopted"}),!0},"markUp"),k=i(async e=>{if(v(e.pool)>0)return f(e,"adopted"),!0;if(t.stopped())return!1;e.state!=="starting"&&!e.proc&&S(e);const o=Date.now()+t.startupTimeoutMs,r=Date.now()+12e3;for(;!t.stopped()&&Date.now()<o;){if(await sleep(Math.min(700,t.startupTimeoutMs/4)),e.buildFailed&&Date.now()>r)return!1;if(v(e.pool)>0){e.state="up",e.buildFailed=!1;const s=e.spawnAt?Date.now()-e.spawnAt:void 0;return console.log(`[gateway] ${e.project} up (${e.pool.socks.length} pods${s?`, cold start ${s}ms`:""})`),a(e,"info",`cold start: ${e.project} (${e.pool.socks.length} pods, ${s??"?"}ms)`,`cluster-service:${e.project}`,{kind:"cold-start",project:e.project,pods:e.pool.socks.length,durationMs:s}),!0}if(e.state==="down")return!1}return!1},"ensureUp"),M=i(e=>{const o=e.decl.maxPods??e.decl.minPods??1,r=e.pool.socks.length,s=e.decl.adminUrl??(e.decl.port?`http://localhost:${e.decl.port}`:void 0);if(!s||r===0||r>=o||e.inflight<=r||Date.now()-e.lastScaleAt<5e3)return;e.lastScaleAt=Date.now();const c=(s.startsWith("https")?require("node:https"):http).request(`${s}/webpack/scale`,{method:"POST",rejectUnauthorized:!1},d=>d.resume());c.on("error",()=>{}),c.end(JSON.stringify({pods:r+1})),console.log(`[gateway] autoscale ${e.project}: ${r} \u2192 ${r+1} pods (inflight ${e.inflight})`),a(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"),y=i((e,o,r)=>{if(!e.proc)return;console.log(`[gateway] ${e.project} ${o} \u2014 scaling to zero.`),r?.announce!==!1&&a(e,"info",`idle kill: ${e.project} (${o})`,`cluster-service:${e.project}`,{kind:"idle-kill",project:e.project,why:o});const s=e.proc;try{s.stdout?.removeAllListeners("data"),s.stderr?.removeAllListeners("data"),s.removeAllListeners("exit")}catch{}try{process.kill(-s.pid,"SIGTERM")}catch{try{s.kill("SIGTERM")}catch{}}e.proc=void 0,e.state="down",setTimeout(()=>{try{for(const c of fs.readdirSync(e.pool.dir)){if(!c.endsWith(".sock"))continue;const d=path.join(e.pool.dir,c);try{const w=Number(fs.readFileSync(`${d}.owner`,"utf8").trim());if(Number.isFinite(w)&&w>0)try{process.kill(w,0);continue}catch{}}catch{}try{fs.unlinkSync(d)}catch{}try{fs.unlinkSync(`${d}.owner`)}catch{}}}catch{}e.pool.socks=[],e.pool.scannedAt=0},1200).unref?.()},"killSpawned"),m=i(e=>{const o=Date.now();if(e.restarts=(e.restarts??[]).filter(r=>o-r<types_1.SERVICE_RESTART_WINDOW_MS),e.restarts.length>=types_1.SERVICE_MAX_RESTARTS){a(e,"error",`service crash-looping: ${e.project} \u2014 wedged ${types_1.SERVICE_MAX_RESTARTS}+\xD7/min, not auto-recycling (the next request retries)`,`cluster-service:${e.project}`,{kind:"service-crash-loop",project:e.project}),y(e,"wedged (crash-loop cap) \u2014 leaving down for the next request",{announce:!1});return}e.restarts.push(o),console.log(`[gateway] self-heal: recycling WEDGED ${e.project} (live parent, 0 live pods; ${e.restarts.length}/${types_1.SERVICE_MAX_RESTARTS})`),a(e,"warning",`self-heal: recycling wedged ${e.project} (0 live pods)`,`cluster-service:${e.project}`,{kind:"service-recycle",project:e.project,attempt:e.restarts.length}),y(e,"wedged: 0 live pods \u2014 recycling",{announce:!1}),setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&S(e)},1500).unref?.()},"recycleWedged"),A={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 R;const D=new Map;let C=!1,T=0;const b=i(()=>{const e=t.projectRoots??{},o=new Set(t.services.map(s=>s.project));for(const s of t.routes)s.lens&&o.add(s.lens.project);const r=new Set;for(const s of o){const c=e[s];if(!c)continue;const d=path.relative(t.root,c).split(path.sep).filter(Boolean)[0];d&&!d.startsWith("..")&&r.add(d)}return[...r]},"managedBuckets"),L=i((e,o,r,s)=>{const c=t.projectRoots?.[e];if(!c)return;const d=sourceScopeDirs(t.root,c,r,o,s);return d.length>0?d:void 0},"scopeFor"),P=i(()=>{C||Date.now()-T<types_1.POD_STALE_SCAN_MS||(C=!0,T=Date.now(),(async()=>{const e=(0,scope_1.readSourceSetExclude)(t.root),o=await sourceSetSignature(t.root,void 0,e);if(R!==void 0&&o!==R&&(t.sourceSetChangedAt=Date.now(),l("info","source file set changed \u2014 stale pods recycle when idle","cluster-gateway",{kind:"source-set-changed"})),R=o,!t.projectRoots)return;let r;try{r=(await fs.promises.readdir(t.root,{withFileTypes:!0})).filter(g=>g.isDirectory()).map(g=>g.name)}catch{return}const s=b(),c=new Set(t.services.map(g=>g.project));for(const g of t.routes)g.lens&&c.add(g.lens.project);const d=new Map;for(const g of c){const u=L(g,r,s,e);if(!u)continue;const h=u.join("|"),j=d.get(h)??{dirs:u,projects:[]};j.projects.push(g),d.set(h,j)}const w=Date.now();for(const[g,{dirs:u,projects:h}]of d){const j=await sourceSetSignature(t.root,u,e),$=D.get(g);if(D.set(g,j),!($===void 0||j===$)){t.sourceSetChangedFor??=new Map;for(const x of h)t.sourceSetChangedFor.set(x,w);l("info",`source file set changed in ${h.join(", ")}'s scope`,"cluster-gateway",{kind:"source-set-changed",scoped:!0,projects:h})}}})().catch(()=>{}).finally(()=>{C=!1}))},"maybeScanSourceSet"),F=i((e,o)=>{e.lastStaleRecycleAt=Date.now(),console.log(`[gateway] zombie recycle: ${e.project} (${o}) \u2014 stale fs view; fresh pod incoming`),a(e,"warning",`zombie recycle: ${e.project} (${o})`,`cluster-service:${e.project}`,{kind:"pod-recycle",project:e.project,reason:o});const r=!!e.decl.eager;y(e,`${o} \u2014 recycling stale pod`,{announce:!1}),r&&setTimeout(()=>{!t.stopped()&&!e.proc&&e.state==="down"&&S(e)},1500).unref?.()},"recyclePod");return{scanNow:v,spawnService:S,ensureUp:k,scaleUp:M,killSpawned:y,markUp:f,startSweeper:i(()=>{const e=setInterval(()=>{if(!t.stopped()){types_1.POD_RECYCLE_ENABLED&&P();for(const o of t.services){if(t.stopped()||!o.proc){o.wedgedSince=void 0;continue}const r=o.decl.idleKillSec;if(r&&o.inflight===0&&Date.now()-o.lastUsed>r*1e3){y(o,`idle ${r}s`),o.wedgedSince=void 0;continue}const s=o.state==="starting"&&o.spawnAt!==void 0&&Date.now()-o.spawnAt<t.startupTimeoutMs,c=v(o.pool)>0;if(c||s){if(o.wedgedSince=void 0,c&&f(o,"observed"),c&&!s&&types_1.POD_RECYCLE_ENABLED){const d=t.sourceSetChangedFor?.get(o.project)??t.sourceSetChangedAt??0,w=podRecycleReason(o,Date.now(),d,A);w&&F(o,w)}continue}o.wedgedSince??=Date.now(),Date.now()-o.wedgedSince>types_1.WEDGE_RECYCLE_GRACE_MS&&(o.wedgedSince=void 0,m(o))}}},t.sweepMs);return e.unref(),e},"startSweeper")}}E(createServiceLayer,"createServiceLayer"),i(createServiceLayer,"createServiceLayer");