@lensmcp/cluster 1.25.37 → 1.25.39
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.
- package/basic-ssl.js +1 -1
- package/build-host.js +1 -1
- package/build-scope-patterns.js +1 -1
- package/bundle-parent.js +1 -1
- package/checkout-cache.js +1 -1
- package/child-process-env.js +1 -1
- package/compile-retry.js +1 -1
- package/config-files.js +1 -1
- package/create-webpack-dev.js +1 -1
- package/create-webpack-prod.js +1 -1
- package/dev-devtool.js +1 -1
- package/devserver-host.js +1 -1
- package/devserver-notifier.js +1 -1
- package/devserver-transport.js +1 -1
- package/error-stack-release.js +1 -1
- package/executors/build/build.impl.js +1 -1
- package/executors/gateway/body-stall.js +1 -1
- package/executors/gateway/canonical-path.js +1 -1
- package/executors/gateway/client-ip.js +1 -1
- package/executors/gateway/gateway-errors.js +1 -1
- package/executors/gateway/gateway.impl.js +1 -1
- package/executors/gateway/gateway.lib.js +1 -1
- package/executors/gateway/health-check.js +1 -1
- package/executors/gateway/jwks-verify.js +1 -1
- package/executors/gateway/jwt-claims.js +1 -1
- package/executors/gateway/main.prod-gateway.js +2 -2
- package/executors/gateway/main.rollout.js +4 -4
- package/executors/gateway/manifest.js +1 -1
- package/executors/gateway/metrics.js +1 -1
- package/executors/gateway/otel-tracing.js +1 -1
- package/executors/gateway/prod-config.js +1 -1
- package/executors/gateway/prod-gateway.lib.js +1 -1
- package/executors/gateway/prod-runtime/access-log.js +1 -1
- package/executors/gateway/prod-runtime/app.js +1 -1
- package/executors/gateway/prod-runtime/auth.js +1 -1
- package/executors/gateway/prod-runtime/body-stall.js +1 -1
- package/executors/gateway/prod-runtime/cors.js +1 -1
- package/executors/gateway/prod-runtime/edge.js +1 -1
- package/executors/gateway/prod-runtime/handler.js +1 -1
- package/executors/gateway/prod-runtime/hooks.js +1 -1
- package/executors/gateway/prod-runtime/observability.d.ts +4 -2
- package/executors/gateway/prod-runtime/observability.js +1 -1
- package/executors/gateway/prod-runtime/rollout.js +1 -1
- package/executors/gateway/prod-runtime/routing.js +1 -1
- package/executors/gateway/prod-runtime/server.js +1 -1
- package/executors/gateway/prod-runtime/trust.js +1 -1
- package/executors/gateway/prod-runtime/upgrade.js +6 -6
- package/executors/gateway/prod-runtime/upstream.js +1 -1
- package/executors/gateway/providers-prod.js +1 -1
- package/executors/gateway/rate-limit.js +2 -2
- package/executors/gateway/registry-source.js +1 -1
- package/executors/gateway/rollout-ops.js +2 -2
- package/executors/gateway/runtime/act.js +1 -1
- package/executors/gateway/runtime/auth.js +1 -1
- package/executors/gateway/runtime/awake.d.ts +42 -0
- package/executors/gateway/runtime/awake.js +1 -0
- package/executors/gateway/runtime/chooser.js +12 -12
- package/executors/gateway/runtime/control.d.ts +11 -1
- package/executors/gateway/runtime/control.js +2 -2
- package/executors/gateway/runtime/dev-auth.js +1 -1
- package/executors/gateway/runtime/discovery.js +1 -1
- package/executors/gateway/runtime/edge.js +1 -1
- package/executors/gateway/runtime/guest-graph.js +2 -2
- package/executors/gateway/runtime/guest-replay.js +2 -2
- package/executors/gateway/runtime/handler.js +1 -1
- package/executors/gateway/runtime/hmr-reload.js +1 -1
- package/executors/gateway/runtime/hooks.js +1 -1
- package/executors/gateway/runtime/hosted-dashboard.js +1 -1
- package/executors/gateway/runtime/instance.js +2 -2
- package/executors/gateway/runtime/lens-children.js +1 -1
- package/executors/gateway/runtime/lifecycle.d.ts +4 -1
- package/executors/gateway/runtime/lifecycle.js +3 -3
- package/executors/gateway/runtime/observability.d.ts +8 -3
- package/executors/gateway/runtime/observability.js +3 -3
- package/executors/gateway/runtime/orphan-reaper.js +2 -2
- package/executors/gateway/runtime/own-log.js +6 -6
- package/executors/gateway/runtime/pod-env.js +2 -2
- package/executors/gateway/runtime/pod-output.js +1 -1
- package/executors/gateway/runtime/pod-probe.js +1 -1
- package/executors/gateway/runtime/process-names.js +1 -1
- package/executors/gateway/runtime/project-graph-store.js +1 -1
- package/executors/gateway/runtime/proxy.js +1 -1
- package/executors/gateway/runtime/registration-guards.js +1 -1
- package/executors/gateway/runtime/route-registry.js +1 -1
- package/executors/gateway/runtime/scope.js +1 -1
- package/executors/gateway/runtime/serve-launch.js +3 -3
- package/executors/gateway/runtime/server.js +2 -2
- package/executors/gateway/runtime/service-keys.js +1 -1
- package/executors/gateway/runtime/shared-build.js +3 -3
- package/executors/gateway/runtime/source-poller.js +1 -1
- package/executors/gateway/runtime/types.d.ts +24 -0
- package/executors/gateway/runtime/types.js +1 -1
- package/executors/gateway/runtime/upgrade.js +1 -1
- package/executors/gateway/runtime/warm-start.d.ts +18 -0
- package/executors/gateway/runtime/warm-start.js +1 -1
- package/executors/gateway/runtime/workspace-log.js +1 -1
- package/executors/gateway/runtime/workspace-middleware.js +1 -1
- package/executors/gateway/runtime/workspace-registry.js +1 -1
- package/executors/gateway/ssrf-guard.js +1 -1
- package/executors/serve/serve.impl.js +1 -1
- package/executors/serve/stop-on-signal.js +1 -1
- package/executors/trust/hostname.js +1 -1
- package/executors/trust/trust.impl.js +3 -3
- package/exit-reason.js +2 -2
- package/fork-ts-check-reporter.js +1 -1
- package/gateway.js +1 -1
- package/gc-notice.js +1 -1
- package/graph-snapshot.js +1 -1
- package/handoff-retire.js +1 -1
- package/heap-snapshot.js +1 -1
- package/hot-swap-registries.js +1 -1
- package/index.js +1 -1
- package/inspector-secret.js +1 -1
- package/main.devserver.js +11 -11
- package/notice-generation.js +1 -1
- package/package.json +4 -4
- package/perf/heavy-barrel-import.js +1 -1
- package/perf/perf-config.js +1 -1
- package/perf/perf-rule-events.js +2 -2
- package/perf/perf-rules-analysis.js +1 -1
- package/perf/perf-rules-plugin.js +1 -1
- package/perf/source-scan.js +5 -5
- package/perf/workspace-libraries.js +2 -2
- package/pod-memory-events.js +2 -2
- package/pod-memory.js +1 -1
- package/pod-sock.js +1 -1
- package/process-name.js +1 -1
- package/redis-disconnect-fix.js +1 -1
- package/schema-first-instance.js +1 -1
- package/serve-runner.js +1 -1
- package/service-build-events.js +2 -2
- package/shared-build/attach.js +2 -2
- package/shared-build/host.js +3 -3
- package/shared-build/protocol.js +1 -1
- package/shared-build/shared-compilation.js +3 -3
- package/shared-build/signature.js +1 -1
- package/stdio-guard.js +1 -1
- package/swap-mode.js +1 -1
- package/swap-recycle.js +1 -1
- package/tsgo-check-plugin.js +4 -4
- package/typecheck-bus.js +3 -3
- package/typecheck-queue.js +1 -1
- package/worker-guard.js +1 -1
- package/worker-retire.js +1 -1
- package/worker-supervisor.js +2 -2
- package/workspace-paths.js +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`)}
|
|
1
|
+
"use strict";var d=Object.defineProperty;var c=(n,r)=>d(n,"name",{value:r,configurable:!0});var h=Object.defineProperty,i=c((n,r)=>h(n,"name",{value:r,configurable:!0}),"i");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=i(n=>JSON.parse(JSON.stringify(n)),"clone");function asSpec(n,r){if(r===void 0)return{rules:[],cohorts:[]};if(Array.isArray(r)){if(r.length&&typeof r[0]=="string")throw new Error(`service "${n}" 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??[]}}c(asSpec,"asSpec"),i(asSpec,"asSpec");const findCohort=i((n,r)=>n.cohorts.find(o=>o.version===r),"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"),i(reqWeight,"reqWeight");function applyRolloutOp(n,r){const o=clone(n),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 t=findCohort(e,r.version);if(!t)throw new Error(`no cohort "${r.version}" on "${r.service}"`);t.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(t=>t.version!==r.version);break}case"promote":{if(!findCohort(e,r.version))throw new Error(`no cohort "${r.version}" on "${r.service}"`);for(const t of e.cohorts)t.weight=t.version===r.version?100:0;break}case"abort":{const t=findCohort(e,r.version);if(!t)throw new Error(`no cohort "${r.version}" on "${r.service}"`);t.weight=0;break}case"add-rule":{validateRule(r.rule),e.rules=[...(e.rules??[]).filter(t=>!r.rule.name||t.name!==r.rule.name),r.rule];break}case"remove-rule":{const t=(e.rules??[]).length;if(e.rules=(e.rules??[]).filter(s=>s.name!==r.name),e.rules.length===t)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}c(applyRolloutOp,"applyRolloutOp"),i(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 r=i(o=>{const e=o;if("all"in e||"any"in e||"not"in e){for(const t of["all","any"])Array.isArray(e[t])&&e[t].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(n.when)}c(validateRule,"validateRule"),i(validateRule,"validateRule");function summarizeRollout(n,r){const o=r?[r]:Object.keys(n);if(!o.length)return"(no services in rollout config)";const e=[];for(const t of o){const s=n[t];if(s===void 0){e.push(`${t}: (not present)`);continue}if(Array.isArray(s)&&s.length&&typeof s[0]=="string"){e.push(`${t}: flat LB \u2192 ${s.join(", ")}`);continue}const u=asSpec(t,s),l=u.cohorts.reduce((a,v)=>a+Math.max(0,v.weight),0)||1;e.push(`${t}:`);for(const a of u.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 u.rules??[])e.push(` \u25B8 rule ${(a.name??"(unnamed)").padEnd(16)} \u2192 ${a.version} ${ruleSummary(a.when)}`)}return e.join(`
|
|
2
|
+
`)}c(summarizeRollout,"summarizeRollout"),i(summarizeRollout,"summarizeRollout");function ruleSummary(n){const r=n;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):""}`}c(ruleSummary,"ruleSummary"),i(ruleSummary,"ruleSummary");function parseRolloutCommand(n){const[r,...o]=n,e=i((t,s)=>{if(o.length<t)throw new Error(`usage: ${r} ${s}`)},"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":{e(6,"<service> <name> <attr> <op> <value> <version>");const t=o[3],s=o[4]??"",u=t==="in"||t==="nin"?s.split(",").map(l=>l.trim()).filter(Boolean):s;return{op:{kind:"add-rule",service:o[0],rule:{name:o[1],when:{attr:o[2],op:t,value:u},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`)}}c(parseRolloutCommand,"parseRolloutCommand"),i(parseRolloutCommand,"parseRolloutCommand");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var b=Object.defineProperty;var S=(e,a)=>b(e,"name",{value:a,configurable:!0});var h=Object.defineProperty,c=S((e,a)=>h(e,"name",{value:a,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.ACT_MAX_IDENTITIES=exports.ACT_RETAIN_MS=exports.ACT_TICKET_MS=exports.ACT_TICKET=exports.ACT_HEADER=void 0,exports.fromBrowser=fromBrowser,exports.actTicketOf=actTicketOf,exports.setActTicketAside=setActTicketAside,exports.createActStore=createActStore;const tslib_1=require("tslib"),node_crypto_1=require("node:crypto"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path"));exports.ACT_HEADER="x-lensmcp-act",exports.ACT_TICKET=Symbol.for("lensmcp.gateway.actTicket"),exports.ACT_TICKET_MS=6e4,exports.ACT_RETAIN_MS=720*60*6e4,exports.ACT_MAX_IDENTITIES=20;const TOKEN_MARGIN_S=30,MAX_OPEN_TICKETS=200,WRITE_EVERY_MS=3e4,TIME_CLAIMS=new Set(["exp","iat","nbf","jti"]),TICKET_VALUE=/^[A-Za-z0-9_-]{43}$/;function fromBrowser(e){return typeof e.headers["sec-fetch-mode"]=="string"||typeof e.headers["sec-fetch-site"]=="string"}S(fromBrowser,"fromBrowser"),c(fromBrowser,"fromBrowser");function actTicketOf(e){return e[exports.ACT_TICKET]}S(actTicketOf,"actTicketOf"),c(actTicketOf,"actTicketOf");function setActTicketAside(e){const a=e.headers[exports.ACT_HEADER];typeof a=="string"&&TICKET_VALUE.test(a)&&(e[exports.ACT_TICKET]=a)}S(setActTicketAside,"setActTicketAside"),c(setActTicketAside,"setActTicketAside");const str=c(e=>typeof e=="string"&&e.length>0&&e.length<=256?e:void 0,"str"),keyOf=c(e=>{const a=str(e.sub);return a?`${a}\0${str(e.tid)??""}`:void 0},"keyOf"),safeName=c(e=>e.replace(/[^A-Za-z0-9._-]/g,"_").slice(0,120)||"_","safeName");function viewOf(e,a){const A=e.claims,d=Array.isArray(A.roles)?A.roles.filter(T=>typeof T=="string").slice(0,20):void 0,y=str(A.tid),u=str(A.email),E=str(A.name)??str(A.preferred_username);return{user:str(A.sub),...y?{tenant:y}:{},...u?{email:u}:{},...E?{name:E}:{},...d?.length?{roles:d}:{},host:e.host,firstSeenAt:e.firstSeenAt,lastSeenAt:e.lastSeenAt,mode:e.token&&e.tokenExp!==void 0&&e.tokenExp-TOKEN_MARGIN_S>a/1e3?"token":"identity"}}S(viewOf,"viewOf"),c(viewOf,"viewOf");function createActStore(e={}){const a=e.now??Date.now,A=new Map,d=new Map,y=new Map,u=new Set,E=c(t=>e.dir?path.join(e.dir,`${safeName(t)}.json`):void 0,"file"),T=c(t=>{let s=A.get(t);if(s)return s;s=new Map,A.set(t,s);const n=E(t);if(!n)return s;try{const o=JSON.parse(fs.readFileSync(n,"utf8"));for(const i of Array.isArray(o.identities)?o.identities:[]){const r=i;if(!r||typeof r!="object"||!r.claims||typeof r.claims!="object"||typeof r.host!="string"||typeof r.firstSeenAt!="number"||typeof r.lastSeenAt!="number")continue;const f=keyOf(r.claims);f&&s.set(f,{claims:r.claims,host:r.host,firstSeenAt:r.firstSeenAt,lastSeenAt:r.lastSeenAt})}}catch{}return s},"load"),g=c((t,s)=>{for(const[n,o]of t)s-o.lastSeenAt>exports.ACT_RETAIN_MS&&t.delete(n);if(t.size>exports.ACT_MAX_IDENTITIES){const n=[...t.entries()].sort((o,i)=>o[1].lastSeenAt-i[1].lastSeenAt).slice(0,t.size-exports.ACT_MAX_IDENTITIES);for(const[o]of n)t.delete(o)}},"prune"),k=c((t,s)=>{const n=E(t),o=A.get(t);if(u.delete(t),!(!n||!o)){y.set(t,s);try{fs.mkdirSync(path.dirname(n),{recursive:!0,mode:448});const i=[...o.values()].map(f=>({claims:f.claims,host:f.host,firstSeenAt:f.firstSeenAt,lastSeenAt:f.lastSeenAt})),r=`${n}.${process.pid}.tmp`;fs.writeFileSync(r,JSON.stringify({identities:i},null,1),{mode:384}),fs.renameSync(r,n)}catch{}}},"write"),I=c((t,s,n,o,i=a())=>{const r=keyOf(s);if(!r)return;const f=T(t),_={};for(const[x,w]of Object.entries(s))TIME_CLAIMS.has(x)||(_[x]=w);const m=f.get(r),l=typeof s.exp=="number"?s.exp:void 0,p=!m||m.host!==o||JSON.stringify(m.claims)!==JSON.stringify(_);f.set(r,{claims:_,host:o,firstSeenAt:m?.firstSeenAt??i,lastSeenAt:i,...n&&l!==void 0?{token:n,tokenExp:l}:m?.token?{token:m.token,tokenExp:m.tokenExp}:{}}),g(f,i),p&&u.add(t),(p||i-(y.get(t)??0)>=WRITE_EVERY_MS)&&k(t,i)},"remember"),C=c((t,s=a())=>{const n=T(t);return g(n,s),[...n.values()].sort((o,i)=>i.lastSeenAt-o.lastSeenAt).map(o=>viewOf(o,s))},"list");return{remember:I,list:C,mint:c((t,s,n=a())=>{const o=T(t);g(o,n);const i=s.user?.trim().toLowerCase(),r=s.tenant?.trim(),f=[...o.entries()].filter(([,l])=>{const p=l.claims;return r&&str(p.tid)!==r?!1:i?[p.sub,p.email,p.name,p.preferred_username].some(x=>typeof x=="string"&&x.toLowerCase()===i):!0}).sort((l,p)=>p[1].lastSeenAt-l[1].lastSeenAt)[0];if(!f){const l=C(t,n);return{error:l.length?`No signed-in identity matches${i?` user '${s.user}'`:""}${r?` in tenant '${r}'`:""} \u2014 ${l.length} known (see identities).`:"No one has signed in to this workspace's app since the gateway started remembering identities. Sign in once in the browser \u2014 the gateway keeps the identity it verified for 30 days.",identities:l}}for(const[l,p]of d)p.expiresAt<=n&&d.delete(l);d.size>=MAX_OPEN_TICKETS&&d.delete(d.keys().next().value);const _=(0,node_crypto_1.randomBytes)(32).toString("base64url"),m=n+exports.ACT_TICKET_MS;return d.set(_,{wsKey:t,key:f[0],expiresAt:m}),{ticket:_,expiresAt:m,identity:viewOf(f[1],n)}},"mint"),redeem:c((t,s,n=a())=>{const o=d.get(t);if(!o||(d.delete(t),o.expiresAt<=n||o.wsKey!==s))return;const i=T(s).get(o.key);if(!i)return;const r=viewOf(i,n);return r.mode==="token"?{token:i.token,claims:i.claims,view:r}:{claims:i.claims,view:r}},"redeem"),flush:c(()=>{const t=a();for(const s of[...u])k(s,t)},"flush")}}S(createActStore,"createActStore"),c(createActStore,"createActStore");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var _=Object.defineProperty;var w=(a,s)=>_(a,"name",{value:s,configurable:!0});var b=Object.defineProperty,p=w((a,s)=>b(a,"name",{value:s,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0}),exports.declaredCallerIds=declaredCallerIds,exports.createEdgeAuth=createEdgeAuth;const manifest_1=require("../manifest"),act_1=require("./act"),chooser_1=require("./chooser"),dev_auth_1=require("./dev-auth"),edge_1=require("./edge"),NO_CALLER_IDS=new Set;function declaredCallerIds(a,s,c){const k=(a.registry?a.registry.allServices():a.services).filter(r=>c===void 0||r.wsKey===c),i=k.find(r=>r.project===s)?.decl.callerIds;if(!Array.isArray(i)||i.length===0)return NO_CALLER_IDS;const e=new Set(k.map(r=>r.project));for(const[r,n]of a.keyToProject)(c===void 0||a.keyToWorkspace?.get(r)===c)&&e.add(n);return new Set(i.filter(r=>typeof r=="string"&&r!==s&&!e.has(r)))}w(declaredCallerIds,"declaredCallerIds"),p(declaredCallerIds,"declaredCallerIds");const bearerOf=p(a=>{const s=a.headers.authorization,c=Array.isArray(s)?s[0]:s;return typeof c=="string"&&c.startsWith("Bearer ")?c.slice(7):void 0},"bearerOf");function createEdgeAuth(a,s,c,k){const{traceStep:i}=s;return{internal:p((e,r,n)=>{const v=String(e.headers["x-api-key"]??""),o=a.keyToProject.get(v);if(!o)return i(n,"auth",{mode:"x-api-key",ok:!1}),{ok:!1,status:401,reason:"unauthorized",detail:"Invalid or missing x-api-key for internal route."};const l=a.keyToWorkspace?.get(v),t=r.svc?.wsKey;if(l&&t&&l!==t)return i(n,"auth",{mode:"x-api-key",ok:!1,reason:"cross-workspace",caller:o,callerWorkspace:l,routeWorkspace:t}),{ok:!1,status:401,reason:"unauthorized",detail:"Invalid or missing x-api-key for internal route."};const d=(0,edge_1.claimedCallerIdOf)(e),h=d!==void 0&&d!==o&&declaredCallerIds(a,o,l).has(d)?d:o;e.headers["x-api-key-id"]=h,l?e.headers["x-api-key-workspace"]=l:delete e.headers["x-api-key-workspace"],e.__lensmcpCaller=o,delete e.headers["x-api-key"];const u=(0,edge_1.callerContextOf)(e);for(const[g,y]of Object.entries(u))e.headers[g]=y;return i(n,"auth",{mode:"x-api-key",ok:!0,caller:o,...h!==o?{as:h}:{},...d!==void 0&&d!==o&&h===o?{claimRefused:d}:{},...l?{callerWorkspace:l}:{},...u["x-tenant-id"]!==void 0?{callerTenant:!0}:{},...u["x-user-id"]!==void 0?{callerUser:!0}:{}}),{ok:!0,caller:o}},"internal"),public:p((e,r,n,v)=>{const o=r.wsKey??r.svc?.wsKey??a.wsKey,l=(0,act_1.actTicketOf)(e);let t;if(l!==void 0){if(t=k&&(0,chooser_1.isLoopbackRequest)(e)?k.redeem(l,o):void 0,!t)return i(n,"auth",{mode:"act",ok:!1}),{ok:!1,status:401,reason:"unauthorized",detail:"Unauthorized: the act ticket is unknown, spent, expired or for another workspace \u2014 ask for a new one (lensmcp call)."};t.token?e.headers.authorization=`Bearer ${t.token}`:delete e.headers.authorization}const d=p(u=>t?{act:u,as:t.view.user,...t.view.tenant?{tenant:t.view.tenant}:{}}:{},"actStep"),h=r.auth;if(h){const u=v??(0,edge_1.canonicalPath)(e.url??"/")??(e.url??"/").split("?")[0],g=(e.method??"GET").toUpperCase(),y=(0,manifest_1.authRuleFor)(h,u,g);if(y.mode==="jwt"){const x=String(e.headers.host??"").split(":")[0].toLowerCase();let m=t&&!t.token?"identity":"token",f=m==="identity"?t.claims:(0,dev_auth_1.verifyDevJwt)(e,c?.for(x));return!f&&t&&(m="identity",f=t.claims,delete e.headers.authorization),f?y.rule&&!(0,manifest_1.evalRule)(y.rule,(0,dev_auth_1.devAttrs)(e,f,u))?(i(n,"authz",{ok:!1,...d(m)}),{ok:!1,status:403,reason:"forbidden",detail:"Forbidden: insufficient permission"}):((0,dev_auth_1.stampIdentity)(e,f),k&&!t&&(0,act_1.fromBrowser)(e)&&k.remember(o,f,bearerOf(e),x),i(n,"auth",{mode:"jwt",ok:!0,...d(m)}),{ok:!0,claims:f}):(i(n,"auth",{mode:"jwt",ok:!1}),{ok:!1,status:401,reason:"unauthorized",detail:"Unauthorized: invalid or missing token"})}return i(n,"auth",{mode:y.mode,...d(t?.token?"token":"identity")}),{ok:!0}}return i(n,"auth",{mode:"public",unconfigured:!0,...d(t?.token?"token":"identity")}),{ok:!0}},"publicAuth")}}w(createEdgeAuth,"createEdgeAuth"),p(createEdgeAuth,"createEdgeAuth");
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ServiceCtl, ServiceWake } from './types';
|
|
2
|
+
/** The longest hold: a forgotten one costs its memory for no longer than this, plus one idle window. */
|
|
3
|
+
export declare const MAX_AWAKE_SEC: number;
|
|
4
|
+
/**
|
|
5
|
+
* When the idle sweep would stop `svc` (ms): a whole window after the later of its last request and its hold's end —
|
|
6
|
+
* so never before the hold ends. `undefined` when it has no window (it never idle-stops).
|
|
7
|
+
*/
|
|
8
|
+
export declare function idleStopAt(svc: Pick<ServiceCtl, 'lastUsed' | 'awakeUntil'>, idleSec: number | undefined): number | undefined;
|
|
9
|
+
/** What `/status` and `/awake` say of a service's idle clock. `idleStopAt` only for an up tree this gateway would stop. */
|
|
10
|
+
export declare function idleFields(svc: Pick<ServiceCtl, 'state' | 'proc' | 'lastUsed' | 'awakeUntil' | 'lastIdleStop' | 'wake'>, idleSec: number | undefined, now: number): {
|
|
11
|
+
idleSec?: number;
|
|
12
|
+
idleStopAt?: number;
|
|
13
|
+
awakeUntil?: number;
|
|
14
|
+
lastIdleStop?: {
|
|
15
|
+
at: number;
|
|
16
|
+
why: string;
|
|
17
|
+
};
|
|
18
|
+
wake?: ServiceWake;
|
|
19
|
+
};
|
|
20
|
+
export interface WakeDeps {
|
|
21
|
+
/** Start it and wait until it answers — control.ts's `POST /service` start: `true` once it does. */
|
|
22
|
+
start(svc: ServiceCtl): Promise<boolean>;
|
|
23
|
+
/** warm-start.ts `availableMemory`. */
|
|
24
|
+
available(): number | undefined;
|
|
25
|
+
/** warm-start.ts `warmMinFreeBytes`. */
|
|
26
|
+
minFreeBytes: number;
|
|
27
|
+
/** Is this service still its workspace's — not unregistered, not replaced, the gateway not stopping? */
|
|
28
|
+
current(svc: ServiceCtl): boolean;
|
|
29
|
+
/** Is a stop, start or restart of it under way (what `POST /service` refuses another for)? */
|
|
30
|
+
busy(svc: ServiceCtl): boolean;
|
|
31
|
+
/** Onto the service's own bus, as the lifecycle's events go. */
|
|
32
|
+
emit(svc: ServiceCtl, severity: 'info' | 'warning', title: string, raw: Record<string, unknown>): void;
|
|
33
|
+
log(line: string): void;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Start each queued service, ONE AT A TIME, each awaited, while the memory floor holds — writing how it went on its
|
|
37
|
+
* `wake` record. A service whose record a newer wake replaced is that wake's to start. Never throws.
|
|
38
|
+
*/
|
|
39
|
+
export declare function wakeServices(queued: ReadonlyArray<{
|
|
40
|
+
svc: ServiceCtl;
|
|
41
|
+
wake: ServiceWake;
|
|
42
|
+
}>, deps: WakeDeps): Promise<void>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var k=Object.defineProperty;var d=(t,e)=>k(t,"name",{value:e,configurable:!0});var p=Object.defineProperty,l=d((t,e)=>p(t,"name",{value:e,configurable:!0}),"l");Object.defineProperty(exports,"__esModule",{value:!0}),exports.MAX_AWAKE_SEC=void 0,exports.idleStopAt=idleStopAt,exports.idleFields=idleFields,exports.wakeServices=wakeServices;const warm_start_1=require("./warm-start");exports.MAX_AWAKE_SEC=14400;function idleStopAt(t,e){if(e)return Math.max(t.lastUsed||0,t.awakeUntil??0)+e*1e3}d(idleStopAt,"idleStopAt"),l(idleStopAt,"idleStopAt");function idleFields(t,e,n){const s=t.state==="up"&&t.proc?idleStopAt(t,e):void 0;return{...e?{idleSec:e}:{},...s!==void 0?{idleStopAt:s}:{},...(t.awakeUntil??0)>n?{awakeUntil:t.awakeUntil}:{},...t.lastIdleStop?{lastIdleStop:t.lastIdleStop}:{},...t.wake?{wake:t.wake}:{}}}d(idleFields,"idleFields"),l(idleFields,"idleFields");async function wakeServices(t,e){const n=l(a=>a.svc.wake===a.wake,"mine"),s=l((a,r,i)=>{a.wake={at:a.wake.at,outcome:r,...i?{reason:i}:{}},a.svc.wake=a.wake},"settle"),u=t.map(a=>({...a}));for(let a=0;a<u.length;a++){const r=u[a];if(!n(r))continue;const{svc:i}=r;if(!e.current(i)){for(const f of u.slice(a).filter(n))s(f,"failed","its workspace left the gateway before its turn");return}if(i.state==="up"){s(r,"up");continue}if(i.manualStopped){s(r,"skipped","stopped by hand while it waited its turn");continue}if(e.busy(i)){s(r,"skipped","a stop, start or restart of it was under way");continue}const c=(0,warm_start_1.belowFloor)(e.available(),e.minFreeBytes);if(c){const f=u.slice(a).filter(o=>n(o)&&o.svc.state!=="up");e.log(`[gateway] wake of ${i.wsKey} stopped: ${c} \u2014 ${f.map(o=>o.svc.project).join(", ")} start on their first request.`);for(const o of f)s(o,"deferred",c),e.emit(o.svc,"info",`wake deferred: ${o.svc.project} \u2014 ${c}`,{kind:"service-warm-deferred",project:o.svc.project,reason:c,minFreeBytes:e.minFreeBytes});return}s(r,"starting");const w=await e.start(i).catch(()=>!1);n(r)&&(w&&(i.lastUsed=Date.now()),s(r,w?"started":"failed",w?void 0:i.actionFailure??"it did not become ready"))}}d(wakeServices,"wakeServices"),l(wakeServices,"wakeServices");
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var m=Object.defineProperty;var l=(a,t)=>m(a,"name",{value:t,configurable:!0});var b=Object.defineProperty,i=l((a,t)=>b(a,"name",{value:t,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.isLoopbackRequest=isLoopbackRequest,exports.displayRoot=displayRoot,exports.renderChooserPage=renderChooserPage,exports.serveChooser=serveChooser;const node_os_1=require("node:os"),workspace_registry_1=require("./workspace-registry"),esc=i(a=>a.replace(/[&<>"']/g,t=>({"&":"&","<":"<",">":">",'"':""","'":"'"})[t]),"esc");function isLoopbackRequest(a){const t=a.socket?.remoteAddress;if(!t)return!1;const o=t.startsWith("::ffff:")?t.slice(7):t;return o==="::1"||o==="127.0.0.1"||/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(o)}l(isLoopbackRequest,"isLoopbackRequest"),i(isLoopbackRequest,"isLoopbackRequest");function displayRoot(a,t=(0,node_os_1.homedir)()){const o=a.replace(/\/+$/,"").split("/").filter(Boolean),c=o.slice(-2).join("/");if(!c)return"/";const p=t!==""&&(a===t||a.startsWith(`${t}/`)),s=o.length>2;return p?s?`~/\u2026/${c}`:`~/${c}`:s?`\u2026/${c}`:`/${c}`}l(displayRoot,"displayRoot"),i(displayRoot,"displayRoot");function renderChooserPage(a,t){const o=a.map(e=>({...e,live:e.key===t||(0,workspace_registry_1.isWorkspaceLive)(e)})).sort((e,n)=>Number(n.live)-Number(e.live)||e.key.localeCompare(n.key)),c=new Set(o.map(e=>e.key)),p=i(e=>!!e.instance&&c.has(e.instance.baseKey),"nested"),s=o.filter(e=>!p(e)),x=i(e=>o.filter(n=>n.instance?.baseKey===e),"worktreesOf"),f=i(e=>e.key===t?'<span class="badge here">running here</span>':e.live?'<span class="badge live">live</span>':'<span class="badge off">offline</span>',"badgeOf"),h=i(e=>{const n=x(e);if(n.length===0)return"";const d=n.map(r=>{const g=`<span class="wt-name">${esc(r.instance?.name??r.key)}</span> ${f(r)}<span class="wt-apex">${esc(r.instance?.apex??"")}</span>`;return r.live?`<a class="wt is-live" href="${esc(r.base)}/">${g}<span class="open">Open \u2192</span></a>`:`<div class="wt is-off">${g}</div>`}).join(`
|
|
2
2
|
`);return`
|
|
3
|
-
<div class="worktrees"><div class="wt-head">Worktrees \xB7 ${n.filter(
|
|
4
|
-
${
|
|
5
|
-
</div>`},"worktreeList"),
|
|
6
|
-
<div class="key">${esc(e.key)} ${
|
|
7
|
-
<div class="root">${esc(displayRoot(e.root))}</div>${
|
|
3
|
+
<div class="worktrees"><div class="wt-head">Worktrees \xB7 ${n.filter(r=>r.live).length} running</div>
|
|
4
|
+
${d}
|
|
5
|
+
</div>`},"worktreeList"),u=s.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>':s.map(e=>{const n=e.live?'<span class="open">Open dashboard \u2192</span>':'<span class="hint">start its gateway to open</span>',d=e.instance?`<div class="root">worktree <b>${esc(e.instance.name)}</b> of ${esc(e.instance.baseKey)} \xB7 ${esc(e.instance.apex)}</div>`:"",r=`<div class="card-main">
|
|
6
|
+
<div class="key">${esc(e.key)} ${f(e)}</div>
|
|
7
|
+
<div class="root">${esc(displayRoot(e.root))}</div>${d}
|
|
8
8
|
</div>
|
|
9
9
|
<div class="card-action">${n}</div>`;return`<div class="group">${e.live?`<a class="card is-live" href="${esc(e.base)}/">
|
|
10
|
-
${
|
|
10
|
+
${r}
|
|
11
11
|
</a>`:`<div class="card is-off">
|
|
12
|
-
${
|
|
13
|
-
</div>`}${
|
|
12
|
+
${r}
|
|
13
|
+
</div>`}${h(e.key)}</div>`}).join(`
|
|
14
14
|
`);return`<!doctype html>
|
|
15
15
|
<html lang="en"><head>
|
|
16
16
|
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
<body><div class="wrap">
|
|
56
56
|
<header>
|
|
57
57
|
<h1>LensMCP workspaces</h1>
|
|
58
|
-
<p class="sub">Pick a workspace to open its dashboard. ${
|
|
58
|
+
<p class="sub">Pick a workspace to open its dashboard. ${s.filter(e=>e.live).length} live \xB7 ${s.length} known${o.length>s.length?` \xB7 ${o.length-s.length} worktree${o.length-s.length===1?"":"s"}`:""}.</p>
|
|
59
59
|
</header>
|
|
60
60
|
<div class="list">
|
|
61
|
-
${
|
|
61
|
+
${u}
|
|
62
62
|
</div>
|
|
63
63
|
<footer>Served by the gateway on <code>lensmcp.local</code>. This list updates as gateways start and stop.</footer>
|
|
64
|
-
</div></body></html>`}
|
|
64
|
+
</div></body></html>`}l(renderChooserPage,"renderChooserPage"),i(renderChooserPage,"renderChooserPage");function serveChooser(a,t,o){if(o&&!isLoopbackRequest(o)){a.writeHead(404,{"content-type":"text/plain; charset=utf-8","cache-control":"no-store"}),a.end("not found");return}const c=renderChooserPage((0,workspace_registry_1.readWorkspaces)(),t);a.writeHead(200,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),a.end(c)}l(serveChooser,"serveChooser"),i(serveChooser,"serveChooser");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ActStore } from './act';
|
|
2
2
|
import type { ServiceLayer } from './lifecycle';
|
|
3
|
-
import type { GatewayRuntime } from './types';
|
|
3
|
+
import type { GatewayRuntime, ServiceCtl } from './types';
|
|
4
4
|
import type { WorkspaceFragment } from './route-registry';
|
|
5
5
|
/** What `/list` and `/status` say about a workspace that is a worktree instance; nothing for any other. */
|
|
6
6
|
export declare function instanceSummary(f: WorkspaceFragment | undefined): {
|
|
@@ -19,6 +19,16 @@ export interface ControlServerDeps {
|
|
|
19
19
|
/** Restart one service (the svcLayer's `restartService`); `POST /restart` answers 501 without it. */
|
|
20
20
|
restartService?: ServiceLayer['restartService'];
|
|
21
21
|
ensureService?: ServiceLayer['ensureUp'];
|
|
22
|
+
/**
|
|
23
|
+
* The machine's one start at a time (warm-start.ts `createWorktreeWarmer().take`): a `POST /awake` start waits its
|
|
24
|
+
* turn behind the worktree warm starts. Without it, the starts this control plane asks for take turns among themselves.
|
|
25
|
+
*/
|
|
26
|
+
startTurn?: <T>(job: () => Promise<T>) => Promise<T>;
|
|
27
|
+
/** Onto a service's own bus, as the lifecycle's events go (lifecycle.ts `emitSvc`) — `service-awake`; none without it. */
|
|
28
|
+
emitService?: (svc: ServiceCtl, severity: 'info' | 'warning', title: string, raw: Record<string, unknown>) => void;
|
|
29
|
+
/** The memory the system can still give out (warm-start.ts `availableMemory`) and the floor a start keeps. */
|
|
30
|
+
available?: () => number | undefined;
|
|
31
|
+
minFreeBytes?: number;
|
|
22
32
|
/** Who signed in, and tickets to act as them (act.ts); `/identities` and `/act` answer 501 without it. */
|
|
23
33
|
act?: ActStore;
|
|
24
34
|
/** Hooks so the daemon can host/tear-down a workspace's dashboard around the route merge (P3d).
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`)}catch{}let k;if(t.graphStores){const S={root:e.root,daemonRoot:t.root,otherRoots:t.registry.keys().filter(T=>T!==e.wsKey).flatMap(T=>t.registry.get(T)?.root??[])};k=e.projectGraph===void 0?void 0:(0,guest_graph_1.admitForwardedGraph)(e.projectGraph,{...S,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,t.root)||t.graphStores.set((0,guest_graph_1.createGuestGraphStore)({wsKey:e.wsKey,root:e.root,...k?{admission:k}:{},context:S}))}return t.registry.register(_),await i.onRegister?.(_),t.rebuildRoutes(),t.root&&!(0,registration_guards_1.sameRoot)(e.root,t.root)&&(0,guest_replay_1.recordGuest)({wsKey:e.wsKey,root:e.root,projects:r,...e.projectDependencies?{projectDependencies:e.projectDependencies}:{},daemonRoot:t.root,registeredAt:new Date().toISOString()}),{status:200,json:{ok:!0,wsKey:e.wsKey,routes:p.length,services:u.length,workspaces:t.registry.keys(),...t.graphStores?{projectGraph:graphVerdict(k)}:{}}}},"register"),K=f((e,n)=>{const c=t.registry.get(e);if(!c)return{error:`no workspace '${e}' is registered`,workspaces:t.registry.keys()};const r=c.services.find(s=>s.project===n);return r?{svc:r}:{error:`'${e}' has no service '${n}'`,services:c.services.map(s=>s.project)}},"findService"),m=f(e=>({wsKey:e.wsKey,service:e.project,state:e.state,...e.generation!==void 0?{generation:e.generation}:{},...e.restart?{restart:e.restart}:{}}),"restartView"),j=http.createServer((e,n)=>{(async()=>{try{const c=(e.url??"").split("?")[0];if(e.method==="GET"&&c==="/list")return a(n,200,{workspaces:t.registry.keys().map(r=>{const s=t.registry.get(r);return{key:r,root:s?.root,routes:s?.routes.length??0,services:s?.services.length??0,...instanceSummary(s)}})});if(e.method==="GET"&&c==="/status"){const r=(0,exports.clusterVersion)();return a(n,200,{daemon:{pid:process.pid,wsKey:t.wsKey,...r?{version:r}:{}},workspaces:t.registry.keys().map(s=>{const o=t.registry.get(s);return{key:s,root:o?.root,routes:o?.routes.length??0,services:o?.services.length??0,isDaemon:s===t.wsKey,...instanceSummary(o)}}),services:t.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 r=new URL(e.url??"/","http://control").searchParams.get("wsKey")??"",s=t.registry.get(r);return s?s.instance?a(n,200,{wsKey:r,root:s.root,...instanceSummary(s),env:s.instance.env}):a(n,404,{error:`'${r}' is not an instance`}):a(n,404,{error:`no workspace '${r}' is registered`})}if(e.method==="POST"&&c==="/instance-env"){const{value:r,bytes:s}=await readJsonBody(e,CONTROL_BODY_BYTES+guest_graph_1.MAX_FORWARDED_GRAPH_BYTES),o=await h(r,s,!0);return a(n,o.status,o.json)}if(c==="/service"&&(e.method==="GET"||e.method==="POST")){const r=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{};if(typeof r.wsKey!="string"||typeof r.service!="string")return a(n,400,{error:"wsKey and service are required"});const s=K(r.wsKey,r.service);if("error"in s)return a(n,404,s);const o=s.svc,d=f(()=>{let u=(o.stoppingUntil??0)>Date.now();if(o.stoppingPid)try{process.kill(o.stoppingPid,0),u=!0}catch{o.stoppingPid=void 0}return{service:o.project,state:o.state,stopped:o.manualStopped===!0&&!u,ready:!o.manualStopped&&o.state==="up"&&o.pool.socks.length>0,busy:u||o.actionStarting===!0||!!o.rolling||!!(o.restart&&!o.restart.done),failure:o.actionFailure??(!o.manualStopped&&o.restart?.done?.ok===!1?o.restart.done.error:void 0)}},"view");if(e.method==="GET")return a(n,200,d());const y=r.action;if(!["start","stop","restart"].includes(String(y)))return a(n,400,{error:"action must be start, stop or restart"});if(d().busy&&!(y==="stop"&&o.actionStarting&&!o.rolling))return a(n,409,{error:"A service lifecycle operation is already in progress"});if(y==="stop")return!o.proc&&o.pool.socks.length&&!o.manualStopped?a(n,409,{error:"This gateway does not own the adopted service process; stop it with its owner"}):(o.manualStopped=!0,o.stoppingPid=o.proc?.pid,o.stoppingUntil=Date.now()+(o.proc?1600:0),i.killService(o,"workspace action stop"),o.state="down",a(n,202,d()));if(y==="restart"){if(!i.restartService)return a(n,501,{error:"This gateway cannot restart services"});const u=i.restartService(o,{hard:r.hard===!0,reason:"workspace action restart"});return"refused"in u?a(n,409,{error:u.refused}):a(n,202,d())}if(!i.ensureService)return a(n,501,{error:"This gateway cannot start services"});const p=o.state!=="up"&&!o.proc;return o.manualStopped=!1,o.actionFailure=void 0,o.restart=void 0,o.actionStarting=!0,i.ensureService(o).then(u=>{!u&&!o.manualStopped&&(o.actionFailure="Service did not become ready")},u=>{o.actionFailure=String(u).slice(0,500)}).finally(()=>{o.actionStarting=!1}),a(n,202,{...d(),started:p})}if(c==="/restart"&&(e.method==="POST"||e.method==="GET")){const r=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},s=typeof r.wsKey=="string"?r.wsKey:"",o=typeof r.service=="string"?r.service:"";if(!s||!o)return a(n,400,{error:"wsKey and service are required"});const d=K(s,o);if("error"in d)return a(n,404,d);const y=d.svc;if(e.method==="GET")return a(n,200,m(y));if(!i.restartService)return a(n,501,{error:"this gateway cannot restart a service"});const p=typeof r.reason=="string"&&r.reason.trim()?r.reason.trim().slice(0,200):"asked for",u=i.restartService(y,{hard:r.hard===!0,reason:p});return"refused"in u?a(n,409,{error:u.refused,...m(y)}):a(n,202,{ok:!0,already:u.already,...m(y)})}if(e.method==="GET"&&c==="/identities"||e.method==="POST"&&c==="/act"){const r=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},s=typeof r.wsKey=="string"?r.wsKey:"";if(!s)return a(n,400,{error:"wsKey is required"});if(!t.registry.get(s)&&s!==t.wsKey)return a(n,404,{error:`no workspace '${s}' is registered`,workspaces:t.registry.keys()});if(!i.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:i.act.list(s)});const o=f(u=>typeof r[u]=="string"&&r[u].trim()?r[u].trim().slice(0,256):void 0,"pick"),d=o("user"),y=o("tenant"),p=i.act.mint(s,{...d?{user:d}:{},...y?{tenant:y}:{}});return"error"in p?a(n,404,{wsKey:s,...p}):a(n,200,{wsKey:s,...p})}if(e.method==="POST"&&c==="/register"){const{value:r,bytes:s}=await readJsonBody(e,CONTROL_BODY_BYTES+guest_graph_1.MAX_FORWARDED_GRAPH_BYTES),o=await h(r,s);return a(n,o.status,o.json)}if(e.method==="POST"&&c==="/unregister"){const r=(await readJsonBody(e)).value;if(!r.wsKey)return a(n,400,{error:"wsKey is required"});const s=(0,registration_guards_1.refuseUnregistration)(t.registry,{wsKey:r.wsKey,...r.root?{root:r.root}:{}},t.wsKey);if(s)return console.warn(`[gateway] refused to unregister '${r.wsKey}'${r.root?` for ${r.root}`:""}: ${s.error}`),a(n,409,s);const o=t.registry.unregister(r.wsKey);if(o){for(const d of o.services){const y=(0,service_keys_1.serviceKeyId)(d.wsKey??r.wsKey,d.project),p=t.serviceKeys[y];p&&(delete t.serviceKeys[y],t.keyToProject.delete(p),t.keyToWorkspace?.delete(p))}for(const d of o.services)i.killService(d,"workspace unregistered");t.graphStores?.delete(o.root),(0,guest_replay_1.forgetGuest)(r.wsKey),i.onUnregister?.(o)}return t.rebuildRoutes(),a(n,200,{ok:!0,removed:!!o,workspaces:t.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 j.listen(l,()=>{try{fs.chmodSync(l,384)}catch(e){console.warn(`[gateway] could not restrict ${l} to 0600:`,e.message)}}),{socketPath:l,async replay(){const{replay:e,gone:n}=(0,guest_replay_1.guestsToReplay)(t.root,(0,guest_replay_1.readGuests)()),c={replayed:[],refused:[],gone:n.map(r=>r.wsKey)};for(const r of n)(0,guest_replay_1.forgetGuest)(r.wsKey),console.log(`[gateway] not re-hosting '${r.wsKey}': ${r.root} is no longer a workspace \u2014 forgotten`);for(const r of e)if(!t.registry.has(r.wsKey))try{const s=await h({wsKey:r.wsKey,root:r.root,projects:r.projects,...r.projectDependencies?{projectDependencies:r.projectDependencies}:{}},0);s.status===200?(c.replayed.push(r.wsKey),console.log(`[gateway] re-hosted '${r.wsKey}' (${r.root}) \u2014 registered before this daemon restarted`)):(c.refused.push(r.wsKey),console.warn(`[gateway] could not re-host '${r.wsKey}' (${r.root}): ${JSON.stringify(s.json)} \u2014 \`lensmcp gateway start\` there`))}catch(s){c.refused.push(r.wsKey),console.warn(`[gateway] could not re-host '${r.wsKey}' (${r.root}): ${s.message}`)}return c},close:f(()=>{j.close();try{fs.unlinkSync(l)}catch{}},"close")}}g(startControlServer,"startControlServer"),w(startControlServer,"startControlServer"),f(startControlServer,"startControlServer");
|
|
1
|
+
"use strict";var q=Object.defineProperty;var h=(c,t)=>q(c,"name",{value:t,configurable:!0});var x=Object.defineProperty,l=h((c,t)=>x(c,"name",{value:t,configurable:!0}),"l");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"),awake_1=require("./awake"),warm_start_1=require("./warm-start"),service_keys_1=require("./service-keys"),workspace_log_1=require("./workspace-log"),workspace_middleware_1=require("./workspace-middleware");function instanceSummary(c){const t=c?.instance;return t?{instance:t.name,baseKey:t.baseKey,apex:t.apex,baseApex:t.baseApex,...t.frozen?{frozen:!0}:{}}:{}}h(instanceSummary,"instanceSummary"),l(instanceSummary,"instanceSummary");function controlSocketPath(){const c=process.env.LENSMCP_HOME||os.homedir();return path.join(c,".lensmcp","control.sock")}h(controlSocketPath,"controlSocketPath"),l(controlSocketPath,"controlSocketPath");const CONTROL_BODY_BYTES=1e6;class BodyTooLarge extends Error{static{h(this,"BodyTooLarge")}static{l(this,"BodyTooLarge")}constructor(t){super(`body too large (over ${t} bytes)`),this.limit=t}}exports.clusterVersion=versionAtLoad(()=>readClusterVersion(__dirname));function versionAtLoad(c){const t=c();return()=>t}h(versionAtLoad,"versionAtLoad"),l(versionAtLoad,"versionAtLoad");function readClusterVersion(c){let t=c;for(let f=0;f<6;f++){const k=path.join(t,"package.json");if(fs.existsSync(k))try{const v=JSON.parse(fs.readFileSync(k,"utf8"));if(v.name==="@lensmcp/cluster"&&typeof v.version=="string")return v.version}catch{}const i=path.dirname(t);if(i===t)break;t=i}}h(readClusterVersion,"readClusterVersion"),l(readClusterVersion,"readClusterVersion");function readJsonBody(c,t=CONTROL_BODY_BYTES){return new Promise((f,k)=>{const i=[];let v=0,_=!1;c.on("data",S=>{if(!_){if(v+=S.length,v>t){_=!0,i.length=0,k(new BodyTooLarge(t));return}i.push(S)}}),c.on("end",()=>{if(_)return;const S=Buffer.concat(i).toString("utf8");try{f({value:S?JSON.parse(S):{},bytes:v})}catch(T){k(T)}}),c.on("error",k)})}h(readJsonBody,"readJsonBody"),l(readJsonBody,"readJsonBody");function graphShare(c,t){return t-Buffer.byteLength(JSON.stringify({...c,projectGraph:null}))}h(graphShare,"graphShare"),l(graphShare,"graphShare");function graphVerdict(c){return c?c.refused===void 0?{forwarded:!0,accepted:!0}:{forwarded:!0,accepted:!1,reason:c.refused}:{forwarded:!1,accepted:!1}}h(graphVerdict,"graphVerdict"),l(graphVerdict,"graphVerdict");function ensureOwnerOnlyDir(c){try{fs.mkdirSync(c,{recursive:!0,mode:448})}catch{}try{const t=fs.lstatSync(c);return t.isDirectory()?((t.mode&63)!==0&&fs.chmodSync(c,448),!0):!1}catch{return!1}}h(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),l(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function startControlServer(c){const{rt:t}=c,f=controlSocketPath(),k=path.dirname(f);ensureOwnerOnlyDir(k)||console.warn(`[gateway] control home ${k} is not a usable owner-only directory \u2014 refusing to expose the control plane there.`);try{fs.unlinkSync(f)}catch{}const i=l((e,s,u)=>{e.writeHead(s,{"content-type":"application/json"}),e.end(JSON.stringify(u))},"send"),v=l(async(e,s,u=!1)=>{if(!e.wsKey||!e.root)return{status:400,json:{error:"wsKey and root are required"}};const r=e.projects??{},o=(0,discovery_1.projectSourceRoots)(e.root,r,e.projectDependencies),a=(0,scope_1.readLensScope)(e.root),d=a.key===e.wsKey,y=d&&a.sockNs!==e.wsKey?a.sockNs:void 0,{routes:p,services:w,instance:m}=(0,discovery_1.discoverRoutes)(e.root,r,void 0,e.wsKey,o,y,d?a.instance:void 0),b=(0,workspace_middleware_1.findGatewayMiddleware)(e.root,r),$={wsKey:e.wsKey,...y?{sockNs:y}:{},root:e.root,routes:p,services:w,...m?{instance:m}:{},...b?{middleware:b}:{}},K=(0,registration_guards_1.refuseRegistration)(t.registry,$);if(K)return console.warn(`[gateway] refused to register '${e.wsKey}' from ${e.root}: ${K.error}`),{status:409,json:K};if(u)return m?{status:200,json:{wsKey:e.wsKey,root:e.root,...instanceSummary($),env:m.env}}:{status:400,json:{error:`'${e.wsKey}' has no resolvable instance configuration`}};try{const n=(0,service_keys_1.adoptServiceKeys)(t,e.root,w,e.wsKey);n.added.length>0&&console.log(`[gateway] adopted east-west keys for ${e.wsKey}: ${n.added.join(", ")}`),n.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}: ${n.regenerated.join(", ")}`)}catch(n){console.error(`[gateway] could not adopt east-west keys for ${e.wsKey}: ${n.message}`)}try{(0,workspace_log_1.workspaceLogWriter)(e.root)?.(`[gateway] ${new Date().toISOString()} this workspace ('${e.wsKey}') is served by the daemon at ${t.root} (pid ${process.pid}); its gateway-level log is ${path.join(t.root,".lensmcp","gateway.log")}. Pod output for this workspace follows here. Attach live with \`lensmcp logs <service>\`.
|
|
2
|
+
`)}catch{}let j;if(t.graphStores){const n={root:e.root,daemonRoot:t.root,otherRoots:t.registry.keys().filter(g=>g!==e.wsKey).flatMap(g=>t.registry.get(g)?.root??[])};j=e.projectGraph===void 0?void 0:(0,guest_graph_1.admitForwardedGraph)(e.projectGraph,{...n,bytes:graphShare(e,s)}),j?.refused!==void 0&&console.warn(`[gateway] refused the project graph '${e.wsKey}' forwarded from ${e.root}: ${j.refused} \u2014 its services start through \`nx run\` until the gateway computes it`),(0,registration_guards_1.sameRoot)(e.root,t.root)||t.graphStores.set((0,guest_graph_1.createGuestGraphStore)({wsKey:e.wsKey,root:e.root,...j?{admission:j}:{},context:n}))}return t.registry.register($),await c.onRegister?.($),t.rebuildRoutes(),t.root&&!(0,registration_guards_1.sameRoot)(e.root,t.root)&&(0,guest_replay_1.recordGuest)({wsKey:e.wsKey,root:e.root,projects:r,...e.projectDependencies?{projectDependencies:e.projectDependencies}:{},daemonRoot:t.root,registeredAt:new Date().toISOString()}),{status:200,json:{ok:!0,wsKey:e.wsKey,routes:p.length,services:w.length,workspaces:t.registry.keys(),...t.graphStores?{projectGraph:graphVerdict(j)}:{}}}},"register"),_=l((e,s)=>{const u=t.registry.get(e);if(!u)return{error:`no workspace '${e}' is registered`,workspaces:t.registry.keys()};const r=u.services.find(o=>o.project===s);return r?{svc:r}:{error:`'${e}' has no service '${s}'`,services:u.services.map(o=>o.project)}},"findService"),S=l(e=>{let s=(e.stoppingUntil??0)>Date.now();if(e.stoppingPid)try{process.kill(e.stoppingPid,0),s=!0}catch{e.stoppingPid=void 0}return{service:e.project,state:e.state,stopped:e.manualStopped===!0&&!s,ready:!e.manualStopped&&e.state==="up"&&e.pool.socks.length>0,busy:s||e.actionStarting===!0||!!e.rolling||!!(e.restart&&!e.restart.done),failure:e.actionFailure??(!e.manualStopped&&e.restart?.done?.ok===!1?e.restart.done.error:void 0)}},"serviceView"),T=l((e,s)=>(e.manualStopped=!1,e.actionFailure=void 0,e.restart=void 0,e.actionStarting=!0,s(e).then(u=>(!u&&!e.manualStopped&&(e.actionFailure="Service did not become ready"),u),u=>(e.actionFailure=String(u).slice(0,500),!1)).finally(()=>{e.actionStarting=!1})),"startService"),O=l((e,s)=>({project:e.project,wsKey:e.wsKey,host:e.decl.host,state:e.state,...e.generation!==void 0?{generation:e.generation}:{},...e.restart?{restart:e.restart}:{},...(0,awake_1.idleFields)(e,(0,warm_start_1.serviceIdleSec)(t,e),s)}),"statusRow");let P=Promise.resolve();const A=c.startTurn??(e=>{const s=P.then(e);return P=s.catch(()=>{}),s}),E=l(e=>({wsKey:e.wsKey,service:e.project,state:e.state,...e.generation!==void 0?{generation:e.generation}:{},...e.restart?{restart:e.restart}:{}}),"restartView"),G=http.createServer((e,s)=>{(async()=>{try{const u=(e.url??"").split("?")[0];if(e.method==="GET"&&u==="/list")return i(s,200,{workspaces:t.registry.keys().map(r=>{const o=t.registry.get(r);return{key:r,root:o?.root,routes:o?.routes.length??0,services:o?.services.length??0,...instanceSummary(o)}})});if(e.method==="GET"&&u==="/status"){const r=(0,exports.clusterVersion)();return i(s,200,{daemon:{pid:process.pid,wsKey:t.wsKey,...r?{version:r}:{}},workspaces:t.registry.keys().map(o=>{const a=t.registry.get(o);return{key:o,root:a?.root,routes:a?.routes.length??0,services:a?.services.length??0,isDaemon:o===t.wsKey,...instanceSummary(a)}}),services:t.registry.allServices().map(o=>O(o,Date.now()))})}if(e.method==="GET"&&u==="/instance-env"){const r=new URL(e.url??"/","http://control").searchParams.get("wsKey")??"",o=t.registry.get(r);return o?o.instance?i(s,200,{wsKey:r,root:o.root,...instanceSummary(o),env:o.instance.env}):i(s,404,{error:`'${r}' is not an instance`}):i(s,404,{error:`no workspace '${r}' is registered`})}if(e.method==="POST"&&u==="/instance-env"){const{value:r,bytes:o}=await readJsonBody(e,CONTROL_BODY_BYTES+guest_graph_1.MAX_FORWARDED_GRAPH_BYTES),a=await v(r,o,!0);return i(s,a.status,a.json)}if(u==="/service"&&(e.method==="GET"||e.method==="POST")){const r=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{};if(typeof r.wsKey!="string"||typeof r.service!="string")return i(s,400,{error:"wsKey and service are required"});const o=_(r.wsKey,r.service);if("error"in o)return i(s,404,o);const a=o.svc,d=l(()=>S(a),"view");if(e.method==="GET")return i(s,200,d());const y=r.action;if(!["start","stop","restart"].includes(String(y)))return i(s,400,{error:"action must be start, stop or restart"});if(d().busy&&!(y==="stop"&&a.actionStarting&&!a.rolling))return i(s,409,{error:"A service lifecycle operation is already in progress"});if(y==="stop")return!a.proc&&a.pool.socks.length&&!a.manualStopped?i(s,409,{error:"This gateway does not own the adopted service process; stop it with its owner"}):(a.manualStopped=!0,a.stoppingPid=a.proc?.pid,a.stoppingUntil=Date.now()+(a.proc?1600:0),c.killService(a,"workspace action stop"),a.state="down",i(s,202,d()));if(y==="restart"){if(!c.restartService)return i(s,501,{error:"This gateway cannot restart services"});const w=c.restartService(a,{hard:r.hard===!0,reason:"workspace action restart"});return"refused"in w?i(s,409,{error:w.refused}):i(s,202,d())}if(!c.ensureService)return i(s,501,{error:"This gateway cannot start services"});const p=a.state!=="up"&&!a.proc;return T(a,c.ensureService),i(s,202,{...d(),started:p})}if(e.method==="POST"&&u==="/awake"){const r=(await readJsonBody(e)).value??{},o=typeof r.wsKey=="string"?r.wsKey:"";if(!o)return i(s,400,{error:"wsKey is required"});const a=typeof r.forSec=="number"?r.forSec:NaN;if(!Number.isFinite(a)||a<0)return i(s,400,{error:"forSec is required: the seconds to hold them awake, 0 to release the hold"});if(a>awake_1.MAX_AWAKE_SEC)return i(s,400,{error:`forSec ${a} is over the ${awake_1.MAX_AWAKE_SEC} s (4 h) a hold may last \u2014 ask again before it ends to keep them longer`,maxSec:awake_1.MAX_AWAKE_SEC});const d=r.services;if(d!==void 0&&(!Array.isArray(d)||d.length===0||!d.every(n=>typeof n=="string"&&n.length>0)))return i(s,400,{error:"services, when given, names one service or more"});const y=t.registry.get(o);if(!y)return i(s,404,{error:`no workspace '${o}' is registered`,workspaces:t.registry.keys()});const p=[];for(const n of d??[]){const g=_(o,n);if("error"in g)return i(s,404,g);p.includes(g.svc)||p.push(g.svc)}const w=p.length>0;w||p.push(...y.services);const m=Date.now();if(a===0){for(const n of p)(n.awakeUntil??0)<=m||(n.awakeUntil=m,c.emitService?.(n,"info",`awake hold released: ${n.project}`,{kind:"service-awake",project:n.project,until:null,released:!0}));return i(s,202,{ok:!0,wsKey:o,forSec:0,released:!0,services:p.map(n=>O(n,m))})}const b=m+Math.round(a*1e3),$=[],K=l((n,g)=>({at:m,outcome:n,...g?{reason:g}:{}}),"asked");for(const n of p)n.awakeUntil=Math.max(n.awakeUntil??0,b),n.state==="up"?n.wake=K("up"):S(n).busy?n.wake=K("skipped","a stop, start or restart of it is under way \u2014 `lensmcp instance status` shows how it ends"):n.manualStopped&&!w?n.wake=K("skipped","stopped by hand \u2014 name it to start it"):c.ensureService?(n.manualStopped=!1,n.wake=K("queued"),$.push({svc:n,wake:n.wake})):n.wake=K("failed","this gateway cannot start services"),c.emitService?.(n,"info",`awake until ${new Date(n.awakeUntil).toISOString()}: ${n.project}`,{kind:"service-awake",project:n.project,until:n.awakeUntil,forSec:a,wake:n.wake.outcome});const j=c.ensureService;return $.length>0&&j&&A(()=>(0,awake_1.wakeServices)($,{start:l(n=>T(n,j),"start"),available:c.available??(()=>(0,warm_start_1.availableMemory)()),minFreeBytes:c.minFreeBytes??(0,warm_start_1.warmMinFreeBytes)(),current:l(n=>!t.stopped?.()&&!!t.registry.get(n.wsKey)?.services.includes(n),"current"),busy:l(n=>S(n).busy,"busy"),emit:l((n,g,D,R)=>c.emitService?.(n,g,D,R),"emit"),log:l(n=>console.log(n),"log")})).catch(n=>console.error(`[gateway] wake of ${o} failed:`,n)),i(s,202,{ok:!0,wsKey:o,forSec:a,until:b,services:p.map(n=>O(n,m))})}if(u==="/restart"&&(e.method==="POST"||e.method==="GET")){const r=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},o=typeof r.wsKey=="string"?r.wsKey:"",a=typeof r.service=="string"?r.service:"";if(!o||!a)return i(s,400,{error:"wsKey and service are required"});const d=_(o,a);if("error"in d)return i(s,404,d);const y=d.svc;if(e.method==="GET")return i(s,200,E(y));if(!c.restartService)return i(s,501,{error:"this gateway cannot restart a service"});const p=typeof r.reason=="string"&&r.reason.trim()?r.reason.trim().slice(0,200):"asked for",w=c.restartService(y,{hard:r.hard===!0,reason:p});return"refused"in w?i(s,409,{error:w.refused,...E(y)}):i(s,202,{ok:!0,already:w.already,...E(y)})}if(e.method==="GET"&&u==="/identities"||e.method==="POST"&&u==="/act"){const r=e.method==="GET"?Object.fromEntries(new URL(e.url??"/","http://control").searchParams):(await readJsonBody(e)).value??{},o=typeof r.wsKey=="string"?r.wsKey:"";if(!o)return i(s,400,{error:"wsKey is required"});if(!t.registry.get(o)&&o!==t.wsKey)return i(s,404,{error:`no workspace '${o}' is registered`,workspaces:t.registry.keys()});if(!c.act)return i(s,501,{error:"acting as a signed-in user is turned off on this gateway (LENSMCP_GW_ACT=0)"});if(e.method==="GET")return i(s,200,{wsKey:o,identities:c.act.list(o)});const a=l(w=>typeof r[w]=="string"&&r[w].trim()?r[w].trim().slice(0,256):void 0,"pick"),d=a("user"),y=a("tenant"),p=c.act.mint(o,{...d?{user:d}:{},...y?{tenant:y}:{}});return"error"in p?i(s,404,{wsKey:o,...p}):i(s,200,{wsKey:o,...p})}if(e.method==="POST"&&u==="/register"){const{value:r,bytes:o}=await readJsonBody(e,CONTROL_BODY_BYTES+guest_graph_1.MAX_FORWARDED_GRAPH_BYTES),a=await v(r,o);return i(s,a.status,a.json)}if(e.method==="POST"&&u==="/unregister"){const r=(await readJsonBody(e)).value;if(!r.wsKey)return i(s,400,{error:"wsKey is required"});const o=(0,registration_guards_1.refuseUnregistration)(t.registry,{wsKey:r.wsKey,...r.root?{root:r.root}:{}},t.wsKey);if(o)return console.warn(`[gateway] refused to unregister '${r.wsKey}'${r.root?` for ${r.root}`:""}: ${o.error}`),i(s,409,o);const a=t.registry.unregister(r.wsKey);if(a){for(const d of a.services){const y=(0,service_keys_1.serviceKeyId)(d.wsKey??r.wsKey,d.project),p=t.serviceKeys[y];p&&(delete t.serviceKeys[y],t.keyToProject.delete(p),t.keyToWorkspace?.delete(p))}for(const d of a.services)c.killService(d,"workspace unregistered");t.graphStores?.delete(a.root),(0,guest_replay_1.forgetGuest)(r.wsKey),c.onUnregister?.(a)}return t.rebuildRoutes(),i(s,200,{ok:!0,removed:!!a,workspaces:t.registry.keys()})}i(s,404,{error:"not found"})}catch(u){if(u instanceof BodyTooLarge)return e.resume(),i(s,413,{error:u.message});i(s,500,{error:u.message})}})()});return G.listen(f,()=>{try{fs.chmodSync(f,384)}catch(e){console.warn(`[gateway] could not restrict ${f} to 0600:`,e.message)}}),{socketPath:f,async replay(){const{replay:e,gone:s}=(0,guest_replay_1.guestsToReplay)(t.root,(0,guest_replay_1.readGuests)()),u={replayed:[],refused:[],gone:s.map(r=>r.wsKey)};for(const r of s)(0,guest_replay_1.forgetGuest)(r.wsKey),console.log(`[gateway] not re-hosting '${r.wsKey}': ${r.root} is no longer a workspace \u2014 forgotten`);for(const r of e)if(!t.registry.has(r.wsKey))try{const o=await v({wsKey:r.wsKey,root:r.root,projects:r.projects,...r.projectDependencies?{projectDependencies:r.projectDependencies}:{}},0);o.status===200?(u.replayed.push(r.wsKey),console.log(`[gateway] re-hosted '${r.wsKey}' (${r.root}) \u2014 registered before this daemon restarted`)):(u.refused.push(r.wsKey),console.warn(`[gateway] could not re-host '${r.wsKey}' (${r.root}): ${JSON.stringify(o.json)} \u2014 \`lensmcp gateway start\` there`))}catch(o){u.refused.push(r.wsKey),console.warn(`[gateway] could not re-host '${r.wsKey}' (${r.root}): ${o.message}`)}return u},close:l(()=>{G.close();try{fs.unlinkSync(f)}catch{}},"close")}}h(startControlServer,"startControlServer"),l(startControlServer,"startControlServer");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var h=Object.defineProperty;var a=(e,r)=>h(e,"name",{value:r,configurable:!0});var l=Object.defineProperty,u=a((e,r)=>l(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"),edge_1=require("./edge");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[s,n,f]=t.split(".");if(!s||!n||!f)return;if(r)return r.verify(t);let o;try{o=JSON.parse(Buffer.from(s,"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(n,"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",y=(0,node_crypto_1.createHmac)("sha256",d).update(`${s}.${n}`).digest("base64url"),p=Buffer.from(f),c=Buffer.from(y);if(!(p.length!==c.length||!(0,node_crypto_1.timingSafeEqual)(p,c)))try{const i=JSON.parse(Buffer.from(n,"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 s=r.roles;Array.isArray(s)&&s.length&&(e.headers["x-user-roles"]=s.map(String).join(","));const n=r.perms;Array.isArray(n)&&n.length&&(e.headers["x-user-permissions"]=n.map(String).join(","))}a(stampIdentity,"stampIdentity"),u(stampIdentity,"stampIdentity");const devAttrs=u((e,r,t)=>({...r,ip:(0,edge_1.clientIpOf)(e),method:(e.method??"GET").toUpperCase(),path:t??(0,edge_1.canonicalPath)(e.url??"/")??(e.url??"/").split("?")[0]}),"devAttrs");exports.devAttrs=devAttrs;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var A=Object.defineProperty;var h=(e,s)=>A(e,"name",{value:s,configurable:!0});var I=Object.defineProperty,p=h((e,s)=>I(e,"name",{value:s,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0}),exports.RETIRING_SUFFIX=void 0,exports.stopRoutingTo=stopRoutingTo,exports.pickSock=pickSock,exports.lensPortFromUpstream=lensPortFromUpstream,exports.rewriteInstanceDecls=rewriteInstanceDecls,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"),pod_sock_1=require("../../../pod-sock"),instance_1=require("./instance"),scope_1=require("./scope");exports.RETIRING_SUFFIX=".retiring";function sockIdentity(e){try{const s=fs.statSync(e);return`${s.dev}:${s.ino}:${s.ctimeMs}`}catch{return}}h(sockIdentity,"sockIdentity"),p(sockIdentity,"sockIdentity");function stopRoutingTo(e,s=()=>!0){let a;try{a=fs.readdirSync(e.dir)}catch{a=[]}const r=a.filter(t=>(0,pod_sock_1.isPodSockName)(t)&&s(t)).map(t=>path.join(e.dir,t));if(r.length===0)return r;const l=e.retired??=new Map;for(const t of r){const d=sockIdentity(t);d!==void 0&&l.set(t,d)}const n=new Set(r);return e.socks=e.socks.filter(t=>!n.has(t)),e.idx=-1,r}h(stopRoutingTo,"stopRoutingTo"),p(stopRoutingTo,"stopRoutingTo");function stillRetired(e,s){const a=e.retired?.get(s);return a===void 0?!1:sockIdentity(s)===a?!0:(e.retired?.delete(s),!1)}h(stillRetired,"stillRetired"),p(stillRetired,"stillRetired");function pickSock(e,s=1500){const a=Date.now();if(a-e.scannedAt>s){try{const r=fs.readdirSync(e.dir),l=r.filter(t=>(0,pod_sock_1.isPodSockName)(t)&&!stillRetired(e,path.join(e.dir,t)));if(e.retired)for(const t of[...e.retired.keys()])r.includes(path.basename(t))||e.retired.delete(t);const n=l.filter(t=>!r.includes(t+exports.RETIRING_SUFFIX));e.socks=(n.length>0?n:l).map(t=>path.join(e.dir,t))}catch{e.socks=[]}e.scannedAt=a}if(e.socks.length!==0)return e.idx=(e.idx+1)%e.socks.length,e.socks[e.idx]}h(pickSock,"pickSock"),p(pickSock,"pickSock");function lensPortFromUpstream(e){if(!e)return;const s=/:(\d+)/.exec(e);return s?Number(s[1]):void 0}h(lensPortFromUpstream,"lensPortFromUpstream"),p(lensPortFromUpstream,"lensPortFromUpstream");function rewriteInstanceDecls(e,s){const a=e.flatMap(([,n])=>[...n.host?[n.host]:[],...typeof n.internalHost=="string"?[n.internalHost]:[]]),r=s.apex??(0,instance_1.commonApex)(a);if(!r)return console.warn(`[gateway] instance '${s.name}': the declared hosts share no apex \u2014 set \`instance.apex\` in .lensmcp/config.json; no host is served for it.`),{decls:e.map(([n,t])=>[n,{...t,host:void 0,internalHost:!1,default:!1}])};const l=[];for(const[n,t]of e){const d={...t};if(t.host){const c=(0,instance_1.rewriteHost)(t.host,r,s.name);c||console.warn(`[gateway] instance '${s.name}': ${n}'s host ${t.host} is outside ${r} \u2014 not served for the instance.`),d.host=c}if(typeof t.internalHost=="string"){const c=(0,instance_1.rewriteHost)(t.internalHost,r,s.name);c||console.warn(`[gateway] instance '${s.name}': ${n}'s internal host ${t.internalHost} is outside ${r} \u2014 not served for the instance.`),d.internalHost=c??!1}l.push([n,d])}return{decls:l,baseApex:r,apex:`${s.name}.${r}`}}h(rewriteInstanceDecls,"rewriteInstanceDecls"),p(rewriteInstanceDecls,"rewriteInstanceDecls");function discoverRoutes(e,s,a=os.tmpdir(),r="",l={},n=r,t){const d=n||r,c=p(i=>d?path.join(a,d,`${i}-devserver`):path.join(a,`${i}-devserver`),"sockDirFor"),u=[],g=[],_=[],H=[],S={};let y,w=[];const F=new Map;for(const[i,o]of Object.entries(s))try{const f=JSON.parse(fs.readFileSync(path.join(e,o.root,"project.json"),"utf8")),m=f.cluster??f.davnx;m&&(w.push([i,m]),F.set(i,o.root))}catch{continue}let v={};t&&({decls:w,...v}=rewriteInstanceDecls(w,t));for(const[i,o]of w){const f={root:F.get(i)};let m,j,k,R;if(!o.service&&o.sources!==void 0&&console.warn(`[gateway] ${i}: cluster.sources is read from pod (service) decls only \u2014 ignored.`),o.service)o.lens&&console.warn(`[gateway] ${i}: cluster.lens is ignored for pod (service) decls \u2014 backends are instrumented by serve-hmr.`),m={dir:c(o.service),socks:[],idx:-1,scannedAt:0},j={project:i,decl:o,root:e,wsKey:r,...d!==r?{sockNs:d}:{},pool:m,state:"down",lastUsed:Date.now(),inflight:0,lastScaleAt:0},g.push(j);else if(o.lens){const x=o.port??lensPortFromUpstream(o.upstream)??5173;k=`http://localhost:${x}`,R={project:i,...r?{workspaceKey:r}:{},workspaceRoot:e,projectRoot:path.join(e,f.root),port:x,...l[i]?.length?{sourceRoots:l[i]}:{}},_.push(R)}else if(k=o.upstream??(o.port?`http://localhost:${o.port}`:void 0),!k){console.warn(`[gateway] ${i}: no service/upstream/port \u2014 skipped.`);continue}const b={...r?{wsKey:r}:{},project:i,prependPrefix:o.prependPrefix,target:k,pool:m,svc:j,prefix:o.path,auth:o.auth,lens:R};if(o.host&&(u.push({...b,host:o.host}),H.push(o.host),S[i]={host:o.host},m&&o.internalHost!==!1)){const x=typeof o.internalHost=="string"?o.internalHost:`internal.${o.host}`;u.push({...b,host:x,internal:!0}),S[i]={host:o.host,internalHost:x}}o.default&&(y={...b,prefix:void 0})}y&&(y.apex=v.apex??(0,scope_1.baseDomainOf)(H),u.push(y));let $;if(t&&v.apex&&v.baseApex){const i={name:t.name,apex:v.apex,baseApex:v.baseApex},o=(0,instance_1.instanceEnv)(e,(0,instance_1.instanceBuiltinEnv)(i,S));$={...i,baseKey:t.baseKey,env:o,...t.frozen?{frozen:!0}:{}};for(const f of g)f.instanceEnv=o,t.frozen&&(f.frozen=!0);for(const f of _)f.env=o,t.frozen&&(f.frozen=!0)}return(0,manifest_1.sortBySpecificity)(u),{routes:u,services:g,...$?{instance:$}:{}}}h(discoverRoutes,"discoverRoutes"),p(discoverRoutes,"discoverRoutes");function projectSourceRoots(e,s,a={}){const r={};for(const l of Object.keys(s)){const n=new Set,t=p(c=>{if(!n.has(c)){n.add(c);for(const u of a[c]??[])t(u)}},"visit");t(l);const d=[...n].map(c=>s[c]?.root).filter(c=>typeof c=="string").map(c=>path.resolve(e,c)).filter(c=>{const u=path.relative(e,c);return u===""||u!==".."&&!u.startsWith(`..${path.sep}`)&&!path.isAbsolute(u)});r[l]=[...new Set(d)]}return r}h(projectSourceRoots,"projectSourceRoots"),p(projectSourceRoots,"projectSourceRoots");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var E=Object.defineProperty;var c=(e,r)=>E(e,"name",{value:r,configurable:!0});var p=Object.defineProperty,o=c((e,r)=>p(e,"name",{value:r,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.PREFLIGHT_METHODS_ROUTED=exports.PREFLIGHT_METHODS_NOROUTE=exports.corsPreflight=exports.PREFLIGHT_MAX_AGE=exports.cors=exports.isRoutedOrigin=exports.callerContextOf=exports.claimedCallerIdOf=exports.CLAIMED_CALLER_ID=exports.CALLER_CONTEXT=exports.CALLER_CONTEXT_HEADERS=exports.GATEWAY_OWNED_HEADERS=exports.sendError=exports.stampForwardingHeaders=exports.clientIpOf=exports.resolveClientIp=exports.CLIENT_IP=exports.rememberRawTarget=exports.rawTargetOf=exports.RAW_TARGET=exports.canonicalPath=exports.canonicalize=exports.ENCODED_STRUCTURE=void 0,exports.stripOwnedHeaders=stripOwnedHeaders;const gateway_errors_1=require("../gateway-errors"),client_ip_1=require("../client-ip"),manifest_1=require("../manifest"),act_1=require("./act");var canonical_path_1=require("../canonical-path");Object.defineProperty(exports,"ENCODED_STRUCTURE",{enumerable:!0,get:o(function(){return canonical_path_1.ENCODED_STRUCTURE},"get")}),Object.defineProperty(exports,"canonicalize",{enumerable:!0,get:o(function(){return canonical_path_1.canonicalize},"get")}),Object.defineProperty(exports,"canonicalPath",{enumerable:!0,get:o(function(){return canonical_path_1.canonicalPath},"get")}),Object.defineProperty(exports,"RAW_TARGET",{enumerable:!0,get:o(function(){return canonical_path_1.RAW_TARGET},"get")}),Object.defineProperty(exports,"rawTargetOf",{enumerable:!0,get:o(function(){return canonical_path_1.rawTargetOf},"get")}),Object.defineProperty(exports,"rememberRawTarget",{enumerable:!0,get:o(function(){return canonical_path_1.rememberRawTarget},"get")}),exports.CLIENT_IP=Symbol.for("lensmcp.gateway.clientIp");const FORWARDED=Symbol.for("lensmcp.gateway.forwarded"),peerOnly=(0,client_ip_1.createClientIpResolver)({}),resolverOf=o(e=>e.clientIp??peerOnly,"resolverOf"),peerOf=o(e=>e.socket?.remoteAddress??"","peerOf"),resolveClientIp=o((e,r)=>{const t=resolverOf(e).clientIpOf(r,peerOf(r));return r[exports.CLIENT_IP]=t,t},"resolveClientIp");exports.resolveClientIp=resolveClientIp;const clientIpOf=o(e=>e[exports.CLIENT_IP]??peerOf(e),"clientIpOf");exports.clientIpOf=clientIpOf;const stampForwardingHeaders=o((e,r)=>{const t=r;if(t[FORWARDED])return;t[FORWARDED]=!0;const s=peerOf(r);r.headers["x-forwarded-for"]=resolverOf(e).forwardedFor(r,s),r.headers["x-forwarded-proto"]=e.https?"https":"http";const n=r.headers.host;(resolverOf(e).trustProxyHops===0||!r.headers["x-forwarded-host"])&&(n?r.headers["x-forwarded-host"]=n:delete r.headers["x-forwarded-host"])},"stampForwardingHeaders");exports.stampForwardingHeaders=stampForwardingHeaders;const sendError=o((e,r,t,s,n,i,l)=>{const a=(0,gateway_errors_1.ensureRequestId)(r);return e.headersSent||e.writeHead(t,{"content-type":gateway_errors_1.GATEWAY_ERROR_CONTENT_TYPE,"x-request-id":a,...l??{}}),e.end((0,gateway_errors_1.gatewayErrorBody)(t,s,a,n,i)),a},"sendError");exports.sendError=sendError,exports.GATEWAY_OWNED_HEADERS=["x-internal-token","x-api-key-id","x-api-key-workspace","x-user-id","x-tenant-id","x-user-roles","x-user-permissions",act_1.ACT_HEADER],exports.CALLER_CONTEXT_HEADERS=["x-tenant-id","x-user-id"],exports.CALLER_CONTEXT=Symbol.for("lensmcp.gateway.callerContext"),exports.CLAIMED_CALLER_ID=Symbol.for("lensmcp.gateway.claimedCallerId");const CALLER_ID_VALUE=/^[a-z0-9][a-z0-9._-]{0,63}$/i,CALLER_CONTEXT_VALUE=/^[\x21-\x2b\x2d-\x7e]{1,128}$/;function stripOwnedHeaders(e){const r={};for(const s of exports.CALLER_CONTEXT_HEADERS){const n=e.headers[s];typeof n=="string"&&CALLER_CONTEXT_VALUE.test(n)&&(r[s]=n)}e[exports.CALLER_CONTEXT]=r;const t=e.headers["x-api-key-id"];typeof t=="string"&&CALLER_ID_VALUE.test(t)&&(e[exports.CLAIMED_CALLER_ID]=t),(0,act_1.setActTicketAside)(e);for(const s of exports.GATEWAY_OWNED_HEADERS)delete e.headers[s]}c(stripOwnedHeaders,"stripOwnedHeaders"),o(stripOwnedHeaders,"stripOwnedHeaders");const claimedCallerIdOf=o(e=>e[exports.CLAIMED_CALLER_ID],"claimedCallerIdOf");exports.claimedCallerIdOf=claimedCallerIdOf;const callerContextOf=o(e=>e[exports.CALLER_CONTEXT]??{},"callerContextOf");exports.callerContextOf=callerContextOf;const isRoutedOrigin=o((e,r)=>{if(!e||e==="null")return!1;let t;try{t=new URL(e).hostname.toLowerCase()}catch{return!1}return t?t==="localhost"||t==="127.0.0.1"||t==="::1"?!0:r.some(s=>!!s.host&&(0,manifest_1.hostMatches)(s.host,t)):!1},"isRoutedOrigin");exports.isRoutedOrigin=isRoutedOrigin;const cors=o((e,r,t)=>{const s=e.headers.origin;typeof s!="string"||s===""||(r.setHeader("vary","Origin"),(0,exports.isRoutedOrigin)(s,t)&&(r.setHeader("access-control-allow-origin",s),r.setHeader("access-control-allow-credentials","true")))},"cors");exports.cors=cors,exports.PREFLIGHT_MAX_AGE="7200";const corsPreflight=o((e,r,t,s)=>{(0,exports.cors)(e,r,s),r.writeHead(204,{"access-control-allow-methods":t,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*"),"access-control-max-age":exports.PREFLIGHT_MAX_AGE}),r.end()},"corsPreflight");exports.corsPreflight=corsPreflight,exports.PREFLIGHT_METHODS_NOROUTE="GET,HEAD,OPTIONS",exports.PREFLIGHT_METHODS_ROUTED="GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`).pop();p(new Error(`the graph computation in ${e} exited ${
|
|
1
|
+
"use strict";var _=Object.defineProperty;var u=(e,r)=>_(e,"name",{value:r,configurable:!0});var l=Object.defineProperty,f=u((e,r)=>l(e,"name",{value:r,configurable:!0}),"f");Object.defineProperty(exports,"__esModule",{value:!0}),exports.GUEST_GRAPH_RETRY_MS=exports.MAX_FORWARDED_PROJECTS=exports.MAX_FORWARDED_GRAPH_BYTES=exports.FORWARDED_GRAPH_FORMAT=void 0,exports.ownedByThisUser=ownedByThisUser,exports.admitForwardedGraph=admitForwardedGraph,exports.compactSourceMaps=compactSourceMaps,exports.projectsOnly=projectsOnly,exports.snapshotWorkspaceGraph=snapshotWorkspaceGraph,exports.graphSnapshotScript=graphSnapshotScript,exports.guestGraphEnv=guestGraphEnv,exports.forwardedGraphInChild=forwardedGraphInChild,exports.createGuestGraphStore=createGuestGraphStore;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path")),pod_env_1=require("./pod-env"),project_graph_store_1=require("./project-graph-store"),registration_guards_1=require("./registration-guards");exports.FORWARDED_GRAPH_FORMAT=1,exports.MAX_FORWARDED_GRAPH_BYTES=8*1024*1024,exports.MAX_FORWARDED_PROJECTS=2e4,exports.GUEST_GRAPH_RETRY_MS=6e4;const GUEST_GRAPH_TIMEOUT_MS=12e4,SIGNATURE=/^(?:-|\?|\d+(?:\.\d+)?:\d+:\d+)$/;function megabytes(e){return`${(e/(1024*1024)).toFixed(1)} MB`}u(megabytes,"megabytes"),f(megabytes,"megabytes");const isRecord=f(e=>!!e&&typeof e=="object"&&!Array.isArray(e),"isRecord");function insideWorkspace(e){return e===""||path.isAbsolute(e)||/^[a-zA-Z]:/.test(e)?!1:!path.posix.normalize(e.replace(/\\/g,"/")).split("/").includes("..")}u(insideWorkspace,"insideWorkspace"),f(insideWorkspace,"insideWorkspace");function ownedByThisUser(e){const r=fs.statSync(e,{throwIfNoEntry:!1});if(!r?.isDirectory())return!1;if(typeof process.getuid!="function")return!0;const t=process.getuid();return r.uid===t||t===0&&r.uid===Number(process.env.SUDO_UID)}u(ownedByThisUser,"ownedByThisUser"),f(ownedByThisUser,"ownedByThisUser");function relativeDeclaringFiles(e,r){return(0,project_graph_store_1.declaringFiles)(e,r).map(t=>path.relative(e,t).split(path.sep).join("/"))}u(relativeDeclaringFiles,"relativeDeclaringFiles"),f(relativeDeclaringFiles,"relativeDeclaringFiles");function shapeProblem(e){if(!isRecord(e)||!isRecord(e.nodes)||!isRecord(e.dependencies))return"it has no `nodes` and `dependencies`";const r=e.nodes,t=Object.keys(r);if(t.length>exports.MAX_FORWARDED_PROJECTS)return`it names ${t.length} projects (at most ${exports.MAX_FORWARDED_PROJECTS})`;for(const o of t){const s=r[o];if(!isRecord(s)||s.name!==o||typeof s.type!="string"||!isRecord(s.data))return`project '${o}' is not a project node`;const n=s.data.root;if(typeof n!="string"||!insideWorkspace(n))return`project '${o}' has a root outside the workspace (${String(n)})`;const p=s.data.targets;if(p!==void 0&&(!isRecord(p)||!Object.values(p).every(isRecord)))return`project '${o}' has malformed targets`}for(const[o,s]of Object.entries(e.dependencies)){if(!(o in r)||!Array.isArray(s))return`the dependencies of '${o}' are not a known project's`;for(const n of s)if(!isRecord(n)||n.source!==o||typeof n.target!="string"||!(n.target in r)||typeof n.type!="string")return`a dependency of '${o}' is malformed`}}u(shapeProblem,"shapeProblem"),f(shapeProblem,"shapeProblem");function sourceMapsProblem(e){if(e!==void 0){if(!isRecord(e))return"its source maps are not a map";for(const[r,t]of Object.entries(e)){if(!isRecord(t))return`the source map of ${r} is malformed`;for(const o of Object.values(t))if(!Array.isArray(o)||o.length!==2||o[0]!==null&&typeof o[0]!="string"||typeof o[1]!="string")return`the source map of ${r} is malformed`}}}u(sourceMapsProblem,"sourceMapsProblem"),f(sourceMapsProblem,"sourceMapsProblem");function admitForwardedGraph(e,r){const t=f(a=>({refused:a}),"refuse");if(r.bytes>exports.MAX_FORWARDED_GRAPH_BYTES)return t(`${megabytes(r.bytes)} is over the ${megabytes(exports.MAX_FORWARDED_GRAPH_BYTES)} limit`);if(!isRecord(e))return t("it is not a project graph");if(e.format!==exports.FORWARDED_GRAPH_FORMAT)return t(`its format is ${String(e.format)}, and this gateway reads ${exports.FORWARDED_GRAPH_FORMAT}`);const o=e.root;if(typeof o!="string"||!path.isAbsolute(o))return t("it does not say which workspace it describes");if(!(0,registration_guards_1.sameRoot)(o,r.root))return t(`it describes ${o}, not the registering workspace ${r.root}`);if((0,registration_guards_1.sameRoot)(r.root,r.daemonRoot))return t(`${r.root} is the gateway's own workspace \u2014 the gateway reads that graph itself`);if(r.otherRoots.some(a=>(0,registration_guards_1.sameRoot)(a,r.root)))return t(`${r.root} is registered under another key`);if(!ownedByThisUser(r.root))return t(`${r.root} is not a directory this user owns`);const s=e.startedAt;if(typeof s!="number"||!Number.isFinite(s))return t("it does not say when it was computed");const n=shapeProblem(e.projectGraph)??sourceMapsProblem(e.sourceMaps);if(n)return t(n);const p=e.projectGraph,i=e.files;if(!isRecord(i))return t("it carries no fingerprint of its declaring files");const c=(0,registration_guards_1.canonicalRoot)(r.root),h=relativeDeclaringFiles(c,p),d=Object.keys(i),g=h.filter(a=>!(a in i));return g.length>0||d.length!==h.length?t(`its fingerprint does not cover exactly the files that declare it${g.length>0?` (no ${g[0]})`:""}`):Object.values(i).every(a=>typeof a=="string"&&SIGNATURE.test(a))?{snapshot:{root:c,startedAt:s,files:new Map(h.map(a=>[path.join(c,a),i[a]])),projectGraph:{nodes:p.nodes,dependencies:p.dependencies,externalNodes:{}},...e.sourceMaps!==void 0?{sourceMaps:e.sourceMaps}:{}}}:t("its fingerprint is malformed")}u(admitForwardedGraph,"admitForwardedGraph"),f(admitForwardedGraph,"admitForwardedGraph");function compactSourceMaps(e,r){const t={};for(const o of Object.values(e.nodes)){const s=r[o.data.root];if(!s)continue;const n=new Set;for(const i of Object.keys(o.data.targets??{})){const c=`targets.${i}`,h=new Set;for(const[d,g]of Object.entries(s)){if(d!==c&&!d.startsWith(`${c}.`))continue;const a=JSON.stringify(g);h.has(a)||(h.add(a),n.add(d))}}const p={};for(const[i,c]of Object.entries(s))n.has(i)&&(p[i]=c);t[o.data.root]=p}return t}u(compactSourceMaps,"compactSourceMaps"),f(compactSourceMaps,"compactSourceMaps");function projectsOnly(e){const r={};for(const[t,o]of Object.entries(e.dependencies))t in e.nodes&&(r[t]=o.filter(s=>s.target in e.nodes));return{nodes:e.nodes,dependencies:r}}u(projectsOnly,"projectsOnly"),f(projectsOnly,"projectsOnly");async function snapshotWorkspaceGraph(e,r=()=>(0,project_graph_store_1.computeProjectGraphInProcess)(e)){const t=Date.now(),o=(0,project_graph_store_1.filesystemNow)(),s=await r(),n=projectsOnly(s.projectGraph),p={};for(const i of(0,project_graph_store_1.declaringFiles)(e,s.projectGraph)){const c=fs.statSync(i,{throwIfNoEntry:!1});p[path.relative(e,i).split(path.sep).join("/")]=c&&c.mtimeMs>o?"?":(0,project_graph_store_1.statSignature)(i)}return{format:exports.FORWARDED_GRAPH_FORMAT,root:e,startedAt:t,projectGraph:n,...s.sourceMaps?{sourceMaps:compactSourceMaps(n,s.sourceMaps)}:{},files:p}}u(snapshotWorkspaceGraph,"snapshotWorkspaceGraph"),f(snapshotWorkspaceGraph,"snapshotWorkspaceGraph");function graphSnapshotScript(e=path.join(__dirname,"../../..")){const r=path.join(e,"graph-snapshot.js");if(fs.existsSync(r))return{path:r,execArgv:[]};const t=path.join(e,"graph-snapshot.ts");if(fs.existsSync(t))try{return{path:t,execArgv:["--require",require.resolve("@swc-node/register")]}}catch{return}}u(graphSnapshotScript,"graphSnapshotScript"),f(graphSnapshotScript,"graphSnapshotScript");function guestGraphEnv(e,r,t){const o={...(0,pod_env_1.podBaseEnv)(e,r,t)};for(const s of Object.keys(o))(s.startsWith("NX_TASK_")||["NX_WORKSPACE_ROOT","NX_WORKSPACE_ROOT_PATH","NX_WORKSPACE_DATA_DIRECTORY","NX_PROJECT_GRAPH_CACHE_DIRECTORY"].includes(s))&&delete o[s];return o.NX_DAEMON="false",o.NX_ISOLATE_PLUGINS="false",o}u(guestGraphEnv,"guestGraphEnv"),f(guestGraphEnv,"guestGraphEnv");function forwardedGraphInChild(e,r,t=graphSnapshotScript()){if(!t)return Promise.reject(new Error("graph-snapshot.js is missing from @lensmcp/cluster"));const o=fs.mkdtempSync(path.join(os.tmpdir(),"lensmcp-graph-")),s=path.join(o,"graph.json");return new Promise((n,p)=>{let i="";const c=(0,node_child_process_1.spawn)(process.execPath,[...t.execArgv,t.path,s],{cwd:e,env:r,stdio:["ignore","ignore","pipe"]});c.stderr?.on("data",d=>{i.length<4096&&(i+=d.toString())});const h=setTimeout(()=>c.kill("SIGKILL"),GUEST_GRAPH_TIMEOUT_MS);h.unref(),c.on("error",d=>{clearTimeout(h),p(d)}),c.on("exit",(d,g)=>{if(clearTimeout(h),d!==0){const a=i.trim().split(`
|
|
2
|
+
`).pop();p(new Error(`the graph computation in ${e} exited ${g??d}${a?` \u2014 ${a}`:""}`));return}try{const a=fs.statSync(s).size;n({bytes:a,graph:a>exports.MAX_FORWARDED_GRAPH_BYTES?void 0:JSON.parse(fs.readFileSync(s,"utf8"))})}catch(a){p(a)}})}).finally(()=>fs.rmSync(o,{recursive:!0,force:!0}))}u(forwardedGraphInChild,"forwardedGraphInChild"),f(forwardedGraphInChild,"forwardedGraphInChild");function createGuestGraphStore(e,r=process.env){const{wsKey:t,admission:o,context:s}=e,n=(0,registration_guards_1.canonicalRoot)(e.root),p=e.compute??(()=>forwardedGraphInChild(n,guestGraphEnv(r,s.daemonRoot,n))),i=o===void 0?"its registration forwarded none":o.refused!==void 0?`the project graph its registration forwarded was refused: ${o.refused}`:void 0;return(0,project_graph_store_1.createProjectGraphStore)(n,async()=>{const{graph:c,bytes:h}=await p(),d=admitForwardedGraph(c,{...s,bytes:h});if(d.refused!==void 0)throw new Error(`the computed project graph was refused: ${d.refused}`);return{projectGraph:d.snapshot.projectGraph,...d.snapshot.sourceMaps?{sourceMaps:d.snapshot.sourceMaps}:{}}},{...o?.snapshot?{seed:o.snapshot}:{},...i?{error:i}:{},retryAfterFailureMs:exports.GUEST_GRAPH_RETRY_MS,onRefreshed:f(c=>console.log(`[gateway] project graph of '${t}' (${n}) refreshed \u2014 ${Object.keys(c.projectGraph.nodes).length} projects; its services start through the serve runner again`),"onRefreshed")})}u(createGuestGraphStore,"createGuestGraphStore"),f(createGuestGraphStore,"createGuestGraphStore");
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
`,{mode:384}),fs.renameSync(e,t)}
|
|
1
|
+
"use strict";var a=Object.defineProperty;var n=(r,t)=>a(r,"name",{value:t,configurable:!0});var c=Object.defineProperty,o=n((r,t)=>c(r,"name",{value:t,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.guestsFile=guestsFile,exports.readGuests=readGuests,exports.recordGuest=recordGuest,exports.forgetGuest=forgetGuest,exports.guestsToReplay=guestsToReplay;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"));function guestsFile(){return path.join(process.env.LENSMCP_HOME||os.homedir(),".lensmcp","guests.json")}n(guestsFile,"guestsFile"),o(guestsFile,"guestsFile");function readGuests(r=guestsFile()){try{const t=JSON.parse(fs.readFileSync(r,"utf8"));return Array.isArray(t)?t.filter(e=>!!e&&typeof e=="object"&&typeof e.wsKey=="string"&&typeof e.root=="string"&&typeof e.daemonRoot=="string"&&!!e.projects&&typeof e.projects=="object"):[]}catch{return[]}}n(readGuests,"readGuests"),o(readGuests,"readGuests");function writeGuests(r,t){fs.mkdirSync(path.dirname(t),{recursive:!0,mode:448});const e=`${t}.${process.pid}.tmp`;fs.writeFileSync(e,JSON.stringify(r,null,2)+`
|
|
2
|
+
`,{mode:384}),fs.renameSync(e,t)}n(writeGuests,"writeGuests"),o(writeGuests,"writeGuests");function recordGuest(r,t=guestsFile()){try{writeGuests([...readGuests(t).filter(e=>e.wsKey!==r.wsKey),r],t)}catch(e){console.warn(`[gateway] could not record '${r.wsKey}' for replay (${e.message}) \u2014 a daemon restart will not bring it back`)}}n(recordGuest,"recordGuest"),o(recordGuest,"recordGuest");function forgetGuest(r,t=guestsFile()){try{const e=readGuests(t),s=e.filter(i=>i.wsKey!==r);s.length!==e.length&&writeGuests(s,t)}catch{}}n(forgetGuest,"forgetGuest"),o(forgetGuest,"forgetGuest");const same=o((r,t)=>{const e=o(s=>{try{return fs.realpathSync(s)}catch{return path.resolve(s)}},"real");return e(r)===e(t)},"same");function guestsToReplay(r,t){const e=[],s=[];for(const i of t)!same(i.daemonRoot,r)||same(i.root,r)||(fs.existsSync(path.join(i.root,"package.json"))?e:s).push(i);return{replay:e,gone:s}}n(guestsToReplay,"guestsToReplay"),o(guestsToReplay,"guestsToReplay");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var T=Object.defineProperty;var y=(f,n)=>T(f,"name",{value:n,configurable:!0});var O=Object.defineProperty,p=y((f,n)=>O(f,"name",{value:n,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHandler=createHandler;const manifest_1=require("../manifest"),service_keys_1=require("./service-keys"),gateway_errors_1=require("../gateway-errors"),discovery_1=require("./discovery"),edge_1=require("./edge"),observability_1=require("./observability"),chooser_1=require("./chooser");function createHandler(f){const{rt:n,obs:u,auth:v,proxy:E,hooks:i}=f,R=p(e=>(e.headers.host||"").split(":")[0],"hostOf"),S=p((e,t)=>{if((0,edge_1.cors)(e,t,n.routes),e.method==="OPTIONS"){t.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_NOROUTE,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*"),"access-control-max-age":edge_1.PREFLIGHT_MAX_AGE}),t.end();return}const o=(e.headers.host||"").split(":")[0],h=n.routes.filter(r=>r.host&&(0,manifest_1.hostMatches)(r.host,o)&&r.prefix),s=(e.url??"/").split("?")[0];if((s==="/"||s==="/index.html"||s==="")&&n.routes.some(r=>r.host&&(0,manifest_1.hostMatches)(r.host,o)&&r.project===observability_1.LENS_DASHBOARD_PROJECT)){(0,chooser_1.serveChooser)(t,n.wsKey,e);return}if(h.length>0&&(s==="/health"||s==="/healthz")){const r=h.map(l=>({project:l.project,prefix:l.prefix,pods:l.pool?((0,discovery_1.pickSock)(l.pool,0),l.pool.socks.length):void 0,state:l.svc?.state??(l.target?"up":"unknown")})),d=r.every(l=>l.state!=="down"||!0);t.writeHead(d?200:503,{"content-type":"application/json"}),t.end(JSON.stringify({status:"ok",host:o,services:r},null,2));return}if(h.length>0&&s==="/.well-known/services"){t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({host:o,services:h.map(r=>({prefix:r.prefix,project:r.project,internal:!!r.internal}))},null,2));return}(0,edge_1.sendError)(t,e,404,"not_found",h.length>0?`No service mounted at ${s}. Mounted on ${o}: ${[...new Set(h.map(r=>r.prefix))].join(", ")} (+ /health, /.well-known/services)`:"No gateway route for this hostname.")},"handleNoRoute"),H=p(async(e,t)=>{if(!e.headers.host){const r=e.authority??e.headers[":authority"];r&&(e.headers.host=r)}(0,edge_1.stripOwnedHeaders)(e);let o=u.beginTrace(e);const h=Date.now();(0,gateway_errors_1.ensureRequestId)(e),o&&(u.traceStep(o,"received",{host:o.host,method:o.method,url:o.url}),e.__lensmcpTrace=o);const s=i.httpContext(e,t,o);try{const r=e.url??"/",d=(0,edge_1.canonicalize)(r);if(d===void 0){u.traceStep(o,"bad-path",{}),u.finishTrace(o,"gateway",400),(0,edge_1.sendError)(t,e,400,"bad_request","Bad Request");return}const l=d.policy,_=d.path+d.query;if(_!==r&&(0,edge_1.rememberRawTarget)(e,r),e.url=_,(0,edge_1.resolveClientIp)(n,e),i.has("onRequest")&&(await i.run("onRequest",s),s.responded()))return;const a=(0,manifest_1.matchRoute)(n.routes,R(e),l+d.query);if(!a){S(e,t);return}s.route=a;const w=u.startHeadlessFlow(e,a,o,h);if(w!==o&&(o=w,s.trace=w,e.__lensmcpTrace=w),u.traceStep(o,"route-match",{project:a.project,...a.prefix?{prefix:a.prefix}:{},...a.host?{routeHost:a.host}:{},...a.internal?{internal:!0}:{}}),e.method==="OPTIONS"){(0,edge_1.cors)(e,t,n.routes),t.writeHead(204,{"access-control-allow-methods":edge_1.PREFLIGHT_METHODS_ROUTED,"access-control-allow-headers":String(e.headers["access-control-request-headers"]??"*"),"access-control-max-age":edge_1.PREFLIGHT_MAX_AGE}),t.end();return}if((0,edge_1.cors)(e,t,n.routes),i.has("beforeAuth")&&(await i.run("beforeAuth",s),s.responded()))return;if(a.internal){const c=v.internal(e,a,o);if(!c.ok){u.finishTrace(o,a.project,c.status,void 0,a),(0,edge_1.sendError)(t,e,c.status,c.reason,c.detail);return}s.caller=c.caller}else{const c=v.public(e,a,o,l);if(!c.ok){u.finishTrace(o,a.project,c.status,void 0,a),(0,edge_1.sendError)(t,e,c.status,c.reason,c.detail);return}if(s.claims=c.claims,i.has("afterAuth")&&(await i.run("afterAuth",s),s.responded()))return}const g=a.pool?n.serviceKeys[(0,service_keys_1.serviceKeyId)(a.svc?.wsKey,a.project)]:void 0;if(g&&(e.headers["x-internal-token"]=g),(0,edge_1.stampForwardingHeaders)(n,e),i.has("beforeForward")&&(await i.run("beforeForward",s),s.responded()))return;i.has("onResponse")&&t.on("close",()=>{s.status=t.statusCode??0,i.run("onResponse",s)});const m=a.project===observability_1.LENS_DASHBOARD_PROJECT&&a.target?n.hostedDashboards?.get(a.target):void 0;m&&await m.serve(t),E.forward(a,e,t)}catch(r){s.error=r,console.error(`[gateway] ${new Date().toISOString()} pipeline error traceId=${(0,gateway_errors_1.ensureRequestId)(e)}: ${r?.stack??String(r)}`),i.has("onError")&&await i.run("onError",s),s.responded()||(0,edge_1.sendError)(t,e,500,"internal","Internal gateway error.")}},"runPipeline");return(e,t)=>{H(e,t)}}y(createHandler,"createHandler"),p(createHandler,"createHandler");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
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");
|