@lensmcp/cluster 1.25.21 → 1.25.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,2 +1,2 @@
1
- "use strict";var b=Object.defineProperty;var g=(s,r)=>b(s,"name",{value:r,configurable:!0});var G=Object.defineProperty,u=g((s,r)=>G(s,"name",{value:r,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.clusterVersion=void 0,exports.instanceSummary=instanceSummary,exports.controlSocketPath=controlSocketPath,exports.versionAtLoad=versionAtLoad,exports.readClusterVersion=readClusterVersion,exports.ensureOwnerOnlyDir=ensureOwnerOnlyDir,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"),guest_graph_1=require("./guest-graph"),guest_replay_1=require("./guest-replay"),registration_guards_1=require("./registration-guards"),scope_1=require("./scope"),service_keys_1=require("./service-keys"),workspace_log_1=require("./workspace-log"),workspace_middleware_1=require("./workspace-middleware");function instanceSummary(s){const r=s?.instance;return r?{instance:r.name,baseKey:r.baseKey,apex:r.apex,baseApex:r.baseApex,...r.frozen?{frozen:!0}:{}}:{}}g(instanceSummary,"instanceSummary"),u(instanceSummary,"instanceSummary");function controlSocketPath(){const s=process.env.LENSMCP_HOME||os.homedir();return path.join(s,".lensmcp","control.sock")}g(controlSocketPath,"controlSocketPath"),u(controlSocketPath,"controlSocketPath");const CONTROL_BODY_BYTES=1e6;class BodyTooLarge extends Error{static{g(this,"BodyTooLarge")}static{u(this,"BodyTooLarge")}constructor(r){super(`body too large (over ${r} bytes)`),this.limit=r}}exports.clusterVersion=versionAtLoad(()=>readClusterVersion(__dirname));function versionAtLoad(s){const r=s();return()=>r}g(versionAtLoad,"versionAtLoad"),u(versionAtLoad,"versionAtLoad");function readClusterVersion(s){let r=s;for(let w=0;w<6;w++){const h=path.join(r,"package.json");if(fs.existsSync(h))try{const l=JSON.parse(fs.readFileSync(h,"utf8"));if(l.name==="@lensmcp/cluster"&&typeof l.version=="string")return l.version}catch{}const a=path.dirname(r);if(a===r)break;r=a}}g(readClusterVersion,"readClusterVersion"),u(readClusterVersion,"readClusterVersion");function readJsonBody(s,r=CONTROL_BODY_BYTES){return new Promise((w,h)=>{const a=[];let l=0,k=!1;s.on("data",m=>{if(!k){if(l+=m.length,l>r){k=!0,a.length=0,h(new BodyTooLarge(r));return}a.push(m)}}),s.on("end",()=>{if(k)return;const m=Buffer.concat(a).toString("utf8");try{w({value:m?JSON.parse(m):{},bytes:l})}catch(S){h(S)}}),s.on("error",h)})}g(readJsonBody,"readJsonBody"),u(readJsonBody,"readJsonBody");function graphShare(s,r){return r-Buffer.byteLength(JSON.stringify({...s,projectGraph:null}))}g(graphShare,"graphShare"),u(graphShare,"graphShare");function graphVerdict(s){return s?s.refused===void 0?{forwarded:!0,accepted:!0}:{forwarded:!0,accepted:!1,reason:s.refused}:{forwarded:!1,accepted:!1}}g(graphVerdict,"graphVerdict"),u(graphVerdict,"graphVerdict");function ensureOwnerOnlyDir(s){try{fs.mkdirSync(s,{recursive:!0,mode:448})}catch{}try{const r=fs.lstatSync(s);return r.isDirectory()?((r.mode&63)!==0&&fs.chmodSync(s,448),!0):!1}catch{return!1}}g(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),u(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function startControlServer(s){const{rt:r}=s,w=controlSocketPath(),h=path.dirname(w);ensureOwnerOnlyDir(h)||console.warn(`[gateway] control home ${h} is not a usable owner-only directory \u2014 refusing to expose the control plane there.`);try{fs.unlinkSync(w)}catch{}const a=u((e,n,i)=>{e.writeHead(n,{"content-type":"application/json"}),e.end(JSON.stringify(i))},"send"),l=u(async(e,n)=>{if(!e.wsKey||!e.root)return{status:400,json:{error:"wsKey and root are required"}};const i=e.projects??{},t=(0,discovery_1.projectSourceRoots)(e.root,i,e.projectDependencies),o=(0,scope_1.readLensScope)(e.root),c=o.key===e.wsKey,y=c&&o.sockNs!==e.wsKey?o.sockNs:void 0,{routes:d,services:p,instance:f}=(0,discovery_1.discoverRoutes)(e.root,i,void 0,e.wsKey,t,y,c?o.instance:void 0),O=(0,workspace_middleware_1.findGatewayMiddleware)(e.root,i),$={wsKey:e.wsKey,...y?{sockNs:y}:{},root:e.root,routes:d,services:p,...f?{instance:f}:{},...O?{middleware:O}:{}},j=(0,registration_guards_1.refuseRegistration)(r.registry,$);if(j)return console.warn(`[gateway] refused to register '${e.wsKey}' from ${e.root}: ${j.error}`),{status:409,json:j};try{const v=(0,service_keys_1.adoptServiceKeys)(r,e.root,p,e.wsKey);v.added.length>0&&console.log(`[gateway] adopted east-west keys for ${e.wsKey}: ${v.added.join(", ")}`),v.regenerated.length>0&&console.warn(`[gateway] ${e.root} carried another workspace's east-west keys (a copied key file) \u2014 new keys for ${e.wsKey}: ${v.regenerated.join(", ")}`)}catch(v){console.error(`[gateway] could not adopt east-west keys for ${e.wsKey}: ${v.message}`)}try{(0,workspace_log_1.workspaceLogWriter)(e.root)?.(`[gateway] ${new Date().toISOString()} this workspace ('${e.wsKey}') is served by the daemon at ${r.root} (pid ${process.pid}); its gateway-level log is ${path.join(r.root,".lensmcp","gateway.log")}. Pod output for this workspace follows here. Attach live with \`lensmcp logs <service>\`.
2
- `)}catch{}let K;if(r.graphStores){const v={root:e.root,daemonRoot:r.root,otherRoots:r.registry.keys().filter(_=>_!==e.wsKey).flatMap(_=>r.registry.get(_)?.root??[])};K=e.projectGraph===void 0?void 0:(0,guest_graph_1.admitForwardedGraph)(e.projectGraph,{...v,bytes:graphShare(e,n)}),K?.refused!==void 0&&console.warn(`[gateway] refused the project graph '${e.wsKey}' forwarded from ${e.root}: ${K.refused} \u2014 its services start through \`nx run\` until the gateway computes it`),(0,registration_guards_1.sameRoot)(e.root,r.root)||r.graphStores.set((0,guest_graph_1.createGuestGraphStore)({wsKey:e.wsKey,root:e.root,...K?{admission:K}:{},context:v}))}return r.registry.register($),await s.onRegister?.($),r.rebuildRoutes(),r.root&&!(0,registration_guards_1.sameRoot)(e.root,r.root)&&(0,guest_replay_1.recordGuest)({wsKey:e.wsKey,root:e.root,projects:i,...e.projectDependencies?{projectDependencies:e.projectDependencies}:{},daemonRoot:r.root,registeredAt:new Date().toISOString()}),{status:200,json:{ok:!0,wsKey:e.wsKey,routes:d.length,services:p.length,workspaces:r.registry.keys(),...r.graphStores?{projectGraph:graphVerdict(K)}:{}}}},"register"),k=u((e,n)=>{const i=r.registry.get(e);if(!i)return{error:`no workspace '${e}' is registered`,workspaces:r.registry.keys()};const t=i.services.find(o=>o.project===n);return t?{svc:t}:{error:`'${e}' has no service '${n}'`,services:i.services.map(o=>o.project)}},"findService"),m=u(e=>({wsKey:e.wsKey,service:e.project,state:e.state,...e.generation!==void 0?{generation:e.generation}:{},...e.restart?{restart:e.restart}:{}}),"restartView"),S=http.createServer((e,n)=>{(async()=>{try{const i=(e.url??"").split("?")[0];if(e.method==="GET"&&i==="/list")return a(n,200,{workspaces:r.registry.keys().map(t=>{const o=r.registry.get(t);return{key:t,root:o?.root,routes:o?.routes.length??0,services:o?.services.length??0,...instanceSummary(o)}})});if(e.method==="GET"&&i==="/status"){const t=(0,exports.clusterVersion)();return a(n,200,{daemon:{pid:process.pid,wsKey:r.wsKey,...t?{version:t}:{}},workspaces:r.registry.keys().map(o=>{const c=r.registry.get(o);return{key:o,root:c?.root,routes:c?.routes.length??0,services:c?.services.length??0,isDaemon:o===r.wsKey,...instanceSummary(c)}}),services:r.registry.allServices().map(o=>({project:o.project,wsKey:o.wsKey,host:o.decl.host,state:o.state,...o.generation!==void 0?{generation:o.generation}:{},...o.restart?{restart:o.restart}:{}}))})}if(e.method==="GET"&&i==="/instance-env"){const t=new URL(e.url??"/","http://control").searchParams.get("wsKey")??"",o=r.registry.get(t);return o?o.instance?a(n,200,{wsKey:t,root:o.root,...instanceSummary(o),env:o.instance.env}):a(n,404,{error:`'${t}' is not an instance`}):a(n,404,{error:`no workspace '${t}' is registered`})}if(i==="/restart"&&(e.method==="POST"||e.method==="GET")){const t=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},o=typeof t.wsKey=="string"?t.wsKey:"",c=typeof t.service=="string"?t.service:"";if(!o||!c)return a(n,400,{error:"wsKey and service are required"});const y=k(o,c);if("error"in y)return a(n,404,y);const d=y.svc;if(e.method==="GET")return a(n,200,m(d));if(!s.restartService)return a(n,501,{error:"this gateway cannot restart a service"});const p=typeof t.reason=="string"&&t.reason.trim()?t.reason.trim().slice(0,200):"asked for",f=s.restartService(d,{hard:t.hard===!0,reason:p});return"refused"in f?a(n,409,{error:f.refused,...m(d)}):a(n,202,{ok:!0,already:f.already,...m(d)})}if(e.method==="GET"&&i==="/identities"||e.method==="POST"&&i==="/act"){const t=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},o=typeof t.wsKey=="string"?t.wsKey:"";if(!o)return a(n,400,{error:"wsKey is required"});if(!r.registry.get(o)&&o!==r.wsKey)return a(n,404,{error:`no workspace '${o}' is registered`,workspaces:r.registry.keys()});if(!s.act)return a(n,501,{error:"acting as a signed-in user is turned off on this gateway (LENSMCP_GW_ACT=0)"});if(e.method==="GET")return a(n,200,{wsKey:o,identities:s.act.list(o)});const c=u(f=>typeof t[f]=="string"&&t[f].trim()?t[f].trim().slice(0,256):void 0,"pick"),y=c("user"),d=c("tenant"),p=s.act.mint(o,{...y?{user:y}:{},...d?{tenant:d}:{}});return"error"in p?a(n,404,{wsKey:o,...p}):a(n,200,{wsKey:o,...p})}if(e.method==="POST"&&i==="/register"){const{value:t,bytes:o}=await readJsonBody(e,CONTROL_BODY_BYTES+guest_graph_1.MAX_FORWARDED_GRAPH_BYTES),c=await l(t,o);return a(n,c.status,c.json)}if(e.method==="POST"&&i==="/unregister"){const t=(await readJsonBody(e)).value;if(!t.wsKey)return a(n,400,{error:"wsKey is required"});const o=(0,registration_guards_1.refuseUnregistration)(r.registry,{wsKey:t.wsKey,...t.root?{root:t.root}:{}},r.wsKey);if(o)return console.warn(`[gateway] refused to unregister '${t.wsKey}'${t.root?` for ${t.root}`:""}: ${o.error}`),a(n,409,o);const c=r.registry.unregister(t.wsKey);if(c){for(const y of c.services){const d=(0,service_keys_1.serviceKeyId)(y.wsKey??t.wsKey,y.project),p=r.serviceKeys[d];p&&(delete r.serviceKeys[d],r.keyToProject.delete(p),r.keyToWorkspace?.delete(p))}for(const y of c.services)s.killService(y,"workspace unregistered");r.graphStores?.delete(c.root),(0,guest_replay_1.forgetGuest)(t.wsKey),s.onUnregister?.(c)}return r.rebuildRoutes(),a(n,200,{ok:!0,removed:!!c,workspaces:r.registry.keys()})}a(n,404,{error:"not found"})}catch(i){if(i instanceof BodyTooLarge)return e.resume(),a(n,413,{error:i.message});a(n,500,{error:i.message})}})()});return S.listen(w,()=>{try{fs.chmodSync(w,384)}catch(e){console.warn(`[gateway] could not restrict ${w} to 0600:`,e.message)}}),{socketPath:w,async replay(){const{replay:e,gone:n}=(0,guest_replay_1.guestsToReplay)(r.root,(0,guest_replay_1.readGuests)()),i={replayed:[],refused:[],gone:n.map(t=>t.wsKey)};for(const t of n)(0,guest_replay_1.forgetGuest)(t.wsKey),console.log(`[gateway] not re-hosting '${t.wsKey}': ${t.root} is no longer a workspace \u2014 forgotten`);for(const t of e)if(!r.registry.has(t.wsKey))try{const o=await l({wsKey:t.wsKey,root:t.root,projects:t.projects,...t.projectDependencies?{projectDependencies:t.projectDependencies}:{}},0);o.status===200?(i.replayed.push(t.wsKey),console.log(`[gateway] re-hosted '${t.wsKey}' (${t.root}) \u2014 registered before this daemon restarted`)):(i.refused.push(t.wsKey),console.warn(`[gateway] could not re-host '${t.wsKey}' (${t.root}): ${JSON.stringify(o.json)} \u2014 \`lensmcp gateway start\` there`))}catch(o){i.refused.push(t.wsKey),console.warn(`[gateway] could not re-host '${t.wsKey}' (${t.root}): ${o.message}`)}return i},close:u(()=>{S.close();try{fs.unlinkSync(w)}catch{}},"close")}}g(startControlServer,"startControlServer"),u(startControlServer,"startControlServer");
1
+ "use strict";var T=Object.defineProperty;var g=(o,r)=>T(o,"name",{value:r,configurable:!0});var G=Object.defineProperty,u=g((o,r)=>G(o,"name",{value:r,configurable:!0}),"u");Object.defineProperty(exports,"__esModule",{value:!0}),exports.clusterVersion=void 0,exports.instanceSummary=instanceSummary,exports.controlSocketPath=controlSocketPath,exports.versionAtLoad=versionAtLoad,exports.readClusterVersion=readClusterVersion,exports.ensureOwnerOnlyDir=ensureOwnerOnlyDir,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"),guest_graph_1=require("./guest-graph"),guest_replay_1=require("./guest-replay"),registration_guards_1=require("./registration-guards"),scope_1=require("./scope"),service_keys_1=require("./service-keys"),workspace_log_1=require("./workspace-log"),workspace_middleware_1=require("./workspace-middleware");function instanceSummary(o){const r=o?.instance;return r?{instance:r.name,baseKey:r.baseKey,apex:r.apex,baseApex:r.baseApex,...r.frozen?{frozen:!0}:{}}:{}}g(instanceSummary,"instanceSummary"),u(instanceSummary,"instanceSummary");function controlSocketPath(){const o=process.env.LENSMCP_HOME||os.homedir();return path.join(o,".lensmcp","control.sock")}g(controlSocketPath,"controlSocketPath"),u(controlSocketPath,"controlSocketPath");const CONTROL_BODY_BYTES=1e6;class BodyTooLarge extends Error{static{g(this,"BodyTooLarge")}static{u(this,"BodyTooLarge")}constructor(r){super(`body too large (over ${r} bytes)`),this.limit=r}}exports.clusterVersion=versionAtLoad(()=>readClusterVersion(__dirname));function versionAtLoad(o){const r=o();return()=>r}g(versionAtLoad,"versionAtLoad"),u(versionAtLoad,"versionAtLoad");function readClusterVersion(o){let r=o;for(let p=0;p<6;p++){const h=path.join(r,"package.json");if(fs.existsSync(h))try{const l=JSON.parse(fs.readFileSync(h,"utf8"));if(l.name==="@lensmcp/cluster"&&typeof l.version=="string")return l.version}catch{}const a=path.dirname(r);if(a===r)break;r=a}}g(readClusterVersion,"readClusterVersion"),u(readClusterVersion,"readClusterVersion");function readJsonBody(o,r=CONTROL_BODY_BYTES){return new Promise((p,h)=>{const a=[];let l=0,k=!1;o.on("data",m=>{if(!k){if(l+=m.length,l>r){k=!0,a.length=0,h(new BodyTooLarge(r));return}a.push(m)}}),o.on("end",()=>{if(k)return;const m=Buffer.concat(a).toString("utf8");try{p({value:m?JSON.parse(m):{},bytes:l})}catch(S){h(S)}}),o.on("error",h)})}g(readJsonBody,"readJsonBody"),u(readJsonBody,"readJsonBody");function graphShare(o,r){return r-Buffer.byteLength(JSON.stringify({...o,projectGraph:null}))}g(graphShare,"graphShare"),u(graphShare,"graphShare");function graphVerdict(o){return o?o.refused===void 0?{forwarded:!0,accepted:!0}:{forwarded:!0,accepted:!1,reason:o.refused}:{forwarded:!1,accepted:!1}}g(graphVerdict,"graphVerdict"),u(graphVerdict,"graphVerdict");function ensureOwnerOnlyDir(o){try{fs.mkdirSync(o,{recursive:!0,mode:448})}catch{}try{const r=fs.lstatSync(o);return r.isDirectory()?((r.mode&63)!==0&&fs.chmodSync(o,448),!0):!1}catch{return!1}}g(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),u(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function startControlServer(o){const{rt:r}=o,p=controlSocketPath(),h=path.dirname(p);ensureOwnerOnlyDir(h)||console.warn(`[gateway] control home ${h} is not a usable owner-only directory \u2014 refusing to expose the control plane there.`);try{fs.unlinkSync(p)}catch{}const a=u((e,n,c)=>{e.writeHead(n,{"content-type":"application/json"}),e.end(JSON.stringify(c))},"send"),l=u(async(e,n,c=!1)=>{if(!e.wsKey||!e.root)return{status:400,json:{error:"wsKey and root are required"}};const t=e.projects??{},s=(0,discovery_1.projectSourceRoots)(e.root,t,e.projectDependencies),i=(0,scope_1.readLensScope)(e.root),y=i.key===e.wsKey,d=y&&i.sockNs!==e.wsKey?i.sockNs:void 0,{routes:w,services:f,instance:j}=(0,discovery_1.discoverRoutes)(e.root,t,void 0,e.wsKey,s,d,y?i.instance:void 0),b=(0,workspace_middleware_1.findGatewayMiddleware)(e.root,t),$={wsKey:e.wsKey,...d?{sockNs:d}:{},root:e.root,routes:w,services:f,...j?{instance:j}:{},...b?{middleware:b}:{}},_=(0,registration_guards_1.refuseRegistration)(r.registry,$);if(_)return console.warn(`[gateway] refused to register '${e.wsKey}' from ${e.root}: ${_.error}`),{status:409,json:_};if(c)return j?{status:200,json:{wsKey:e.wsKey,root:e.root,...instanceSummary($),env:j.env}}:{status:400,json:{error:`'${e.wsKey}' has no resolvable instance configuration`}};try{const v=(0,service_keys_1.adoptServiceKeys)(r,e.root,f,e.wsKey);v.added.length>0&&console.log(`[gateway] adopted east-west keys for ${e.wsKey}: ${v.added.join(", ")}`),v.regenerated.length>0&&console.warn(`[gateway] ${e.root} carried another workspace's east-west keys (a copied key file) \u2014 new keys for ${e.wsKey}: ${v.regenerated.join(", ")}`)}catch(v){console.error(`[gateway] could not adopt east-west keys for ${e.wsKey}: ${v.message}`)}try{(0,workspace_log_1.workspaceLogWriter)(e.root)?.(`[gateway] ${new Date().toISOString()} this workspace ('${e.wsKey}') is served by the daemon at ${r.root} (pid ${process.pid}); its gateway-level log is ${path.join(r.root,".lensmcp","gateway.log")}. Pod output for this workspace follows here. Attach live with \`lensmcp logs <service>\`.
2
+ `)}catch{}let K;if(r.graphStores){const v={root:e.root,daemonRoot:r.root,otherRoots:r.registry.keys().filter(O=>O!==e.wsKey).flatMap(O=>r.registry.get(O)?.root??[])};K=e.projectGraph===void 0?void 0:(0,guest_graph_1.admitForwardedGraph)(e.projectGraph,{...v,bytes:graphShare(e,n)}),K?.refused!==void 0&&console.warn(`[gateway] refused the project graph '${e.wsKey}' forwarded from ${e.root}: ${K.refused} \u2014 its services start through \`nx run\` until the gateway computes it`),(0,registration_guards_1.sameRoot)(e.root,r.root)||r.graphStores.set((0,guest_graph_1.createGuestGraphStore)({wsKey:e.wsKey,root:e.root,...K?{admission:K}:{},context:v}))}return r.registry.register($),await o.onRegister?.($),r.rebuildRoutes(),r.root&&!(0,registration_guards_1.sameRoot)(e.root,r.root)&&(0,guest_replay_1.recordGuest)({wsKey:e.wsKey,root:e.root,projects:t,...e.projectDependencies?{projectDependencies:e.projectDependencies}:{},daemonRoot:r.root,registeredAt:new Date().toISOString()}),{status:200,json:{ok:!0,wsKey:e.wsKey,routes:w.length,services:f.length,workspaces:r.registry.keys(),...r.graphStores?{projectGraph:graphVerdict(K)}:{}}}},"register"),k=u((e,n)=>{const c=r.registry.get(e);if(!c)return{error:`no workspace '${e}' is registered`,workspaces:r.registry.keys()};const t=c.services.find(s=>s.project===n);return t?{svc:t}:{error:`'${e}' has no service '${n}'`,services:c.services.map(s=>s.project)}},"findService"),m=u(e=>({wsKey:e.wsKey,service:e.project,state:e.state,...e.generation!==void 0?{generation:e.generation}:{},...e.restart?{restart:e.restart}:{}}),"restartView"),S=http.createServer((e,n)=>{(async()=>{try{const c=(e.url??"").split("?")[0];if(e.method==="GET"&&c==="/list")return a(n,200,{workspaces:r.registry.keys().map(t=>{const s=r.registry.get(t);return{key:t,root:s?.root,routes:s?.routes.length??0,services:s?.services.length??0,...instanceSummary(s)}})});if(e.method==="GET"&&c==="/status"){const t=(0,exports.clusterVersion)();return a(n,200,{daemon:{pid:process.pid,wsKey:r.wsKey,...t?{version:t}:{}},workspaces:r.registry.keys().map(s=>{const i=r.registry.get(s);return{key:s,root:i?.root,routes:i?.routes.length??0,services:i?.services.length??0,isDaemon:s===r.wsKey,...instanceSummary(i)}}),services:r.registry.allServices().map(s=>({project:s.project,wsKey:s.wsKey,host:s.decl.host,state:s.state,...s.generation!==void 0?{generation:s.generation}:{},...s.restart?{restart:s.restart}:{}}))})}if(e.method==="GET"&&c==="/instance-env"){const t=new URL(e.url??"/","http://control").searchParams.get("wsKey")??"",s=r.registry.get(t);return s?s.instance?a(n,200,{wsKey:t,root:s.root,...instanceSummary(s),env:s.instance.env}):a(n,404,{error:`'${t}' is not an instance`}):a(n,404,{error:`no workspace '${t}' is registered`})}if(e.method==="POST"&&c==="/instance-env"){const{value:t,bytes:s}=await readJsonBody(e,CONTROL_BODY_BYTES+guest_graph_1.MAX_FORWARDED_GRAPH_BYTES),i=await l(t,s,!0);return a(n,i.status,i.json)}if(c==="/restart"&&(e.method==="POST"||e.method==="GET")){const t=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},s=typeof t.wsKey=="string"?t.wsKey:"",i=typeof t.service=="string"?t.service:"";if(!s||!i)return a(n,400,{error:"wsKey and service are required"});const y=k(s,i);if("error"in y)return a(n,404,y);const d=y.svc;if(e.method==="GET")return a(n,200,m(d));if(!o.restartService)return a(n,501,{error:"this gateway cannot restart a service"});const w=typeof t.reason=="string"&&t.reason.trim()?t.reason.trim().slice(0,200):"asked for",f=o.restartService(d,{hard:t.hard===!0,reason:w});return"refused"in f?a(n,409,{error:f.refused,...m(d)}):a(n,202,{ok:!0,already:f.already,...m(d)})}if(e.method==="GET"&&c==="/identities"||e.method==="POST"&&c==="/act"){const t=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},s=typeof t.wsKey=="string"?t.wsKey:"";if(!s)return a(n,400,{error:"wsKey is required"});if(!r.registry.get(s)&&s!==r.wsKey)return a(n,404,{error:`no workspace '${s}' is registered`,workspaces:r.registry.keys()});if(!o.act)return a(n,501,{error:"acting as a signed-in user is turned off on this gateway (LENSMCP_GW_ACT=0)"});if(e.method==="GET")return a(n,200,{wsKey:s,identities:o.act.list(s)});const i=u(f=>typeof t[f]=="string"&&t[f].trim()?t[f].trim().slice(0,256):void 0,"pick"),y=i("user"),d=i("tenant"),w=o.act.mint(s,{...y?{user:y}:{},...d?{tenant:d}:{}});return"error"in w?a(n,404,{wsKey:s,...w}):a(n,200,{wsKey:s,...w})}if(e.method==="POST"&&c==="/register"){const{value:t,bytes:s}=await readJsonBody(e,CONTROL_BODY_BYTES+guest_graph_1.MAX_FORWARDED_GRAPH_BYTES),i=await l(t,s);return a(n,i.status,i.json)}if(e.method==="POST"&&c==="/unregister"){const t=(await readJsonBody(e)).value;if(!t.wsKey)return a(n,400,{error:"wsKey is required"});const s=(0,registration_guards_1.refuseUnregistration)(r.registry,{wsKey:t.wsKey,...t.root?{root:t.root}:{}},r.wsKey);if(s)return console.warn(`[gateway] refused to unregister '${t.wsKey}'${t.root?` for ${t.root}`:""}: ${s.error}`),a(n,409,s);const i=r.registry.unregister(t.wsKey);if(i){for(const y of i.services){const d=(0,service_keys_1.serviceKeyId)(y.wsKey??t.wsKey,y.project),w=r.serviceKeys[d];w&&(delete r.serviceKeys[d],r.keyToProject.delete(w),r.keyToWorkspace?.delete(w))}for(const y of i.services)o.killService(y,"workspace unregistered");r.graphStores?.delete(i.root),(0,guest_replay_1.forgetGuest)(t.wsKey),o.onUnregister?.(i)}return r.rebuildRoutes(),a(n,200,{ok:!0,removed:!!i,workspaces:r.registry.keys()})}a(n,404,{error:"not found"})}catch(c){if(c instanceof BodyTooLarge)return e.resume(),a(n,413,{error:c.message});a(n,500,{error:c.message})}})()});return S.listen(p,()=>{try{fs.chmodSync(p,384)}catch(e){console.warn(`[gateway] could not restrict ${p} to 0600:`,e.message)}}),{socketPath:p,async replay(){const{replay:e,gone:n}=(0,guest_replay_1.guestsToReplay)(r.root,(0,guest_replay_1.readGuests)()),c={replayed:[],refused:[],gone:n.map(t=>t.wsKey)};for(const t of n)(0,guest_replay_1.forgetGuest)(t.wsKey),console.log(`[gateway] not re-hosting '${t.wsKey}': ${t.root} is no longer a workspace \u2014 forgotten`);for(const t of e)if(!r.registry.has(t.wsKey))try{const s=await l({wsKey:t.wsKey,root:t.root,projects:t.projects,...t.projectDependencies?{projectDependencies:t.projectDependencies}:{}},0);s.status===200?(c.replayed.push(t.wsKey),console.log(`[gateway] re-hosted '${t.wsKey}' (${t.root}) \u2014 registered before this daemon restarted`)):(c.refused.push(t.wsKey),console.warn(`[gateway] could not re-host '${t.wsKey}' (${t.root}): ${JSON.stringify(s.json)} \u2014 \`lensmcp gateway start\` there`))}catch(s){c.refused.push(t.wsKey),console.warn(`[gateway] could not re-host '${t.wsKey}' (${t.root}): ${s.message}`)}return c},close:u(()=>{S.close();try{fs.unlinkSync(p)}catch{}},"close")}}g(startControlServer,"startControlServer"),u(startControlServer,"startControlServer");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.25.21",
3
+ "version": "1.25.22",
4
4
  "description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -58,9 +58,9 @@
58
58
  }
59
59
  },
60
60
  "dependencies": {
61
- "@lensmcp/core": "1.25.21",
62
- "@lensmcp/node-instrumentation": "1.25.21",
63
- "@lensmcp/nx-plugin": "1.25.21",
61
+ "@lensmcp/core": "1.25.22",
62
+ "@lensmcp/node-instrumentation": "1.25.22",
63
+ "@lensmcp/nx-plugin": "1.25.22",
64
64
  "fastify": "^5.8.5",
65
65
  "@fastify/reply-from": "^12.6.2",
66
66
  "@typescript/native-preview": "^7.0.0-dev.20260707.2",