@lensmcp/cluster 1.20.0 → 1.20.1

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 k=Object.defineProperty;var l=(t,r)=>k(t,"name",{value:r,configurable:!0});var v=Object.defineProperty,d=l((t,r)=>v(t,"name",{value:r,configurable:!0}),"d"),w=Object.defineProperty,y=d((t,r)=>w(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 o=e.projects??{},p=(0,discovery_1.projectSourceRoots)(e.root,o,e.projectDependencies),{routes:g,services:h}=(0,discovery_1.discoverRoutes)(e.root,o,void 0,e.wsKey,p),S={wsKey:e.wsKey,root:e.root,routes:g,services:h};return r.registry.register(S),await t.onRegister?.(S),r.rebuildRoutes(),s(i,200,{ok:!0,wsKey:e.wsKey,routes:g.length,services:h.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 p of o.services)t.killService(p,"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 v=Object.defineProperty;var d=(o,r)=>v(o,"name",{value:r,configurable:!0});var w=Object.defineProperty,y=d((o,r)=>w(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 t=e.projects??{},l=(0,discovery_1.projectSourceRoots)(e.root,t,e.projectDependencies),{routes:p,services:g}=(0,discovery_1.discoverRoutes)(e.root,t,void 0,e.wsKey,l),h={wsKey:e.wsKey,root:e.root,routes:p,services:g};return r.registry.register(h),await o.onRegister?.(h),r.rebuildRoutes(),s(i,200,{ok:!0,wsKey:e.wsKey,routes:p.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 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 w=Object.defineProperty;var l=(e,o)=>w(e,"name",{value:o,configurable:!0});var b=Object.defineProperty,a=l((e,o)=>b(e,"name",{value:o,configurable:!0}),"a"),S=Object.defineProperty,u=a((e,o)=>S(e,"name",{value:o,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.pickSock=pickSock,exports.lensPortFromUpstream=lensPortFromUpstream,exports.discoverRoutes=discoverRoutes,exports.projectSourceRoots=projectSourceRoots;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 p=Date.now();if(p-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=p}if(e.socks.length!==0)return e.idx=(e.idx+1)%e.socks.length,e.socks[e.idx]}l(pickSock,"pickSock"),a(pickSock,"pickSock"),u(pickSock,"pickSock");function lensPortFromUpstream(e){if(!e)return;const o=/:(\d+)/.exec(e);return o?Number(o[1]):void 0}l(lensPortFromUpstream,"lensPortFromUpstream"),a(lensPortFromUpstream,"lensPortFromUpstream"),u(lensPortFromUpstream,"lensPortFromUpstream");function discoverRoutes(e,o,p=os.tmpdir(),s="",d={}){const f=u(c=>s?path.join(p,s,`${c}-devserver`):path.join(p,`${c}-devserver`),"sockDirFor"),i=[],v=[],r=[];let n;for(const[c,x]of Object.entries(o)){let t;try{const h=JSON.parse(fs.readFileSync(path.join(e,x.root,"project.json"),"utf8"));t=h.cluster??h.davnx}catch{continue}if(!t)continue;let m,k,j,g;if(t.service)t.lens&&console.warn(`[gateway] ${c}: cluster.lens is ignored for pod (service) decls \u2014 backends are instrumented by serve-hmr.`),m={dir:f(t.service),socks:[],idx:-1,scannedAt:0},k={project:c,decl:t,root:e,wsKey:s,pool:m,state:"down",lastUsed:Date.now(),inflight:0,lastScaleAt:0},v.push(k);else if(t.lens){const h=t.port??lensPortFromUpstream(t.upstream)??5173;j=`http://localhost:${h}`,g={project:c,...s?{workspaceKey:s}:{},projectRoot:path.join(e,x.root),port:h,...d[c]?.length?{sourceRoots:d[c]}:{}}}else if(j=t.upstream??(t.port?`http://localhost:${t.port}`:void 0),!j){console.warn(`[gateway] ${c}: no service/upstream/port \u2014 skipped.`);continue}const y={project:c,prependPrefix:t.prependPrefix,target:j,pool:m,svc:k,prefix:t.path,auth:t.auth,lens:g};t.host&&(i.push({...y,host:t.host}),r.push(t.host),m&&t.internalHost!==!1&&i.push({...y,host:typeof t.internalHost=="string"?t.internalHost:`internal.${t.host}`,internal:!0})),t.default&&(n={...y,prefix:void 0})}return n&&(n.apex=(0,scope_1.baseDomainOf)(r),i.push(n)),(0,manifest_1.sortBySpecificity)(i),{routes:i,services:v}}l(discoverRoutes,"discoverRoutes"),a(discoverRoutes,"discoverRoutes"),u(discoverRoutes,"discoverRoutes");function projectSourceRoots(e,o,p={}){const s={};for(const d of Object.keys(o)){const f=new Set,i=u(r=>{if(!f.has(r)){f.add(r);for(const n of p[r]??[])i(n)}},"visit");i(d);const v=[...f].map(r=>o[r]?.root).filter(r=>typeof r=="string").map(r=>path.resolve(e,r)).filter(r=>{const n=path.relative(e,r);return n===""||n!==".."&&!n.startsWith(`..${path.sep}`)&&!path.isAbsolute(n)});s[d]=[...new Set(v)]}return s}l(projectSourceRoots,"projectSourceRoots"),a(projectSourceRoots,"projectSourceRoots"),u(projectSourceRoots,"projectSourceRoots");
1
+ "use strict";var b=Object.defineProperty;var a=(e,r)=>b(e,"name",{value:r,configurable:!0});var S=Object.defineProperty,u=a((e,r)=>S(e,"name",{value:r,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.pickSock=pickSock,exports.lensPortFromUpstream=lensPortFromUpstream,exports.discoverRoutes=discoverRoutes,exports.projectSourceRoots=projectSourceRoots;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 p=Date.now();if(p-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=p}if(e.socks.length!==0)return e.idx=(e.idx+1)%e.socks.length,e.socks[e.idx]}a(pickSock,"pickSock"),u(pickSock,"pickSock");function lensPortFromUpstream(e){if(!e)return;const r=/:(\d+)/.exec(e);return r?Number(r[1]):void 0}a(lensPortFromUpstream,"lensPortFromUpstream"),u(lensPortFromUpstream,"lensPortFromUpstream");function discoverRoutes(e,r,p=os.tmpdir(),s="",l={}){const d=u(i=>s?path.join(p,s,`${i}-devserver`):path.join(p,`${i}-devserver`),"sockDirFor"),c=[],h=[],o=[];let n;for(const[i,x]of Object.entries(r)){let t;try{const f=JSON.parse(fs.readFileSync(path.join(e,x.root,"project.json"),"utf8"));t=f.cluster??f.davnx}catch{continue}if(!t)continue;let v,j,m,y;if(t.service)t.lens&&console.warn(`[gateway] ${i}: cluster.lens is ignored for pod (service) decls \u2014 backends are instrumented by serve-hmr.`),v={dir:d(t.service),socks:[],idx:-1,scannedAt:0},j={project:i,decl:t,root:e,wsKey:s,pool:v,state:"down",lastUsed:Date.now(),inflight:0,lastScaleAt:0},h.push(j);else if(t.lens){const f=t.port??lensPortFromUpstream(t.upstream)??5173;m=`http://localhost:${f}`,y={project:i,...s?{workspaceKey:s}:{},projectRoot:path.join(e,x.root),port:f,...l[i]?.length?{sourceRoots:l[i]}:{}}}else if(m=t.upstream??(t.port?`http://localhost:${t.port}`:void 0),!m){console.warn(`[gateway] ${i}: no service/upstream/port \u2014 skipped.`);continue}const k={project:i,prependPrefix:t.prependPrefix,target:m,pool:v,svc:j,prefix:t.path,auth:t.auth,lens:y};t.host&&(c.push({...k,host:t.host}),o.push(t.host),v&&t.internalHost!==!1&&c.push({...k,host:typeof t.internalHost=="string"?t.internalHost:`internal.${t.host}`,internal:!0})),t.default&&(n={...k,prefix:void 0})}return n&&(n.apex=(0,scope_1.baseDomainOf)(o),c.push(n)),(0,manifest_1.sortBySpecificity)(c),{routes:c,services:h}}a(discoverRoutes,"discoverRoutes"),u(discoverRoutes,"discoverRoutes");function projectSourceRoots(e,r,p={}){const s={};for(const l of Object.keys(r)){const d=new Set,c=u(o=>{if(!d.has(o)){d.add(o);for(const n of p[o]??[])c(n)}},"visit");c(l);const h=[...d].map(o=>r[o]?.root).filter(o=>typeof o=="string").map(o=>path.resolve(e,o)).filter(o=>{const n=path.relative(e,o);return n===""||n!==".."&&!n.startsWith(`..${path.sep}`)&&!path.isAbsolute(n)});s[l]=[...new Set(h)]}return s}a(projectSourceRoots,"projectSourceRoots"),u(projectSourceRoots,"projectSourceRoots");
@@ -1 +1 @@
1
- "use strict";var O=Object.defineProperty;var a=(r,e)=>O(r,"name",{value:e,configurable:!0});var T=Object.defineProperty,n=a((r,e)=>T(r,"name",{value:e,configurable:!0}),"n"),i=Object.defineProperty,s=n((r,e)=>i(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,c,E,d,l)=>{const t=(0,gateway_errors_1.ensureRequestId)(e);return r.headersSent||r.writeHead(o,{"content-type":gateway_errors_1.GATEWAY_ERROR_CONTENT_TYPE,"x-request-id":t,...l??{}}),r.end((0,gateway_errors_1.gatewayErrorBody)(o,c,t,E,d)),t},"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 T=Object.defineProperty;var n=(r,e)=>T(r,"name",{value:e,configurable:!0});var i=Object.defineProperty,s=n((r,e)=>i(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,a,E,c,d)=>{const t=(0,gateway_errors_1.ensureRequestId)(e);return r.headersSent||r.writeHead(o,{"content-type":gateway_errors_1.GATEWAY_ERROR_CONTENT_TYPE,"x-request-id":t,...d??{}}),r.end((0,gateway_errors_1.gatewayErrorBody)(o,a,t,E,c)),t},"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,f=l((o,e)=>c(o,"name",{value:e,configurable:!0}),"f");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"),f(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 s=f(()=>{t.delete(r),t.size===0&&n.delete(e)},"drop");r.once("close",s),r.once("error",s)}i(registerHmrClient,"registerHmrClient"),l(registerHmrClient,"registerHmrClient"),f(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 s of r)if(!s.destroyed)try{s.write(n),t+=1}catch{}return t}i(broadcastFullReload,"broadcastFullReload"),l(broadcastFullReload,"broadcastFullReload"),f(broadcastFullReload,"broadcastFullReload");
1
+ "use strict";var a=Object.defineProperty;var l=(n,e)=>a(n,"name",{value:e,configurable:!0});var c=Object.defineProperty,f=l((n,e)=>c(n,"name",{value:e,configurable:!0}),"f");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"),f(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 s=f(()=>{r.delete(t),r.size===0&&o.delete(e)},"drop");t.once("close",s),t.once("error",s)}l(registerHmrClient,"registerHmrClient"),f(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 s of t)if(!s.destroyed)try{s.write(o),r+=1}catch{}return r}l(broadcastFullReload,"broadcastFullReload"),f(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 ve=Object.defineProperty;var C=(s,u)=>ve(s,"name",{value:u,configurable:!0});var ye=Object.defineProperty,M=C((s,u)=>ye(s,"name",{value:u,configurable:!0}),"M"),_e=Object.defineProperty,l=M((s,u)=>_e(s,"name",{value:u,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,u){return new Promise(f=>{const y=net.createServer();y.unref(),y.once("error",p=>{const E=p.code??"";f(E==="EADDRINUSE"||E==="EACCES"?"in-use":"unavailable")}),y.listen(u===void 0?{port:s}:{port:s,host:u},()=>y.close(()=>f("free")))})}C(tryBind,"tryBind"),M(tryBind,"tryBind"),l(tryBind,"tryBind");async function probePortFree(s){for(const u of[void 0,"0.0.0.0","127.0.0.1","::1"])if(await tryBind(s,u)==="in-use")return!1;return!0}C(probePortFree,"probePortFree"),M(probePortFree,"probePortFree"),l(probePortFree,"probePortFree");function probeConnect(s,u,f){return new Promise(y=>{let p=!1;const E=net.createConnection({host:s,port:u});E.unref();const b=setTimeout(()=>S(!1),f);b.unref?.();function S(O){p||(p=!0,clearTimeout(b),E.destroy(),y(O))}C(S,"S"),M(S,"S"),l(S,"finish"),E.once("connect",()=>S(!0)),E.once("error",()=>S(!1))})}C(probeConnect,"probeConnect"),M(probeConnect,"probeConnect"),l(probeConnect,"probeConnect");async function probePortListening(s,u=1e3){return await probeConnect("127.0.0.1",s,u)?!0:probeConnect("::1",s,u)}C(probePortListening,"probePortListening"),M(probePortListening,"probePortListening"),l(probePortListening,"probePortListening");async function probeWatcherHealth(s,u=1500){try{const f=new AbortController,y=setTimeout(()=>f.abort(),u);y.unref?.();try{const p=await fetch(`http://127.0.0.1:${s}/__lensmcp/watcher-health`,{signal:f.signal});if(!p.ok)return null;const E=await p.json();if(typeof E?.lastFsSetEventAt!="number")return null;const b=Array.isArray(E.recentEvents)?E.recentEvents.filter(S=>S&&typeof S.path=="string"&&typeof S.at=="number"&&typeof S.sequence=="number"&&["add","addDir","unlink","unlinkDir"].includes(S.kind)):[];return{schemaVersion:E.schemaVersion===2?2:1,ready:E.ready!==!1,sequence:typeof E.sequence=="number"?E.sequence:0,lastFsSetEventAt:E.lastFsSetEventAt,fsSetEvents:typeof E.fsSetEvents=="number"?E.fsSetEvents:0,recentEvents:b}}finally{clearTimeout(y)}}catch{return null}}C(probeWatcherHealth,"probeWatcherHealth"),M(probeWatcherHealth,"probeWatcherHealth"),l(probeWatcherHealth,"probeWatcherHealth");const sleepUnref=l(s=>new Promise(u=>{setTimeout(u,s).unref?.()}),"sleepUnref");async function waitForPortFree(s,u,f={}){const y=f.isPortFree??probePortFree,p=Math.max(1,f.intervalMs??types_1.PORT_FREE_POLL_MS),E=Date.now()+Math.max(0,u);for(;;){if(await y(s))return!0;const b=E-Date.now();if(b<=0)return!1;await sleepUnref(Math.min(p,b))}}C(waitForPortFree,"waitForPortFree"),M(waitForPortFree,"waitForPortFree"),l(waitForPortFree,"waitForPortFree");function viteArgPort(s){const u=l(f=>Number.isInteger(f)&&f>0&&f<65536?f:void 0,"valid");for(let f=0;f<s.length;f+=1){const y=s[f];if(y==="--port")return u(Number(s[f+1]));if(y.startsWith("--port="))return u(Number(y.slice(7)))}}C(viteArgPort,"viteArgPort"),M(viteArgPort,"viteArgPort"),l(viteArgPort,"viteArgPort");function createLensChildren(s,u,f,y){const{emit:p}=u,E=y?.isPortFree??probePortFree,b=y?.probeWatcherHealth??probeWatcherHealth,S=[],O=new Map,A=new Map,j=new Map,G=new Map,x=new Set;let D;const k=new Map,$=new Set,W=new Set,N=new Set,L=new Map,F=new WeakMap,B=new Set,J=l((e,r,n)=>{if(B.has(e))return;B.add(e);const t=setInterval(()=>{(async()=>{if(s.stopped()||$.has(e)){clearInterval(t),B.delete(e);return}await E(r)&&(clearInterval(t),B.delete(e),console.log(`[gateway] lens child ${e}: :${r} freed \u2014 respawning.`),n())})().catch(()=>{})},types_1.LENS_PARKED_PORT_REPROBE_MS);t.unref?.()},"parkUntilPortFree"),I=l(async(e,r)=>{let n=e;for(;r.has(n)||!await E(n);)n+=1;return r.add(n),n!==e&&console.warn(`[gateway] port ${e} is unavailable (another process holds it \u2014 an orphan from a crashed gateway?) \u2014 using ${n} instead.`),n},"claimFreePort"),ie=l((e,r,n)=>{const t=n?.LENSMCP_FRONTEND_KEY,o=Number(n?.LENSMCP_FRONTEND_GENERATION);if(!t||!Number.isFinite(o))return;const a=[...L.get(t)?.generations??[]].find(i=>i.generation===o);a&&(e.endsWith(":vite")?a.vite=r:e.endsWith(":browser-capture")&&(a.capture=r))},"attachRespawnedGenerationChild"),T=l((e,r,n,t,o=!1,a,i,c)=>{const g=Date.now(),d=(0,node_child_process_1.spawn)(r,n,{cwd:i??s.root,env:{...(0,pod_env_1.podBaseEnv)(process.env,s.root,i??s.root),...t??{},LENSMCP_PARENT_PID:String(process.pid)},stdio:"inherit"});S.push(d),k.set(e,d),ie(e,d,t);const h=e==="vite"||e.endsWith(":vite"),R=typeof t?.LENSMCP_EVENT_FILE=="string"?t.LENSMCP_EVENT_FILE:void 0,w=R&&R!==s.eventFile?R:void 0,_=h?viteArgPort(n):c;h&&A.set(d,{key:t?.LENSMCP_FRONTEND_KEY??`${i??s.root}:${t?.LENSMCP_PROJECT??"app"}`,project:t?.LENSMCP_PROJECT??"app",workspaceRoot:i??s.root,spawnedAt:g,port:_,probeFails:0,listening:!1,generation:Number(t?.LENSMCP_FRONTEND_GENERATION??0),role:t?.LENSMCP_FRONTEND_ROLE==="replacement"?"replacement":"active",...w?{bus:w}:{}});const v=e==="lens-mcp"?"lens-mcp":e==="lens-dashboard"||e.startsWith("lens-dashboard:")?"lens-dashboard":void 0;return v&&_!==void 0&&(p("info",`lens child starting: ${v} (:${_})`,`cluster-lens-child:${v}`,{kind:"lens-child-starting",project:v,label:e,pid:d.pid,port:_},void 0,w),(async()=>{const m=Date.now()+types_1.LENS_REPLACEMENT_READY_MS;for(;!s.stopped()&&Date.now()<m;){if(d.exitCode!==null||d.signalCode!==null||$.has(e))return;if(await probePortListening(_,Math.min(500,types_1.LENS_PROBE_TIMEOUT_MS))){p("info",`lens child up: ${v} (:${_})`,`cluster-lens-child:${v}`,{kind:"lens-child-up",project:v,label:e,pid:d.pid,port:_},void 0,w);return}await sleepUnref(150)}})().catch(()=>{})),d.on("exit",(m,z)=>{const oe=S.indexOf(d);oe>=0&&S.splice(oe,1),k.get(e)===d&&k.delete(e);const ne=A.get(d);A.delete(d);const we=$.delete(e);if(s.stopped()||we||(v&&p("warning",`lens child exited: ${v}`,`cluster-lens-child:${v}`,{kind:"lens-child-exit",project:v,label:e,pid:d.pid,code:m,signal:z},void 0,w),ne&&ce(d,ne)))return;const re=Date.now()-g,P=O.get(e)??{attempts:0,portConflicts:0};(async()=>{const Se=_!==void 0&&!await E(_),se=(0,lifecycle_1.isPortConflictExit)({code:m,signal:z,ranMs:re,portHeld:Se},{fastFailMs:types_1.CHILD_START_FAIL_MS});if(se&&P.portConflicts<types_1.MAX_PORT_CONFLICT_RETRIES)P.portConflicts+=1,O.set(e,P),console.warn(`[gateway] lens child ${e} lost the :${_} bind race (EADDRINUSE, code=${m}); waiting for the port, then retrying (port-retry ${P.portConflicts}/${types_1.MAX_PORT_CONFLICT_RETRIES}; crash budget untouched).`);else if(se){P.portConflicts=0,O.set(e,P),console.warn(`[gateway] lens child ${e}: :${_} 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.`),p("warning",`lens child parked: ${e} (:${_} held externally)`,`cluster-lens-child:${e}`,{kind:"lens-child-parked",label:e,port:_}),J(e,_,()=>T(e,r,n,t,o,a,i,c));return}else{P.portConflicts=0,re>=HEAL_HEALTHY_MS&&(P.attempts=0);const ae=Math.min(HEAL_MAX_MS,HEAL_BASE_MS*2**P.attempts);P.attempts+=1,O.set(e,P),console.warn(`[gateway] lens child ${e} exited (code=${m} sig=${z}); auto-healing in ${Math.round(ae/1e3)}s (attempt ${P.attempts}).`),await sleepUnref(ae)}s.stopped()||$.has(e)||(_!==void 0&&!await waitForPortFree(_,types_1.PORT_FREE_WAIT_MS,{isPortFree:E,intervalMs:types_1.PORT_FREE_POLL_MS})&&console.warn(`[gateway] lens child ${e}: :${_} still held after ${Math.round(types_1.PORT_FREE_WAIT_MS/1e3)}s \u2014 spawning anyway.`),!(s.stopped()||$.has(e))&&T(e,r,n,t,o,a,i,c))})().catch(()=>{})}),d},"spawnManagedChild"),X=l(e=>{const r=`${e.wsKey}:${e.project}`,n=L.get(r);if(n)return n;const t={key:r,...e,sourceDirs:e.frontend.sourceRoots?.length?e.frontend.sourceRoots:[e.projectRoot],generation:0,state:"starting",generations:new Set,disposed:!1};L.set(r,t);for(const o of e.routes)o.lens&&(o.lens.workspaceKey??=e.wsKey,F.set(o.lens,t));return e.frontend.workspaceKey??=e.wsKey,F.set(e.frontend,t),t},"registerFrontend"),Q=l((e,r,n)=>{const t=++e.generation,o=`${e.key}:g${t}:`,a=`${o}vite`,i=`${o}browser-capture`;$.delete(a),$.delete(i);const c=l((h,R,w,_,v,m)=>T(h,R,w,_,v,m,e.workspaceRoot),"spawnInRoot"),g=(0,lens_frontend_1.spawnLensFrontend)({projectRoot:e.projectRoot,workspaceRoot:e.workspaceRoot,project:e.project,eventFile:e.eventFile,port:r,chrome:!0,headless:!0,viteEnv:{LENSMCP_PROJECT:e.project,LENSMCP_FRONTEND_KEY:e.key,LENSMCP_FRONTEND_GENERATION:String(t),LENSMCP_FRONTEND_ROLE:n},labelPrefix:o,log:l(h=>console.log(h),"log")},c),d={ownerKey:e.key,generation:t,port:r,labelPrefix:o,vite:g?.vite??k.get(a),capture:k.get(i)};return e.generations.add(d),d},"spawnFrontendGeneration"),K=l((e,r)=>{if(!e)return;L.get(e.ownerKey)?.generations.delete(e);const n=[[`${e.labelPrefix}browser-capture`,e.capture],[`${e.labelPrefix}vite`,e.vite]];for(const[t,o]of n)if($.add(t),!(!o||o.exitCode!==null||o.signalCode!==null)){try{o.kill("SIGTERM")}catch{}setTimeout(()=>{if(o.exitCode===null&&o.signalCode===null)try{o.kill("SIGKILL")}catch{}},types_1.LENS_RECYCLE_KILL_GRACE_MS).unref?.()}r&&N.delete(e.port)},"terminateGeneration"),le=l(async e=>{const r=Date.now()+types_1.LENS_REPLACEMENT_READY_MS;for(;!s.stopped()&&Date.now()<r;){const n=e.vite;if(!n||n.exitCode!==null||n.signalCode!==null)return!1;if(await probePortListening(e.port,Math.min(500,types_1.LENS_PROBE_TIMEOUT_MS))){const t=await b(e.port);if(!t||t.ready)return!0}await sleepUnref(150)}return!1},"waitForGenerationReady"),H=l(async e=>{const r=e?.vite;return!!(e&&r&&r.exitCode===null&&r.signalCode===null&&await probePortListening(e.port,Math.min(500,types_1.LENS_PROBE_TIMEOUT_MS)))},"generationIsServing"),Y=l(e=>!e.disposed&&!s.stopped()&&L.get(e.key)===e,"frontendIsCurrent"),q=l((e,r)=>{if(!Y(e))return Promise.resolve(!1);if(e.replacement)return e.replacement;const n=e.active;e.state=n?"replacing":"starting",e.reason=r,e.startedAt=Date.now();const t=(async()=>{let o,a;const i=Date.now();try{const c=await I(e.desiredPort,N);if(a=c,!Y(e))return N.delete(c),!1;o=Q(e,c,"replacement"),e.candidate=o;const g=await H(n);console.log(`[gateway] lens handoff start: ${e.key} g${n?.generation??0}\u2192g${o.generation} :${n?.port??"down"}\u2192:${c} reason=${r}`),p("info",`lens handoff starting: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-start",wsKey:e.wsKey,project:e.project,reason:r,generation:o.generation,oldPort:n?.port,newPort:c,oldServing:g},void 0,e.eventFile===s.eventFile?void 0:e.eventFile);const d=await le(o);if(!Y(e))return K(o,!0),e.candidate=void 0,!1;if(!d){K(o,!0),e.candidate=void 0;const w=await H(n);return e.state=w?"ready":"failed",w&&(e.reason=void 0,e.startedAt=void 0),console.error(`[gateway] lens handoff failed: ${e.key} g${o.generation} did not become ready within ${types_1.LENS_REPLACEMENT_READY_MS}ms; ${e.state==="ready"?"old generation kept serving":"no active generation"}.`),p("warning",`lens handoff failed: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-failed",wsKey:e.wsKey,project:e.project,reason:r,generation:o.generation,port:c,durationMs:Date.now()-i,oldKept:w},void 0,e.eventFile===s.eventFile?void 0:e.eventFile),!1}for(const w of e.routes)w.target=`http://localhost:${o.port}`,w.lens&&(w.lens.port=o.port);e.frontend.port=o.port,e.active=o,e.candidate=void 0,e.state="ready",e.reason=void 0,e.startedAt=void 0;const h=o.vite?A.get(o.vite):void 0;h&&(h.role="active",h.listening=!0);const R=(0,hmr_reload_1.broadcastFullReload)(s,e.key);if(console.log(`[gateway] lens handoff ready: ${e.key} g${o.generation} :${o.port} in ${Date.now()-i}ms; draining g${n?.generation??0}${R>0?`; reloading ${R} tab(s)`:""}.`),p("info",`lens handoff complete: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-complete",wsKey:e.wsKey,project:e.project,reason:r,generation:o.generation,oldPort:n?.port,newPort:o.port,durationMs:Date.now()-i,reloadedTabs:R},void 0,e.eventFile===s.eventFile?void 0:e.eventFile),n){const w=`${n.labelPrefix}browser-capture`;$.add(w);try{n.capture?.kill("SIGTERM")}catch{}setTimeout(()=>K(n,!0),types_1.LENS_REPLACEMENT_DRAIN_MS).unref?.()}return!0}catch(c){K(o,!0),!o&&a!==void 0&&N.delete(a),e.candidate=void 0;const g=await H(n);e.state=g?"ready":"failed",g&&(e.reason=void 0,e.startedAt=void 0);const d=c.message;return console.error(`[gateway] lens handoff failed: ${e.key}: ${d}`),p("warning",`lens handoff failed: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-failed",wsKey:e.wsKey,project:e.project,reason:r,generation:o?.generation??e.generation,port:o?.port??a,durationMs:Date.now()-i,oldKept:g,error:d},void 0,e.eventFile===s.eventFile?void 0:e.eventFile),!1}finally{e.replacement=void 0}})();return e.replacement=t,t},"rollFrontend"),Z=l((e,r)=>{e.active=Q(e,r,"active"),e.state="starting",e.startedAt=Date.now()},"spawnInitialFrontend"),ce=l((e,r)=>{const n=L.get(r.key);return!n||types_1.LENS_RESTART_MODE==="in-place"?!1:(n.candidate?.vite===e||n.active?.vite!==e||(n.state="starting",n.reason="child-exit",n.startedAt=Date.now(),console.warn(`[gateway] lens frontend exited: ${n.key} g${r.generation} :${r.port}; starting an alternate-port replacement.`),q(n,"child-exit")),!0)},"handleManagedViteExit");s.lensSupervisor=types_1.LENS_RESTART_MODE==="rolling"?{status(e){const r=F.get(e);return r?{state:r.state,generation:r.candidate?.generation??r.active?.generation??r.generation,reason:r.reason,startedAt:r.startedAt}:void 0},recover(e,r){const n=F.get(e);return n?q(n,r):Promise.resolve(!1)}}:void 0;let U=!1,ee=0;const de=l(()=>{U||Date.now()-ee<types_1.POD_STALE_SCAN_MS||(U=!0,ee=Date.now(),(async()=>{for(const e of[...L.values()]){if(s.stopped())return;const r=await(0,lifecycle_1.sourceSetSnapshot)(e.workspaceRoot,e.sourceDirs,(0,scope_1.readSourceSetExclude)(e.workspaceRoot));if(L.get(e.key)!==e)continue;const n=e.sourceSnapshot;if(e.sourceSnapshot=r,!n)continue;const t=(0,lifecycle_1.diffSourceSets)(n,r,Date.now());t&&(e.sourceChange=t,p("info",`source file set changed for ${e.key}`,`cluster-lens-app:${e.project}`,{kind:"source-set-changed",scoped:!0,wsKey:e.wsKey,project:e.project,added:t.added,removed:t.removed,truncated:t.truncated},void 0,e.eventFile===s.eventFile?void 0:e.eventFile))}})().catch(()=>{}).finally(()=>{U=!1}))},"scanFrontendSourceSets");let V=!1;const pe=l(async(e,r)=>{if(!V){V=!0;try{for(const[n,t]of[...A]){if(s.stopped())return;if(t.port===void 0||n.exitCode!==null||n.signalCode!==null)continue;if(await probePortListening(t.port,types_1.LENS_PROBE_TIMEOUT_MS)){if(t.probeFails=0,!t.listening){t.listening=!0;const c=L.get(t.key);c?.active?.vite===n&&(c.state="ready",c.reason=void 0),p("info",`lens app up: ${t.project} (vite :${t.port} listening)`,`cluster-lens-app:${t.project}`,{kind:"service-up",project:t.project,port:t.port},void 0,t.bus)}continue}const o=t.listening?e.graceMs:Math.max(e.graceMs,e.neverListenedGraceMs);if(Date.now()-t.spawnedAt<o){t.probeFails=0;continue}if(!r)continue;t.probeFails+=1;const a=(0,lifecycle_1.lensChildWedged)({spawnedAt:t.spawnedAt,probeFails:t.probeFails,hasListened:t.listening},j.get(t.key)??0,Date.now(),e);if(!a)continue;const i=t.listening?"its listener VANISHED (was up, now refusing)":`it NEVER bound within the ${Math.round(o/1e3)}s boot allowance`;j.set(t.key,Date.now()),t.probeFails=0,t.listening=!1,console.error(`[gateway] wedge recycle: lens app ${t.project} is ALIVE but NOT LISTENING on :${t.port} after ${Math.max(2,e.failureThreshold)} probes \u2014 ${i} \u2014 SIGKILL; a fresh vite is coming.`),p("warning",`wedge recycle: ${t.project} (${a})`,`cluster-lens-app:${t.project}`,{kind:"pod-recycle",project:t.project,reason:a,port:t.port},void 0,t.bus);try{n.kill("SIGKILL")}catch{}}}finally{V=!1}}},"probeLensChildren"),ue=l(()=>{if(D||!types_1.POD_RECYCLE_ENABLED&&!types_1.LENS_PROBE_ENABLED)return;const e={graceMs:types_1.POD_STALE_GRACE_MS,settleMs:types_1.POD_RECYCLE_SETTLE_MS,cooldownMs:types_1.POD_STALE_RECYCLE_COOLDOWN_MS};D=setInterval(()=>{if(s.stopped()||A.size===0||(te(),!types_1.POD_RECYCLE_ENABLED))return;de();const r=Date.now();for(const[n,t]of A){const o=L.get(t.key),a=o?.sourceChange?.changedAt??0,i=j.get(t.key)??0;(0,lifecycle_1.lensFrontendStale)(t.spawnedAt,i,a,r,e)&&((G.get(t.key)??0)>=a||x.has(t.key)||(x.add(t.key),(async()=>{try{const c=o?.sourceChange,g=c&&!c.truncated?[...c.added,...c.removed].map(w=>path.resolve(t.workspaceRoot,w)):[],d=types_1.LENS_WATCHER_PROBE_ENABLED&&t.port!==void 0?(0,lifecycle_1.lensWatcherVerdict)(await b(t.port),a,Math.min(types_1.POD_STALE_SCAN_MS,15e3)+types_1.POD_RECYCLE_SETTLE_MS,g):"unknown";if(d==="heard"){G.set(t.key,a),console.log(`[gateway] stale source set: lens app ${t.project} watcher CONFIRMED the change \u2014 no recycle, HMR intact.`),p("info",`stale check: ${t.project} watcher confirmed \u2014 no recycle`,`cluster-lens-app:${t.project}`,{kind:"pod-stale-verified",project:t.project},void 0,t.bus);return}if(n.exitCode!==null||n.signalCode!==null)return;j.set(t.key,Date.now());const h=L.get(t.key);if(types_1.LENS_RESTART_MODE==="rolling"&&h){console.log(`[gateway] stale refresh: lens app ${t.key} (stale-source-set) \u2014 watcher ${d==="missed"?"missed the exact path change":"unverifiable"}; preparing a standby without dropping traffic.`),p("warning",`stale refresh: ${t.project} (stale-source-set)`,`cluster-lens-app:${t.project}`,{kind:"pod-refresh",wsKey:h.wsKey,project:t.project,reason:"stale-source-set",watcher:d,added:c?.added,removed:c?.removed},void 0,t.bus),await q(h,"stale-source-set");return}t.listening=!1,console.log(`[gateway] zombie recycle: lens app ${t.project} (stale-source-set) \u2014 watcher ${d==="missed"?"SILENT across the change":"unverifiable"}; restarting it`),p("warning",`zombie recycle: ${t.project} (stale-source-set)`,`cluster-lens-app:${t.project}`,{kind:"pod-recycle",project:t.project,reason:"stale-source-set",watcher:d},void 0,t.bus);const R=(0,hmr_reload_1.broadcastFullReload)(s,t.key);R>0&&console.log(`[gateway] \u2192 told ${R} open tab(s) to reload (stale optimize-dep generation)`);try{n.kill("SIGTERM")}catch{}setTimeout(()=>{if(n.exitCode===null&&n.signalCode===null){console.log(`[gateway] zombie recycle: lens app ${t.project} ignored SIGTERM for ${Math.round(types_1.LENS_RECYCLE_KILL_GRACE_MS/1e3)}s \u2014 escalating to SIGKILL.`);try{n.kill("SIGKILL")}catch{}}},types_1.LENS_RECYCLE_KILL_GRACE_MS).unref?.()}finally{x.delete(t.key)}})()))}},Math.min(types_1.POD_STALE_SCAN_MS,15e3)),D.unref?.()},"startLensSweeper"),fe=l(async()=>{const e=[...new Map(s.routes.filter(t=>t.lens).map(t=>[t.project,t.lens])).values()],r=f.dashboard!==!1,n=f.mcp!==!1&&e.length>0;if(r){const t=(0,lens_frontend_1.findDashboardBundle)(s.root);if(!t)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 o=(0,scope_1.readLensScope)(s.root),a=await I(f.dashboardPort??o.dashboardPort,W),i=f.lensHost??"lensmcp.local",c=o.basePath;s.registry.appendRoutes(s.wsKey,[{host:i,prefix:c,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${a}`}]),s.rebuildRoutes(),(0,workspace_registry_1.registerWorkspace)({key:o.key,root:s.root,base:c,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] starting lens dashboard \u2192 http://localhost:${a} (routed at https://${i}${c}/)`),p("info",`lens dashboard up: ${i}${c} \u2192 :${a}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:i,port:a,base:c}),T("lens-dashboard",process.execPath,[t],{LENSMCP_EVENT_FILE:s.eventFile,LENSMCP_PROCESS_ROLE:"dashboard",LENSMCP_DASHBOARD_PORT:String(a),LENSMCP_DASHBOARD_BASE:c},!1,void 0,void 0,a)}}if(n){const t=(0,lens_frontend_1.findMcpBundle)(s.root);if(!t)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 o=(0,scope_1.readLensScope)(s.root),a=Number(process.env.LENSMCP_PORT??o.mcpHttpPort),i=l(()=>{console.log(`[gateway] starting lens MCP server on 127.0.0.1:${a} (${t})`),T("lens-mcp",process.execPath,[t],{LENSMCP_EVENT_FILE:s.eventFile,LENSMCP_PROCESS_ROLE:"mcp",LENSMCP_TRANSPORT:process.env.LENSMCP_TRANSPORT??"http",LENSMCP_PORT:String(a),LENSMCP_WORKSPACE_KEY:o.key,LENSMCP_WORKSPACE_ROOT:s.root},!1,void 0,void 0,a)},"spawnMcp");await E(a)?i():(console.warn(`[gateway] lens MCP port :${a} 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.`),p("info",`lens MCP served externally on :${a} \u2014 gateway parked its own`,"cluster-lens-child:lens-mcp",{kind:"lens-child-up",project:"lens-mcp",label:"lens-mcp",port:a,external:!0}),J("lens-mcp",a,i))}}for(const t of e){if(t.started)continue;t.started=!0;const o=t.port,a=(s.registry.get(s.wsKey)?.routes??s.routes).filter(g=>g.lens?.project===t.project),i=await I(t.port,N);for(const g of a)g.lens&&(g.lens.port=i),g.target=`http://localhost:${i}`;t.port=i;const c=X({wsKey:s.wsKey,project:t.project,workspaceRoot:s.root,projectRoot:t.projectRoot,eventFile:s.eventFile,routes:a,frontend:t,desiredPort:o});try{Z(c,i),p("info",`lens app started: ${t.project} (vite :${t.port})`,`cluster-lens-app:${t.project}`,{kind:"lens-app-up",project:t.project,port:t.port})}catch(g){N.delete(i),c.state="failed",console.error(`[gateway] lens app ${t.project}: ${g.message}`)}}ue()},"bootSingletonsAndApps"),Ee=l(async e=>{const r=(0,lens_frontend_1.findDashboardBundle)(s.root);if(!r)return;const n=(0,scope_1.readLensScope)(e.root),t=f.lensHost??"lensmcp.local",o=n.basePath,a=await I(n.dashboardPort,W),i=`lens-dashboard:${e.wsKey}`,c=path.join(e.root,".lensmcp","events.jsonl");return $.delete(i),s.registry.appendRoutes(e.wsKey,[{host:t,prefix:o,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${a}`}]),(0,workspace_registry_1.registerWorkspace)({key:e.wsKey,root:e.root,base:o,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] hosting dashboard for workspace '${e.wsKey}' \u2192 http://localhost:${a} (routed at https://${t}${o}/)`),p("info",`lens dashboard up: ${t}${o} \u2192 :${a}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:t,port:a,base:o}),T(i,process.execPath,[r],{LENSMCP_EVENT_FILE:c,LENSMCP_PROCESS_ROLE:"dashboard",LENSMCP_DASHBOARD_PORT:String(a),LENSMCP_DASHBOARD_BASE:o,LENSMCP_WS_KEY:e.wsKey},!1,void 0,e.root,a),()=>{$.add(i),W.delete(a),(0,workspace_registry_1.unregisterWorkspace)(e.wsKey);try{k.get(i)?.kill("SIGTERM")}catch{}k.delete(i)}},"hostWorkspaceDashboard"),ge=l(async e=>{const r=new Map;for(const o of e.routes){if(!o.lens)continue;const a=r.get(o.lens.project)??[];a.push(o),r.set(o.lens.project,a)}if(r.size===0)return()=>{};const n=path.join(e.root,".lensmcp","events.jsonl"),t=[];for(const[o,a]of r){const i=a[0].lens;if(i.started)continue;const c=await I(i.port,N),g=i.port;for(const h of a)h.lens.started=!0,h.lens.port=c,h.target=`http://localhost:${c}`;const d=X({wsKey:e.wsKey,project:o,workspaceRoot:e.root,projectRoot:i.projectRoot,eventFile:n,routes:a,frontend:i,desiredPort:g});t.push(d),console.log(`[gateway] hosting lens app '${o}' of '${e.wsKey}' \u2192 vite :${c}`);try{Z(d,c),p("info",`lens app started: ${e.wsKey}/${o} (vite :${c})`,`cluster-lens-app:${e.wsKey}:${o}`,{kind:"lens-app-up",project:o,port:c},void 0,n)}catch(h){N.delete(c),d.state="failed",console.error(`[gateway] lens app ${e.wsKey}/${o}: ${h.message}`)}}return()=>{for(const o of t){o.disposed=!0;for(const a of[...o.generations])K(a,!0);for(const a of o.routes)a.lens&&F.delete(a.lens);F.delete(o.frontend),j.delete(o.key),G.delete(o.key),x.delete(o.key),L.delete(o.key)}}},"hostWorkspaceApps"),he=l(async(e=2500)=>{D&&(clearInterval(D),D=void 0);const r=[...S];for(const o of r)try{o.kill("SIGTERM")}catch{}const n=l(o=>o.exitCode===null&&o.signalCode===null,"alive"),t=Date.now()+e;for(;Date.now()<t&&r.some(n);)await new Promise(o=>setTimeout(o,100));for(const o of r)if(n(o))try{o.kill("SIGKILL")}catch{}},"reapAll"),te=l(()=>pe({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:T,bootSingletonsAndApps:fe,hostWorkspaceDashboard:Ee,hostWorkspaceApps:ge,probeLensFrontends:te,reapAll:he}}C(createLensChildren,"createLensChildren"),M(createLensChildren,"createLensChildren"),l(createLensChildren,"createLensChildren");
1
+ "use strict";var ye=Object.defineProperty;var M=(s,E)=>ye(s,"name",{value:E,configurable:!0});var _e=Object.defineProperty,l=M((s,E)=>_e(s,"name",{value:E,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,E){return new Promise(u=>{const y=net.createServer();y.unref(),y.once("error",p=>{const f=p.code??"";u(f==="EADDRINUSE"||f==="EACCES"?"in-use":"unavailable")}),y.listen(E===void 0?{port:s}:{port:s,host:E},()=>y.close(()=>u("free")))})}M(tryBind,"tryBind"),l(tryBind,"tryBind");async function probePortFree(s){for(const E of[void 0,"0.0.0.0","127.0.0.1","::1"])if(await tryBind(s,E)==="in-use")return!1;return!0}M(probePortFree,"probePortFree"),l(probePortFree,"probePortFree");function probeConnect(s,E,u){return new Promise(y=>{let p=!1;const f=net.createConnection({host:s,port:E});f.unref();const C=setTimeout(()=>S(!1),u);C.unref?.();function S(T){p||(p=!0,clearTimeout(C),f.destroy(),y(T))}M(S,"S"),l(S,"finish"),f.once("connect",()=>S(!0)),f.once("error",()=>S(!1))})}M(probeConnect,"probeConnect"),l(probeConnect,"probeConnect");async function probePortListening(s,E=1e3){return await probeConnect("127.0.0.1",s,E)?!0:probeConnect("::1",s,E)}M(probePortListening,"probePortListening"),l(probePortListening,"probePortListening");async function probeWatcherHealth(s,E=1500){try{const u=new AbortController,y=setTimeout(()=>u.abort(),E);y.unref?.();try{const p=await fetch(`http://127.0.0.1:${s}/__lensmcp/watcher-health`,{signal:u.signal});if(!p.ok)return null;const f=await p.json();if(typeof f?.lastFsSetEventAt!="number")return null;const C=Array.isArray(f.recentEvents)?f.recentEvents.filter(S=>S&&typeof S.path=="string"&&typeof S.at=="number"&&typeof S.sequence=="number"&&["add","addDir","unlink","unlinkDir"].includes(S.kind)):[];return{schemaVersion:f.schemaVersion===2?2:1,ready:f.ready!==!1,sequence:typeof f.sequence=="number"?f.sequence:0,lastFsSetEventAt:f.lastFsSetEventAt,fsSetEvents:typeof f.fsSetEvents=="number"?f.fsSetEvents:0,recentEvents:C}}finally{clearTimeout(y)}}catch{return null}}M(probeWatcherHealth,"probeWatcherHealth"),l(probeWatcherHealth,"probeWatcherHealth");const sleepUnref=l(s=>new Promise(E=>{setTimeout(E,s).unref?.()}),"sleepUnref");async function waitForPortFree(s,E,u={}){const y=u.isPortFree??probePortFree,p=Math.max(1,u.intervalMs??types_1.PORT_FREE_POLL_MS),f=Date.now()+Math.max(0,E);for(;;){if(await y(s))return!0;const C=f-Date.now();if(C<=0)return!1;await sleepUnref(Math.min(p,C))}}M(waitForPortFree,"waitForPortFree"),l(waitForPortFree,"waitForPortFree");function viteArgPort(s){const E=l(u=>Number.isInteger(u)&&u>0&&u<65536?u:void 0,"valid");for(let u=0;u<s.length;u+=1){const y=s[u];if(y==="--port")return E(Number(s[u+1]));if(y.startsWith("--port="))return E(Number(y.slice(7)))}}M(viteArgPort,"viteArgPort"),l(viteArgPort,"viteArgPort");function createLensChildren(s,E,u,y){const{emit:p}=E,f=y?.isPortFree??probePortFree,C=y?.probeWatcherHealth??probeWatcherHealth,S=[],T=new Map,N=new Map,F=new Map,B=new Map,K=new Set;let O;const A=new Map,$=new Set,G=new Set,b=new Set,R=new Map,D=new WeakMap,x=new Set,z=l((e,r,n)=>{if(x.has(e))return;x.add(e);const t=setInterval(()=>{(async()=>{if(s.stopped()||$.has(e)){clearInterval(t),x.delete(e);return}await f(r)&&(clearInterval(t),x.delete(e),console.log(`[gateway] lens child ${e}: :${r} freed \u2014 respawning.`),n())})().catch(()=>{})},types_1.LENS_PARKED_PORT_REPROBE_MS);t.unref?.()},"parkUntilPortFree"),j=l(async(e,r)=>{let n=e;for(;r.has(n)||!await f(n);)n+=1;return r.add(n),n!==e&&console.warn(`[gateway] port ${e} is unavailable (another process holds it \u2014 an orphan from a crashed gateway?) \u2014 using ${n} instead.`),n},"claimFreePort"),ae=l((e,r,n)=>{const t=n?.LENSMCP_FRONTEND_KEY,o=Number(n?.LENSMCP_FRONTEND_GENERATION);if(!t||!Number.isFinite(o))return;const a=[...R.get(t)?.generations??[]].find(i=>i.generation===o);a&&(e.endsWith(":vite")?a.vite=r:e.endsWith(":browser-capture")&&(a.capture=r))},"attachRespawnedGenerationChild"),k=l((e,r,n,t,o=!1,a,i,c)=>{const g=Date.now(),d=(0,node_child_process_1.spawn)(r,n,{cwd:i??s.root,env:{...(0,pod_env_1.podBaseEnv)(process.env,s.root,i??s.root),...t??{},LENSMCP_PARENT_PID:String(process.pid)},stdio:"inherit"});S.push(d),A.set(e,d),ae(e,d,t);const h=e==="vite"||e.endsWith(":vite"),L=typeof t?.LENSMCP_EVENT_FILE=="string"?t.LENSMCP_EVENT_FILE:void 0,w=L&&L!==s.eventFile?L:void 0,_=h?viteArgPort(n):c;h&&N.set(d,{key:t?.LENSMCP_FRONTEND_KEY??`${i??s.root}:${t?.LENSMCP_PROJECT??"app"}`,project:t?.LENSMCP_PROJECT??"app",workspaceRoot:i??s.root,spawnedAt:g,port:_,probeFails:0,listening:!1,generation:Number(t?.LENSMCP_FRONTEND_GENERATION??0),role:t?.LENSMCP_FRONTEND_ROLE==="replacement"?"replacement":"active",...w?{bus:w}:{}});const v=e==="lens-mcp"?"lens-mcp":e==="lens-dashboard"||e.startsWith("lens-dashboard:")?"lens-dashboard":void 0;return v&&_!==void 0&&(p("info",`lens child starting: ${v} (:${_})`,`cluster-lens-child:${v}`,{kind:"lens-child-starting",project:v,label:e,pid:d.pid,port:_},void 0,w),(async()=>{const m=Date.now()+types_1.LENS_REPLACEMENT_READY_MS;for(;!s.stopped()&&Date.now()<m;){if(d.exitCode!==null||d.signalCode!==null||$.has(e))return;if(await probePortListening(_,Math.min(500,types_1.LENS_PROBE_TIMEOUT_MS))){p("info",`lens child up: ${v} (:${_})`,`cluster-lens-child:${v}`,{kind:"lens-child-up",project:v,label:e,pid:d.pid,port:_},void 0,w);return}await sleepUnref(150)}})().catch(()=>{})),d.on("exit",(m,V)=>{const te=S.indexOf(d);te>=0&&S.splice(te,1),A.get(e)===d&&A.delete(e);const oe=N.get(d);N.delete(d);const he=$.delete(e);if(s.stopped()||he||(v&&p("warning",`lens child exited: ${v}`,`cluster-lens-child:${v}`,{kind:"lens-child-exit",project:v,label:e,pid:d.pid,code:m,signal:V},void 0,w),oe&&le(d,oe)))return;const ne=Date.now()-g,P=T.get(e)??{attempts:0,portConflicts:0};(async()=>{const we=_!==void 0&&!await f(_),re=(0,lifecycle_1.isPortConflictExit)({code:m,signal:V,ranMs:ne,portHeld:we},{fastFailMs:types_1.CHILD_START_FAIL_MS});if(re&&P.portConflicts<types_1.MAX_PORT_CONFLICT_RETRIES)P.portConflicts+=1,T.set(e,P),console.warn(`[gateway] lens child ${e} lost the :${_} bind race (EADDRINUSE, code=${m}); waiting for the port, then retrying (port-retry ${P.portConflicts}/${types_1.MAX_PORT_CONFLICT_RETRIES}; crash budget untouched).`);else if(re){P.portConflicts=0,T.set(e,P),console.warn(`[gateway] lens child ${e}: :${_} 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.`),p("warning",`lens child parked: ${e} (:${_} held externally)`,`cluster-lens-child:${e}`,{kind:"lens-child-parked",label:e,port:_}),z(e,_,()=>k(e,r,n,t,o,a,i,c));return}else{P.portConflicts=0,ne>=HEAL_HEALTHY_MS&&(P.attempts=0);const se=Math.min(HEAL_MAX_MS,HEAL_BASE_MS*2**P.attempts);P.attempts+=1,T.set(e,P),console.warn(`[gateway] lens child ${e} exited (code=${m} sig=${V}); auto-healing in ${Math.round(se/1e3)}s (attempt ${P.attempts}).`),await sleepUnref(se)}s.stopped()||$.has(e)||(_!==void 0&&!await waitForPortFree(_,types_1.PORT_FREE_WAIT_MS,{isPortFree:f,intervalMs:types_1.PORT_FREE_POLL_MS})&&console.warn(`[gateway] lens child ${e}: :${_} still held after ${Math.round(types_1.PORT_FREE_WAIT_MS/1e3)}s \u2014 spawning anyway.`),!(s.stopped()||$.has(e))&&k(e,r,n,t,o,a,i,c))})().catch(()=>{})}),d},"spawnManagedChild"),J=l(e=>{const r=`${e.wsKey}:${e.project}`,n=R.get(r);if(n)return n;const t={key:r,...e,sourceDirs:e.frontend.sourceRoots?.length?e.frontend.sourceRoots:[e.projectRoot],generation:0,state:"starting",generations:new Set,disposed:!1};R.set(r,t);for(const o of e.routes)o.lens&&(o.lens.workspaceKey??=e.wsKey,D.set(o.lens,t));return e.frontend.workspaceKey??=e.wsKey,D.set(e.frontend,t),t},"registerFrontend"),X=l((e,r,n)=>{const t=++e.generation,o=`${e.key}:g${t}:`,a=`${o}vite`,i=`${o}browser-capture`;$.delete(a),$.delete(i);const c=l((h,L,w,_,v,m)=>k(h,L,w,_,v,m,e.workspaceRoot),"spawnInRoot"),g=(0,lens_frontend_1.spawnLensFrontend)({projectRoot:e.projectRoot,workspaceRoot:e.workspaceRoot,project:e.project,eventFile:e.eventFile,port:r,chrome:!0,headless:!0,viteEnv:{LENSMCP_PROJECT:e.project,LENSMCP_FRONTEND_KEY:e.key,LENSMCP_FRONTEND_GENERATION:String(t),LENSMCP_FRONTEND_ROLE:n},labelPrefix:o,log:l(h=>console.log(h),"log")},c),d={ownerKey:e.key,generation:t,port:r,labelPrefix:o,vite:g?.vite??A.get(a),capture:A.get(i)};return e.generations.add(d),d},"spawnFrontendGeneration"),I=l((e,r)=>{if(!e)return;R.get(e.ownerKey)?.generations.delete(e);const n=[[`${e.labelPrefix}browser-capture`,e.capture],[`${e.labelPrefix}vite`,e.vite]];for(const[t,o]of n)if($.add(t),!(!o||o.exitCode!==null||o.signalCode!==null)){try{o.kill("SIGTERM")}catch{}setTimeout(()=>{if(o.exitCode===null&&o.signalCode===null)try{o.kill("SIGKILL")}catch{}},types_1.LENS_RECYCLE_KILL_GRACE_MS).unref?.()}r&&b.delete(e.port)},"terminateGeneration"),ie=l(async e=>{const r=Date.now()+types_1.LENS_REPLACEMENT_READY_MS;for(;!s.stopped()&&Date.now()<r;){const n=e.vite;if(!n||n.exitCode!==null||n.signalCode!==null)return!1;if(await probePortListening(e.port,Math.min(500,types_1.LENS_PROBE_TIMEOUT_MS))){const t=await C(e.port);if(!t||t.ready)return!0}await sleepUnref(150)}return!1},"waitForGenerationReady"),W=l(async e=>{const r=e?.vite;return!!(e&&r&&r.exitCode===null&&r.signalCode===null&&await probePortListening(e.port,Math.min(500,types_1.LENS_PROBE_TIMEOUT_MS)))},"generationIsServing"),H=l(e=>!e.disposed&&!s.stopped()&&R.get(e.key)===e,"frontendIsCurrent"),Y=l((e,r)=>{if(!H(e))return Promise.resolve(!1);if(e.replacement)return e.replacement;const n=e.active;e.state=n?"replacing":"starting",e.reason=r,e.startedAt=Date.now();const t=(async()=>{let o,a;const i=Date.now();try{const c=await j(e.desiredPort,b);if(a=c,!H(e))return b.delete(c),!1;o=X(e,c,"replacement"),e.candidate=o;const g=await W(n);console.log(`[gateway] lens handoff start: ${e.key} g${n?.generation??0}\u2192g${o.generation} :${n?.port??"down"}\u2192:${c} reason=${r}`),p("info",`lens handoff starting: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-start",wsKey:e.wsKey,project:e.project,reason:r,generation:o.generation,oldPort:n?.port,newPort:c,oldServing:g},void 0,e.eventFile===s.eventFile?void 0:e.eventFile);const d=await ie(o);if(!H(e))return I(o,!0),e.candidate=void 0,!1;if(!d){I(o,!0),e.candidate=void 0;const w=await W(n);return e.state=w?"ready":"failed",w&&(e.reason=void 0,e.startedAt=void 0),console.error(`[gateway] lens handoff failed: ${e.key} g${o.generation} did not become ready within ${types_1.LENS_REPLACEMENT_READY_MS}ms; ${e.state==="ready"?"old generation kept serving":"no active generation"}.`),p("warning",`lens handoff failed: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-failed",wsKey:e.wsKey,project:e.project,reason:r,generation:o.generation,port:c,durationMs:Date.now()-i,oldKept:w},void 0,e.eventFile===s.eventFile?void 0:e.eventFile),!1}for(const w of e.routes)w.target=`http://localhost:${o.port}`,w.lens&&(w.lens.port=o.port);e.frontend.port=o.port,e.active=o,e.candidate=void 0,e.state="ready",e.reason=void 0,e.startedAt=void 0;const h=o.vite?N.get(o.vite):void 0;h&&(h.role="active",h.listening=!0);const L=(0,hmr_reload_1.broadcastFullReload)(s,e.key);if(console.log(`[gateway] lens handoff ready: ${e.key} g${o.generation} :${o.port} in ${Date.now()-i}ms; draining g${n?.generation??0}${L>0?`; reloading ${L} tab(s)`:""}.`),p("info",`lens handoff complete: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-complete",wsKey:e.wsKey,project:e.project,reason:r,generation:o.generation,oldPort:n?.port,newPort:o.port,durationMs:Date.now()-i,reloadedTabs:L},void 0,e.eventFile===s.eventFile?void 0:e.eventFile),n){const w=`${n.labelPrefix}browser-capture`;$.add(w);try{n.capture?.kill("SIGTERM")}catch{}setTimeout(()=>I(n,!0),types_1.LENS_REPLACEMENT_DRAIN_MS).unref?.()}return!0}catch(c){I(o,!0),!o&&a!==void 0&&b.delete(a),e.candidate=void 0;const g=await W(n);e.state=g?"ready":"failed",g&&(e.reason=void 0,e.startedAt=void 0);const d=c.message;return console.error(`[gateway] lens handoff failed: ${e.key}: ${d}`),p("warning",`lens handoff failed: ${e.project}`,`cluster-lens-app:${e.project}`,{kind:"lens-handoff-failed",wsKey:e.wsKey,project:e.project,reason:r,generation:o?.generation??e.generation,port:o?.port??a,durationMs:Date.now()-i,oldKept:g,error:d},void 0,e.eventFile===s.eventFile?void 0:e.eventFile),!1}finally{e.replacement=void 0}})();return e.replacement=t,t},"rollFrontend"),Q=l((e,r)=>{e.active=X(e,r,"active"),e.state="starting",e.startedAt=Date.now()},"spawnInitialFrontend"),le=l((e,r)=>{const n=R.get(r.key);return!n||types_1.LENS_RESTART_MODE==="in-place"?!1:(n.candidate?.vite===e||n.active?.vite!==e||(n.state="starting",n.reason="child-exit",n.startedAt=Date.now(),console.warn(`[gateway] lens frontend exited: ${n.key} g${r.generation} :${r.port}; starting an alternate-port replacement.`),Y(n,"child-exit")),!0)},"handleManagedViteExit");s.lensSupervisor=types_1.LENS_RESTART_MODE==="rolling"?{status(e){const r=D.get(e);return r?{state:r.state,generation:r.candidate?.generation??r.active?.generation??r.generation,reason:r.reason,startedAt:r.startedAt}:void 0},recover(e,r){const n=D.get(e);return n?Y(n,r):Promise.resolve(!1)}}:void 0;let q=!1,Z=0;const ce=l(()=>{q||Date.now()-Z<types_1.POD_STALE_SCAN_MS||(q=!0,Z=Date.now(),(async()=>{for(const e of[...R.values()]){if(s.stopped())return;const r=await(0,lifecycle_1.sourceSetSnapshot)(e.workspaceRoot,e.sourceDirs,(0,scope_1.readSourceSetExclude)(e.workspaceRoot));if(R.get(e.key)!==e)continue;const n=e.sourceSnapshot;if(e.sourceSnapshot=r,!n)continue;const t=(0,lifecycle_1.diffSourceSets)(n,r,Date.now());t&&(e.sourceChange=t,p("info",`source file set changed for ${e.key}`,`cluster-lens-app:${e.project}`,{kind:"source-set-changed",scoped:!0,wsKey:e.wsKey,project:e.project,added:t.added,removed:t.removed,truncated:t.truncated},void 0,e.eventFile===s.eventFile?void 0:e.eventFile))}})().catch(()=>{}).finally(()=>{q=!1}))},"scanFrontendSourceSets");let U=!1;const de=l(async(e,r)=>{if(!U){U=!0;try{for(const[n,t]of[...N]){if(s.stopped())return;if(t.port===void 0||n.exitCode!==null||n.signalCode!==null)continue;if(await probePortListening(t.port,types_1.LENS_PROBE_TIMEOUT_MS)){if(t.probeFails=0,!t.listening){t.listening=!0;const c=R.get(t.key);c?.active?.vite===n&&(c.state="ready",c.reason=void 0),p("info",`lens app up: ${t.project} (vite :${t.port} listening)`,`cluster-lens-app:${t.project}`,{kind:"service-up",project:t.project,port:t.port},void 0,t.bus)}continue}const o=t.listening?e.graceMs:Math.max(e.graceMs,e.neverListenedGraceMs);if(Date.now()-t.spawnedAt<o){t.probeFails=0;continue}if(!r)continue;t.probeFails+=1;const a=(0,lifecycle_1.lensChildWedged)({spawnedAt:t.spawnedAt,probeFails:t.probeFails,hasListened:t.listening},F.get(t.key)??0,Date.now(),e);if(!a)continue;const i=t.listening?"its listener VANISHED (was up, now refusing)":`it NEVER bound within the ${Math.round(o/1e3)}s boot allowance`;F.set(t.key,Date.now()),t.probeFails=0,t.listening=!1,console.error(`[gateway] wedge recycle: lens app ${t.project} is ALIVE but NOT LISTENING on :${t.port} after ${Math.max(2,e.failureThreshold)} probes \u2014 ${i} \u2014 SIGKILL; a fresh vite is coming.`),p("warning",`wedge recycle: ${t.project} (${a})`,`cluster-lens-app:${t.project}`,{kind:"pod-recycle",project:t.project,reason:a,port:t.port},void 0,t.bus);try{n.kill("SIGKILL")}catch{}}}finally{U=!1}}},"probeLensChildren"),pe=l(()=>{if(O||!types_1.POD_RECYCLE_ENABLED&&!types_1.LENS_PROBE_ENABLED)return;const e={graceMs:types_1.POD_STALE_GRACE_MS,settleMs:types_1.POD_RECYCLE_SETTLE_MS,cooldownMs:types_1.POD_STALE_RECYCLE_COOLDOWN_MS};O=setInterval(()=>{if(s.stopped()||N.size===0||(ee(),!types_1.POD_RECYCLE_ENABLED))return;ce();const r=Date.now();for(const[n,t]of N){const o=R.get(t.key),a=o?.sourceChange?.changedAt??0,i=F.get(t.key)??0;(0,lifecycle_1.lensFrontendStale)(t.spawnedAt,i,a,r,e)&&((B.get(t.key)??0)>=a||K.has(t.key)||(K.add(t.key),(async()=>{try{const c=o?.sourceChange,g=c&&!c.truncated?[...c.added,...c.removed].map(w=>path.resolve(t.workspaceRoot,w)):[],d=types_1.LENS_WATCHER_PROBE_ENABLED&&t.port!==void 0?(0,lifecycle_1.lensWatcherVerdict)(await C(t.port),a,Math.min(types_1.POD_STALE_SCAN_MS,15e3)+types_1.POD_RECYCLE_SETTLE_MS,g):"unknown";if(d==="heard"){B.set(t.key,a),console.log(`[gateway] stale source set: lens app ${t.project} watcher CONFIRMED the change \u2014 no recycle, HMR intact.`),p("info",`stale check: ${t.project} watcher confirmed \u2014 no recycle`,`cluster-lens-app:${t.project}`,{kind:"pod-stale-verified",project:t.project},void 0,t.bus);return}if(n.exitCode!==null||n.signalCode!==null)return;F.set(t.key,Date.now());const h=R.get(t.key);if(types_1.LENS_RESTART_MODE==="rolling"&&h){console.log(`[gateway] stale refresh: lens app ${t.key} (stale-source-set) \u2014 watcher ${d==="missed"?"missed the exact path change":"unverifiable"}; preparing a standby without dropping traffic.`),p("warning",`stale refresh: ${t.project} (stale-source-set)`,`cluster-lens-app:${t.project}`,{kind:"pod-refresh",wsKey:h.wsKey,project:t.project,reason:"stale-source-set",watcher:d,added:c?.added,removed:c?.removed},void 0,t.bus),await Y(h,"stale-source-set");return}t.listening=!1,console.log(`[gateway] zombie recycle: lens app ${t.project} (stale-source-set) \u2014 watcher ${d==="missed"?"SILENT across the change":"unverifiable"}; restarting it`),p("warning",`zombie recycle: ${t.project} (stale-source-set)`,`cluster-lens-app:${t.project}`,{kind:"pod-recycle",project:t.project,reason:"stale-source-set",watcher:d},void 0,t.bus);const L=(0,hmr_reload_1.broadcastFullReload)(s,t.key);L>0&&console.log(`[gateway] \u2192 told ${L} open tab(s) to reload (stale optimize-dep generation)`);try{n.kill("SIGTERM")}catch{}setTimeout(()=>{if(n.exitCode===null&&n.signalCode===null){console.log(`[gateway] zombie recycle: lens app ${t.project} ignored SIGTERM for ${Math.round(types_1.LENS_RECYCLE_KILL_GRACE_MS/1e3)}s \u2014 escalating to SIGKILL.`);try{n.kill("SIGKILL")}catch{}}},types_1.LENS_RECYCLE_KILL_GRACE_MS).unref?.()}finally{K.delete(t.key)}})()))}},Math.min(types_1.POD_STALE_SCAN_MS,15e3)),O.unref?.()},"startLensSweeper"),ue=l(async()=>{const e=[...new Map(s.routes.filter(t=>t.lens).map(t=>[t.project,t.lens])).values()],r=u.dashboard!==!1,n=u.mcp!==!1&&e.length>0;if(r){const t=(0,lens_frontend_1.findDashboardBundle)(s.root);if(!t)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 o=(0,scope_1.readLensScope)(s.root),a=await j(u.dashboardPort??o.dashboardPort,G),i=u.lensHost??"lensmcp.local",c=o.basePath;s.registry.appendRoutes(s.wsKey,[{host:i,prefix:c,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${a}`}]),s.rebuildRoutes(),(0,workspace_registry_1.registerWorkspace)({key:o.key,root:s.root,base:c,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] starting lens dashboard \u2192 http://localhost:${a} (routed at https://${i}${c}/)`),p("info",`lens dashboard up: ${i}${c} \u2192 :${a}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:i,port:a,base:c}),k("lens-dashboard",process.execPath,[t],{LENSMCP_EVENT_FILE:s.eventFile,LENSMCP_PROCESS_ROLE:"dashboard",LENSMCP_DASHBOARD_PORT:String(a),LENSMCP_DASHBOARD_BASE:c},!1,void 0,void 0,a)}}if(n){const t=(0,lens_frontend_1.findMcpBundle)(s.root);if(!t)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 o=(0,scope_1.readLensScope)(s.root),a=Number(process.env.LENSMCP_PORT??o.mcpHttpPort),i=l(()=>{console.log(`[gateway] starting lens MCP server on 127.0.0.1:${a} (${t})`),k("lens-mcp",process.execPath,[t],{LENSMCP_EVENT_FILE:s.eventFile,LENSMCP_PROCESS_ROLE:"mcp",LENSMCP_TRANSPORT:process.env.LENSMCP_TRANSPORT??"http",LENSMCP_PORT:String(a),LENSMCP_WORKSPACE_KEY:o.key,LENSMCP_WORKSPACE_ROOT:s.root},!1,void 0,void 0,a)},"spawnMcp");await f(a)?i():(console.warn(`[gateway] lens MCP port :${a} 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.`),p("info",`lens MCP served externally on :${a} \u2014 gateway parked its own`,"cluster-lens-child:lens-mcp",{kind:"lens-child-up",project:"lens-mcp",label:"lens-mcp",port:a,external:!0}),z("lens-mcp",a,i))}}for(const t of e){if(t.started)continue;t.started=!0;const o=t.port,a=(s.registry.get(s.wsKey)?.routes??s.routes).filter(g=>g.lens?.project===t.project),i=await j(t.port,b);for(const g of a)g.lens&&(g.lens.port=i),g.target=`http://localhost:${i}`;t.port=i;const c=J({wsKey:s.wsKey,project:t.project,workspaceRoot:s.root,projectRoot:t.projectRoot,eventFile:s.eventFile,routes:a,frontend:t,desiredPort:o});try{Q(c,i),p("info",`lens app started: ${t.project} (vite :${t.port})`,`cluster-lens-app:${t.project}`,{kind:"lens-app-up",project:t.project,port:t.port})}catch(g){b.delete(i),c.state="failed",console.error(`[gateway] lens app ${t.project}: ${g.message}`)}}pe()},"bootSingletonsAndApps"),fe=l(async e=>{const r=(0,lens_frontend_1.findDashboardBundle)(s.root);if(!r)return;const n=(0,scope_1.readLensScope)(e.root),t=u.lensHost??"lensmcp.local",o=n.basePath,a=await j(n.dashboardPort,G),i=`lens-dashboard:${e.wsKey}`,c=path.join(e.root,".lensmcp","events.jsonl");return $.delete(i),s.registry.appendRoutes(e.wsKey,[{host:t,prefix:o,project:observability_1.LENS_DASHBOARD_PROJECT,target:`http://localhost:${a}`}]),(0,workspace_registry_1.registerWorkspace)({key:e.wsKey,root:e.root,base:o,pid:process.pid,procStamp:(0,workspace_registry_1.processStamp)(process.pid),updatedAt:new Date().toISOString()}),console.log(`[gateway] hosting dashboard for workspace '${e.wsKey}' \u2192 http://localhost:${a} (routed at https://${t}${o}/)`),p("info",`lens dashboard up: ${t}${o} \u2192 :${a}`,"cluster-lens-dashboard",{kind:"lens-dashboard-up",host:t,port:a,base:o}),k(i,process.execPath,[r],{LENSMCP_EVENT_FILE:c,LENSMCP_PROCESS_ROLE:"dashboard",LENSMCP_DASHBOARD_PORT:String(a),LENSMCP_DASHBOARD_BASE:o,LENSMCP_WS_KEY:e.wsKey},!1,void 0,e.root,a),()=>{$.add(i),G.delete(a),(0,workspace_registry_1.unregisterWorkspace)(e.wsKey);try{A.get(i)?.kill("SIGTERM")}catch{}A.delete(i)}},"hostWorkspaceDashboard"),Ee=l(async e=>{const r=new Map;for(const o of e.routes){if(!o.lens)continue;const a=r.get(o.lens.project)??[];a.push(o),r.set(o.lens.project,a)}if(r.size===0)return()=>{};const n=path.join(e.root,".lensmcp","events.jsonl"),t=[];for(const[o,a]of r){const i=a[0].lens;if(i.started)continue;const c=await j(i.port,b),g=i.port;for(const h of a)h.lens.started=!0,h.lens.port=c,h.target=`http://localhost:${c}`;const d=J({wsKey:e.wsKey,project:o,workspaceRoot:e.root,projectRoot:i.projectRoot,eventFile:n,routes:a,frontend:i,desiredPort:g});t.push(d),console.log(`[gateway] hosting lens app '${o}' of '${e.wsKey}' \u2192 vite :${c}`);try{Q(d,c),p("info",`lens app started: ${e.wsKey}/${o} (vite :${c})`,`cluster-lens-app:${e.wsKey}:${o}`,{kind:"lens-app-up",project:o,port:c},void 0,n)}catch(h){b.delete(c),d.state="failed",console.error(`[gateway] lens app ${e.wsKey}/${o}: ${h.message}`)}}return()=>{for(const o of t){o.disposed=!0;for(const a of[...o.generations])I(a,!0);for(const a of o.routes)a.lens&&D.delete(a.lens);D.delete(o.frontend),F.delete(o.key),B.delete(o.key),K.delete(o.key),R.delete(o.key)}}},"hostWorkspaceApps"),ge=l(async(e=2500)=>{O&&(clearInterval(O),O=void 0);const r=[...S];for(const o of r)try{o.kill("SIGTERM")}catch{}const n=l(o=>o.exitCode===null&&o.signalCode===null,"alive"),t=Date.now()+e;for(;Date.now()<t&&r.some(n);)await new Promise(o=>setTimeout(o,100));for(const o of r)if(n(o))try{o.kill("SIGKILL")}catch{}},"reapAll"),ee=l(()=>de({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:k,bootSingletonsAndApps:ue,hostWorkspaceDashboard:fe,hostWorkspaceApps:Ee,probeLensFrontends:ee,reapAll:ge}}M(createLensChildren,"createLensChildren"),l(createLensChildren,"createLensChildren");