@lensmcp/cluster 1.23.12 → 1.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-host.d.ts +1 -0
- package/build-host.js +1 -0
- package/create-webpack-dev.d.ts +75 -39
- package/create-webpack-dev.js +1 -1
- package/dev-devtool.d.ts +45 -0
- package/dev-devtool.js +1 -0
- package/devserver-notifier.d.ts +57 -0
- package/devserver-notifier.js +1 -0
- package/executors/gateway/body-stall.d.ts +13 -1
- package/executors/gateway/body-stall.js +1 -1
- package/executors/gateway/canonical-path.d.ts +1 -1
- package/executors/gateway/client-ip.d.ts +1 -1
- package/executors/gateway/gateway.impl.js +1 -1
- package/executors/gateway/jwt-claims.d.ts +1 -1
- package/executors/gateway/prod-config.d.ts +1 -1
- package/executors/gateway/prod-runtime/body-stall.d.ts +1 -1
- package/executors/gateway/rate-limit.d.ts +1 -1
- package/executors/gateway/runtime/auth.d.ts +1 -1
- package/executors/gateway/runtime/chooser.d.ts +2 -2
- package/executors/gateway/runtime/control.d.ts +3 -3
- package/executors/gateway/runtime/control.js +2 -2
- package/executors/gateway/runtime/dev-auth.d.ts +1 -1
- package/executors/gateway/runtime/edge.d.ts +3 -3
- package/executors/gateway/runtime/guest-graph.d.ts +113 -0
- package/executors/gateway/runtime/guest-graph.js +2 -0
- package/executors/gateway/runtime/lens-children.d.ts +11 -1
- package/executors/gateway/runtime/lens-children.js +1 -1
- package/executors/gateway/runtime/lifecycle.d.ts +74 -3
- package/executors/gateway/runtime/lifecycle.js +3 -3
- package/executors/gateway/runtime/process-names.d.ts +18 -0
- package/executors/gateway/runtime/process-names.js +1 -0
- package/executors/gateway/runtime/project-graph-store.d.ts +74 -0
- package/executors/gateway/runtime/project-graph-store.js +1 -0
- package/executors/gateway/runtime/proxy.d.ts +2 -2
- package/executors/gateway/runtime/proxy.js +1 -1
- package/executors/gateway/runtime/registration-guards.d.ts +32 -0
- package/executors/gateway/runtime/registration-guards.js +1 -0
- package/executors/gateway/runtime/scope.d.ts +1 -1
- package/executors/gateway/runtime/serve-launch.d.ts +118 -0
- package/executors/gateway/runtime/serve-launch.js +3 -0
- package/executors/gateway/runtime/server.d.ts +3 -3
- package/executors/gateway/runtime/server.js +2 -2
- package/executors/gateway/runtime/shared-build.d.ts +65 -0
- package/executors/gateway/runtime/shared-build.js +4 -0
- package/executors/gateway/runtime/types.d.ts +35 -9
- package/executors/gateway/runtime/types.js +1 -1
- package/executors/gateway/runtime/workspace-log.js +1 -1
- package/executors/gateway/schema.d.ts +3 -0
- package/executors/gateway/schema.json +4 -0
- package/executors/serve/schema.d.ts +2 -0
- package/executors/serve/schema.json +4 -0
- package/executors/serve/serve.impl.d.ts +12 -6
- package/executors/serve/serve.impl.js +1 -1
- package/executors/trust/hostname.d.ts +1 -1
- package/exit-reason.d.ts +1 -1
- package/fork-ts-check-reporter.d.ts +12 -0
- package/fork-ts-check-reporter.js +1 -1
- package/graph-snapshot.d.ts +1 -0
- package/graph-snapshot.js +1 -0
- package/heap-snapshot.d.ts +66 -0
- package/heap-snapshot.js +1 -0
- package/hot-swap-registries.d.ts +1 -1
- package/inspector-secret.d.ts +39 -0
- package/inspector-secret.js +1 -0
- package/main.devserver.d.ts +50 -4
- package/main.devserver.js +11 -9
- package/package.json +4 -4
- package/perf/heavy-barrel-import.d.ts +135 -0
- package/perf/heavy-barrel-import.js +1 -0
- package/perf/perf-config.d.ts +29 -0
- package/perf/perf-config.js +1 -0
- package/perf/perf-rule-events.d.ts +71 -0
- package/perf/perf-rule-events.js +2 -0
- package/perf/perf-rules-analysis.d.ts +28 -0
- package/perf/perf-rules-analysis.js +1 -0
- package/perf/perf-rules-plugin.d.ts +71 -0
- package/perf/perf-rules-plugin.js +1 -0
- package/perf/perf-rules-worker.d.ts +1 -0
- package/perf/perf-rules-worker.js +1 -0
- package/perf/source-scan.d.ts +59 -0
- package/perf/source-scan.js +5 -0
- package/perf/workspace-libraries.d.ts +52 -0
- package/perf/workspace-libraries.js +2 -0
- package/pod-memory-events.d.ts +71 -0
- package/pod-memory-events.js +2 -0
- package/pod-memory.d.ts +258 -0
- package/pod-memory.js +1 -0
- package/process-name.d.ts +45 -0
- package/process-name.js +1 -1
- package/serve-runner.d.ts +2 -0
- package/serve-runner.js +1 -0
- package/shared-build/attach.d.ts +56 -0
- package/shared-build/attach.js +3 -0
- package/shared-build/host.d.ts +52 -0
- package/shared-build/host.js +4 -0
- package/shared-build/protocol.d.ts +118 -0
- package/shared-build/protocol.js +1 -0
- package/shared-build/shared-compilation.d.ts +91 -0
- package/shared-build/shared-compilation.js +3 -0
- package/shared-build/signature.d.ts +52 -0
- package/shared-build/signature.js +1 -0
- package/stdio-guard.d.ts +1 -1
- package/swap-recycle.d.ts +21 -0
- package/swap-recycle.js +1 -1
- package/tsgo-check-plugin.d.ts +1 -1
- package/workspace-paths.d.ts +76 -0
- package/workspace-paths.js +1 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"use strict";var T=Object.defineProperty;var S=(r,t)=>T(r,"name",{value:t,configurable:!0});var L=Object.defineProperty,i=S((r,t)=>L(r,"name",{value:t,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.HOST_MAX_RESTARTS=void 0,exports.buildHostScript=buildHostScript,exports.workspaceBuildHostScript=workspaceBuildHostScript,exports.createSharedBuildRelay=createSharedBuildRelay;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),fs=tslib_1.__importStar(require("node:fs")),node_module_1=require("node:module"),path=tslib_1.__importStar(require("node:path")),process_name_1=require("../../../process-name"),protocol_1=require("../../../shared-build/protocol"),pod_env_1=require("./pod-env"),registration_guards_1=require("./registration-guards");exports.HOST_MAX_RESTARTS=3;const CRASH_WINDOW_MS=6e4;function buildHostScript(r=path.join(__dirname,"../../..")){const t=path.join(r,"build-host.js");if(fs.existsSync(t))return{path:t,execArgv:[]};const u=path.join(r,"build-host.ts");if(fs.existsSync(u))try{return{path:u,execArgv:["--require",require.resolve("@swc-node/register")]}}catch{return}}S(buildHostScript,"buildHostScript"),i(buildHostScript,"buildHostScript");function workspaceBuildHostScript(r){try{return{path:(0,node_module_1.createRequire)(path.join(r,"package.json")).resolve("@lensmcp/cluster/build-host.js"),execArgv:[]}}catch{return}}S(workspaceBuildHostScript,"workspaceBuildHostScript"),i(workspaceBuildHostScript,"workspaceBuildHostScript");function createSharedBuildRelay(r,t={}){const u=t.script===void 0?buildHostScript():t.script??void 0,_=t.scriptFor??workspaceBuildHostScript,h=new Map,f=new WeakMap,d=(0,registration_guards_1.canonicalRoot)(r.root),v=i(e=>{const a=(0,registration_guards_1.canonicalRoot)(e.root);let c=h.get(a);return c||(c=a===d?createWorkspaceRelay(r,{root:r.root,...r.wsKey?{wsKey:r.wsKey}:{},eventFile:r.eventFile},u,t):createWorkspaceRelay(r,{root:e.root,...e.wsKey?{wsKey:e.wsKey}:{},eventFile:path.join(e.root,".lensmcp","events.jsonl")},_(e.root),t),h.set(a,c)),c},"relayFor");return{offeredTo:i(e=>(0,protocol_1.sharedCompilerEnabled)(t.gatewayEnv??process.env,r.sharedCompiler)&&v(e).offered(),"offeredTo"),attach(e,a,c){const y=v(e);f.set(c,y),y.attach(`${e.project}#g${a}`,c)},detach:i(e=>f.get(e)?.detach(e),"detach"),hostPid:i((e=r.root)=>h.get((0,registration_guards_1.canonicalRoot)(e))?.hostPid(),"hostPid"),release(e){const a=(0,registration_guards_1.canonicalRoot)(e),c=h.get(a);return h.delete(a),c?.stop()??Promise.resolve()},async stop(){const e=[...h.values()];h.clear(),await Promise.all(e.map(a=>a.stop()))}}}S(createSharedBuildRelay,"createSharedBuildRelay"),i(createSharedBuildRelay,"createSharedBuildRelay");function createWorkspaceRelay(r,t,u,_){const h=_.spawnProcess??node_child_process_1.spawn,f=_.log??(o=>console.log(o)),d=new Map,v=new WeakMap;let e,a=[],c,y=!1;const E=(0,registration_guards_1.canonicalRoot)(t.root)===(0,registration_guards_1.canonicalRoot)(r.root)?"this workspace":`'${t.wsKey??path.basename(t.root)}'`,b=i((o,s)=>{if(!o.connected)return;const n={channel:protocol_1.SHARED_BUILD_CHANNEL,event:s};try{o.send(n,void 0,void 0,()=>{})}catch{}},"toRunner"),R=i(o=>{if(e?.connected)try{e.send(o,void 0,void 0,()=>{})}catch{}},"toHost"),A=i(()=>{if(_.reapStale===!1)return;let o;try{o=(0,node_child_process_1.execFileSync)("ps",["ax","-ww","-o","pid=,pgid=,command="],{encoding:"utf8",timeout:5e3})}catch{return}for(const s of o.split(`
|
|
2
|
+
`)){const n=s.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);if(!n||!(0,protocol_1.isBuildHostCommandOf)(n[3],t.root))continue;const l=Number(n[1]),p=Number(n[2]);if(!(l===e?.pid||l===process.pid)){try{process.kill(-p,"SIGKILL")}catch{try{process.kill(l,"SIGKILL")}catch{}}f(`[gateway] reaped a stale build host (pid ${l}) of ${E}`)}}},"reapStaleHosts"),x=i(()=>{if(e||y||c||!u)return e;A();const o=t.wsKey||path.basename(t.root),s={...(0,pod_env_1.podBaseEnv)(process.env,r.root,t.root),NX_DAEMON:"false",NODE_ENV:"development",NX_WORKSPACE_ROOT:t.root,...t.wsKey?{LENSMCP_WS_KEY:t.wsKey}:{},LENSMCP_EVENT_FILE:t.eventFile,LENSMCP_GATEWAY_PID:String(process.pid)},n=h((0,process_name_1.namedExecPath)((0,process_name_1.processName)(o,protocol_1.BUILD_HOST_ROLE)),[...u.execArgv,u.path,...(0,protocol_1.buildHostArgs)(t.root)],{cwd:t.root,detached:!0,env:s,stdio:["ignore","pipe","pipe","ipc"]});e=n,f(`[gateway] build host starting (one compilation for the services of ${E} \u2014 LENSMCP_SHARED_COMPILER)`);const l=i(p=>m=>{if(!r.stopped())for(const g of m.toString().split(`
|
|
3
|
+
`))g.trim()&&p.write(`[build] ${g}
|
|
4
|
+
`)},"relayOut");return n.stdout?.on("data",l(process.stdout)),n.stderr?.on("data",l(process.stderr)),n.on("message",p=>{if(p?.type==="member"){const m=d.get(p.member);m&&b(m.proc,p.event)}}),n.on("exit",(p,m)=>{if(e===n&&(e=void 0),y||r.stopped())return;const g=Date.now();if(a=[...a.filter(w=>g-w<CRASH_WINDOW_MS),g],a.length>=exports.HOST_MAX_RESTARTS){c=`the build host exited ${a.length} times in a minute (last: ${p??m})`,f(`[gateway] ${c} \u2014 every service compiles on its own from now on`);for(const w of d.values())b(w.proc,{type:"refused",reason:c});d.clear();return}if(d.size!==0&&(f(`[gateway] build host exited (${p??m}) \u2014 restarting it for ${d.size} service tree(s)`),x()))for(const[w,{join:P}]of d)R({type:"join",member:w,join:P})}),n},"start"),j=i(o=>{const s=v.get(o);!s||d.get(s)?.proc!==o||(d.delete(s),R({type:"leave",member:s}))},"detach");return{offered:i(()=>!!u&&!c,"offered"),attach:i((o,s)=>{v.set(s,o),s.on("message",n=>{if((0,protocol_1.isRunnerJoinMessage)(n)){if(c||!x()){b(s,{type:"refused",reason:c??"the build host could not be started"});return}d.set(o,{proc:s,join:n.join}),R({type:"join",member:o,join:n.join})}}),s.once("exit",()=>j(s))},"attach"),detach:j,hostPid:i(()=>e?.pid,"hostPid"),stop(){y=!0,d.clear();const o=e;return e=void 0,!o||o.exitCode!==null||o.signalCode!==null?Promise.resolve():new Promise(s=>{const n=i(()=>{clearTimeout(l),s()},"done");o.once("exit",n);try{process.kill(-o.pid,"SIGTERM")}catch{try{o.kill("SIGTERM")}catch{}}const l=setTimeout(()=>{try{process.kill(-o.pid,"SIGKILL")}catch{try{o.kill("SIGKILL")}catch{}}s()},6e3);l.unref?.()})}}}S(createWorkspaceRelay,"createWorkspaceRelay"),i(createWorkspaceRelay,"createWorkspaceRelay");
|
|
@@ -14,6 +14,7 @@ import type { AuthPolicy, Attrs } from '../manifest';
|
|
|
14
14
|
import type { GatewayErrorReason } from '../gateway-errors';
|
|
15
15
|
import type { RouteRegistry } from './route-registry';
|
|
16
16
|
import type { PodFaults } from './pod-output';
|
|
17
|
+
import type { ProjectGraphStore, ProjectGraphStores } from './project-graph-store';
|
|
17
18
|
export type { Attrs };
|
|
18
19
|
export interface GatewayContext {
|
|
19
20
|
root: string;
|
|
@@ -25,6 +26,10 @@ export interface GatewayContext {
|
|
|
25
26
|
};
|
|
26
27
|
/** Nx project-graph adjacency, populated by the executor wrapper when available. */
|
|
27
28
|
projectDependencies?: Record<string, string[]>;
|
|
29
|
+
/** The workspace's project graph, kept current by the executor wrapper — what lets the gateway start a
|
|
30
|
+
* service's serve task without an `nx run` client (serve-launch.ts). Absent ⇒ every service starts
|
|
31
|
+
* through `nx run`, as before. */
|
|
32
|
+
projectGraphStore?: ProjectGraphStore;
|
|
28
33
|
}
|
|
29
34
|
/** Per-project declaration: top-level `cluster` field in project.json (`davnx` legacy). */
|
|
30
35
|
export interface ClusterProjectDecl {
|
|
@@ -145,7 +150,7 @@ export interface SourceSetChange {
|
|
|
145
150
|
* COMPLETION, but the fs event happened anywhere after the PREVIOUS scan started — so the window is
|
|
146
151
|
* "now − previous scan start" (+ the settle margin), not a fixed fraction of the scan period. The old
|
|
147
152
|
* fixed `min(scanPeriod, 15s) + settle` = 23s against a 30s period falsely read a heard add as `missed`
|
|
148
|
-
* whenever the event fell in the first 7+ seconds of the period (issues/dev-gateway
|
|
153
|
+
* whenever the event fell in the first 7+ seconds of the period (issues/closed/dev-gateway-03).
|
|
149
154
|
*/
|
|
150
155
|
windowMs?: number;
|
|
151
156
|
}
|
|
@@ -227,6 +232,9 @@ export interface ServiceCtl {
|
|
|
227
232
|
rolling?: Promise<boolean>;
|
|
228
233
|
/** The pod GENERATION the current `proc` was spawned as (`LENSMCP_POD_GENERATION`, echoed in `/__lensmcp/alive`). */
|
|
229
234
|
generation?: number;
|
|
235
|
+
/** How the current `proc` was started: the gateway's serve runner, or `nx run` (serve-launch.ts). An `nx run`
|
|
236
|
+
* tree holds nx's task lock for its target, so no second generation can start beside it. */
|
|
237
|
+
launcher?: 'runner' | 'nx';
|
|
230
238
|
lastScaleAt: number;
|
|
231
239
|
spawnAt?: number;
|
|
232
240
|
lastErrors?: string[];
|
|
@@ -390,7 +398,7 @@ export declare const LENS_WATCHER_UNVERIFIED: 'recycle' | 'keep';
|
|
|
390
398
|
* roots to Vite's chokidar. Vite watches only `config.root`; a file outside it is watched one-at-a-time
|
|
391
399
|
* as it is imported, so a NEW file in `shared/` or `web/libs/` never produced an `add` event, the
|
|
392
400
|
* watcher-health endpoint never stamped it, and the verdict above was `missed` BY CONSTRUCTION for every
|
|
393
|
-
* out-of-root add — 16 of 16 rolling handoffs in one foodguard day (issues/dev-gateway
|
|
401
|
+
* out-of-root add — 16 of 16 rolling handoffs in one foodguard day (issues/closed/dev-gateway-02). With the scope
|
|
394
402
|
* watched, the add is heard, the verdict is `heard`, and Vite's own create-handling works for those files
|
|
395
403
|
* too. Set `LENSMCP_LENS_WATCH_SCOPE=0` to hand nothing over (inotify-constrained Linux hosts).
|
|
396
404
|
*/
|
|
@@ -399,7 +407,7 @@ export declare const LENS_WATCH_SCOPE_ENABLED: boolean;
|
|
|
399
407
|
* POD watcher testimony ({@link PodWatcherTestimony}) — the pod-side twin of the two lens knobs above.
|
|
400
408
|
* The stale-source-set signal scaled EVERY pod to zero on EVERY source-set change in its scope — 83 cold
|
|
401
409
|
* recycles of six services in one foodguard day, each a ~30-60s API outage the app rendered as "not
|
|
402
|
-
* connected" (issues/dev-gateway
|
|
410
|
+
* connected" (issues/closed/dev-gateway-07). Now the sweeper reads the pod's testimony first.
|
|
403
411
|
*
|
|
404
412
|
* `LENSMCP_POD_WATCHER_UNVERIFIED` — no testimony at all (a pod on an older `@lensmcp/cluster` bundle,
|
|
405
413
|
* or an unreachable probe). `recycle` (default) is today's behaviour for those pods; `keep` leaves them.
|
|
@@ -414,7 +422,7 @@ export declare const POD_WATCHER_UNVERIFIED: 'recycle' | 'keep';
|
|
|
414
422
|
*/
|
|
415
423
|
export declare const POD_WATCHER_SILENT: 'recycle' | 'keep';
|
|
416
424
|
/**
|
|
417
|
-
* HOW a pod is recycled (stale-source-set / max-age) — issues/dev-gateway
|
|
425
|
+
* HOW a pod is recycled (stale-source-set / max-age) — issues/closed/dev-gateway-09. `rolling` (default): spawn a
|
|
418
426
|
* STANDBY `serve-hmr` generation for the same service, wait until one of its pods answers
|
|
419
427
|
* `/__lensmcp/alive` with the new generation number, then retire the old tree — the pool dir is shared, the
|
|
420
428
|
* devserver's bind-steal hands each socket path to the newer pod, and the gateway's rotation never runs
|
|
@@ -425,6 +433,15 @@ export declare const POD_RECYCLE_MODE: 'rolling' | 'in-place';
|
|
|
425
433
|
* generation kept (a cold nx + webpack compile of a large service can take minutes). Override
|
|
426
434
|
* `LENSMCP_POD_ROLL_READY_MS`. Default 3 min. */
|
|
427
435
|
export declare const POD_ROLL_READY_MS: number;
|
|
436
|
+
/**
|
|
437
|
+
* How many rolling recycles may be IN FLIGHT at once, gateway-wide (issues/closed/dev-gateway-17). Each roll
|
|
438
|
+
* holds a standby `serve-hmr` tree — an nx client, a devserver parent, its pods and a cold webpack compile —
|
|
439
|
+
* beside the live one for up to {@link POD_ROLL_READY_MS}, and a shared-lib edit makes every service stale
|
|
440
|
+
* in the same sweep: uncapped, that doubled the fleet (twelve concurrent cold compiles for foodguard's six
|
|
441
|
+
* eager services). The rest queue and roll as slots free. Override `LENSMCP_POD_MAX_CONCURRENT_ROLLS`;
|
|
442
|
+
* `0` removes the cap. Default 2.
|
|
443
|
+
*/
|
|
444
|
+
export declare const POD_MAX_CONCURRENT_ROLLS: number;
|
|
428
445
|
/** Spawn grace: a child is never probed (and so never recycled) until it has had this long to BIND its port
|
|
429
446
|
* — a cold vite/nx boot in a big monorepo takes many seconds, and killing a still-booting dev server would
|
|
430
447
|
* be a self-inflicted outage. Override `LENSMCP_LENS_PROBE_GRACE_MS`. Default 45s. */
|
|
@@ -518,7 +535,7 @@ export declare const LENS_REQUEST_RECOVERY_WAIT_MS: number;
|
|
|
518
535
|
* Override `LENSMCP_KEEPALIVE_TIMEOUT_MS`. Default 76s. */
|
|
519
536
|
export declare const GATEWAY_KEEPALIVE_TIMEOUT_MS: number;
|
|
520
537
|
/**
|
|
521
|
-
* The HEADER-phase bound, INDEPENDENT of `keepAliveTimeout` (issues/dev-gateway
|
|
538
|
+
* The HEADER-phase bound, INDEPENDENT of `keepAliveTimeout` (issues/closed/dev-gateway-12, the prod-gateway-2 #14
|
|
522
539
|
* twin). The old derivation (`keepAliveTimeout + 5s` = 81s) rested on a premise verified obsolete on Node
|
|
523
540
|
* 24: an idle keep-alive socket is NOT closed by a shorter `headersTimeout` — only a connection that has
|
|
524
541
|
* begun a request and stalled mid-headers is. So a partial-header hold was 81s for no reason; Node's own
|
|
@@ -539,7 +556,7 @@ export declare const GATEWAY_MAX_CONNECTIONS: number;
|
|
|
539
556
|
* 90s). 0 (default) keeps Node's value. Override `LENSMCP_CONN_CHECK_MS`. */
|
|
540
557
|
export declare const GATEWAY_CONN_CHECK_MS: number;
|
|
541
558
|
/**
|
|
542
|
-
* The transfer STALL bound (issues/dev-gateway
|
|
559
|
+
* The transfer STALL bound (issues/closed/dev-gateway-12, the prod-gateway-2 #06 / round-1 #10 twins): a request
|
|
543
560
|
* body or a response that has moved NOTHING for this long is cut, in either direction. A slow-but-moving
|
|
544
561
|
* upload, a draining download and an idle SSE stream (nothing queued) are never candidates. Dev streams
|
|
545
562
|
* long (MCP streamable-HTTP, SSE), so this is a stall bound, never a duration bound. `0` disables.
|
|
@@ -547,7 +564,7 @@ export declare const GATEWAY_CONN_CHECK_MS: number;
|
|
|
547
564
|
*/
|
|
548
565
|
export declare const BODY_STALL_MS: number;
|
|
549
566
|
/**
|
|
550
|
-
* The h2 front door's RST_STREAM budget, PER SESSION (issues/foodguard
|
|
567
|
+
* The h2 front door's RST_STREAM budget, PER SESSION (issues/closed/foodguard-14). nghttp2's Rapid-Reset mitigation
|
|
551
568
|
* (CVE-2023-44487) allows 1000 resets per session, refilling at 33/s, and CLOSES the session (GOAWAY
|
|
552
569
|
* INTERNAL_ERROR) on the next one — with NO 'error' event on the server side. A browser keeps ONE h2 session
|
|
553
570
|
* per origin for the life of the tab and cancels every in-flight stream on each navigation (a Vite dev page:
|
|
@@ -587,7 +604,7 @@ export interface GatewayRuntimeOptions extends GatewayExecutorSchema {
|
|
|
587
604
|
bodyStallMs?: number;
|
|
588
605
|
/** Per-session h2 RST_STREAM budget — burst (`LENSMCP_H2_RESET_BURST`, default 100k) and refill per second
|
|
589
606
|
* (`LENSMCP_H2_RESET_RATE`, default 10k). nghttp2's 1000 / 33 default tears a dev browser's session down
|
|
590
|
-
* after a few navigations (issues/foodguard
|
|
607
|
+
* after a few navigations (issues/closed/foodguard-14). Both must be set for Node to apply either. */
|
|
591
608
|
h2ResetBurst?: number;
|
|
592
609
|
h2ResetRate?: number;
|
|
593
610
|
/** Customer-supplied lifecycle hooks layered on top of the built-in pipeline. */
|
|
@@ -602,6 +619,8 @@ export interface GatewayHandle {
|
|
|
602
619
|
/** The lens-frontend supervisor (rolling handoffs) — `recover(route.lens, reason)` rolls an app on demand.
|
|
603
620
|
* Present when the gateway runs in `rolling` mode; what an operator command or an e2e drives. */
|
|
604
621
|
lensSupervisor?: LensFrontendSupervisor;
|
|
622
|
+
/** The project graphs services are planned from, one per workspace root (guest-graph.ts) — for the spec and `gateway status`. */
|
|
623
|
+
graphStores?: ProjectGraphStores;
|
|
605
624
|
}
|
|
606
625
|
export interface GwTrace {
|
|
607
626
|
startedAt: number;
|
|
@@ -651,7 +670,7 @@ export interface GatewayRuntime {
|
|
|
651
670
|
* workspace that joins a running daemon via `POST /register` adopts its keys here. */
|
|
652
671
|
readonly keyToProject: Map<string, string>;
|
|
653
672
|
/** key → owning workspace slug, the twin of `keyToProject` — so an internal caller can be attributed to
|
|
654
|
-
* `<wsKey>/<project>`, not just a bare project name (issues/dev-gateway
|
|
673
|
+
* `<wsKey>/<project>`, not just a bare project name (issues/closed/dev-gateway-13). Mutable like its twin. */
|
|
655
674
|
readonly keyToWorkspace: Map<string, string>;
|
|
656
675
|
/** The client-address resolver (shared with prod — `../client-ip`), built from options/env at boot. Optional
|
|
657
676
|
* only so a partial runtime in a spec still works: the edge helpers fall back to a peer-only resolver. */
|
|
@@ -684,6 +703,13 @@ export interface GatewayRuntime {
|
|
|
684
703
|
readonly projectRoots?: Readonly<Record<string, string>>;
|
|
685
704
|
/** Project root plus transitive Nx dependency roots. */
|
|
686
705
|
readonly projectSourceRoots?: Readonly<Record<string, string[]>>;
|
|
706
|
+
/** The project graphs the serve runner is planned from, one per workspace root: the daemon's own
|
|
707
|
+
* ({@link GatewayContext.projectGraphStore}) and each registered workspace's (guest-graph.ts). The sweeper keeps
|
|
708
|
+
* each current. Absent when the gateway was started without a graph — every service then starts through `nx run`. */
|
|
709
|
+
readonly graphStores?: ProjectGraphStores;
|
|
710
|
+
/** The gateway's `sharedCompiler` option — one webpack compilation for this workspace's services
|
|
711
|
+
* (shared-build.ts, issues/open/pod-memory-05). `LENSMCP_SHARED_COMPILER` wins over it. */
|
|
712
|
+
readonly sharedCompiler?: boolean;
|
|
687
713
|
/** Managed lens frontend handoff state, installed by the child supervisor. */
|
|
688
714
|
lensSupervisor?: LensFrontendSupervisor;
|
|
689
715
|
/** frontend identity (`workspace:project`) → live HMR sockets, registered by the WS upgrade pipeline. A
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var M=Object.defineProperty;var e=(_,E)=>M(_,"name",{value:E,configurable:!0});var
|
|
1
|
+
"use strict";var M=Object.defineProperty;var e=(_,E)=>M(_,"name",{value:E,configurable:!0});var O=Object.defineProperty,R=e((_,E)=>O(_,"name",{value:E,configurable:!0}),"R");Object.defineProperty(exports,"__esModule",{value:!0}),exports.GATEWAY_H2_RESET_RATE=exports.GATEWAY_H2_RESET_BURST=exports.BODY_STALL_MS=exports.GATEWAY_CONN_CHECK_MS=exports.GATEWAY_MAX_CONNECTIONS=exports.GATEWAY_STOP_GRACE_MS=exports.GATEWAY_HEADERS_TIMEOUT_MS=exports.GATEWAY_KEEPALIVE_TIMEOUT_MS=exports.LENS_REQUEST_RECOVERY_WAIT_MS=exports.LENS_REPLACEMENT_DRAIN_MS=exports.LENS_REPLACEMENT_READY_MS=exports.LENS_RESTART_MODE=exports.LENS_RECYCLE_KILL_GRACE_MS=exports.LENS_PARKED_PORT_REPROBE_MS=exports.MAX_PORT_CONFLICT_RETRIES=exports.CHILD_START_FAIL_MS=exports.PORT_FREE_POLL_MS=exports.PORT_FREE_WAIT_MS=exports.LENS_PROBE_BOOT_GRACE_MS=exports.LENS_PROBE_COOLDOWN_MS=exports.LENS_PROBE_TIMEOUT_MS=exports.LENS_PROBE_FAILS=exports.LENS_PROBE_GRACE_MS=exports.POD_MAX_CONCURRENT_ROLLS=exports.POD_ROLL_READY_MS=exports.POD_RECYCLE_MODE=exports.POD_WATCHER_SILENT=exports.POD_WATCHER_UNVERIFIED=exports.LENS_WATCH_SCOPE_ENABLED=exports.LENS_WATCHER_UNVERIFIED=exports.LENS_WATCHER_PROBE_ENABLED=exports.LENS_PROBE_ENABLED=exports.POD_STALE_RECYCLE_COOLDOWN_MS=exports.POD_STALE_SCAN_MS=exports.POD_RECYCLE_SETTLE_MS=exports.POD_STALE_GRACE_MS=exports.POD_MAX_AGE_MS=exports.POD_RECYCLE_ENABLED=exports.WEDGE_RECYCLE_GRACE_MS=exports.POD_ALIVE_PATH=exports.POD_ALIVE_PROBE_TIMEOUT_MS=exports.POD_SLOW_FIRST_BYTE_MS=exports.POD_RESPONSE_TIMEOUT_MS=exports.UPSTREAM_HEAL_STEP_MS=exports.UPSTREAM_HEAL_WINDOW_MS=exports.SERVICE_RESTART_WINDOW_MS=exports.SERVICE_MAX_RESTARTS=exports.SERVICE_ERROR_THROTTLE_MS=void 0;function envInt(_,E,L=!1){const S=Number(process.env[_]);return!Number.isFinite(S)||S<0?E:S===0?L?0:E:S}e(envInt,"envInt"),R(envInt,"envInt"),exports.SERVICE_ERROR_THROTTLE_MS=5e3,exports.SERVICE_MAX_RESTARTS=5,exports.SERVICE_RESTART_WINDOW_MS=6e4,exports.UPSTREAM_HEAL_WINDOW_MS=8e3,exports.UPSTREAM_HEAL_STEP_MS=300,exports.POD_RESPONSE_TIMEOUT_MS=(()=>{const _=Number(process.env.LENSMCP_POD_RESPONSE_TIMEOUT_MS);return Number.isFinite(_)&&_>0?_:3e4})(),exports.POD_SLOW_FIRST_BYTE_MS=envInt("LENSMCP_POD_SLOW_FIRST_BYTE_MS",10*6e4,!0),exports.POD_ALIVE_PROBE_TIMEOUT_MS=envInt("LENSMCP_POD_ALIVE_PROBE_TIMEOUT_MS",1e3),exports.POD_ALIVE_PATH="/__lensmcp/alive",exports.WEDGE_RECYCLE_GRACE_MS=(()=>{const _=Number(process.env.LENSMCP_WEDGE_RECYCLE_GRACE_MS);return Number.isFinite(_)&&_>0?_:15e3})(),exports.POD_RECYCLE_ENABLED=process.env.LENSMCP_POD_RECYCLE!=="0",exports.POD_MAX_AGE_MS=envInt("LENSMCP_POD_MAX_AGE_MS",240*6e4,!0),exports.POD_STALE_GRACE_MS=envInt("LENSMCP_POD_STALE_GRACE_MS",1e4),exports.POD_RECYCLE_SETTLE_MS=envInt("LENSMCP_POD_RECYCLE_SETTLE_MS",8e3),exports.POD_STALE_SCAN_MS=envInt("LENSMCP_POD_STALE_SCAN_MS",3e4),exports.POD_STALE_RECYCLE_COOLDOWN_MS=envInt("LENSMCP_POD_STALE_RECYCLE_COOLDOWN_MS",6e4),exports.LENS_PROBE_ENABLED=process.env.LENSMCP_LENS_PROBE!=="0",exports.LENS_WATCHER_PROBE_ENABLED=process.env.LENSMCP_LENS_WATCHER_PROBE!=="0",exports.LENS_WATCHER_UNVERIFIED=process.env.LENSMCP_LENS_WATCHER_UNVERIFIED==="keep"?"keep":"recycle",exports.LENS_WATCH_SCOPE_ENABLED=process.env.LENSMCP_LENS_WATCH_SCOPE!=="0",exports.POD_WATCHER_UNVERIFIED=process.env.LENSMCP_POD_WATCHER_UNVERIFIED==="keep"?"keep":"recycle",exports.POD_WATCHER_SILENT=process.env.LENSMCP_POD_WATCHER_SILENT==="recycle"?"recycle":"keep",exports.POD_RECYCLE_MODE=process.env.LENSMCP_POD_RECYCLE_MODE==="in-place"?"in-place":"rolling",exports.POD_ROLL_READY_MS=envInt("LENSMCP_POD_ROLL_READY_MS",18e4),exports.POD_MAX_CONCURRENT_ROLLS=envInt("LENSMCP_POD_MAX_CONCURRENT_ROLLS",2,!0),exports.LENS_PROBE_GRACE_MS=envInt("LENSMCP_LENS_PROBE_GRACE_MS",45e3),exports.LENS_PROBE_FAILS=envInt("LENSMCP_LENS_PROBE_FAILS",2),exports.LENS_PROBE_TIMEOUT_MS=envInt("LENSMCP_LENS_PROBE_TIMEOUT_MS",1e3),exports.LENS_PROBE_COOLDOWN_MS=envInt("LENSMCP_LENS_PROBE_COOLDOWN_MS",3e4),exports.LENS_PROBE_BOOT_GRACE_MS=envInt("LENSMCP_LENS_PROBE_BOOT_GRACE_MS",24e4),exports.PORT_FREE_WAIT_MS=envInt("LENSMCP_PORT_FREE_WAIT_MS",1e4),exports.PORT_FREE_POLL_MS=envInt("LENSMCP_PORT_FREE_POLL_MS",250),exports.CHILD_START_FAIL_MS=envInt("LENSMCP_CHILD_START_FAIL_MS",1e4),exports.MAX_PORT_CONFLICT_RETRIES=envInt("LENSMCP_MAX_PORT_CONFLICT_RETRIES",5),exports.LENS_PARKED_PORT_REPROBE_MS=envInt("LENSMCP_PARKED_PORT_REPROBE_MS",3e5),exports.LENS_RECYCLE_KILL_GRACE_MS=envInt("LENSMCP_RECYCLE_KILL_GRACE_MS",5e3),exports.LENS_RESTART_MODE=process.env.LENSMCP_LENS_RESTART_MODE==="in-place"?"in-place":"rolling",exports.LENS_REPLACEMENT_READY_MS=envInt("LENSMCP_LENS_REPLACEMENT_READY_MS",3e4),exports.LENS_REPLACEMENT_DRAIN_MS=envInt("LENSMCP_LENS_REPLACEMENT_DRAIN_MS",2e3,!0),exports.LENS_REQUEST_RECOVERY_WAIT_MS=envInt("LENSMCP_LENS_REQUEST_RECOVERY_WAIT_MS",15e3),exports.GATEWAY_KEEPALIVE_TIMEOUT_MS=envInt("LENSMCP_KEEPALIVE_TIMEOUT_MS",76e3),exports.GATEWAY_HEADERS_TIMEOUT_MS=envInt("LENSMCP_HEADERS_TIMEOUT_MS",6e4),exports.GATEWAY_STOP_GRACE_MS=envInt("LENSMCP_STOP_GRACE_MS",3e3),exports.GATEWAY_MAX_CONNECTIONS=envInt("LENSMCP_MAX_CONNECTIONS",0,!0),exports.GATEWAY_CONN_CHECK_MS=envInt("LENSMCP_CONN_CHECK_MS",0,!0),exports.BODY_STALL_MS=envInt("LENSMCP_BODY_STALL_MS",6e4,!0),exports.GATEWAY_H2_RESET_BURST=envInt("LENSMCP_H2_RESET_BURST",1e5),exports.GATEWAY_H2_RESET_RATE=envInt("LENSMCP_H2_RESET_RATE",1e4);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var a=Object.defineProperty;var r=(e,t)=>a(e,"name",{value:t,configurable:!0});var
|
|
1
|
+
"use strict";var a=Object.defineProperty;var r=(e,t)=>a(e,"name",{value:t,configurable:!0});var s=Object.defineProperty,n=r((e,t)=>s(e,"name",{value:t,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.workspaceLogWriter=workspaceLogWriter,exports.closeWorkspaceLogs=closeWorkspaceLogs;const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),inspector_secret_1=require("../../../inspector-secret"),fds=new Map,written=new Map,MAX_BYTES=50*1024*1024,CHECK_EVERY=1024*1024;function ensurePrivateDir(e){fs.mkdirSync(e,{recursive:!0,mode:448});try{const t=fs.statSync(e);(t.mode&63)!==0&&(typeof process.getuid!="function"||t.uid===process.getuid())&&fs.chmodSync(e,448)}catch{}}r(ensurePrivateDir,"ensurePrivateDir"),n(ensurePrivateDir,"ensurePrivateDir");function ownerOnly(e){try{fs.chmodSync(e,384)}catch{}}r(ownerOnly,"ownerOnly"),n(ownerOnly,"ownerOnly");function ensureFd(e){const t=fds.get(e);if(t!==void 0)return t;try{ensurePrivateDir(path.dirname(e));const o=fs.openSync(e,"a",384);return ownerOnly(e),fds.set(e,o),written.set(e,0),o}catch{return}}r(ensureFd,"ensureFd"),n(ensureFd,"ensureFd");function closeFd(e){const t=fds.get(e);if(fds.delete(e),written.delete(e),t!==void 0)try{fs.closeSync(t)}catch{}}r(closeFd,"closeFd"),n(closeFd,"closeFd");function rotateIfHuge(e){try{if(fs.statSync(e).size<=MAX_BYTES)return}catch{return}closeFd(e);try{fs.renameSync(e,`${e}.1`)}catch{}ownerOnly(`${e}.1`)}r(rotateIfHuge,"rotateIfHuge"),n(rotateIfHuge,"rotateIfHuge");function workspaceLogWriter(e){const t=path.join(e,".lensmcp","gateway.log");if(ensureFd(t)!==void 0)return o=>{const c=ensureFd(t);if(c===void 0)return;try{fs.writeSync(c,(0,inspector_secret_1.redactInspectorUrls)(o))}catch{closeFd(t);return}const i=(written.get(t)??0)+o.length;i>=CHECK_EVERY?(written.set(t,0),rotateIfHuge(t)):written.set(t,i)}}r(workspaceLogWriter,"workspaceLogWriter"),n(workspaceLogWriter,"workspaceLogWriter");function closeWorkspaceLogs(){for(const e of[...fds.keys()])closeFd(e)}r(closeWorkspaceLogs,"closeWorkspaceLogs"),n(closeWorkspaceLogs,"closeWorkspaceLogs");
|
|
@@ -16,4 +16,7 @@ export interface GatewayExecutorSchema {
|
|
|
16
16
|
dashboardPort?: number;
|
|
17
17
|
/** Override the dashboard hostname. Default lens.<baseDomain>. */
|
|
18
18
|
lensHost?: string;
|
|
19
|
+
/** Compile every service of this workspace in ONE webpack compilation (a build host) instead of one watcher per
|
|
20
|
+
* service. `LENSMCP_SHARED_COMPILER=1|0` wins over it. */
|
|
21
|
+
sharedCompiler?: boolean;
|
|
19
22
|
}
|
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
"lensHost": {
|
|
43
43
|
"type": "string",
|
|
44
44
|
"description": "Override the dashboard's dev hostname. Default lens.<baseDomain>, where <baseDomain> is the longest common dotted suffix of the declared cluster hosts (e.g. lens.tetros.ai.local)."
|
|
45
|
+
},
|
|
46
|
+
"sharedCompiler": {
|
|
47
|
+
"type": "boolean",
|
|
48
|
+
"description": "Compile every service of this workspace in ONE webpack compilation (a build host) instead of one watcher per service: shared libraries are compiled once. LENSMCP_SHARED_COMPILER=1|0 wins over it."
|
|
45
49
|
}
|
|
46
50
|
},
|
|
47
51
|
"additionalProperties": false
|
|
@@ -54,4 +54,6 @@ export interface ServeExecutorSchema {
|
|
|
54
54
|
debugPort?: number;
|
|
55
55
|
/** Tee pod/worker logs (ANSI-stripped) to this file for IDE tailing (WebStorm Logs tab). Env fallback: LENSMCP_LOG_FILE. */
|
|
56
56
|
logFile?: string;
|
|
57
|
+
/** webpack `devtool` for the dev build (default `eval`: no source maps). Env fallback: LENSMCP_DEV_DEVTOOL. */
|
|
58
|
+
devtool?: string;
|
|
57
59
|
}
|
|
@@ -194,6 +194,10 @@
|
|
|
194
194
|
"logFile": {
|
|
195
195
|
"type": "string",
|
|
196
196
|
"description": "Tee every pod/worker log line (ANSI-stripped) to this file, relative to workspace root (e.g. '.lensmcp/logs/my-service.log'). Point an IDE at it — WebStorm: Run/Debug configuration → Logs tab — to see live service logs next to the debugger. Truncated once per serve run. Also settable via the LENSMCP_LOG_FILE env var (this option wins)."
|
|
197
|
+
},
|
|
198
|
+
"devtool": {
|
|
199
|
+
"type": "string",
|
|
200
|
+
"description": "webpack devtool for the dev build. Default 'eval': no source maps — every stack frame names its module by absolute path, at the transpiled line, and pods hold one copy of the bundle's text. A map-carrying value (e.g. 'cheap-module-source-map', the cheapest for pods) maps frames to TypeScript lines: ts-loader is told to emit maps and pods run with --enable-source-maps. 'false' = none. Also settable via the LENSMCP_DEV_DEVTOOL env var (this option wins)."
|
|
197
201
|
}
|
|
198
202
|
},
|
|
199
203
|
"required": [
|
|
@@ -2,23 +2,29 @@ import type { ServeExecutorSchema } from './schema';
|
|
|
2
2
|
interface ExecutorContext {
|
|
3
3
|
root: string;
|
|
4
4
|
projectName?: string;
|
|
5
|
+
targetName?: string;
|
|
6
|
+
configurationName?: string;
|
|
5
7
|
projectsConfigurations?: {
|
|
6
8
|
projects: Record<string, {
|
|
7
9
|
root: string;
|
|
8
10
|
}>;
|
|
9
11
|
};
|
|
10
12
|
}
|
|
13
|
+
interface ServeResult {
|
|
14
|
+
success: boolean;
|
|
15
|
+
baseUrl?: string;
|
|
16
|
+
}
|
|
11
17
|
/**
|
|
12
18
|
* Dev serve executor — webpack watch mode with integrated devserver.
|
|
13
19
|
*
|
|
14
20
|
* Owns the full devserver lifecycle:
|
|
15
|
-
* 1. Starts webpack in watch mode
|
|
21
|
+
* 1. Starts webpack in watch mode — or, under a gateway running the shared compiler, joins the workspace's ONE
|
|
22
|
+
* compilation (shared-build/attach.ts, issues/open/pod-memory-05) and is told about each build of its bundle
|
|
16
23
|
* 2. On first successful build, forks main.devserver.js with proper env vars
|
|
17
|
-
* 3. On subsequent builds, POSTs /webpack/reload to devserver
|
|
24
|
+
* 3. On subsequent builds, POSTs /webpack/reload to devserver — attached: only when ITS bundle changed
|
|
18
25
|
* 4. On shutdown, cleans up devserver and webpack watcher
|
|
26
|
+
*
|
|
27
|
+
* webpack and the dev config are loaded only when this process compiles: an attached executor holds neither.
|
|
19
28
|
*/
|
|
20
|
-
declare function serveExecutor(options: ServeExecutorSchema, context: ExecutorContext): AsyncGenerator<
|
|
21
|
-
success: boolean;
|
|
22
|
-
baseUrl?: string;
|
|
23
|
-
}>;
|
|
29
|
+
declare function serveExecutor(options: ServeExecutorSchema, context: ExecutorContext): AsyncGenerator<ServeResult>;
|
|
24
30
|
export default serveExecutor;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var ne=Object.defineProperty;var c=(e,r)=>ne(e,"name",{value:r,configurable:!0});var te=Object.defineProperty,o=c((e,r)=>te(e,"name",{value:r,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),node_child_process_1=require("node:child_process"),yaml=tslib_1.__importStar(require("js-yaml")),dev_devtool_1=require("../../dev-devtool"),devserver_notifier_1=require("../../devserver-notifier"),process_name_1=require("../../process-name"),attach_1=require("../../shared-build/attach");function definedEnv(e){const r={};for(const[_,i]of Object.entries(e))typeof i=="string"&&(r[_]=i);return r}c(definedEnv,"definedEnv"),o(definedEnv,"definedEnv");async function*serveExecutor(e,r){process.env.NODE_ENV="development";const _=r.projectsConfigurations.projects[r.projectName],i=r.root,g=path.join(i,_.root),M=e.outputPath?path.join(i,e.outputPath):path.join(i,"dist",_.root),U=e.configEnv||"development",L=path.join(i,"config",`config.${U}.yaml`),C=3050;let u=C,l=r.projectName,S={};fs.existsSync(L)?(S=yaml.load(fs.readFileSync(L,"utf8")),u=Number(S.port)||u,l=S.serviceName||l):console.warn(`[serve] ${r.projectName}: no config file at ${L} \u2014 public devserver port defaults to ${C}. If another service also lacks its config, both bind ${C} \u2192 EADDRINUSE. Add config/config.${U}.yaml with a distinct \`port:\` (matching this project's cluster.port).`),e.serviceName&&(l=e.serviceName);const R=e.servePrefix??"",v=e.debugPort??(Number(process.env.LENSMCP_DEBUG_PORT)||void 0),H=/^(1|true|yes|on)$/i.test((process.env.LENSMCP_DEBUG_FIXED_PORTS??"").trim()),p=e.logFile?path.resolve(i,e.logFile):process.env.LENSMCP_LOG_FILE||void 0;if(p){fs.mkdirSync(path.dirname(p),{recursive:!0}),fs.writeFileSync(p,"",{mode:384});try{fs.chmodSync(p,384)}catch{}}let O,T;e.gateway?.middleware&&(O=path.resolve(g,e.gateway.middleware),T=JSON.stringify(S));const K=e.entryFile||"./src/deployments/service/main.ts",Q=e.tsConfigFile||"./tsconfig.app.json",z=path.resolve(g,K),F=path.resolve(g,Q),Z=(e.workers||[]).map(t=>({entryName:t.name,entryPath:path.resolve(g,t.entryPath)})),B={appName:r.projectName,appRoot:g,outputDir:M,main:z,tsConfig:F,assets:e.assets||[],additionalEntryPoints:Z,port:u,serviceName:l,memoryLimit:e.memoryLimit||8192,buildLibsFromSource:e.buildLibsFromSource!==!1,workspaceRoot:i,orgScopes:e.orgScopes||[],bundlePackages:e.bundlePackages||[],nodeExternalsConfig:e.nodeExternalsConfig,webpackConfigPath:e.webpackConfigPath,httpsReload:e.gateway?.https===!0,...e.devtool!==void 0&&{devtool:e.devtool}};let d=null,$=!1,D=!1,m=!1;const W=path.join(__dirname,"../../main.devserver.js"),q=path.join(__dirname,"../../main.devserver.ts"),V=!fs.existsSync(W)&&fs.existsSync(q),k=V?q:W,ee=path.join(M,"main.js");function I(){d&&!d.killed&&(d.kill("SIGTERM"),d=null)}c(I,"W"),o(I,"cleanup");function y(){m=!0,I()}c(y,"O"),o(y,"stop");const oe=["SIGINT","SIGTERM","SIGHUP","SIGQUIT"].map(t=>{const n=o(()=>y(),"handler");return process.on(t,n),{sig:t,handler:n}});process.on("exit",y);function E(){if(m)return;if(!fs.existsSync(k)){console.error(`[serve] devserver script not found at ${k}`);return}const t=["--enable-source-maps"];V&&t.unshift("--require","@swc-node/register");const n=(0,node_child_process_1.fork)(k,{cwd:i,env:{...process.env,PORT:String(u),SERVICE_NAME:l,SERVE_PREFIX:R,LENSMCP_PROJECT:process.env.LENSMCP_PROJECT||r.projectName||l,LENSMCP_EVENT_FILE:process.env.LENSMCP_EVENT_FILE||path.join(i,".lensmcp","events.jsonl"),CHILD_COUNT:String(e.childCount||process.env.CHILD_COUNT||1),BUNDLE_PATH:ee,...O&&{GATEWAY_MIDDLEWARE:O},...T&&{GATEWAY_CONFIG:T},...e.gateway?.routes?.length&&{GATEWAY_ROUTES:JSON.stringify(e.gateway.routes)},...e.gateway?.https&&{GATEWAY_HTTPS:"1"},...e.gateway?.extraPorts?.length&&{GATEWAY_EXTRA_PORTS:JSON.stringify(e.gateway.extraPorts)},...e.internalPort&&{INTERNAL_PORT:String(e.internalPort)},...v&&{LENSMCP_DEBUG_PORT:String(v)},...p&&{LENSMCP_LOG_FILE:p},...e.workers?.length&&{WORKERS:JSON.stringify(e.workers.map(a=>a.name))},[dev_devtool_1.DEV_SOURCE_MAPS_ENV]:D?"1":"0"},stdio:["inherit","inherit","inherit","ipc"],execArgv:t,execPath:(0,process_name_1.namedExecPath)((0,process_name_1.processName)(process.env.LENSMCP_WS_KEY||path.basename(i),l,"dev"))});d=n,n.on("exit",(a,f)=>{d===n&&(d=null),!(f||a===0||m)&&(console.error(`[serve] devserver exited unexpectedly (code=${a}, signal=${f}) \u2014 respawning in 1.5s`),setTimeout(()=>{!m&&!d&&$&&E()},1500).unref?.())}),console.log(`[serve] Devserver started on port ${u} (service: ${l}${R?`, prefix: /${R}`:""})`),v?(console.warn(`[serve] DEBUGGER ENABLED for ${l}. Node's inspector has NO authentication \u2014 the ws:// UUID is the whole credential, and any account on this host that obtains it runs code as you. Do not enable this on a shared machine.`),H?console.warn(`[serve] LENSMCP_DEBUG_FIXED_PORTS=1 \u2014 pod inspectors bind PREDICTABLE ports 127.0.0.1:${v}+slot (workers ${v}+20+i), reachable by any account on this host. Attach WebStorm (Attach to Node.js/Chrome) to ${v}. Unset it to bind ephemeral ports instead.`):console.log(`[serve] Debug: pod inspectors bind an EPHEMERAL 127.0.0.1 port. Get the ws:// URLs with \`lensmcp debug ${l}\` (the owner-only control socket); a devserver run in your own terminal also prints them there. Set LENSMCP_DEBUG_FIXED_PORTS=1 if your IDE needs a fixed attach port.`)):console.log(`[serve] Debug on demand: \`lensmcp debug ${l}\` opens pod inspectors without a restart`),p&&console.log(`[serve] Logs teed to ${p} \u2014 tail it in WebStorm via Run/Debug configuration \u2192 Logs tab`)}c(E,"S"),o(E,"startDevServer");function re(){if(!d||d.killed){console.log("[serve] Devserver not running, restarting..."),E();return}fetch(`http://localhost:${u}/webpack/reload`,{method:"POST",headers:{"content-type":"application/json"},body:"{}"}).then(()=>console.log("[serve] Notified devserver to reload")).catch(t=>{console.warn("[serve] Failed to notify devserver, restarting...",t.message),I(),E()})}c(re,"oe"),o(re,"_notifyDevServerReload");let N=null;const x=[];function h(t){if(N){const n=N;N=null,n(t)}else x.push(t)}c(h,"p"),o(h,"pushResult");function J(){return x.length>0?Promise.resolve(x.shift()):new Promise(t=>{N=t})}c(J,"Z"),o(J,"waitForNext");let w=o(()=>{},"closeBuild");function A(){return!$||!d?($=!0,E(),!0):!1}c(A,"q"),o(A,"builtOk");function b(){if(m)return;const{createDevWebpackConfig:t}=require("../../create-webpack-dev"),n=require("webpack"),a=t(B),{watch:f,...P}=a,s=n(P);D=(0,dev_devtool_1.bundleCarriesSourceMaps)(s.options.devtool);const G=s.watch(a.watchOptions||{},(j,X)=>{if(j){console.error("[serve] Webpack error:",j),h({success:!1});return}const se=X.hasErrors();if(console.info(X.toString({colors:!0,chunks:!1,assets:!0})),se){h({success:!1});return}A(),h({success:!0,baseUrl:`http://localhost:${u}`})});w=o(()=>G.close(()=>{}),"closeBuild")}c(b,"k"),o(b,"compileOnItsOwn");function Y(t){const n=t.accepted.projects.filter(s=>s!==r.projectName);console.log(`[serve] ${r.projectName}: compiled by the workspace's shared compilation ${t.accepted.group.slice(0,8)}${n.length?` with ${n.join(", ")}`:""} \u2014 no webpack in this process`);const a=new devserver_notifier_1.DevserverNotifier(u,e.gateway?.https===!0,(0,devserver_notifier_1.devserverControlSock)(l)),f=(0,attach_1.createAttachedTypecheck)({appName:r.projectName,tsConfig:F,workspaceRoot:i,memoryLimit:e.memoryLimit||8192});let P;w=o(()=>{t.close(),f.dispose()},"closeBuild"),t.listen(s=>{if(!m)switch(s.type){case"watch-run":a.watchRun(s.paths);return;case"watch-closed":a.degrade("watch closed");return;case"failed":console.error(`[serve] the shared compiler failed: ${s.message}`),a.degrade(`compiler failed: ${s.message}`),h({success:!1});return;case"refused":console.log(`[serve] ${r.projectName}: compiling on its own from now on \u2014 ${s.reason}`),w(),b();return;case"built":{if(console.info(s.text),D=s.sourceMaps,f.built(s),!s.ok){a.tellWatching(),h({success:!1});return}const G=s.hash!==P;P=s.hash;const j=A();a.built(G&&!j),h({success:!0,baseUrl:`http://localhost:${u}`});return}default:return}})}if(c(Y,"ee"),o(Y,"attachToSharedBuild"),(0,attach_1.sharedBuildOffered)()){const t=await(0,attach_1.joinSharedBuild)({project:r.projectName,target:r.targetName??"serve-hmr",...r.configurationName?{configuration:r.configurationName}:{},options:B,targetOptions:{...e},env:definedEnv(process.env)});t.kind==="attached"?Y(t.session):(console.log(`[serve] ${r.projectName}: compiling on its own \u2014 ${t.reason}`),b())}else b();try{for(;;)yield await J()}finally{y(),w(),oe.forEach(({sig:t,handler:n})=>process.removeListener(t,n))}}c(serveExecutor,"serveExecutor"),o(serveExecutor,"serveExecutor"),exports.default=serveExecutor,module.exports=serveExecutor,module.exports.default=serveExecutor;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `nx trust` collects host strings from every project's `project.json`, from embedded route options,
|
|
5
5
|
* and from its own `hosts` option, then writes them into `/etc/hosts` as root. Workspace
|
|
6
6
|
* configuration therefore reaches a privileged command, and the only check was "does not contain
|
|
7
|
-
* `*`" (issues/codex-security
|
|
7
|
+
* `*`" (issues/closed/codex-security-33).
|
|
8
8
|
*
|
|
9
9
|
* This is the first of two defences. The second — removing the shell — is what actually closes the
|
|
10
10
|
* injection; this one means a value that could never be a hostname fails loudly at the point it is
|
package/exit-reason.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Why a pod died, in words (issues/foodguard
|
|
2
|
+
* Why a pod died, in words (issues/closed/foodguard-22). "socket hang up" was the only trace a pod left when
|
|
3
3
|
* it died mid-work: a crash, a SIGKILL and an OOM kill read the same, and the stderr that explained
|
|
4
4
|
* which was gone with the process. Pure helpers so the devserver's exit handler stays a few lines.
|
|
5
5
|
*/
|
|
@@ -5,6 +5,18 @@ export interface ForkTsCheckReporterOptions {
|
|
|
5
5
|
/** Override the lens bus file; defaults to `LENSMCP_EVENT_FILE` / `<root>/.lensmcp/events.jsonl`. */
|
|
6
6
|
eventFile?: string;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Load the SAME module instance `@nx/webpack` loaded.
|
|
10
|
+
*
|
|
11
|
+
* `getCompilerHooks` reads a module-level `WeakMap` keyed by compiler, so hooks registered
|
|
12
|
+
* through a second copy of the package are a different map and never fire. `@nx/webpack` does a
|
|
13
|
+
* bare `require('fork-ts-checker-webpack-plugin')` from its own directory, so resolving from
|
|
14
|
+
* there first is what guarantees we get its copy; the workspace root and our own resolution are
|
|
15
|
+
* fallbacks for a layout that hoists differently.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadForkTsChecker(workspaceRoot: string): {
|
|
18
|
+
getCompilerHooks?: (c: unknown) => unknown;
|
|
19
|
+
} | null;
|
|
8
20
|
export declare class ForkTsCheckReporterPlugin {
|
|
9
21
|
private readonly opts;
|
|
10
22
|
private startedAt;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var p=Object.defineProperty;var
|
|
1
|
+
"use strict";var p=Object.defineProperty;var o=(e,t)=>p(e,"name",{value:t,configurable:!0});var a=Object.defineProperty,r=o((e,t)=>a(e,"name",{value:t,configurable:!0}),"r");Object.defineProperty(exports,"__esModule",{value:!0}),exports.ForkTsCheckReporterPlugin=void 0,exports.loadForkTsChecker=loadForkTsChecker;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),typecheck_bus_1=require("./typecheck-bus");function loadForkTsChecker(e){const t=[];try{t.push(path.dirname(require.resolve("@nx/webpack/package.json",{paths:[e]})))}catch{}t.push(e,__dirname);for(const s of t)try{return require(require.resolve("fork-ts-checker-webpack-plugin",{paths:[s]}))}catch{}return null}o(loadForkTsChecker,"loadForkTsChecker"),r(loadForkTsChecker,"loadForkTsChecker");function toDiagnostic(e,t){return{file:e.file?path.relative(t,e.file)||e.file:"<unknown>",line:e.location?.start?.line??0,column:e.location?.start?.column??0,severity:e.severity==="warning"?"warning":"error",code:/^TS\d+$/.test(e.code)?e.code:`TS${e.code}`,message:e.message}}o(toDiagnostic,"toDiagnostic"),r(toDiagnostic,"toDiagnostic");class ForkTsCheckReporterPlugin{static{o(this,"ForkTsCheckReporterPlugin")}static{r(this,"ForkTsCheckReporterPlugin")}constructor(t){this.startedAt=0,this.opts=t}apply(t){const s=loadForkTsChecker(this.opts.workspaceRoot)?.getCompilerHooks;if(typeof s!="function")return;let c;try{c=s(t)}catch{return}c.start?.tapPromise("LensmcpForkTsCheckReporter",async i=>(this.startedAt=Date.now(),i)),c.issues?.tap("LensmcpForkTsCheckReporter",i=>{try{(0,typecheck_bus_1.publishTypecheckDiagnostics)({workspaceRoot:this.opts.workspaceRoot,project:this.opts.appName,tool:"fork-ts-checker",tsConfig:this.opts.tsConfig,diagnostics:(i??[]).map(n=>toDiagnostic(n,this.opts.workspaceRoot)),durationMs:this.startedAt?Date.now()-this.startedAt:0,...this.opts.eventFile?{eventFile:this.opts.eventFile}:{}})}catch{}return i})}}exports.ForkTsCheckReporterPlugin=ForkTsCheckReporterPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var i=Object.defineProperty;var o=(r,e)=>i(r,"name",{value:e,configurable:!0});var t=Object.defineProperty,s=o((r,e)=>t(r,"name",{value:e,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),guest_graph_1=require("./executors/gateway/runtime/guest-graph");function main(){const r=process.argv[2];r||(console.error("usage: graph-snapshot.js <output file> \u2014 run in the workspace root"),process.exit(2)),(0,guest_graph_1.snapshotWorkspaceGraph)(process.cwd()).then(e=>{fs.writeFileSync(r,JSON.stringify(e),{mode:384}),process.exit(0)},e=>{console.error(`[graph-snapshot] ${e?.message??String(e)}`),process.exit(1)})}o(main,"main"),s(main,"main"),require.main===module&&main();
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface HeapSnapshotPolicy {
|
|
2
|
+
/** Refuse above this live heap. */
|
|
3
|
+
maxHeapBytes: number;
|
|
4
|
+
/** Never take one with less than this available on the machine… */
|
|
5
|
+
minFreeBytes: number;
|
|
6
|
+
/** …or less than this many times the live heap (the snapshot graph is built in native memory first). */
|
|
7
|
+
freeFactor: number;
|
|
8
|
+
/** Snapshots kept per directory; older ones are deleted before a new one is written. */
|
|
9
|
+
keep: number;
|
|
10
|
+
/** Snapshots older than this are deleted. */
|
|
11
|
+
maxAgeMs: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* - `LENSMCP_HEAP_SNAPSHOT_MAX_MB` — default `1024`.
|
|
15
|
+
* - `LENSMCP_HEAP_SNAPSHOT_MIN_FREE_MB` — default `1024`.
|
|
16
|
+
* - `LENSMCP_HEAP_SNAPSHOT_FREE_FACTOR` — default `3`.
|
|
17
|
+
* - `LENSMCP_HEAP_SNAPSHOT_KEEP` — default `3`.
|
|
18
|
+
* - `LENSMCP_HEAP_SNAPSHOT_MAX_AGE_H` — default `24`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function heapSnapshotPolicy(env?: NodeJS.ProcessEnv): HeapSnapshotPolicy;
|
|
21
|
+
/** Memory the machine can hand out without paging, and how that was read. */
|
|
22
|
+
export interface AvailableMemory {
|
|
23
|
+
bytes: number;
|
|
24
|
+
source: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* What the machine could give a snapshot without swapping.
|
|
28
|
+
*
|
|
29
|
+
* `os.freemem()` is the wrong number on macOS: it counts only FREE pages, and a Mac under normal use keeps a few
|
|
30
|
+
* hundred MB free while gigabytes of inactive (reclaimable) pages sit beside them — it would refuse every time.
|
|
31
|
+
* `vm_stat`'s free + inactive + speculative + purgeable is what the kernel can reclaim without compressing or
|
|
32
|
+
* swapping. Linux has `MemAvailable` for exactly this. Elsewhere `os.freemem()` is all there is.
|
|
33
|
+
*/
|
|
34
|
+
export declare function availableSystemMemory(platform?: NodeJS.Platform): AvailableMemory | undefined;
|
|
35
|
+
/** Why a snapshot must not be taken now, in the words the caller will print — or `undefined`. */
|
|
36
|
+
export declare function heapSnapshotRefusal(policy: HeapSnapshotPolicy, input: {
|
|
37
|
+
live: number;
|
|
38
|
+
available?: AvailableMemory;
|
|
39
|
+
}): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Where snapshots go: `LENSMCP_HEAP_DIR`, else `heap/` beside the workspace's bus (`<root>/.lensmcp/heap`), else
|
|
42
|
+
* `<cwd>/.lensmcp/heap`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function heapSnapshotDir(env?: NodeJS.ProcessEnv, cwd?: string): string;
|
|
45
|
+
/** A snapshot's file name: service, pod, pid, app generation, time. */
|
|
46
|
+
export declare function heapSnapshotFileName(service: string, pod: number, pid: number, gen: number, at: number): string;
|
|
47
|
+
/**
|
|
48
|
+
* Make `dir` exist and be owner-only — and refuse a symlink there: snapshots carry everything the app holds
|
|
49
|
+
* (tokens, keys, customer data), so where they land must be ours.
|
|
50
|
+
*/
|
|
51
|
+
export declare function ensureHeapDir(dir: string): void;
|
|
52
|
+
/** Delete this directory's snapshots older than `maxAgeMs`, then all but the newest `keep - 1` (room for one more). */
|
|
53
|
+
export declare function pruneHeapSnapshots(dir: string, policy: HeapSnapshotPolicy, now?: number): string[];
|
|
54
|
+
/** What a written snapshot weighs and cost. */
|
|
55
|
+
export interface HeapSnapshotWritten {
|
|
56
|
+
bytes: number;
|
|
57
|
+
ms: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Stream a heap snapshot of THIS process to `file` through an in-process inspector session.
|
|
61
|
+
*
|
|
62
|
+
* The chunks arrive as the snapshot is serialised and are written as they come, so the JS heap never holds the
|
|
63
|
+
* whole thing. The file is created exclusively (`O_EXCL | O_NOFOLLOW`, `0600`): never someone else's file,
|
|
64
|
+
* never through a planted link. A failure removes what was written.
|
|
65
|
+
*/
|
|
66
|
+
export declare function writeHeapSnapshot(file: string): Promise<HeapSnapshotWritten>;
|
package/heap-snapshot.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var _=Object.defineProperty;var p=(e,t)=>_(e,"name",{value:t,configurable:!0});var h=Object.defineProperty,i=p((e,t)=>h(e,"name",{value:t,configurable:!0}),"i");Object.defineProperty(exports,"__esModule",{value:!0}),exports.heapSnapshotPolicy=heapSnapshotPolicy,exports.availableSystemMemory=availableSystemMemory,exports.heapSnapshotRefusal=heapSnapshotRefusal,exports.heapSnapshotDir=heapSnapshotDir,exports.heapSnapshotFileName=heapSnapshotFileName,exports.ensureHeapDir=ensureHeapDir,exports.pruneHeapSnapshots=pruneHeapSnapshots,exports.writeHeapSnapshot=writeHeapSnapshot;const tslib_1=require("tslib"),node_child_process_1=require("node:child_process"),fs=tslib_1.__importStar(require("node:fs")),inspector=tslib_1.__importStar(require("node:inspector")),os=tslib_1.__importStar(require("node:os")),path=tslib_1.__importStar(require("node:path")),MB=1024*1024,numberFrom=i((e,t,o)=>{if(e===void 0||e.trim()==="")return t;const a=Number(e);return Number.isFinite(a)&&a>=o?a:t},"numberFrom");function heapSnapshotPolicy(e=process.env){return{maxHeapBytes:numberFrom(e.LENSMCP_HEAP_SNAPSHOT_MAX_MB,1024,1)*MB,minFreeBytes:numberFrom(e.LENSMCP_HEAP_SNAPSHOT_MIN_FREE_MB,1024,0)*MB,freeFactor:numberFrom(e.LENSMCP_HEAP_SNAPSHOT_FREE_FACTOR,3,0),keep:Math.max(1,Math.floor(numberFrom(e.LENSMCP_HEAP_SNAPSHOT_KEEP,3,1))),maxAgeMs:numberFrom(e.LENSMCP_HEAP_SNAPSHOT_MAX_AGE_H,24,0)*36e5}}p(heapSnapshotPolicy,"heapSnapshotPolicy"),i(heapSnapshotPolicy,"heapSnapshotPolicy");function availableSystemMemory(e=process.platform){try{if(e==="darwin"){const t=(0,node_child_process_1.spawnSync)("vm_stat",[],{encoding:"utf8",timeout:2e3}).stdout??"",o=Number(/page size of (\d+) bytes/.exec(t)?.[1]??4096),a=i(r=>Number(new RegExp(`Pages ${r}:\\s+(\\d+)`).exec(t)?.[1]??0),"pages"),n=a("free")+a("inactive")+a("speculative")+a("purgeable");if(n>0)return{bytes:n*o,source:"vm_stat free+inactive+speculative+purgeable"}}else if(e==="linux"){const t=/MemAvailable:\s+(\d+) kB/.exec(fs.readFileSync("/proc/meminfo","utf8"));if(t)return{bytes:Number(t[1])*1024,source:"/proc/meminfo MemAvailable"}}return{bytes:os.freemem(),source:"os.freemem()"}}catch{return}}p(availableSystemMemory,"availableSystemMemory"),i(availableSystemMemory,"availableSystemMemory");function heapSnapshotRefusal(e,t){if(t.live>e.maxHeapBytes)return`the pod's live heap is ${Math.round(t.live/MB)} MB, over the ${Math.round(e.maxHeapBytes/MB)} MB bound (LENSMCP_HEAP_SNAPSHOT_MAX_MB) \u2014 a snapshot this size needs several times that in memory while it is written`;if(t.available){const o=Math.max(e.minFreeBytes,e.freeFactor*t.live);if(t.available.bytes<o)return`only ${Math.round(t.available.bytes/MB)} MB of memory is available (${t.available.source}) and a snapshot of a ${Math.round(t.live/MB)} MB heap needs about ${Math.round(o/MB)} MB \u2014 free some memory first, or lower the floor with LENSMCP_HEAP_SNAPSHOT_MIN_FREE_MB / LENSMCP_HEAP_SNAPSHOT_FREE_FACTOR`}}p(heapSnapshotRefusal,"heapSnapshotRefusal"),i(heapSnapshotRefusal,"heapSnapshotRefusal");function heapSnapshotDir(e=process.env,t=process.cwd()){return e.LENSMCP_HEAP_DIR?path.resolve(e.LENSMCP_HEAP_DIR):e.LENSMCP_EVENT_FILE?path.join(path.dirname(path.resolve(e.LENSMCP_EVENT_FILE)),"heap"):path.join(t,".lensmcp","heap")}p(heapSnapshotDir,"heapSnapshotDir"),i(heapSnapshotDir,"heapSnapshotDir");function heapSnapshotFileName(e,t,o,a,n){const r=(e||"service").replace(/[^A-Za-z0-9_.-]/g,"_"),s=new Date(n).toISOString().replace(/[:.]/g,"-");return`${r}-pod${t}-pid${o}-g${a}-${s}.heapsnapshot`}p(heapSnapshotFileName,"heapSnapshotFileName"),i(heapSnapshotFileName,"heapSnapshotFileName");function ensureHeapDir(e){fs.mkdirSync(e,{recursive:!0,mode:448});const t=fs.lstatSync(e);if(!t.isDirectory())throw new Error(`${e} is not a directory`);(t.mode&63)!==0&&fs.chmodSync(e,448)}p(ensureHeapDir,"ensureHeapDir"),i(ensureHeapDir,"ensureHeapDir");function pruneHeapSnapshots(e,t,o=Date.now()){const a=[];let n;try{n=fs.readdirSync(e).filter(r=>r.endsWith(".heapsnapshot")).map(r=>{const s=path.join(e,r),u=fs.lstatSync(s);return u.isFile()?{file:s,mtime:u.mtimeMs}:void 0}).filter(r=>r!==void 0).sort((r,s)=>s.mtime-r.mtime)}catch{return a}return n.forEach((r,s)=>{if(s>=t.keep-1||o-r.mtime>t.maxAgeMs)try{fs.unlinkSync(r.file),a.push(r.file)}catch{}}),a}p(pruneHeapSnapshots,"pruneHeapSnapshots"),i(pruneHeapSnapshots,"pruneHeapSnapshots");function writeHeapSnapshot(e){const t=Date.now(),o=fs.constants.O_WRONLY|fs.constants.O_CREAT|fs.constants.O_EXCL|(fs.constants.O_NOFOLLOW??0),a=fs.openSync(e,o,384),n=new inspector.Session;let r=0,s;return new Promise((u,S)=>{const m=i(c=>{n.removeAllListeners("HeapProfiler.addHeapSnapshotChunk");try{n.disconnect()}catch{}try{fs.closeSync(a)}catch{}const l=c??s;if(l){try{fs.unlinkSync(e)}catch{}S(l);return}u({bytes:r,ms:Date.now()-t})},"finish");try{n.connect(),n.on("HeapProfiler.addHeapSnapshotChunk",c=>{if(!s)try{r+=fs.writeSync(a,c.params.chunk)}catch(l){s=l}}),n.post("HeapProfiler.takeHeapSnapshot",{reportProgress:!1},c=>m(c))}catch(c){m(c)}})}p(writeHeapSnapshot,"writeHeapSnapshot"),i(writeHeapSnapshot,"writeHeapSnapshot");
|
package/hot-swap-registries.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Why a pod's heap grew by a whole bundle on every hot swap (issues/foodguard
|
|
2
|
+
* Why a pod's heap grew by a whole bundle on every hot swap (issues/closed/foodguard-24).
|
|
3
3
|
*
|
|
4
4
|
* A swap re-requires the webpack bundle in the SAME process, so every decorated class is created again.
|
|
5
5
|
* The old app is closed and its module graph should go with it — but a library loaded from node_modules
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The inspector credential, and where it may go (issues/closed/pod-memory-06).
|
|
3
|
+
*
|
|
4
|
+
* A Node inspector has no authentication: whoever holds its `ws://127.0.0.1:<port>/<uuid>` URL can run code in
|
|
5
|
+
* the pod as the developer. The UUID is the whole credential. It used to reach `.lensmcp/gateway.log` — mode
|
|
6
|
+
* 0644, readable by every account on the machine — three ways: the pod's own "Debugger listening on …" line
|
|
7
|
+
* (Node prints it to the pod's stderr, which the devserver relays), the devserver's echo of it, and the
|
|
8
|
+
* "Debugger ending on …" line at close. The gateway's stdout IS that file.
|
|
9
|
+
*
|
|
10
|
+
* So the URL is handed out on exactly one path, the owner-only control socket (`lensmcp debug <svc>`, and the
|
|
11
|
+
* owner's own `lensmcp logs`), and redacted everywhere it would land in a file. A terminal is the developer's own
|
|
12
|
+
* screen and keeps the attachable link — IDE run consoles make it a click-to-attach — unless what the devserver
|
|
13
|
+
* writes is going into a gateway's log.
|
|
14
|
+
*
|
|
15
|
+
* The inspector itself published the UUID too: `GET /json/list` on its port answers it to anyone who can
|
|
16
|
+
* connect, and an ephemeral loopback port is one `connect()` sweep away. `--inspect-publish-uid=stderr` turns
|
|
17
|
+
* every HTTP endpoint of the inspector off (`/json`, `/json/list`, `/json/version` all answer 404), leaving the
|
|
18
|
+
* URL to the process that opened it — which reports it to the devserver over IPC.
|
|
19
|
+
*/
|
|
20
|
+
/** `text` with every inspector session UUID replaced — host and port kept, so the line still says where. */
|
|
21
|
+
export declare function redactInspectorUrls(text: string): string;
|
|
22
|
+
/** A stream this process writes, as far as the decision needs to know it. */
|
|
23
|
+
export interface StdioStream {
|
|
24
|
+
isTTY?: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* May `stream` carry an attachable inspector URL? Only when it is a terminal AND this process is not writing into
|
|
28
|
+
* a gateway's log — a devserver the gateway started has its stdout piped to the gateway, whose own stdout is
|
|
29
|
+
* `.lensmcp/gateway.log` (`LENSMCP_GATEWAY_PID` marks one, main.devserver.ts `gatewayManaged`).
|
|
30
|
+
*/
|
|
31
|
+
export declare function streamMayCarrySecrets(stream: StdioStream, underGateway: boolean): boolean;
|
|
32
|
+
/** What to write to `stream`: the text itself where it may carry the URL, redacted anywhere else. */
|
|
33
|
+
export declare function forStream(stream: StdioStream, underGateway: boolean, text: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* The flag a pod or worker runs under so its inspector never answers the UUID over HTTP. Left off only under
|
|
36
|
+
* `LENSMCP_DEBUG_FIXED_PORTS` — the opt-in for an IDE that attaches by host:port, which it does by asking
|
|
37
|
+
* `/json/list` for the target. That opt-out already warns that its ports are reachable by every account.
|
|
38
|
+
*/
|
|
39
|
+
export declare function inspectorPublishExecArgv(fixedPorts: boolean): string[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var n=Object.defineProperty;var s=(r,e)=>n(r,"name",{value:e,configurable:!0});var c=Object.defineProperty,t=s((r,e)=>c(r,"name",{value:e,configurable:!0}),"t");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactInspectorUrls=redactInspectorUrls,exports.streamMayCarrySecrets=streamMayCarrySecrets,exports.forStream=forStream,exports.inspectorPublishExecArgv=inspectorPublishExecArgv;const INSPECTOR_TOKEN_RE=/((?:wss?:\/\/[^\s/]+\/|ws=[^\s/]+\/))([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/gi;function redactInspectorUrls(r){return r.replace(INSPECTOR_TOKEN_RE,"$1<inspector-token-redacted>")}s(redactInspectorUrls,"redactInspectorUrls"),t(redactInspectorUrls,"redactInspectorUrls");function streamMayCarrySecrets(r,e){return!e&&r.isTTY===!0}s(streamMayCarrySecrets,"streamMayCarrySecrets"),t(streamMayCarrySecrets,"streamMayCarrySecrets");function forStream(r,e,a){return streamMayCarrySecrets(r,e)?a:redactInspectorUrls(a)}s(forStream,"forStream"),t(forStream,"forStream");function inspectorPublishExecArgv(r){return r?[]:["--inspect-publish-uid=stderr"]}s(inspectorPublishExecArgv,"inspectorPublishExecArgv"),t(inspectorPublishExecArgv,"inspectorPublishExecArgv");
|