@lensmcp/cluster 1.16.27 → 1.16.29

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;AAG1C,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,CAgWd"}
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"}
@@ -13,6 +13,7 @@ const net = tslib_1.__importStar(require("node:net"));
13
13
  const path = tslib_1.__importStar(require("node:path"));
14
14
  const node_child_process_1 = require("node:child_process");
15
15
  const lens_frontend_1 = require("@lensmcp/nx-plugin/lens-frontend");
16
+ const pod_env_1 = require("./pod-env");
16
17
  const scope_1 = require("./scope");
17
18
  const workspace_registry_1 = require("./workspace-registry");
18
19
  const lifecycle_1 = require("./lifecycle");
@@ -91,7 +92,10 @@ deps) {
91
92
  // their ports for the next gateway to route into.
92
93
  const child = (0, node_child_process_1.spawn)(bin, args, {
93
94
  cwd: cwd ?? rt.root,
94
- env: { ...process.env, ...(env ?? {}), LENSMCP_PARENT_PID: String(process.pid) },
95
+ // FOREIGN-POD ENV SCRUB (pod-env.ts): a registered workspace's vite/child runs in ITS root and must
96
+ // not inherit the daemon host's dotenv-loaded env (vite, like nx, keeps an already-set var over the
97
+ // workspace's own .env) — the caller's explicit `env` overlay still applies on top.
98
+ env: { ...(0, pod_env_1.podBaseEnv)(process.env, rt.root, cwd ?? rt.root), ...(env ?? {}), LENSMCP_PARENT_PID: String(process.pid) },
95
99
  stdio: 'inherit',
96
100
  });
97
101
  lensChildren.push(child);
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AA8C/D;;sGAEsG;AACtG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;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,CAoSvF"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AA8C/D;;sGAEsG;AACtG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;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"}
@@ -15,6 +15,7 @@ const fs = tslib_1.__importStar(require("node:fs"));
15
15
  const http = tslib_1.__importStar(require("node:http"));
16
16
  const path = tslib_1.__importStar(require("node:path"));
17
17
  const discovery_1 = require("./discovery");
18
+ const pod_env_1 = require("./pod-env");
18
19
  const types_1 = require("./types");
19
20
  /**
20
21
  * The east-west env a pod needs to call ANOTHER service's `internal.<host>` route (memberships, settings,
@@ -150,11 +151,71 @@ function createServiceLayer(rt, obs) {
150
151
  const serviceBus = (svc) => svc.wsKey === rt.wsKey ? undefined : path.join(svc.root, '.lensmcp', 'events.jsonl');
151
152
  const emitSvc = (svc, severity, title, fingerprint, raw) => emit(severity, title, fingerprint, { wsKey: svc.wsKey, ...raw }, undefined, serviceBus(svc));
152
153
  const scanNow = (pool) => { pool.scannedAt = 0; return (0, discovery_1.pickSock)(pool, rt.scanTtlMs) ? pool.socks.length : 0; };
154
+ /**
155
+ * FOREIGN-GENERATION REAPER (the respawn deadlock, foodguard 2026-07-26): nx records a
156
+ * running continuous task in its workspace-data DB, and a NEW `nx run <svc>:serve-hmr`
157
+ * invocation WAITS on that record ("Waiting for <task> in another nx process") — even with
158
+ * NX_DAEMON=false. When the record's holder is an ORPHANED runner (a replaced daemon's
159
+ * tree, a half-killed generation), every respawn parks forever and `ensureUp` hangs each
160
+ * request with no log. Before spawning, kill any nx runner for THIS target that this
161
+ * gateway does not own — the runner is the detached group leader, so `kill(-pgid)` reaps
162
+ * its whole devserver tree with it. One `ps` exec; a clean cold start is a no-op.
163
+ */
164
+ const reapForeignRunners = (svc, target) => {
165
+ let out = '';
166
+ try {
167
+ out = (0, node_child_process_1.execFileSync)('ps', ['ax', '-ww', '-o', 'pid=,pgid=,command='], { encoding: 'utf8', timeout: 5_000 });
168
+ }
169
+ catch {
170
+ return 0;
171
+ }
172
+ const ownPid = svc.proc?.pid;
173
+ let reaped = 0;
174
+ for (const line of out.split('\n')) {
175
+ const m = line.match(/^\s*(\d+)\s+(\d+)\s+(.*)$/);
176
+ if (!m)
177
+ continue;
178
+ const pid = Number(m[1]);
179
+ const pgid = Number(m[2]);
180
+ const cmd = m[3];
181
+ if (!cmd.includes(`run ${target}`))
182
+ continue; // only THIS service's runners
183
+ // Only nx runners (direct nx.js entry or the yarn/npx shim) — never a stray grep/editor.
184
+ if (!/\bnx(\.js)?\b/.test(cmd))
185
+ continue;
186
+ // A direct-entry runner carries its workspace's nx path — scope to OUR workspace root so a
187
+ // same-named project in another workspace is never touched. (The yarn-shim fallback has no
188
+ // path in argv; the target match alone bounds it.)
189
+ if (cmd.includes('/node_modules/nx/') && !cmd.includes(svc.root))
190
+ continue;
191
+ if (pid === process.pid || pid === ownPid)
192
+ continue;
193
+ if (ownPid !== undefined && pgid === ownPid)
194
+ continue; // our own detached tree
195
+ try {
196
+ process.kill(-pgid, 'SIGKILL');
197
+ }
198
+ catch {
199
+ try {
200
+ process.kill(pid, 'SIGKILL');
201
+ }
202
+ catch { /* already gone */ }
203
+ }
204
+ reaped += 1;
205
+ }
206
+ if (reaped > 0) {
207
+ console.log(`[gateway] ${target}: reaped ${reaped} stale runner tree(s) — a foreign generation held the nx task lock`);
208
+ emitSvc(svc, 'warning', `stale-runner reap: ${svc.project} (${reaped} foreign tree(s))`, `cluster-service:${svc.project}`, { kind: 'stale-runner-reap', project: svc.project, reaped });
209
+ }
210
+ return reaped;
211
+ };
153
212
  const spawnService = (svc) => {
154
213
  if (rt.stopped())
155
214
  return; // never spawn a process after the gateway is told to stop
156
215
  const target = `${svc.project}:${svc.decl.serveTarget ?? 'serve-hmr'}`;
157
216
  const min = Math.max(1, svc.decl.minPods ?? 1);
217
+ reapForeignRunners(svc, target); // clear nx's running-tasks lock BEFORE the spawn can park on it
218
+ svc.lockBlockedAt = undefined;
158
219
  console.log(`[gateway] starting ${target} (pods=${min})…`);
159
220
  emitSvc(svc, 'info', `service starting: ${svc.project} (${min} pod${min > 1 ? 's' : ''})`, `cluster-service:${svc.project}`, { kind: 'service-starting', project: svc.project, pods: min });
160
221
  svc.state = 'starting';
@@ -188,7 +249,11 @@ function createServiceLayer(rt, obs) {
188
249
  // so its dashboard shows its live traces, not the daemon's. The daemon's own services keep rt.eventFile
189
250
  // exactly (svc.root === rt.root), so single-workspace behavior is unchanged.
190
251
  env: {
191
- ...process.env,
252
+ // FOREIGN-POD ENV SCRUB (pod-env.ts): a registered workspace's pod must NOT inherit the daemon
253
+ // host's dotenv-loaded env (nx never overrides an already-set var, so foodguard's PG_URL/REDIS_URL
254
+ // riding the daemon once pointed tetros's auth+iam at foodguard's stores). The daemon's own
255
+ // workspace keeps the full inherit — its pods reload the same files anyway.
256
+ ...(0, pod_env_1.podBaseEnv)(process.env, rt.root, svc.root),
192
257
  NX_DAEMON: 'false',
193
258
  // NX_ISOLATE_PLUGINS=false: with the daemon off (above), EVERY serve client builds the
194
259
  // project graph itself — and with plugin isolation on (nx's default) each client spawns
@@ -229,6 +294,25 @@ function createServiceLayer(rt, obs) {
229
294
  }
230
295
  if (/No typescript errors found|webpack compiled successfully|Child#\d+ ready/.test(line))
231
296
  svc.buildFailed = false;
297
+ // LOCK-BREAK: nx prints exactly this when a FOREIGN runner holds the running-tasks
298
+ // record for our target — this spawn will park forever and every request would hang
299
+ // through ensureUp with no further log. Cure it once per spawn: kill our parked
300
+ // waiter, reap the foreign tree, and respawn clean.
301
+ if (!svc.lockBlockedAt && /Waiting for .+ in another nx process/.test(line)) {
302
+ svc.lockBlockedAt = Date.now();
303
+ console.log(`[gateway] ${target}: spawn blocked by a stale nx runner — breaking the lock + respawning`);
304
+ emitSvc(svc, 'warning', `nx task lock: ${svc.project} blocked by a stale runner — auto-breaking`, `cluster-service:${svc.project}`, { kind: 'nx-lock-break', project: svc.project });
305
+ setTimeout(() => {
306
+ if (rt.stopped() || !svc.lockBlockedAt)
307
+ return;
308
+ killSpawned(svc, 'parked on a foreign nx task lock — recycling');
309
+ reapForeignRunners(svc, target);
310
+ setTimeout(() => {
311
+ if (!rt.stopped() && !svc.proc && svc.state === 'down')
312
+ spawnService(svc);
313
+ }, 1_500).unref?.();
314
+ }, 250).unref?.();
315
+ }
232
316
  }
233
317
  };
234
318
  proc.stdout?.on('data', (d) => { if (rt.stopped())
@@ -345,16 +429,39 @@ function createServiceLayer(rt, obs) {
345
429
  }
346
430
  svc.proc = undefined;
347
431
  svc.state = 'down';
348
- // children die with the tree but their sock FILES survive — remove them
349
- // so the canvas and the proxy never see ghost pods.
432
+ // children die with the tree but their sock FILES survive — remove them so the canvas
433
+ // and the proxy never see ghost pods. OWNER-AWARE: a sock whose `.owner` pidfile names a
434
+ // LIVE process belongs to a NEWER generation binding concurrently — leave it (the
435
+ // devserver's bind-steal owns the path now; unlinking it here recreates the vanished-sock
436
+ // wedge this sweep exists to prevent).
350
437
  setTimeout(() => {
351
438
  try {
352
439
  for (const f of fs.readdirSync(svc.pool.dir)) {
353
- if (f.endsWith('.sock'))
354
- fs.unlinkSync(path.join(svc.pool.dir, f));
440
+ if (!f.endsWith('.sock'))
441
+ continue;
442
+ const sockPath = path.join(svc.pool.dir, f);
443
+ try {
444
+ const owner = Number(fs.readFileSync(`${sockPath}.owner`, 'utf8').trim());
445
+ if (Number.isFinite(owner) && owner > 0) {
446
+ try {
447
+ process.kill(owner, 0); // throws when the owner is dead
448
+ continue; // a LIVE foreign owner — a newer generation's pod; keep it
449
+ }
450
+ catch { /* dead owner → sweep */ }
451
+ }
452
+ }
453
+ catch { /* no owner file (legacy) → sweep */ }
454
+ try {
455
+ fs.unlinkSync(sockPath);
456
+ }
457
+ catch { /* gone */ }
458
+ try {
459
+ fs.unlinkSync(`${sockPath}.owner`);
460
+ }
461
+ catch { /* gone */ }
355
462
  }
356
463
  }
357
- catch { /* gone */ }
464
+ catch { /* dir gone */ }
358
465
  svc.pool.socks = [];
359
466
  svc.pool.scannedAt = 0;
360
467
  }, 1200).unref?.();
@@ -0,0 +1,24 @@
1
+ /** The root-level dotenv files nx (and vite) auto-load for any invocation at a workspace root. */
2
+ export declare const WORKSPACE_DOTENV_FILES: readonly [".env", ".env.local", ".local.env"];
3
+ /**
4
+ * Data-store pointers that must never cross a workspace boundary, even when they arrived in the daemon's
5
+ * env some way other than the host's dotenv (an exported shell var at gateway start). Belt-and-suspenders
6
+ * under the dotenv-derived scrub — kept to the class that caused the incident.
7
+ */
8
+ export declare const FOREIGN_POD_ENV_DENYLIST: readonly ["PG_URL", "REDIS_URL", "DATABASE_URL", "POSTGRES_URL"];
9
+ /**
10
+ * The keys a dotenv file DECLARES — assignment lines only (`KEY=…`, optional `export ` prefix), comments
11
+ * and malformed lines ignored. We only need the key names (what to scrub), never the values.
12
+ */
13
+ export declare function parseDotenvKeys(content: string): Set<string>;
14
+ /** Every key declared across a workspace root's dotenv files (missing/unreadable files contribute nothing). */
15
+ export declare function workspaceDotenvKeys(root: string): Set<string>;
16
+ /** PURE scrub: a copy of `base` without the given keys nor the store-pointer denylist. Never mutates `base`. */
17
+ export declare function scrubEnv(base: NodeJS.ProcessEnv, hostKeys: ReadonlySet<string>): NodeJS.ProcessEnv;
18
+ /**
19
+ * The spawn-base env for a child of `childRoot` under a daemon hosted at `hostRoot`: the daemon's own
20
+ * workspace inherits untouched; a foreign workspace gets the scrubbed copy (the child's own nx/vite dotenv
21
+ * load then supplies THAT workspace's values, which is the whole point).
22
+ */
23
+ export declare function podBaseEnv(base: NodeJS.ProcessEnv, hostRoot: string, childRoot: string): NodeJS.ProcessEnv;
24
+ //# sourceMappingURL=pod-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pod-env.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/pod-env.ts"],"names":[],"mappings":"AAoBA,kGAAkG;AAClG,eAAO,MAAM,sBAAsB,+CAAgD,CAAC;AAEpF;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,kEAAmE,CAAC;AAEzG;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAS5D;AAED,+GAA+G;AAC/G,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAY7D;AAED,gHAAgH;AAChH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,UAAU,CAQlG;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAG1G"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FOREIGN_POD_ENV_DENYLIST = exports.WORKSPACE_DOTENV_FILES = void 0;
4
+ exports.parseDotenvKeys = parseDotenvKeys;
5
+ exports.workspaceDotenvKeys = workspaceDotenvKeys;
6
+ exports.scrubEnv = scrubEnv;
7
+ exports.podBaseEnv = podBaseEnv;
8
+ const tslib_1 = require("tslib");
9
+ /**
10
+ * FOREIGN-POD ENV SCRUB (the cross-workspace store bleed, foodguard↔tetros 2026-07-26).
11
+ *
12
+ * `nx run gateway:serve` auto-loads the DAEMON workspace's root dotenv (`.env`, `.env.local`) into the
13
+ * daemon's own process env, and the spawn sites' `...process.env` handed that to EVERY child — including a
14
+ * REGISTERED (foreign) workspace's pods. nx's dotenv load in the child never overrides an already-set var,
15
+ * so the daemon host's values beat the foreign workspace's own `.env.local`: with the daemon hosted from
16
+ * foodguard (whose root `.env.local` declares `PG_URL`/`REDIS_URL`), tetros's auth+iam pods booted onto
17
+ * foodguard's Postgres/Valkey — foodguard tenants rendered inside the tetros app, and tetros's boot seed
18
+ * wrote rows into foodguard's database.
19
+ *
20
+ * The rule: a FOREIGN workspace's pod gets the inherited env MINUS (every key the daemon workspace's root
21
+ * dotenv files declare ∪ a hard denylist of data-store pointers). The daemon's OWN workspace pods keep the
22
+ * full inherit — their nx/vite reload the exact same files at spawn (so the scrub would change nothing),
23
+ * and a deliberate shell override (`PG_URL=… lensmcp gateway start`) staying live for one's own pods is
24
+ * ordinary env semantics. Hosting order must never matter for anyone else's pods.
25
+ */
26
+ const fs = tslib_1.__importStar(require("node:fs"));
27
+ const path = tslib_1.__importStar(require("node:path"));
28
+ /** The root-level dotenv files nx (and vite) auto-load for any invocation at a workspace root. */
29
+ exports.WORKSPACE_DOTENV_FILES = ['.env', '.env.local', '.local.env'];
30
+ /**
31
+ * Data-store pointers that must never cross a workspace boundary, even when they arrived in the daemon's
32
+ * env some way other than the host's dotenv (an exported shell var at gateway start). Belt-and-suspenders
33
+ * under the dotenv-derived scrub — kept to the class that caused the incident.
34
+ */
35
+ exports.FOREIGN_POD_ENV_DENYLIST = ['PG_URL', 'REDIS_URL', 'DATABASE_URL', 'POSTGRES_URL'];
36
+ /**
37
+ * The keys a dotenv file DECLARES — assignment lines only (`KEY=…`, optional `export ` prefix), comments
38
+ * and malformed lines ignored. We only need the key names (what to scrub), never the values.
39
+ */
40
+ function parseDotenvKeys(content) {
41
+ const keys = new Set();
42
+ for (const raw of content.split('\n')) {
43
+ const line = raw.trim();
44
+ if (!line || line.startsWith('#'))
45
+ continue;
46
+ const m = line.match(/^(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/);
47
+ if (m)
48
+ keys.add(m[1]);
49
+ }
50
+ return keys;
51
+ }
52
+ /** Every key declared across a workspace root's dotenv files (missing/unreadable files contribute nothing). */
53
+ function workspaceDotenvKeys(root) {
54
+ const keys = new Set();
55
+ for (const file of exports.WORKSPACE_DOTENV_FILES) {
56
+ let content;
57
+ try {
58
+ content = fs.readFileSync(path.join(root, file), 'utf8');
59
+ }
60
+ catch {
61
+ continue;
62
+ }
63
+ for (const k of parseDotenvKeys(content))
64
+ keys.add(k);
65
+ }
66
+ return keys;
67
+ }
68
+ /** PURE scrub: a copy of `base` without the given keys nor the store-pointer denylist. Never mutates `base`. */
69
+ function scrubEnv(base, hostKeys) {
70
+ const out = {};
71
+ for (const [k, v] of Object.entries(base)) {
72
+ if (hostKeys.has(k))
73
+ continue;
74
+ if (exports.FOREIGN_POD_ENV_DENYLIST.includes(k))
75
+ continue;
76
+ out[k] = v;
77
+ }
78
+ return out;
79
+ }
80
+ /**
81
+ * The spawn-base env for a child of `childRoot` under a daemon hosted at `hostRoot`: the daemon's own
82
+ * workspace inherits untouched; a foreign workspace gets the scrubbed copy (the child's own nx/vite dotenv
83
+ * load then supplies THAT workspace's values, which is the whole point).
84
+ */
85
+ function podBaseEnv(base, hostRoot, childRoot) {
86
+ if (path.resolve(childRoot) === path.resolve(hostRoot))
87
+ return base;
88
+ return scrubEnv(base, workspaceDotenvKeys(hostRoot));
89
+ }
@@ -147,6 +147,11 @@ export interface ServiceCtl {
147
147
  * stops a rapid source-churn (e.g. a codegen run) from thrash-recycling the same pod. NOT counted against
148
148
  * the crash-loop cap ({@link restarts}) — staleness is not a crash. */
149
149
  lastStaleRecycleAt?: number;
150
+ /** Self-heal: set when THIS spawn printed nx's "Waiting for <task> in another nx process" — a FOREIGN
151
+ * (orphaned) runner holds nx's running-tasks lock and the spawn will never produce a pod. The sniffer
152
+ * fires the lock-break once per spawn (kill our waiter → reap the foreign tree → respawn); cleared on
153
+ * the next spawn. */
154
+ lockBlockedAt?: number;
150
155
  }
151
156
  /** Min interval (ms) between repeat 'service error' emits for the SAME service + same error line. */
152
157
  export declare const SERVICE_ERROR_THROTTLE_MS = 5000;
@@ -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":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,KAAK,MAAM,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,YAAY,EAAE,KAAK,EAAE,CAAC;AAEtB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;CACzE;AAED,2FAA2F;AAC3F,MAAM,WAAW,kBAAkB;IACjC,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sGAAsG;IACtG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8BAA8B;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAC9B;;iGAE6F;IAC7F,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2FAA2F;IAC3F,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE;AAE1F,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;oGACgG;IAChG,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;gEAC4D;IAC5D,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB;AAED;;;yBAGyB;AACzB,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,kBAAkB,CAAC;IACzB;;iHAE6G;IAC7G,IAAI,EAAE,MAAM,CAAC;IACb;yGACqG;IACrG,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;iGAE6F;IAC7F,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;6GAGyG;IACzG,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;mCAE+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;2EAGuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;yBAGqB;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAWD,qGAAqG;AACrG,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;;wDAEwD;AACxD,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAEhD;;sGAEsG;AACtG,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAC5C,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC;;;;;;;sDAOsD;AACtD,eAAO,MAAM,uBAAuB,QAGhC,CAAC;AAEL;;;;;;;iHAOiH;AACjH,eAAO,MAAM,sBAAsB,QAG/B,CAAC;AAEL;;;;;;;;;GASG;AACH,8GAA8G;AAC9G,eAAO,MAAM,mBAAmB,SAA6C,CAAC;AAC9E;;4CAE4C;AAC5C,eAAO,MAAM,cAAc,QAA0D,CAAC;AACtF;;0FAE0F;AAC1F,eAAO,MAAM,kBAAkB,QAA+C,CAAC;AAC/E;;mDAEmD;AACnD,eAAO,MAAM,qBAAqB,QAAiD,CAAC;AACpF;gEACgE;AAChE,eAAO,MAAM,iBAAiB,QAA8C,CAAC;AAC7E;mFACmF;AACnF,eAAO,MAAM,6BAA6B,QAA0D,CAAC;AAErG,2DAA2D;AAC3D,MAAM,WAAW,qBAAsB,SAAQ,qBAAqB;IAClE,oDAAoD;IACpD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iFAAiF;IACjF,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAGD,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAGD;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;sDAEkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;+GAC2G;IAC3G,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;IACzB;8GAC0G;IAC1G,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;IACjC,sGAAsG;IACtG,QAAQ,CAAC,aAAa,EAAE,MAAM,IAAI,CAAC;IACnC,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB;;;mHAG+G;IAC/G,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;wFACoF;IACpF,OAAO,IAAI,OAAO,CAAC;CACpB;AAGD,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,YAAY,GACZ,WAAW,GACX,eAAe,GACf,YAAY,GACZ,SAAS,GACT,WAAW,CAAC;AAEhB,MAAM,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAErE;6EAC6E;AAC7E,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,aAAa,CAAC,EAAE,WAAW,EAAE,CAAC;IAC9B,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,CAAC;IACnC,kDAAkD;IAClD,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,cAAc,CAAC;IACnC,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC;IAChC,gEAAgE;IAChE,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,sFAAsF;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,wCAAwC;IACxC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7E,gGAAgG;IAChG,SAAS,IAAI,OAAO,CAAC;CACtB;AAED;;;0DAG0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC;IACZ,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/main.devserver.js CHANGED
@@ -124,13 +124,57 @@ const SOCK_DIR = LENSMCP_WS_KEY
124
124
  function childSockPath(id) {
125
125
  return path.join(SOCK_DIR, `child-${id}.sock`);
126
126
  }
127
- function cleanupSock(sockPath) {
127
+ /**
128
+ * SOCKET OWNERSHIP (the generation race, foodguard 2026-07-26): when devserver generations
129
+ * overlap — a killed nx runner's orphaned tree dying while the daemon's fresh spawn binds —
130
+ * the DYING generation's cleanup used to unlink `child-1.sock` that the NEW generation had
131
+ * just bound. The file vanished from disk while the live child held the (now anonymous)
132
+ * inode: every gateway connect hit ENOENT/ghost, with no log and no evictable pod.
133
+ *
134
+ * The rule that closes it: **BIND steals, DEATH is guarded.** A binder writes a sidecar
135
+ * `<sock>.owner` pidfile FIRST (so any concurrently-dying process already sees the new
136
+ * owner), then unlinks the stale path and binds. Every death/cleanup path unlinks ONLY if
137
+ * the owner file still names the pid it is cleaning up for — a foreign live owner means a
138
+ * newer generation took the path, and the dying one must leave it alone.
139
+ */
140
+ function sockOwnerPath(sockPath) { return `${sockPath}.owner`; }
141
+ /** Bind-time steal: claim ownership (pidfile first), then clear the stale path. */
142
+ function claimSock(sockPath, ownerPid = process.pid) {
143
+ try {
144
+ fs.mkdirSync(path.dirname(sockPath), { recursive: true });
145
+ }
146
+ catch { /* exists */ }
147
+ try {
148
+ fs.writeFileSync(sockOwnerPath(sockPath), String(ownerPid));
149
+ }
150
+ catch { /* best-effort */ }
151
+ try {
152
+ fs.unlinkSync(sockPath);
153
+ }
154
+ catch { /* no stale socket */ }
155
+ }
156
+ /**
157
+ * Death-path cleanup: unlink the socket ONLY when `expectedOwnerPid` (default: this
158
+ * process) still owns it. A missing/unreadable owner file falls back to unlinking
159
+ * (legacy sockets from a pre-ownership build).
160
+ */
161
+ function cleanupSock(sockPath, expectedOwnerPid = process.pid) {
128
162
  if (!sockPath)
129
163
  return;
164
+ try {
165
+ const owner = fs.readFileSync(sockOwnerPath(sockPath), 'utf8').trim();
166
+ if (owner && owner !== String(expectedOwnerPid))
167
+ return; // a newer generation owns the path
168
+ }
169
+ catch { /* no owner file → legacy behavior */ }
130
170
  try {
131
171
  fs.unlinkSync(sockPath);
132
172
  }
133
173
  catch { /* best-effort: socket already gone */ }
174
+ try {
175
+ fs.unlinkSync(sockOwnerPath(sockPath));
176
+ }
177
+ catch { /* best-effort */ }
134
178
  }
135
179
  // delete from CJS cache + require fresh
136
180
  async function importFresh(spec) {
@@ -270,10 +314,7 @@ if (process.env.APP_RUNNER === '1') {
270
314
  // SIGTERM that missed us), the IPC channel closes — exit immediately and
271
315
  // remove our socket so no ghost pod (live process + stale sock) survives.
272
316
  process.on('disconnect', () => {
273
- try {
274
- fs.unlinkSync(sockPath);
275
- }
276
- catch { /* already gone */ }
317
+ cleanupSock(sockPath); // owner-guarded: never unlink a newer generation's socket
277
318
  process.exit(0);
278
319
  });
279
320
  // boot once, then listen on Unix socket
@@ -281,7 +322,7 @@ if (process.env.APP_RUNNER === '1') {
281
322
  await swapNow();
282
323
  const resolvedBundle = path.isAbsolute(BUNDLE_PATH) ? BUNDLE_PATH : path.join(__dirname, BUNDLE_PATH);
283
324
  console.log(`[child] Using bundle: ${resolvedBundle}`);
284
- cleanupSock(sockPath); // remove stale socket from previous run
325
+ claimSock(sockPath); // bind steals: claim ownership, then clear the stale path
285
326
  server.listen(sockPath, () => {
286
327
  console.log(`[child] Listening on ${sockPath}`);
287
328
  if (process.send)
@@ -570,7 +611,7 @@ else {
570
611
  info.lastError = `Child exited abnormally (code=${code}, signal=${signal ?? 'none'})`;
571
612
  }
572
613
  info.healthy = false;
573
- cleanupSock(info.socketPath);
614
+ cleanupSock(info.socketPath, info.proc.pid); // guarded by the DEAD child's claim
574
615
  info.socketPath = undefined;
575
616
  // Supervise the pool: a CRASHED pod (not a clean shutdown, not a
576
617
  // reload/idle-kill) must be replaced or the pool silently shrinks to
@@ -619,7 +660,7 @@ else {
619
660
  }
620
661
  catch { /* ignore */
621
662
  }
622
- cleanupSock(c.socketPath);
663
+ cleanupSock(c.socketPath, c.proc.pid); // guarded by the replaced child's claim
623
664
  c.socketPath = undefined;
624
665
  // Remove from array
625
666
  const idx = children.indexOf(c);
@@ -1008,7 +1049,7 @@ else {
1008
1049
  // derivation the gateway uses for the pod pool dir. Plain HTTP: it's a
1009
1050
  // per-user unix socket, TLS adds nothing.
1010
1051
  const controlSockPath = path.join(SOCK_DIR, 'parent.sock');
1011
- cleanupSock(controlSockPath); // stale socket from a crashed previous parent
1052
+ claimSock(controlSockPath); // bind steals: claim + clear a crashed previous parent's leftover
1012
1053
  const controlServer = http.createServer(requestHandler);
1013
1054
  controlServer.on('error', (err) => {
1014
1055
  console.warn(`[parent] control socket unavailable (${err.code}) — lensmcp logs/debug attach disabled.`);
@@ -1039,7 +1080,7 @@ else {
1039
1080
  catch { /* ignore */
1040
1081
  }
1041
1082
  if (c.socketPath)
1042
- cleanupSock(c.socketPath);
1083
+ cleanupSock(c.socketPath, c.proc.pid);
1043
1084
  }
1044
1085
  // Clean up socket directory
1045
1086
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.16.27",
3
+ "version": "1.16.29",
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.27",
69
- "@lensmcp/nx-plugin": "1.16.27",
68
+ "@lensmcp/node-instrumentation": "1.16.29",
69
+ "@lensmcp/nx-plugin": "1.16.29",
70
70
  "fork-ts-checker-webpack-plugin": "^9.0.0",
71
71
  "glob": "^11.0.0",
72
72
  "http-proxy": "^1.18.0",
@@ -1 +1 @@
1
- {"version":3,"file":"tsgo-check-plugin.d.ts","sourceRoot":"","sources":["../../../libs/cluster/src/tsgo-check-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAI9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAgBlG;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,sBAAsB;IAMxC,KAAK,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI;IAO1B,sGAAsG;IACtG,QAAQ,IAAI,IAAI;IAOhB,OAAO,CAAC,GAAG;CA0CZ"}
1
+ {"version":3,"file":"tsgo-check-plugin.d.ts","sourceRoot":"","sources":["../../../libs/cluster/src/tsgo-check-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAI9D,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtF;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CA0BlG;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyB;IAC9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,sBAAsB;IAMxC,KAAK,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI;IAO1B,sGAAsG;IACtG,QAAQ,IAAI,IAAI;IAOhB,OAAO,CAAC,GAAG;CA0CZ"}
@@ -50,7 +50,17 @@ function writeShimTsconfig(appName, tsConfig, workspaceRoot) {
50
50
  const file = path.join(dir, `${appName}.tsconfig.json`);
51
51
  fs.writeFileSync(file, JSON.stringify({
52
52
  extends: tsConfig,
53
- compilerOptions: { module: 'preserve', moduleResolution: 'bundler', isolatedModules: false },
53
+ // baseUrl: null UNSETS an inherited baseUrl TS7 removed the option outright (TS5102), and
54
+ // a workspace that anchors its inherited `paths` with baseUrl still resolves them fine
55
+ // without it: TS7 anchors non-relative `paths` at the config file that DECLARES them (the
56
+ // workspace-root base tsconfig — the same directory baseUrl pointed at). A no-op where
57
+ // baseUrl was never set.
58
+ compilerOptions: {
59
+ module: 'preserve',
60
+ moduleResolution: 'bundler',
61
+ isolatedModules: false,
62
+ baseUrl: null,
63
+ },
54
64
  }, null, 2));
55
65
  return file;
56
66
  }