@lensmcp/cluster 1.16.30 → 1.16.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"lens-children.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lens-children.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,kCAAkC,CAAC;AAI1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAuB,MAAM,SAAS,CAAC;AAQ1F,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAM7E;;;;8CAI8C;AAC9C,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO5D;AAED,MAAM,WAAW,YAAY;IAC3B,iBAAiB,EAAE,UAAU,CAAC;IAC9B;;gFAE4E;IAC5E,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC;;6GAEyG;IACzG,sBAAsB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IACvF;;yGAEqG;IACrG,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IACpE;gHAC4G;IAC5G,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,cAAc,EAClB,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,qBAAqB,EAE9B,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,GACzD,YAAY,CAmWd"}
1
+ {"version":3,"file":"lens-children.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lens-children.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,kCAAkC,CAAC;AAI1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAuB,MAAM,SAAS,CAAC;AAQ1F,OAAO,EAA0B,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAM7E;;;;8CAI8C;AAC9C,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO5D;AAED,MAAM,WAAW,YAAY;IAC3B,iBAAiB,EAAE,UAAU,CAAC;IAC9B;;gFAE4E;IAC5E,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC;;6GAEyG;IACzG,sBAAsB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAC,CAAC;IACvF;;yGAEqG;IACrG,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;IACpE;gHAC4G;IAC5G,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,cAAc,EAClB,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,qBAAqB,EAE9B,IAAI,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,GACzD,YAAY,CAsWd"}
@@ -137,11 +137,14 @@ deps) {
137
137
  lensSweeper = setInterval(() => {
138
138
  if (rt.stopped())
139
139
  return;
140
- const changedAt = rt.sourceSetChangedAt ?? 0;
141
- if (changedAt <= 0 || viteChildren.size === 0)
140
+ const wideChangedAt = rt.sourceSetChangedAt ?? 0;
141
+ if (wideChangedAt <= 0 || viteChildren.size === 0)
142
142
  return;
143
143
  const now = Date.now();
144
144
  for (const [child, meta] of viteChildren) {
145
+ // Prefer this app's SCOPED clock — the file set within the subtrees ITS vite can resolve. On the wide
146
+ // clock a backend-only `.ts` add anywhere in the monorepo restarted every frontend (`sourceScopeDirs`).
147
+ const changedAt = rt.sourceSetChangedFor?.get(meta.project) ?? wideChangedAt;
145
148
  const last = viteRecycleAt.get(meta.project) ?? 0;
146
149
  if (!(0, lifecycle_1.lensFrontendStale)(meta.spawnedAt, last, changedAt, now, cfg))
147
150
  continue;
@@ -186,7 +189,7 @@ deps) {
186
189
  rt.rebuildRoutes();
187
190
  // Publish this workspace to the GLOBAL registry so the root chooser at `${lensHost}/` lists it
188
191
  // (and marks it live while this gateway runs). Best-effort — a registry write never blocks serving.
189
- (0, workspace_registry_1.registerWorkspace)({ key: scope.key, root: rt.root, base: lensBase, pid: process.pid, updatedAt: new Date().toISOString() });
192
+ (0, workspace_registry_1.registerWorkspace)({ key: scope.key, root: rt.root, base: lensBase, pid: process.pid, procStamp: (0, workspace_registry_1.processStamp)(process.pid), updatedAt: new Date().toISOString() });
190
193
  console.log(`[gateway] starting lens dashboard → http://localhost:${dashboardPort} (routed at https://${lensHost}${lensBase}/)`);
191
194
  emit('info', `lens dashboard up: ${lensHost}${lensBase} → :${dashboardPort}`, 'cluster-lens-dashboard', { kind: 'lens-dashboard-up', host: lensHost, port: dashboardPort, base: lensBase });
192
195
  spawnManagedChild('lens-dashboard', process.execPath, [dashboardBundle], {
@@ -271,7 +274,7 @@ deps) {
271
274
  rt.registry.appendRoutes(fragment.wsKey, [{ host: lensHost, prefix: lensBase, project: observability_1.LENS_DASHBOARD_PROJECT, target: `http://localhost:${port}` }]);
272
275
  // Publish the hosted workspace to the GLOBAL chooser registry (keyed on the DAEMON's pid — the daemon is
273
276
  // what keeps it live), so `lensmcp.local/` lists it. Reaped on unregister below.
274
- (0, workspace_registry_1.registerWorkspace)({ key: fragment.wsKey, root: fragment.root, base: lensBase, pid: process.pid, updatedAt: new Date().toISOString() });
277
+ (0, workspace_registry_1.registerWorkspace)({ key: fragment.wsKey, root: fragment.root, base: lensBase, pid: process.pid, procStamp: (0, workspace_registry_1.processStamp)(process.pid), updatedAt: new Date().toISOString() });
275
278
  console.log(`[gateway] hosting dashboard for workspace '${fragment.wsKey}' → http://localhost:${port} (routed at https://${lensHost}${lensBase}/)`);
276
279
  emit('info', `lens dashboard up: ${lensHost}${lensBase} → :${port}`, 'cluster-lens-dashboard', { kind: 'lens-dashboard-up', host: lensHost, port, base: lensBase });
277
280
  spawnManagedChild(label, process.execPath, [bundle], {
@@ -3,8 +3,34 @@ import type { Observability } from './observability';
3
3
  /** Bounded, async, non-blocking walk of the workspace source tree → a signature of the file SET
4
4
  * (`<count>:<xor-of-path-hashes>`). Skips node_modules/dist/build/dotdirs + `*-devserver` sock dirs + every
5
5
  * GITIGNORED path (runtime output is not source — see `SRC_EXCLUDE`), and only counts source-extension
6
- * files. Order-independent (readdir order irrelevant) and null-safe. */
7
- export declare function sourceSetSignature(root: string): Promise<string>;
6
+ * files. Order-independent (readdir order irrelevant) and null-safe.
7
+ *
8
+ * `scopeDirs` (absolute, from {@link sourceScopeDirs}) narrows the walk to the subtrees a given child could
9
+ * actually resolve, instead of the whole monorepo — see that function for WHY. Omitted ⇒ the whole root
10
+ * (the original workspace-wide behavior, still used when a child's project root is unknown). Root-level
11
+ * loose files are always counted, so a `package.json`/`tsconfig.base.json` add reaches every scope. */
12
+ export declare function sourceSetSignature(root: string, scopeDirs?: readonly string[]): Promise<string>;
13
+ /**
14
+ * PURE: which subtrees a child of `projectRoot` must watch for a "file set changed" verdict.
15
+ *
16
+ * The staleness signal exists to catch a vite/pod whose fs-watcher MISSED a file add. But the signature was
17
+ * workspace-WIDE while its consumers are PER-CHILD, so adding a backend-only `server/apps/auth/**\/*.ts`
18
+ * — a file in no vite module graph — recycled every frontend vite too. Worked failure (foodguard,
19
+ * 2026-07-28): one session writing `server/apps/auth/src/modules/totp/**` added a `.ts` every ~15-20s and
20
+ * held `app.foodguard.local` in near-continuous 502s, 951 recycles in a session, none of them relevant to
21
+ * the dashboard.
22
+ *
23
+ * The scope is the child's OWN top-level bucket plus every bucket that hosts no OTHER gateway-managed
24
+ * project (`managedBuckets`). In a scope-first monorepo (`web/ server/ shared/ ai/ tools/`) that yields
25
+ * `web + shared + ai + tools` for a frontend and `server + shared + ai + tools` for a service: a
26
+ * `server/**` add no longer touches a frontend, while a `shared/contracts` add still reaches both — which
27
+ * is correct, since both import it. It deliberately OVER-includes (a frontend watches `ai/` it may never
28
+ * import) because a false recycle costs a cold start whereas a MISSED one costs a wedged dev server.
29
+ *
30
+ * Both args are absolute; the result is absolute and deduped. An empty result (the project root IS the
31
+ * workspace root, or it sits outside it) means "no narrowing" → the caller falls back to the whole root.
32
+ */
33
+ export declare function sourceScopeDirs(workspaceRoot: string, projectRoot: string, managedBuckets: readonly string[], topLevelDirs: readonly string[]): string[];
8
34
  export interface PodRecycleConfig {
9
35
  maxAgeMs: number;
10
36
  graceMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AAyE/D;;;yEAGyE;AACzE,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,oBAAoB,CAAC,EACpG,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,gBAAgB,GACpB,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAQvC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAMT;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,2EAA2E;IAC3E,YAAY,IAAI,MAAM,CAAC,OAAO,CAAC;CAChC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,GAAG,YAAY,CA4XvF"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AAyE/D;;;;;;;;wGAQwG;AACxG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqCrG;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,SAAS,MAAM,EAAE,EACjC,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,MAAM,EAAE,CAcV;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,oBAAoB,CAAC,EACpG,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,gBAAgB,GACpB,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAQvC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAMT;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,2EAA2E;IAC3E,YAAY,IAAI,MAAM,CAAC,OAAO,CAAC;CAChC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,GAAG,YAAY,CAgcvF"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sourceSetSignature = sourceSetSignature;
4
+ exports.sourceScopeDirs = sourceScopeDirs;
4
5
  exports.podRecycleReason = podRecycleReason;
5
6
  exports.lensFrontendStale = lensFrontendStale;
6
7
  exports.createServiceLayer = createServiceLayer;
@@ -90,14 +91,19 @@ async function gitIgnoredPaths(root) {
90
91
  /** Bounded, async, non-blocking walk of the workspace source tree → a signature of the file SET
91
92
  * (`<count>:<xor-of-path-hashes>`). Skips node_modules/dist/build/dotdirs + `*-devserver` sock dirs + every
92
93
  * GITIGNORED path (runtime output is not source — see `SRC_EXCLUDE`), and only counts source-extension
93
- * files. Order-independent (readdir order irrelevant) and null-safe. */
94
- async function sourceSetSignature(root) {
94
+ * files. Order-independent (readdir order irrelevant) and null-safe.
95
+ *
96
+ * `scopeDirs` (absolute, from {@link sourceScopeDirs}) narrows the walk to the subtrees a given child could
97
+ * actually resolve, instead of the whole monorepo — see that function for WHY. Omitted ⇒ the whole root
98
+ * (the original workspace-wide behavior, still used when a child's project root is unknown). Root-level
99
+ * loose files are always counted, so a `package.json`/`tsconfig.base.json` add reaches every scope. */
100
+ async function sourceSetSignature(root, scopeDirs) {
95
101
  let count = 0;
96
102
  let xor = 0;
97
103
  const ignored = await gitIgnoredPaths(root);
98
104
  // git speaks posix-relative; `path.relative` yields `\` on win32 — normalize before every lookup.
99
105
  const rel = (abs) => path.relative(root, abs).split(path.sep).join('/');
100
- const walk = async (dir) => {
106
+ const walk = async (dir, recurse = true) => {
101
107
  if (count > SCAN_FILE_CAP)
102
108
  return;
103
109
  let entries;
@@ -113,6 +119,8 @@ async function sourceSetSignature(root) {
113
119
  const name = e.name;
114
120
  const abs = path.join(dir, name);
115
121
  if (e.isDirectory()) {
122
+ if (!recurse)
123
+ continue; // scoped mode: root's own dirs are entered only via scopeDirs
116
124
  if (name.startsWith('.') || SRC_EXCLUDE.has(name) || name.endsWith('-devserver'))
117
125
  continue;
118
126
  if (ignored.has(`${rel(abs)}/`))
@@ -130,9 +138,56 @@ async function sourceSetSignature(root) {
130
138
  }
131
139
  }
132
140
  };
133
- await walk(root);
141
+ if (scopeDirs && scopeDirs.length > 0) {
142
+ await walk(root, false); // root-level loose files only — its subdirs come from scopeDirs
143
+ // Sequential, not Promise.all: `count`/`xor` are shared accumulators, and the xor is
144
+ // order-independent anyway — so concurrency would buy nothing but interleaving risk.
145
+ for (const dir of scopeDirs)
146
+ await walk(dir);
147
+ }
148
+ else {
149
+ await walk(root);
150
+ }
134
151
  return `${count}:${(xor >>> 0).toString(36)}`;
135
152
  }
153
+ /**
154
+ * PURE: which subtrees a child of `projectRoot` must watch for a "file set changed" verdict.
155
+ *
156
+ * The staleness signal exists to catch a vite/pod whose fs-watcher MISSED a file add. But the signature was
157
+ * workspace-WIDE while its consumers are PER-CHILD, so adding a backend-only `server/apps/auth/**\/*.ts`
158
+ * — a file in no vite module graph — recycled every frontend vite too. Worked failure (foodguard,
159
+ * 2026-07-28): one session writing `server/apps/auth/src/modules/totp/**` added a `.ts` every ~15-20s and
160
+ * held `app.foodguard.local` in near-continuous 502s, 951 recycles in a session, none of them relevant to
161
+ * the dashboard.
162
+ *
163
+ * The scope is the child's OWN top-level bucket plus every bucket that hosts no OTHER gateway-managed
164
+ * project (`managedBuckets`). In a scope-first monorepo (`web/ server/ shared/ ai/ tools/`) that yields
165
+ * `web + shared + ai + tools` for a frontend and `server + shared + ai + tools` for a service: a
166
+ * `server/**` add no longer touches a frontend, while a `shared/contracts` add still reaches both — which
167
+ * is correct, since both import it. It deliberately OVER-includes (a frontend watches `ai/` it may never
168
+ * import) because a false recycle costs a cold start whereas a MISSED one costs a wedged dev server.
169
+ *
170
+ * Both args are absolute; the result is absolute and deduped. An empty result (the project root IS the
171
+ * workspace root, or it sits outside it) means "no narrowing" → the caller falls back to the whole root.
172
+ */
173
+ function sourceScopeDirs(workspaceRoot, projectRoot, managedBuckets, topLevelDirs) {
174
+ const bucketOf = (abs) => {
175
+ const r = path.relative(workspaceRoot, abs).split(path.sep).filter(Boolean);
176
+ return r.length === 0 || r[0].startsWith('..') ? '' : r[0];
177
+ };
178
+ const own = bucketOf(projectRoot);
179
+ if (!own)
180
+ return []; // project root === workspace root (or outside it) → cannot narrow
181
+ const managed = new Set(managedBuckets.filter(Boolean));
182
+ const keep = new Set([own]);
183
+ for (const d of topLevelDirs) {
184
+ if (d.startsWith('.') || SRC_EXCLUDE.has(d) || d.endsWith('-devserver'))
185
+ continue;
186
+ if (!managed.has(d))
187
+ keep.add(d); // a bucket owned by no managed project = shared/leaf → always watch
188
+ }
189
+ return [...keep].sort().map((d) => path.join(workspaceRoot, d));
190
+ }
136
191
  /**
137
192
  * PURE decision: should this pod be recycled as a stale-view zombie, and why? A pod qualifies only when it
138
193
  * is a live, UP, gateway-spawned pod that is currently QUIET (0 in-flight AND idle for `settleMs`, so a busy
@@ -196,7 +251,7 @@ function createServiceLayer(rt, obs) {
196
251
  * its whole devserver tree with it. One `ps` exec; a clean cold start is a no-op.
197
252
  */
198
253
  const reapForeignRunners = (svc, target) => {
199
- let out = '';
254
+ let out;
200
255
  try {
201
256
  out = (0, node_child_process_1.execFileSync)('ps', ['ax', '-ww', '-o', 'pid=,pgid=,command='], { encoding: 'utf8', timeout: 5_000 });
202
257
  }
@@ -300,6 +355,11 @@ function createServiceLayer(rt, obs) {
300
355
  NX_ISOLATE_PLUGINS: 'false',
301
356
  ...(svc.wsKey ? { LENSMCP_WS_KEY: svc.wsKey } : {}),
302
357
  LENSMCP_EVENT_FILE: svc.wsKey === rt.wsKey ? rt.eventFile : path.join(svc.root, '.lensmcp', 'events.jsonl'),
358
+ // Rides the env inherit down the whole nx chain (nx run → run-executor → serve fork) into
359
+ // main.devserver, whose gateway watchdog self-exits the pod tree when this pid dies. Without
360
+ // it an UNGRACEFUL gateway death (crash/SIGKILL — no process-group kill runs) orphans the
361
+ // entire chain under launchd forever (2026-07-28: a day-old tetros pod tree at PPID 1).
362
+ LENSMCP_GATEWAY_PID: String(process.pid),
303
363
  ...eastWestEnv(rt, svc.project),
304
364
  },
305
365
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -530,23 +590,97 @@ function createServiceLayer(rt, obs) {
530
590
  settleMs: types_1.POD_RECYCLE_SETTLE_MS, cooldownMs: types_1.POD_STALE_RECYCLE_COOLDOWN_MS,
531
591
  };
532
592
  let prevSig;
593
+ const prevScopedSig = new Map(); // bucket → its last signature
533
594
  let scanInFlight = false;
534
595
  let lastScanAt = 0;
535
- // Kick a bounded, async source-set scan when due; on a SET change (file added/removed) stamp the shared
536
- // clock so pods that predate it recycle. Non-blocking the sweeper never awaits it.
596
+ /** The distinct top-level buckets owned by a gateway-managed project (services + `lens:true` apps) every
597
+ * OTHER bucket is shared/leaf and belongs in every scope. Recomputed per scan so a workspace registered
598
+ * after boot is picked up. */
599
+ const managedBuckets = () => {
600
+ const roots = rt.projectRoots ?? {};
601
+ const names = new Set(rt.services.map((s) => s.project));
602
+ for (const r of rt.routes)
603
+ if (r.lens)
604
+ names.add(r.lens.project);
605
+ const buckets = new Set();
606
+ for (const n of names) {
607
+ const pr = roots[n];
608
+ if (!pr)
609
+ continue;
610
+ const seg = path.relative(rt.root, pr).split(path.sep).filter(Boolean)[0];
611
+ if (seg && !seg.startsWith('..'))
612
+ buckets.add(seg);
613
+ }
614
+ return [...buckets];
615
+ };
616
+ /** project → the absolute dirs its child must watch, or `undefined` when it cannot be narrowed (unknown
617
+ * project root / no `projectsConfigurations`) → that child keeps the workspace-wide clock. */
618
+ const scopeFor = (project, topLevel, managed) => {
619
+ const pr = rt.projectRoots?.[project];
620
+ if (!pr)
621
+ return undefined;
622
+ const dirs = sourceScopeDirs(rt.root, pr, managed, topLevel);
623
+ return dirs.length > 0 ? dirs : undefined;
624
+ };
625
+ // Kick a bounded, async source-set scan when due; on a SET change (file added/removed) stamp the clocks so
626
+ // children that predate it recycle. Non-blocking — the sweeper never awaits it. Two tiers: the
627
+ // workspace-WIDE signature (the fallback + back-compat clock) and one SCOPED signature per managed
628
+ // project's bucket, so a `server/**` add no longer recycles a `web/**` vite (see `sourceScopeDirs`). One
629
+ // scan per bucket, not per project — sibling projects in a bucket share a scope, so `web/apps/dashboard`
630
+ // and `web/apps/authentication` cost a single walk between them.
537
631
  const maybeScanSourceSet = () => {
538
632
  if (scanInFlight || Date.now() - lastScanAt < types_1.POD_STALE_SCAN_MS)
539
633
  return;
540
634
  scanInFlight = true;
541
635
  lastScanAt = Date.now();
542
- void sourceSetSignature(rt.root)
543
- .then((sig) => {
544
- if (prevSig !== undefined && sig !== prevSig) {
636
+ void (async () => {
637
+ const wide = await sourceSetSignature(rt.root);
638
+ if (prevSig !== undefined && wide !== prevSig) {
545
639
  rt.sourceSetChangedAt = Date.now();
546
640
  emit('info', 'source file set changed — stale pods recycle when idle', 'cluster-gateway', { kind: 'source-set-changed' });
547
641
  }
548
- prevSig = sig;
549
- })
642
+ prevSig = wide;
643
+ // Scoped tier. Skipped entirely without project roots (nothing to narrow by) — consumers then read
644
+ // the wide clock exactly as before.
645
+ if (!rt.projectRoots)
646
+ return;
647
+ let topLevel;
648
+ try {
649
+ topLevel = (await fs.promises.readdir(rt.root, { withFileTypes: true }))
650
+ .filter((e) => e.isDirectory()).map((e) => e.name);
651
+ }
652
+ catch {
653
+ return;
654
+ }
655
+ const managed = managedBuckets();
656
+ const projects = new Set(rt.services.map((s) => s.project));
657
+ for (const r of rt.routes)
658
+ if (r.lens)
659
+ projects.add(r.lens.project);
660
+ // bucket → the scope dirs + the projects sharing it (one walk per bucket).
661
+ const byBucket = new Map();
662
+ for (const project of projects) {
663
+ const dirs = scopeFor(project, topLevel, managed);
664
+ if (!dirs)
665
+ continue;
666
+ const key = dirs.join('|');
667
+ const entry = byBucket.get(key) ?? { dirs, projects: [] };
668
+ entry.projects.push(project);
669
+ byBucket.set(key, entry);
670
+ }
671
+ const now = Date.now();
672
+ for (const [key, { dirs, projects: sharing }] of byBucket) {
673
+ const sig = await sourceSetSignature(rt.root, dirs);
674
+ const prev = prevScopedSig.get(key);
675
+ prevScopedSig.set(key, sig);
676
+ if (prev === undefined || sig === prev)
677
+ continue;
678
+ rt.sourceSetChangedFor ??= new Map();
679
+ for (const p of sharing)
680
+ rt.sourceSetChangedFor.set(p, now);
681
+ emit('info', `source file set changed in ${sharing.join(', ')}'s scope`, 'cluster-gateway', { kind: 'source-set-changed', scoped: true, projects: sharing });
682
+ }
683
+ })()
550
684
  .catch(() => undefined)
551
685
  .finally(() => { scanInFlight = false; });
552
686
  };
@@ -590,7 +724,10 @@ function createServiceLayer(rt, obs) {
590
724
  // stale — neither exit-respawn nor wedge-recycle sees it. Idle-gated in podRecycleReason so a busy
591
725
  // pod is never interrupted.
592
726
  if (live && !coldStarting && types_1.POD_RECYCLE_ENABLED) {
593
- const reason = podRecycleReason(svc, Date.now(), rt.sourceSetChangedAt ?? 0, recycleCfg);
727
+ // Prefer this project's SCOPED clock (only the subtrees it can resolve); fall back to the
728
+ // workspace-wide one when the scan hasn't produced a scoped verdict for it.
729
+ const changedAt = rt.sourceSetChangedFor?.get(svc.project) ?? rt.sourceSetChangedAt ?? 0;
730
+ const reason = podRecycleReason(svc, Date.now(), changedAt, recycleCfg);
594
731
  if (reason)
595
732
  recyclePod(svc, reason);
596
733
  }
@@ -1 +1 @@
1
- {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/observability.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD,MAAM,WAAW,aAAa;IAC5B;;4GAEwG;IACxG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvJ,GAAG,IAAI,MAAM,CAAC;IACd,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3D,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1F,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5G,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5E,0FAA0F;IAC1F,gBAAgB,IAAI,MAAM,CAAC,OAAO,CAAC;CACpC;AAKD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAoGtG"}
1
+ {"version":3,"file":"observability.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/observability.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAoB9C,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEpD;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD,MAAM,WAAW,aAAa;IAC5B;;4GAEwG;IACxG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvJ,GAAG,IAAI,MAAM,CAAC;IACd,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3D,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1F,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5G,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5E,0FAA0F;IAC1F,gBAAgB,IAAI,MAAM,CAAC,OAAO,CAAC;CACpC;AAKD,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAiHtG"}
@@ -17,6 +17,15 @@ const tslib_1 = require("tslib");
17
17
  */
18
18
  const fs = tslib_1.__importStar(require("node:fs"));
19
19
  const path = tslib_1.__importStar(require("node:path"));
20
+ const busHygiene = (() => {
21
+ try {
22
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
23
+ return require('@lensmcp/node-instrumentation');
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ })();
20
29
  /**
21
30
  * The lens's OWN dashboard route project (the value `lens-children.ts` gives the `lensmcp.local/<key>/`
22
31
  * route). Traffic to it is the lens UI polling its own control-plane API (`/<key>/api/cluster|logs|snapshot`)
@@ -29,15 +38,28 @@ exports.LENS_DASHBOARD_PROJECT = '(lens-dashboard)';
29
38
  const hdr = (v) => typeof v === 'string' && v !== '' ? v : Array.isArray(v) ? v[0] : undefined;
30
39
  function createObservability(eventFile, opts) {
31
40
  const eid = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 12);
41
+ // The gateway narrates OTHER apps' traffic — an app stuck in a retry loop must not turn that
42
+ // narration into a bus flood (2026-07-28: `gateway-request:<project>` alone wrote thousands of
43
+ // lines/min). Per-fingerprint token bucket: first burst passes, then a trickle carrying
44
+ // `floodSuppressed` counts. Errors stay loud — the burst re-arms once the storm subsides.
45
+ const floodGuard = busHygiene?.createEventFloodGuard({ burst: 30, perSec: 5 });
32
46
  const emit = (severity, title, fingerprint, raw, flowCtx, busFile) => {
33
47
  const target = busFile ?? eventFile; // a registered workspace's service events go to ITS bus, not the daemon's
34
48
  try {
49
+ const admit = floodGuard ? floodGuard.admit(fingerprint) : { ok: true, suppressed: 0 };
50
+ if (!admit.ok)
51
+ return;
35
52
  fs.mkdirSync(path.dirname(target), { recursive: true });
36
- fs.appendFileSync(target, JSON.stringify({
53
+ const line = JSON.stringify({
37
54
  id: eid(), sessionId: 'pending', timestamp: Date.now(),
38
55
  source: 'gateway', category: 'cluster', severity,
39
- context: { sessionId: 'pending', ...flowCtx }, fingerprint, title, raw,
40
- }) + '\n');
56
+ context: { sessionId: 'pending', ...flowCtx }, fingerprint, title,
57
+ raw: admit.suppressed > 0 ? { ...raw, floodSuppressed: admit.suppressed } : raw,
58
+ }) + '\n';
59
+ // The gateway was the one bus writer with NO size cap — enforce the same rotation contract
60
+ // as every other producer (emit.ts / capture-runner), per target file.
61
+ busHygiene?.maybeRotateEventFile(target, line.length);
62
+ fs.appendFileSync(target, line);
41
63
  }
42
64
  catch { /* lens offline — never block traffic */ }
43
65
  };
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/server.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAK/D,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAkB,qBAAqB,EAAe,KAAK,EAAE,MAAM,SAAS,CAAC;AA0BxH;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,KAAK,EAAE,GAAG,gBAAgB,CA4ChF;AAED;mGACmG;AACnG,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAA8E,CAAC;AAkBpI,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CA8RxB"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/server.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AAK/D,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAkB,qBAAqB,EAAe,KAAK,EAAE,MAAM,SAAS,CAAC;AA0BxH;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,KAAK,EAAE,GAAG,gBAAgB,CA4ChF;AAED;mGACmG;AACnG,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAA8E,CAAC;AAkBpI,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAqSxB"}
@@ -193,6 +193,11 @@ async function startGateway(options, context) {
193
193
  sweepMs,
194
194
  trafficFlushMs,
195
195
  https,
196
+ // Absolute project root per project name — the scanner narrows each child's staleness scope with it
197
+ // (`sourceScopeDirs`), so a `server/**` file add stops recycling every `web/**` vite. Absent (a bare
198
+ // context with no projectsConfigurations) ⇒ the workspace-wide clock, i.e. previous behavior.
199
+ projectRoots: Object.fromEntries(Object.entries(context.projectsConfigurations?.projects ?? {})
200
+ .map(([name, cfg]) => [name, path.resolve(context.root, cfg.root)])),
196
201
  stopped: () => stopped,
197
202
  };
198
203
  // --- layers -----------------------------------------------------------------
@@ -279,6 +279,16 @@ export interface GatewayRuntime {
279
279
  * lens-frontend sweeper (lens-children) — recycle any child that spawned before it. `0`/undefined until
280
280
  * the first change is observed. Mutable (the one non-readonly runtime field, written only by the scanner). */
281
281
  sourceSetChangedAt?: number;
282
+ /** Per-PROJECT scoped twin of {@link sourceSetChangedAt}: the last time the file set changed *within the
283
+ * subtrees that project could actually resolve* (`sourceScopeDirs`). The workspace-wide clock above
284
+ * recycles every child for an add ANYWHERE — so a backend-only `.ts` restarted every frontend vite. A
285
+ * consumer prefers its own entry and falls back to the wide clock when absent (project root unknown, or
286
+ * the scan not yet warmed). Mutable, written only by the scanner. */
287
+ sourceSetChangedFor?: Map<string, number>;
288
+ /** Absolute project root per project name (from `projectsConfigurations`) — lets the scanner derive each
289
+ * child's watch scope, and tells it which top-level buckets are OWNED by a managed project. Absent ⇒
290
+ * every consumer falls back to the workspace-wide clock (previous behavior). */
291
+ readonly projectRoots?: Readonly<Record<string, string>>;
282
292
  /** Teardown flag — a GETTER, never a captured boolean, so every layer reads
283
293
  * the LIVE value (an in-flight cold start / auto-heal must not act after stop). */
284
294
  stopped(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/types.ts"],"names":[],"mappings":"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"}
@@ -7,9 +7,23 @@ export interface WorkspaceEntry {
7
7
  base: string;
8
8
  /** The gateway process that registered this workspace (owns :443 for it). Liveness is checked via this. */
9
9
  pid: number;
10
+ /**
11
+ * The registering process's start-time stamp (`ps -o lstart=`), captured at register. A pid alone
12
+ * can LIE: after an ungraceful gateway death the OS recycles the number, and `kill(pid, 0)` then
13
+ * vouches for a stranger (2026-07-28: tetros's dead-gateway pid came back as an nx run-executor and
14
+ * the chooser kept listing the workspace as live). Same pid + same start time = same process.
15
+ * Absent on legacy entries and on platforms without `ps` — liveness then falls back to kill(0).
16
+ */
17
+ procStamp?: string;
10
18
  /** ISO timestamp of the last register. */
11
19
  updatedAt: string;
12
20
  }
21
+ /**
22
+ * The start-time identity stamp for a pid (`''` when `ps` is unavailable — Windows, restricted
23
+ * environments — or the process is gone). Start time is immutable for the life of a process and
24
+ * survives in no recycled successor, which makes it the cheapest cross-platform-ish identity proof.
25
+ */
26
+ export declare function processStamp(pid: number): string;
13
27
  /** `~/.lensmcp/workspaces.json` — override the home dir via `LENSMCP_HOME` (tests). */
14
28
  export declare function registryPath(): string;
15
29
  export declare function readWorkspaces(file?: string): WorkspaceEntry[];
@@ -17,6 +31,10 @@ export declare function readWorkspaces(file?: string): WorkspaceEntry[];
17
31
  export declare function registerWorkspace(entry: WorkspaceEntry, file?: string): void;
18
32
  /** Drop this workspace — called on a graceful gateway stop. */
19
33
  export declare function unregisterWorkspace(key: string, file?: string): void;
20
- /** Is the gateway that registered this workspace still alive? `process.kill(pid, 0)` throws ESRCH if not. */
21
- export declare function isWorkspaceLive(entry: Pick<WorkspaceEntry, 'pid'>): boolean;
34
+ /**
35
+ * Is the gateway that registered this workspace still alive? `process.kill(pid, 0)` throws ESRCH if
36
+ * not — and when the entry carries a `procStamp`, the pid must ALSO still be the SAME process
37
+ * (recycled pids fail the start-time comparison instead of impersonating a live gateway).
38
+ */
39
+ export declare function isWorkspaceLive(entry: Pick<WorkspaceEntry, 'pid' | 'procStamp'>): boolean;
22
40
  //# sourceMappingURL=workspace-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-registry.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/workspace-registry.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,cAAc;IAC7B,4FAA4F;IAC5F,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,2GAA2G;IAC3G,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,uFAAuF;AACvF,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,cAAc,CAAC,IAAI,SAAiB,GAAG,cAAc,EAAE,CAYtE;AAWD,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,SAAiB,GAAG,IAAI,CAKpF;AAED,+DAA+D;AAC/D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAiB,GAAG,IAAI,CAK5E;AAED,6GAA6G;AAC7G,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,OAAO,CAQ3E"}
1
+ {"version":3,"file":"workspace-registry.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/workspace-registry.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,cAAc;IAC7B,4FAA4F;IAC5F,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,2GAA2G;IAC3G,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAWhD;AAED,uFAAuF;AACvF,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,cAAc,CAAC,IAAI,SAAiB,GAAG,cAAc,EAAE,CAYtE;AAWD,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,SAAiB,GAAG,IAAI,CAKpF;AAED,+DAA+D;AAC/D,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAiB,GAAG,IAAI,CAK5E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,WAAW,CAAC,GAAG,OAAO,CAUzF"}
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processStamp = processStamp;
3
4
  exports.registryPath = registryPath;
4
5
  exports.readWorkspaces = readWorkspaces;
5
6
  exports.registerWorkspace = registerWorkspace;
@@ -16,9 +17,29 @@ const tslib_1 = require("tslib");
16
17
  * Concurrency: read-modify-write, best-effort. Writes are rare (start/stop only) and the file is tiny, so
17
18
  * a lost update at most drops one entry from the listing until the next start — never a correctness issue.
18
19
  */
20
+ const node_child_process_1 = require("node:child_process");
19
21
  const fs = tslib_1.__importStar(require("node:fs"));
20
22
  const os = tslib_1.__importStar(require("node:os"));
21
23
  const path = tslib_1.__importStar(require("node:path"));
24
+ /**
25
+ * The start-time identity stamp for a pid (`''` when `ps` is unavailable — Windows, restricted
26
+ * environments — or the process is gone). Start time is immutable for the life of a process and
27
+ * survives in no recycled successor, which makes it the cheapest cross-platform-ish identity proof.
28
+ */
29
+ function processStamp(pid) {
30
+ if (!pid || pid <= 0 || process.platform === 'win32')
31
+ return '';
32
+ try {
33
+ return (0, node_child_process_1.execFileSync)('ps', ['-p', String(pid), '-o', 'lstart='], {
34
+ encoding: 'utf8',
35
+ timeout: 2_000,
36
+ stdio: ['ignore', 'pipe', 'ignore'],
37
+ }).trim();
38
+ }
39
+ catch {
40
+ return '';
41
+ }
42
+ }
22
43
  /** `~/.lensmcp/workspaces.json` — override the home dir via `LENSMCP_HOME` (tests). */
23
44
  function registryPath() {
24
45
  const home = process.env['LENSMCP_HOME'] || os.homedir();
@@ -56,15 +77,23 @@ function registerWorkspace(entry, file = registryPath()) {
56
77
  function unregisterWorkspace(key, file = registryPath()) {
57
78
  writeWorkspaces(readWorkspaces(file).filter((e) => e.key !== key), file);
58
79
  }
59
- /** Is the gateway that registered this workspace still alive? `process.kill(pid, 0)` throws ESRCH if not. */
80
+ /**
81
+ * Is the gateway that registered this workspace still alive? `process.kill(pid, 0)` throws ESRCH if
82
+ * not — and when the entry carries a `procStamp`, the pid must ALSO still be the SAME process
83
+ * (recycled pids fail the start-time comparison instead of impersonating a live gateway).
84
+ */
60
85
  function isWorkspaceLive(entry) {
61
86
  if (!entry.pid || entry.pid <= 0)
62
87
  return false;
63
88
  try {
64
89
  process.kill(entry.pid, 0);
65
- return true; // alive (or EPERM below — exists but not ours)
66
90
  }
67
91
  catch (e) {
68
- return e.code === 'EPERM'; // EPERM = exists but not signalable; ESRCH = gone
92
+ if (e.code !== 'EPERM')
93
+ return false; // EPERM = exists but not signalable; ESRCH = gone
69
94
  }
95
+ if (!entry.procStamp)
96
+ return true; // legacy entry / no-ps platform — pid liveness is all we have
97
+ const now = processStamp(entry.pid);
98
+ return now === '' ? true : now === entry.procStamp; // '' = ps unavailable here — don't false-negative
70
99
  }
package/main.devserver.js CHANGED
@@ -1090,5 +1090,28 @@ else {
1090
1090
  setTimeout(() => process.exit(0), 700).unref();
1091
1091
  };
1092
1092
  ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT', 'SIGUSR2'].forEach(sig => process.on(sig, () => void shutdown(sig)));
1093
+ // Gateway watchdog: the gateway kills pods via its spawned process group, but an UNGRACEFUL
1094
+ // gateway death (crash/SIGKILL) never runs that kill — the whole nx run → run-executor →
1095
+ // devserver chain reparents to launchd and squats its ports/sockets forever (the 2026-07-28
1096
+ // day-old orphan tree). We are layers below the gateway, so a ppid check can't see it — poll
1097
+ // the stamped pid instead and tear down through the SAME graceful path when it's gone. The
1098
+ // chain above us unwinds as our exit propagates. A pod run OUTSIDE a gateway (`nx serve`
1099
+ // directly) has no LENSMCP_GATEWAY_PID and is untouched.
1100
+ const gatewayPid = Number(process.env.LENSMCP_GATEWAY_PID ?? 0);
1101
+ if (Number.isInteger(gatewayPid) && gatewayPid > 0) {
1102
+ const watchdog = setInterval(() => {
1103
+ try {
1104
+ process.kill(gatewayPid, 0);
1105
+ }
1106
+ catch (e) {
1107
+ if (e.code === 'EPERM')
1108
+ return; // exists, just not signalable
1109
+ clearInterval(watchdog);
1110
+ console.error(`[parent] gateway (pid ${gatewayPid}) is gone — shutting the pod down to avoid an orphan tree.`);
1111
+ void shutdown('gateway-gone');
1112
+ }
1113
+ }, 5_000);
1114
+ watchdog.unref(); // never keeps an otherwise-done parent alive
1115
+ }
1093
1116
  })();
1094
1117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.16.30",
3
+ "version": "1.16.33",
4
4
  "description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",
@@ -65,8 +65,8 @@
65
65
  }
66
66
  },
67
67
  "dependencies": {
68
- "@lensmcp/node-instrumentation": "1.16.30",
69
- "@lensmcp/nx-plugin": "1.16.30",
68
+ "@lensmcp/node-instrumentation": "1.16.33",
69
+ "@lensmcp/nx-plugin": "1.16.33",
70
70
  "fork-ts-checker-webpack-plugin": "^9.0.0",
71
71
  "glob": "^11.0.0",
72
72
  "http-proxy": "^1.18.0",