@lensmcp/cluster 1.23.13 → 1.24.1

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.
Files changed (107) hide show
  1. package/build-host.d.ts +1 -0
  2. package/build-host.js +1 -0
  3. package/bundle-parent.d.ts +8 -0
  4. package/bundle-parent.js +1 -0
  5. package/create-webpack-dev.d.ts +74 -40
  6. package/create-webpack-dev.js +1 -1
  7. package/dev-devtool.d.ts +45 -0
  8. package/dev-devtool.js +1 -0
  9. package/devserver-notifier.d.ts +57 -0
  10. package/devserver-notifier.js +1 -0
  11. package/executors/gateway/body-stall.d.ts +13 -1
  12. package/executors/gateway/body-stall.js +1 -1
  13. package/executors/gateway/canonical-path.d.ts +1 -1
  14. package/executors/gateway/client-ip.d.ts +1 -1
  15. package/executors/gateway/gateway.impl.js +1 -1
  16. package/executors/gateway/jwt-claims.d.ts +1 -1
  17. package/executors/gateway/prod-config.d.ts +1 -1
  18. package/executors/gateway/prod-runtime/body-stall.d.ts +1 -1
  19. package/executors/gateway/rate-limit.d.ts +1 -1
  20. package/executors/gateway/runtime/auth.d.ts +1 -1
  21. package/executors/gateway/runtime/chooser.d.ts +2 -2
  22. package/executors/gateway/runtime/control.d.ts +3 -3
  23. package/executors/gateway/runtime/control.js +2 -2
  24. package/executors/gateway/runtime/dev-auth.d.ts +1 -1
  25. package/executors/gateway/runtime/edge.d.ts +3 -3
  26. package/executors/gateway/runtime/guest-graph.d.ts +113 -0
  27. package/executors/gateway/runtime/guest-graph.js +2 -0
  28. package/executors/gateway/runtime/lens-children.d.ts +11 -1
  29. package/executors/gateway/runtime/lens-children.js +1 -1
  30. package/executors/gateway/runtime/lifecycle.d.ts +56 -8
  31. package/executors/gateway/runtime/lifecycle.js +3 -3
  32. package/executors/gateway/runtime/process-names.d.ts +18 -0
  33. package/executors/gateway/runtime/process-names.js +1 -0
  34. package/executors/gateway/runtime/project-graph-store.d.ts +74 -0
  35. package/executors/gateway/runtime/project-graph-store.js +1 -0
  36. package/executors/gateway/runtime/proxy.d.ts +2 -2
  37. package/executors/gateway/runtime/proxy.js +1 -1
  38. package/executors/gateway/runtime/scope.d.ts +1 -1
  39. package/executors/gateway/runtime/serve-launch.d.ts +118 -0
  40. package/executors/gateway/runtime/serve-launch.js +3 -0
  41. package/executors/gateway/runtime/server.d.ts +3 -3
  42. package/executors/gateway/runtime/server.js +2 -2
  43. package/executors/gateway/runtime/shared-build.d.ts +65 -0
  44. package/executors/gateway/runtime/shared-build.js +4 -0
  45. package/executors/gateway/runtime/types.d.ts +39 -9
  46. package/executors/gateway/runtime/types.js +1 -1
  47. package/executors/gateway/runtime/workspace-log.js +1 -1
  48. package/executors/gateway/schema.d.ts +3 -0
  49. package/executors/gateway/schema.json +4 -0
  50. package/executors/serve/schema.d.ts +2 -0
  51. package/executors/serve/schema.json +4 -0
  52. package/executors/serve/serve.impl.d.ts +12 -6
  53. package/executors/serve/serve.impl.js +1 -1
  54. package/executors/trust/hostname.d.ts +1 -1
  55. package/exit-reason.d.ts +1 -1
  56. package/fork-ts-check-reporter.d.ts +12 -0
  57. package/fork-ts-check-reporter.js +1 -1
  58. package/graph-snapshot.d.ts +1 -0
  59. package/graph-snapshot.js +1 -0
  60. package/heap-snapshot.d.ts +66 -0
  61. package/heap-snapshot.js +1 -0
  62. package/hot-swap-registries.d.ts +1 -1
  63. package/inspector-secret.d.ts +39 -0
  64. package/inspector-secret.js +1 -0
  65. package/main.devserver.d.ts +33 -5
  66. package/main.devserver.js +11 -9
  67. package/package.json +4 -4
  68. package/perf/heavy-barrel-import.d.ts +135 -0
  69. package/perf/heavy-barrel-import.js +1 -0
  70. package/perf/perf-config.d.ts +29 -0
  71. package/perf/perf-config.js +1 -0
  72. package/perf/perf-rule-events.d.ts +71 -0
  73. package/perf/perf-rule-events.js +2 -0
  74. package/perf/perf-rules-analysis.d.ts +28 -0
  75. package/perf/perf-rules-analysis.js +1 -0
  76. package/perf/perf-rules-plugin.d.ts +71 -0
  77. package/perf/perf-rules-plugin.js +1 -0
  78. package/perf/perf-rules-worker.d.ts +1 -0
  79. package/perf/perf-rules-worker.js +1 -0
  80. package/perf/source-scan.d.ts +59 -0
  81. package/perf/source-scan.js +5 -0
  82. package/perf/workspace-libraries.d.ts +52 -0
  83. package/perf/workspace-libraries.js +2 -0
  84. package/pod-memory-events.d.ts +71 -0
  85. package/pod-memory-events.js +2 -0
  86. package/pod-memory.d.ts +264 -0
  87. package/pod-memory.js +1 -0
  88. package/process-name.d.ts +45 -0
  89. package/process-name.js +1 -1
  90. package/serve-runner.d.ts +2 -0
  91. package/serve-runner.js +1 -0
  92. package/shared-build/attach.d.ts +56 -0
  93. package/shared-build/attach.js +3 -0
  94. package/shared-build/host.d.ts +52 -0
  95. package/shared-build/host.js +4 -0
  96. package/shared-build/protocol.d.ts +118 -0
  97. package/shared-build/protocol.js +1 -0
  98. package/shared-build/shared-compilation.d.ts +91 -0
  99. package/shared-build/shared-compilation.js +3 -0
  100. package/shared-build/signature.d.ts +52 -0
  101. package/shared-build/signature.js +1 -0
  102. package/stdio-guard.d.ts +1 -1
  103. package/swap-recycle.d.ts +21 -0
  104. package/swap-recycle.js +1 -1
  105. package/tsgo-check-plugin.d.ts +1 -1
  106. package/workspace-paths.d.ts +76 -0
  107. package/workspace-paths.js +1 -0
@@ -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>;
@@ -0,0 +1 @@
1
+ "use strict";var _=Object.defineProperty;var p=(e,t)=>_(e,"name",{value:t,configurable:!0});var m=Object.defineProperty,i=p((e,t)=>m(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,s)=>{if(e===void 0||e.trim()==="")return t;const a=Number(e);return Number.isFinite(a)&&a>=s?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??"",s=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"),o=a("free")+a("inactive")+a("speculative")+a("purgeable");if(o>0)return{bytes:o*s,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){const s=Math.ceil(t.live/MB/256)*256;return`the pod's live heap is ${Math.round(t.live/MB)} MB, over the ${Math.round(e.maxHeapBytes/MB)} MB bound \u2014 a snapshot this size needs several times that in memory while it is written. Raise the bound for this one snapshot with \`--max-mb ${s}\` (the free-memory check still applies); the default is LENSMCP_HEAP_SNAPSHOT_MAX_MB, read when the pod starts`}if(t.available){const s=Math.max(e.minFreeBytes,e.freeFactor*t.live);if(t.available.bytes<s)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(s/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,s,a,o){const r=(e||"service").replace(/[^A-Za-z0-9_.-]/g,"_"),n=new Date(o).toISOString().replace(/[:.]/g,"-");return`${r}-pod${t}-pid${s}-g${a}-${n}.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,s=Date.now()){const a=[];let o;try{o=fs.readdirSync(e).filter(r=>r.endsWith(".heapsnapshot")).map(r=>{const n=path.join(e,r),h=fs.lstatSync(n);return h.isFile()?{file:n,mtime:h.mtimeMs}:void 0}).filter(r=>r!==void 0).sort((r,n)=>n.mtime-r.mtime)}catch{return a}return o.forEach((r,n)=>{if(n>=t.keep-1||s-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(),s=fs.constants.O_WRONLY|fs.constants.O_CREAT|fs.constants.O_EXCL|(fs.constants.O_NOFOLLOW??0),a=fs.openSync(e,s,384),o=new inspector.Session;let r=0,n;return new Promise((h,S)=>{const u=i(c=>{o.removeAllListeners("HeapProfiler.addHeapSnapshotChunk");try{o.disconnect()}catch{}try{fs.closeSync(a)}catch{}const l=c??n;if(l){try{fs.unlinkSync(e)}catch{}S(l);return}h({bytes:r,ms:Date.now()-t})},"finish");try{o.connect(),o.on("HeapProfiler.addHeapSnapshotChunk",c=>{if(!n)try{r+=fs.writeSync(a,c.params.chunk)}catch(l){n=l}}),o.post("HeapProfiler.takeHeapSnapshot",{reportProgress:!1},c=>u(c))}catch(c){u(c)}})}p(writeHeapSnapshot,"writeHeapSnapshot"),i(writeHeapSnapshot,"writeHeapSnapshot");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Why a pod's heap grew by a whole bundle on every hot swap (issues/foodguard/24).
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");
@@ -1,3 +1,5 @@
1
+ import { type Serializable } from 'node:child_process';
2
+ import { redactInspectorUrls } from './inspector-secret';
1
3
  export interface GatewayRoute {
2
4
  /** Hostname to match — exact (`api.tetros.localhost`) or wildcard (`*.tetros.localhost`). */
3
5
  host?: string;
@@ -17,10 +19,10 @@ export interface GatewayRoute {
17
19
  * Did this request arrive on the OWNER-ONLY control socket, rather than the public TCP port?
18
20
  *
19
21
  * `requestHandler` serves both: the Unix socket at `$TMPDIR/<ws>/<svc>-devserver/parent.sock`
20
- * (0700 dir, see issues/codex-security/23) and `server.listen(publicPort)`. Admin branches were
22
+ * (0700 dir, see issues/closed/codex-security-23) and `server.listen(publicPort)`. Admin branches were
21
23
  * reachable on both, so `POST /webpack/debug` — which opens a Node inspector in every pod and
22
24
  * returns the ws:// URLs, and whose UUID is the only thing guarding `Runtime.evaluate` — could be
23
- * called by anything that could reach the port (issues/codex-security/05).
25
+ * called by anything that could reach the port (issues/closed/codex-security-05).
24
26
  *
25
27
  * Node's `Server.address()` returns the PATH STRING for a pipe/Unix socket and an object
26
28
  * `{ address, family, port }` for TCP. That is the discriminator, and it needs no extra plumbing
@@ -29,6 +31,22 @@ export interface GatewayRoute {
29
31
  export declare function isOwnerOnlyTransport(req: {
30
32
  socket?: unknown;
31
33
  }): boolean;
34
+ /** Either end of an IPC channel: a pod or worker as its parent holds it, or the pod's own `process`. */
35
+ export interface IpcEndpoint {
36
+ connected?: boolean;
37
+ send?(message: Serializable, callback: (error: Error | null) => void): boolean;
38
+ }
39
+ /**
40
+ * Tell a pod (or the parent) something over IPC — if it can still hear it (issues/closed/dev-gateway-19).
41
+ *
42
+ * `send` on a channel that has closed does not throw: it emits `'error'` (`ERR_IPC_CHANNEL_CLOSED`) on the next
43
+ * tick, and nothing listened for it. A pod that had exited is still listed until it is replaced, so a file change
44
+ * relayed to "every pod" — or a reload forwarded mid-stop — crashed the devserver PARENT, and the serve executor
45
+ * then respawned one. Never on a closed channel, always with a callback (which takes the error the event would have
46
+ * carried: a close between the check and the write is what the exit handler is for), and `false` when the message
47
+ * did not go, for a caller that has something else to do then.
48
+ */
49
+ export declare function tell(endpoint: IpcEndpoint, message: Serializable): boolean;
32
50
  /** A devserver the gateway started carries its pid; one run by hand (`nx run <svc>:serve-hmr`) does not. */
33
51
  export declare function gatewayManaged(env?: NodeJS.ProcessEnv): boolean;
34
52
  /**
@@ -37,7 +55,7 @@ export declare function gatewayManaged(env?: NodeJS.ProcessEnv): boolean;
37
55
  * Under a gateway, only the control socket may. The TCP port is not an identity: it comes from a
38
56
  * tracked config file, so every checkout of a repo has the same number. A second checkout's FIRST
39
57
  * build has no control socket yet, so its plugin fell back to `localhost:<port>` — and that port
40
- * belonged to the first checkout, whose pods it hot-swapped (issues/foodguard/35). Nothing checked
58
+ * belonged to the first checkout, whose pods it hot-swapped (issues/closed/foodguard-35). Nothing checked
41
59
  * who was asking. The gateway and the service's own build plugin both reach the control socket,
42
60
  * which lives in the workspace's owner-only socket dir; nobody else has a reason to.
43
61
  *
@@ -50,7 +68,7 @@ export declare function mayDriveThePool(req: {
50
68
  /**
51
69
  * Opt-out that restores the pre-`06` PREDICTABLE inspector ports (`base+slot`, `base+20+i`).
52
70
  *
53
- * issues/codex-security/06: with `debugPort` (or `LENSMCP_DEBUG_PORT`) set, every pod and worker
71
+ * issues/closed/codex-security-06: with `debugPort` (or `LENSMCP_DEBUG_PORT`) set, every pod and worker
54
72
  * opened a Node inspector on a port derived from a configured, PRINTED base. Node's inspector has no
55
73
  * application authentication — `GET /json/list` on that port returns the session UUID, and the UUID is
56
74
  * the whole credential for `Runtime.evaluate`, i.e. arbitrary code execution inside the pod with the
@@ -78,6 +96,16 @@ export declare function usesFixedInspectorPorts(env?: NodeJS.ProcessEnv): boolea
78
96
  * degrades to `0` rather than throwing at `inspector.open`.
79
97
  */
80
98
  export declare function inspectorPortFor(basePort: number, offset: number, env?: NodeJS.ProcessEnv): number;
81
- export declare function redactInspectorUrls(text: string): string;
99
+ /**
100
+ * Strip the inspector session UUID out of any `ws://host:port/<uuid>` in a log line (inspector-secret.ts).
101
+ *
102
+ * `GET /webpack/logs` is served on BOTH the owner-only control socket and the public TCP port, and
103
+ * the log hub mirrors the pod's own "Debugger listening on ws://…" line plus the parent's echo of it.
104
+ * So moving the inspector to a random port would have moved the credential rather than removed it:
105
+ * a public reader could simply read the full URL — port AND UUID — out of the log stream. The owner
106
+ * (control socket) still gets the attachable URL; everyone else gets the shape and no secret. What the
107
+ * devserver writes to its own stdout/stderr and to its log tee is redacted the same way (issues/closed/pod-memory-06).
108
+ */
109
+ export { redactInspectorUrls };
82
110
  /** The TCP port out of a `ws://host:port/uuid` inspector URL, or `null`. */
83
111
  export declare function inspectorPortOf(url: string | undefined): number | null;
package/main.devserver.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var Oe=Object.defineProperty;var h=(o,p)=>Oe(o,"name",{value:p,configurable:!0});var Ce=Object.defineProperty,s=h((o,p)=>Ce(o,"name",{value:p,configurable:!0}),"s");Object.defineProperty(exports,"__esModule",{value:!0}),exports.DEBUG_FIXED_PORTS_ENV=void 0,exports.isOwnerOnlyTransport=isOwnerOnlyTransport,exports.gatewayManaged=gatewayManaged,exports.mayDriveThePool=mayDriveThePool,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor,exports.redactInspectorUrls=redactInspectorUrls,exports.inspectorPortOf=inspectorPortOf;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),node_child_process_1=require("node:child_process"),http=tslib_1.__importStar(require("node:http")),readline=tslib_1.__importStar(require("node:readline")),inspector=tslib_1.__importStar(require("node:inspector")),util=tslib_1.__importStar(require("node:util")),v8=tslib_1.__importStar(require("node:v8")),vm=tslib_1.__importStar(require("node:vm")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),schema_first_instance_1=require("./schema-first-instance"),redis_disconnect_fix_1=require("./redis-disconnect-fix"),error_stack_release_1=require("./error-stack-release"),swap_recycle_1=require("./swap-recycle"),stdio_guard_1=require("./stdio-guard"),httpProxy=require("http-proxy"),{prettyFactory}=require("pino-pretty"),BUNDLE_PATH=process.env.BUNDLE_PATH?.trim()||"./main.js",SERVICE_NAME=process.env.SERVICE_NAME||"",SERVICE_PREFIX=process.env.SERVE_PREFIX?`/${process.env.SERVE_PREFIX}`:"",GATEWAY_MIDDLEWARE_PATH=process.env.GATEWAY_MIDDLEWARE||"",GATEWAY_CONFIG=(()=>{const o=process.env.GATEWAY_CONFIG;if(!o)return{};try{return JSON.parse(o)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const o=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof o=="function"?o:typeof o.default=="function"?o.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(o){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,o)}function applyGatewayMiddleware(o){if(gatewayMiddleware)try{gatewayMiddleware(o,GATEWAY_CONFIG)}catch(p){console.error("[gateway] Middleware error:",p)}}h(applyGatewayMiddleware,"applyGatewayMiddleware"),s(applyGatewayMiddleware,"applyGatewayMiddleware");const GATEWAY_ROUTES=(()=>{try{return JSON.parse(process.env.GATEWAY_ROUTES||"[]")}catch{return[]}})(),GATEWAY_HTTPS=process.env.GATEWAY_HTTPS==="1";function hostMatches(o,p){if(o.startsWith("*.")){const g=o.slice(1);return p.endsWith(g)||p===o.slice(2)}return p===o}h(hostMatches,"hostMatches"),s(hostMatches,"hostMatches");function matchGatewayRoute(o){if(GATEWAY_ROUTES.length===0)return;const p=(o.headers.host||"").split(":")[0],g=o.url||"/";for(const _ of GATEWAY_ROUTES)if(!(_.host&&!hostMatches(_.host,p))&&!(_.prefix&&!g.startsWith(_.prefix)))return _}h(matchGatewayRoute,"matchGatewayRoute"),s(matchGatewayRoute,"matchGatewayRoute");const WORKER_NAMES=(()=>{try{return JSON.parse(process.env.WORKERS||"[]")}catch{return[]}})(),LENSMCP_WS_KEY=process.env.LENSMCP_WS_KEY||"",SOCK_DIR=LENSMCP_WS_KEY?path.join(os.tmpdir(),LENSMCP_WS_KEY,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`),POD_GENERATION=(()=>{const o=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(o)&&o>1?Math.floor(o):1})();function childSockPath(o){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${o}-g${POD_GENERATION}.sock`:`child-${o}.sock`)}h(childSockPath,"childSockPath"),s(childSockPath,"childSockPath");function sockOwnerPath(o){return`${o}.owner`}h(sockOwnerPath,"sockOwnerPath"),s(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(o){return`${o}.retiring`}h(sockRetiringPath,"sockRetiringPath"),s(sockRetiringPath,"sockRetiringPath");function isOwnerOnlyTransport(o){try{return typeof o.socket?.server?.address?.()=="string"}catch{return!1}}h(isOwnerOnlyTransport,"isOwnerOnlyTransport"),s(isOwnerOnlyTransport,"isOwnerOnlyTransport");function gatewayManaged(o=process.env){return Number(o.LENSMCP_GATEWAY_PID??0)>0}h(gatewayManaged,"gatewayManaged"),s(gatewayManaged,"gatewayManaged");function mayDriveThePool(o,p=gatewayManaged()){return!p||isOwnerOnlyTransport(o)}h(mayDriveThePool,"mayDriveThePool"),s(mayDriveThePool,"mayDriveThePool");function refusePoolCommand(o,p){console.warn(`[devserver] refused ${o.method} ${o.url} on the public port \u2014 under a gateway the pool is driven on the control socket only.`),p.writeHead(404,{"content-type":"application/json"}),p.end(JSON.stringify({error:"not found"}))}h(refusePoolCommand,"refusePoolCommand"),s(refusePoolCommand,"refusePoolCommand"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(o=process.env){const p=(o[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return p==="1"||p==="true"||p==="yes"||p==="on"}h(usesFixedInspectorPorts,"usesFixedInspectorPorts"),s(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(o,p,g=process.env){if(!usesFixedInspectorPorts(g))return 0;const _=Math.trunc(o)+Math.trunc(p);return Number.isInteger(_)&&_>1024&&_<=65535?_:0}h(inspectorPortFor,"inspectorPortFor"),s(inspectorPortFor,"inspectorPortFor");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(o){return o.replace(INSPECTOR_TOKEN_RE,"$1<inspector-token-redacted>")}h(redactInspectorUrls,"redactInspectorUrls"),s(redactInspectorUrls,"redactInspectorUrls");function inspectorPortOf(o){const p=/^wss?:\/\/[^/]*?:(\d+)\//.exec(o??""),g=p?Number(p[1]):NaN;return Number.isInteger(g)&&g>0?g:null}h(inspectorPortOf,"inspectorPortOf"),s(inspectorPortOf,"inspectorPortOf");function ensureOwnerOnlyDir(o){try{fs.mkdirSync(o,{recursive:!0,mode:448})}catch{}try{const p=fs.lstatSync(o);p.isDirectory()&&(p.mode&63)!==0&&fs.chmodSync(o,448)}catch{}}h(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),s(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(o,p=process.pid){ensureOwnerOnlyDir(path.dirname(o));try{const g=sockOwnerPath(o);try{fs.unlinkSync(g)}catch{}fs.writeFileSync(g,String(p),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(o)}catch{}try{fs.unlinkSync(sockRetiringPath(o))}catch{}}h(claimSock,"claimSock"),s(claimSock,"claimSock");function cleanupSock(o,p=process.pid){if(o){try{const g=fs.readFileSync(sockOwnerPath(o),"utf8").trim();if(g&&g!==String(p))return}catch{}try{fs.unlinkSync(o)}catch{}try{fs.unlinkSync(sockOwnerPath(o))}catch{}try{fs.unlinkSync(sockRetiringPath(o))}catch{}}}h(cleanupSock,"cleanupSock"),s(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const o=process.env.LENSMCP_SERVICE_KEY;if(!o)return;const{createHash:p}=require("node:crypto");return p("sha256").update(o).digest("hex").slice(0,8)}h(serviceKeyFingerprint,"serviceKeyFingerprint"),s(serviceKeyFingerprint,"serviceKeyFingerprint");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const requested=path.isAbsolute(spec)?spec:path.join(__dirname,spec);let resolved=requested;try{resolved=fs.realpathSync(requested)}catch{}return eval(`(() => {
1
+ "use strict";var Ke=Object.defineProperty;var h=(s,u)=>Ke(s,"name",{value:u,configurable:!0});var Ve=Object.defineProperty,n=h((s,u)=>Ve(s,"name",{value:u,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.redactInspectorUrls=exports.DEBUG_FIXED_PORTS_ENV=void 0,exports.isOwnerOnlyTransport=isOwnerOnlyTransport,exports.tell=tell,exports.gatewayManaged=gatewayManaged,exports.mayDriveThePool=mayDriveThePool,exports.usesFixedInspectorPorts=usesFixedInspectorPorts,exports.inspectorPortFor=inspectorPortFor,exports.inspectorPortOf=inspectorPortOf;const tslib_1=require("tslib"),path=tslib_1.__importStar(require("node:path")),fs=tslib_1.__importStar(require("node:fs")),os=tslib_1.__importStar(require("node:os")),node_child_process_1=require("node:child_process"),http=tslib_1.__importStar(require("node:http")),readline=tslib_1.__importStar(require("node:readline")),inspector=tslib_1.__importStar(require("node:inspector")),util=tslib_1.__importStar(require("node:util")),v8=tslib_1.__importStar(require("node:v8")),exit_reason_1=require("./exit-reason"),hot_swap_registries_1=require("./hot-swap-registries"),process_name_1=require("./process-name"),inspector_secret_1=require("./inspector-secret");Object.defineProperty(exports,"redactInspectorUrls",{enumerable:!0,get:n(function(){return inspector_secret_1.redactInspectorUrls},"get")});const schema_first_instance_1=require("./schema-first-instance"),redis_disconnect_fix_1=require("./redis-disconnect-fix"),error_stack_release_1=require("./error-stack-release"),swap_recycle_1=require("./swap-recycle"),stdio_guard_1=require("./stdio-guard"),bundle_parent_1=require("./bundle-parent"),dev_devtool_1=require("./dev-devtool"),pod_memory_1=require("./pod-memory"),pod_memory_events_1=require("./pod-memory-events"),heap_snapshot_1=require("./heap-snapshot"),httpProxy=require("http-proxy"),{prettyFactory}=require("pino-pretty"),BUNDLE_PATH=process.env.BUNDLE_PATH?.trim()||"./main.js",SERVICE_NAME=process.env.SERVICE_NAME||"",SERVICE_PREFIX=process.env.SERVE_PREFIX?`/${process.env.SERVE_PREFIX}`:"",GATEWAY_MIDDLEWARE_PATH=process.env.GATEWAY_MIDDLEWARE||"",GATEWAY_CONFIG=(()=>{const s=process.env.GATEWAY_CONFIG;if(!s)return{};try{return JSON.parse(s)}catch{return{}}})();let gatewayMiddleware=null;if(GATEWAY_MIDDLEWARE_PATH)try{const s=require(GATEWAY_MIDDLEWARE_PATH);gatewayMiddleware=typeof s=="function"?s:typeof s.default=="function"?s.default:null,gatewayMiddleware||console.warn(`[gateway] ${GATEWAY_MIDDLEWARE_PATH} does not export a function, gateway disabled`)}catch(s){console.error(`[gateway] Failed to load middleware from ${GATEWAY_MIDDLEWARE_PATH}:`,s)}function applyGatewayMiddleware(s){if(gatewayMiddleware)try{gatewayMiddleware(s,GATEWAY_CONFIG)}catch(u){console.error("[gateway] Middleware error:",u)}}h(applyGatewayMiddleware,"applyGatewayMiddleware"),n(applyGatewayMiddleware,"applyGatewayMiddleware");const GATEWAY_ROUTES=(()=>{try{return JSON.parse(process.env.GATEWAY_ROUTES||"[]")}catch{return[]}})(),GATEWAY_HTTPS=process.env.GATEWAY_HTTPS==="1";function hostMatches(s,u){if(s.startsWith("*.")){const f=s.slice(1);return u.endsWith(f)||u===s.slice(2)}return u===s}h(hostMatches,"hostMatches"),n(hostMatches,"hostMatches");function matchGatewayRoute(s){if(GATEWAY_ROUTES.length===0)return;const u=(s.headers.host||"").split(":")[0],f=s.url||"/";for(const S of GATEWAY_ROUTES)if(!(S.host&&!hostMatches(S.host,u))&&!(S.prefix&&!f.startsWith(S.prefix)))return S}h(matchGatewayRoute,"matchGatewayRoute"),n(matchGatewayRoute,"matchGatewayRoute");const WORKER_NAMES=(()=>{try{return JSON.parse(process.env.WORKERS||"[]")}catch{return[]}})(),LENSMCP_WS_KEY=process.env.LENSMCP_WS_KEY||"",SOCK_DIR=LENSMCP_WS_KEY?path.join(os.tmpdir(),LENSMCP_WS_KEY,`${SERVICE_NAME||"lensmcp-cluster"}-devserver`):path.join(os.tmpdir(),`${SERVICE_NAME||"lensmcp-cluster"}-devserver`),POD_GENERATION=(()=>{const s=Number(process.env.LENSMCP_POD_GENERATION);return Number.isFinite(s)&&s>1?Math.floor(s):1})();function childSockPath(s){return path.join(SOCK_DIR,POD_GENERATION>1?`child-${s}-g${POD_GENERATION}.sock`:`child-${s}.sock`)}h(childSockPath,"childSockPath"),n(childSockPath,"childSockPath");function sockOwnerPath(s){return`${s}.owner`}h(sockOwnerPath,"sockOwnerPath"),n(sockOwnerPath,"sockOwnerPath");function sockRetiringPath(s){return`${s}.retiring`}h(sockRetiringPath,"sockRetiringPath"),n(sockRetiringPath,"sockRetiringPath");function isOwnerOnlyTransport(s){try{return typeof s.socket?.server?.address?.()=="string"}catch{return!1}}h(isOwnerOnlyTransport,"isOwnerOnlyTransport"),n(isOwnerOnlyTransport,"isOwnerOnlyTransport");function tell(s,u){if(!s.connected||typeof s.send!="function")return!1;try{return s.send(u,()=>{})}catch{return!1}}h(tell,"tell"),n(tell,"tell");function gatewayManaged(s=process.env){return Number(s.LENSMCP_GATEWAY_PID??0)>0}h(gatewayManaged,"gatewayManaged"),n(gatewayManaged,"gatewayManaged");function mayDriveThePool(s,u=gatewayManaged()){return!u||isOwnerOnlyTransport(s)}h(mayDriveThePool,"mayDriveThePool"),n(mayDriveThePool,"mayDriveThePool");function refusePoolCommand(s,u){console.warn(`[devserver] refused ${s.method} ${s.url} on the public port \u2014 under a gateway the pool is driven on the control socket only.`),u.writeHead(404,{"content-type":"application/json"}),u.end(JSON.stringify({error:"not found"}))}h(refusePoolCommand,"refusePoolCommand"),n(refusePoolCommand,"refusePoolCommand"),exports.DEBUG_FIXED_PORTS_ENV="LENSMCP_DEBUG_FIXED_PORTS";function usesFixedInspectorPorts(s=process.env){const u=(s[exports.DEBUG_FIXED_PORTS_ENV]??"").trim().toLowerCase();return u==="1"||u==="true"||u==="yes"||u==="on"}h(usesFixedInspectorPorts,"usesFixedInspectorPorts"),n(usesFixedInspectorPorts,"usesFixedInspectorPorts");function inspectorPortFor(s,u,f=process.env){if(!usesFixedInspectorPorts(f))return 0;const S=Math.trunc(s)+Math.trunc(u);return Number.isInteger(S)&&S>1024&&S<=65535?S:0}h(inspectorPortFor,"inspectorPortFor"),n(inspectorPortFor,"inspectorPortFor");function inspectorPortOf(s){const u=/^wss?:\/\/[^/]*?:(\d+)\//.exec(s??""),f=u?Number(u[1]):NaN;return Number.isInteger(f)&&f>0?f:null}h(inspectorPortOf,"inspectorPortOf"),n(inspectorPortOf,"inspectorPortOf");function ensureOwnerOnlyDir(s){try{fs.mkdirSync(s,{recursive:!0,mode:448})}catch{}try{const u=fs.lstatSync(s);u.isDirectory()&&(u.mode&63)!==0&&fs.chmodSync(s,448)}catch{}}h(ensureOwnerOnlyDir,"ensureOwnerOnlyDir"),n(ensureOwnerOnlyDir,"ensureOwnerOnlyDir");function claimSock(s,u=process.pid){ensureOwnerOnlyDir(path.dirname(s));try{const f=sockOwnerPath(s);try{fs.unlinkSync(f)}catch{}fs.writeFileSync(f,String(u),{flag:"wx",mode:384})}catch{}try{fs.unlinkSync(s)}catch{}try{fs.unlinkSync(sockRetiringPath(s))}catch{}}h(claimSock,"claimSock"),n(claimSock,"claimSock");function sockOwnedByAnother(s,u=process.pid){try{const f=fs.readFileSync(sockOwnerPath(s),"utf8").trim();return!!f&&f!==String(u)}catch{return!1}}h(sockOwnedByAnother,"sockOwnedByAnother"),n(sockOwnedByAnother,"sockOwnedByAnother");function cleanupSock(s,u=process.pid){if(s){try{const f=fs.readFileSync(sockOwnerPath(s),"utf8").trim();if(f&&f!==String(u))return}catch{}try{fs.unlinkSync(s)}catch{}try{fs.unlinkSync(sockOwnerPath(s))}catch{}try{fs.unlinkSync(sockRetiringPath(s))}catch{}}}h(cleanupSock,"cleanupSock"),n(cleanupSock,"cleanupSock");function serviceKeyFingerprint(){const s=process.env.LENSMCP_SERVICE_KEY;if(!s)return;const{createHash:u}=require("node:crypto");return u("sha256").update(s).digest("hex").slice(0,8)}h(serviceKeyFingerprint,"serviceKeyFingerprint"),n(serviceKeyFingerprint,"serviceKeyFingerprint");function rootModuleOf(s){let u;for(const f of s.getModules?.()?.values()??[])typeof f?.metatype!="function"||typeof f.distance!="number"||!Number.isFinite(f.distance)||(!u||f.distance<u.distance)&&(u=f);return u?.metatype}h(rootModuleOf,"rootModuleOf"),n(rootModuleOf,"rootModuleOf");let lastImportedModule;function takeImportedModule(){const s=lastImportedModule;return lastImportedModule=void 0,s}h(takeImportedModule,"takeImportedModule"),n(takeImportedModule,"takeImportedModule");async function importFresh(spec){process.env.DEVSERVER_MODE="1";const requested=path.isAbsolute(spec)?spec:path.join(__dirname,spec);let resolved=requested;try{resolved=fs.realpathSync(requested)}catch{}eval(`(() => {
2
2
  const key = ${JSON.stringify(resolved)};
3
3
  const previous = require.cache[key];
4
4
  delete require.cache[key]; // delete from CJS cache
@@ -9,13 +9,15 @@
9
9
  const at = owner && Array.isArray(owner.children) ? owner.children.indexOf(previous) : -1;
10
10
  if (at >= 0) owner.children.splice(at, 1);
11
11
  }
12
- })()`),eval(`require(${JSON.stringify(resolved)})`)}h(importFresh,"importFresh"),s(importFresh,"importFresh");function debounce(o,p){let g=null;return(..._)=>{g&&clearTimeout(g),g=setTimeout(()=>o(..._),p)}}if(h(debounce,"debounce"),s(debounce,"debounce"),process.env.APP_RUNNER==="1"){(0,stdio_guard_1.guardBrokenStdio)();const o=s(g=>{if(!inspector.url())try{inspector.open(g,"127.0.0.1",!1)}catch(B){console.warn(`[child] inspector.open(${g}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,B.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const _=inspector.url();_&&process.send&&process.send({type:"inspector-url",url:_})},"openInspector"),p=process.env.CHILD_DEBUG_PORT;if(p!==void 0&&p!==""){const g=Number(p);Number.isInteger(g)&&(g===0||g>1024)&&o(g)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let g=null,_=!1,B,j=!1;const N=(0,hot_swap_registries_1.registryPruningEnabled)(),Z=s((r,i)=>{const f=(0,hot_swap_registries_1.describePrune)(r,i);f&&console.log(f)},"logPrune");let $={app:null,handler:s((r,i)=>{i.statusCode=503,i.end("starting")},"handler"),inflight:0};const ee=(()=>{const r=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(r)&&r>=0?r:15*6e4})(),Y=s(r=>{const i=globalThis.__lensmcpActiveWork;if(typeof i!="function")return{count:0};try{const f=i(r);return{count:Number(f?.count??0),...f?.oldestStartedAt?{oldestStartedAt:f.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),te=(()=>{const r=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(r)&&r>=1?Math.floor(r):1})(),C=new Set,P=(0,swap_recycle_1.swapRecyclePolicy)();let oe,le=0;const ge=(()=>{const r=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(r)&&r>=0?r:3500})();let K=!1,V=!1,u;const m=s(()=>{if(u===void 0)try{v8.setFlagsFromString("--expose-gc");const r=vm.runInNewContext("gc");u=typeof r=="function"?r:null}catch{u=null}try{u?.()}catch{}return process.memoryUsage().heapUsed},"liveHeap"),w=(()=>{if(!(0,error_stack_release_1.errorFrameReleaseEnabled)())return null;try{const r=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH))+path.sep,i=`${path.sep}node_modules${path.sep}`,f=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:s(y=>f.has(y)||y.startsWith(r)&&!y.slice(r.length).includes(i)&&!y.slice(r.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let O=!1;function W(){if(!(!w||O))try{const r=w();r.errors>0&&console.log(`[child] read the stack of ${r.errors} Error object(s) that libraries created while loading and keep for good \u2014 unread, each held the functions that were on its stack, this bundle's included`)}catch(r){console.error("[child] could not release library error frames:",r),O=!0}}h(W,"z"),s(W,"releaseLibraryErrorFrames");const D=new Map;let b=Date.now();const T=s(()=>P.settleMs<=0||!_&&!j&&D.size===0&&Date.now()-b>=P.settleMs,"settled");let A;function R(r){if(!(!P.enabled||K||V)){if(P.settleMs<=0){F(r);return}A&&clearTimeout(A),A=setTimeout(()=>{A=void 0,T()?F(r):R(r)},P.settleMs),A.unref?.()}}h(R,"N"),s(R,"recycleCheckWhenSettled");const ne=s(()=>{if(P.stuckCloseMs<=0)return 0;const r=Date.now();let i=0;for(const f of D.values())r-f>=P.stuckCloseMs&&(i+=1);return i},"stuckCloses");function F(r){if(!P.enabled||K||V||!process.send)return;const i=v8.getHeapStatistics().heap_size_limit,f=s(E=>({swaps:le,usedBytes:E,limitBytes:i,stuckCloses:ne(),settled:T(),...oe===void 0?{}:{baselineBytes:oe}}),"state");if(!(0,swap_recycle_1.recycleReason)(P,f(process.memoryUsage().heapUsed)))return;const y=(0,swap_recycle_1.recycleReason)(P,f(m()));y&&(K=!0,console.log(`[child] asking for a fresh pod ${r} \u2014 ${y}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),process.send({type:"recycle-request",reason:y}))}h(F,"K"),s(F,"considerRecycle");async function J(r,i){if(r.closed||(r.closed=!0,C.delete(r),i&&console.error(`[child] ${i}`),!r.app))return;D.set(r,Date.now());const f=P.enabled&&P.stuckCloseMs>0?setTimeout(()=>{D.has(r)&&(console.error(`[child] a previous app has been closing for ${Math.round(P.stuckCloseMs/1e3)}s and its close() has not returned \u2014 one of its shutdown hooks is waiting on something that does not end (a queue worker, a connection, a stream). It stays in memory until this pod is replaced.`),F("with a previous app stuck in close()"))},P.stuckCloseMs):void 0;f?.unref?.();try{await r.app.close()}catch(y){console.error("[child] error closing a previous app:",y)}finally{D.delete(r),b=Date.now(),f&&clearTimeout(f),r.app=null,r.handler=(y,E)=>{E.statusCode=503,E.end("closed")}}if(r.superseded){try{Z((0,hot_swap_registries_1.pruneRegistries)(r.superseded,"serve"),"after closing the previous app")}catch(y){console.error("[child] could not release the previous app's class registrations:",y)}r.superseded=void 0}R("after closing the previous app")}h(J,"Q"),s(J,"closeGeneration");async function de(r){for(;C.size>=te;){const E=C.values().next().value;if(!E)break;await J(E,`closing a still-draining generation early \u2014 ${C.size} were pending and the cap is ${te} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}C.add(r);const i=Date.now(),f=s(()=>({inflight:r.inflight,work:Y(r.workGen).count}),"busy");for(;ee>0&&Date.now()-i<ee;){if(r.closed)return;const E=f();if(E.inflight===0&&E.work===0)break;await new Promise(x=>{setTimeout(x,250).unref?.()})}if(r.closed)return;const y=f();y.inflight>0||y.work>0?console.error(`[child] swap drain expired after ${Date.now()-i}ms \u2014 closing the previous app with ${y.inflight} in-flight request(s) and ${y.work} background job(s) still running. A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${ee}ms) if this is legitimate work.`):Date.now()-i>250&&console.log(`[child] previous app drained in ${Date.now()-i}ms \u2014 closing it`),await J(r)}h(de,"he"),s(de,"drainThenClose");async function M(){if(_)return j=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),g;_=!0,b=Date.now(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(S,L)=>{console.error(`[child] uncaughtException (${L}):`,S)}),process.on("unhandledRejection",S=>{const L=S instanceof Error?S.stack||S.message:String(S);L.includes("FronteggContextScope")||L.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",S)});let r;if(N)try{r=(0,hot_swap_registries_1.captureRegistries)()}catch{r=void 0}try{try{await importFresh(BUNDLE_PATH)}catch(S){if(!(S instanceof SyntaxError))throw S;console.warn(`[child] bundle was unreadable mid-write (${S.message}) \u2014 retrying the swap once`),await new Promise(L=>setTimeout(L,400)),await importFresh(BUNDLE_PATH)}}catch(S){throw _=!1,b=Date.now(),S}if(typeof global.createChildApp!="function")throw _=!1,b=Date.now(),new Error(`Bundle '${BUNDLE_PATH}' does not export createChildApp() and the zero-touch bridge is not active. Either install @lensmcp/node-instrumentation (a plain NestFactory.create + app.listen main.ts then just works) or set global.createChildApp in main.ts when DEVSERVER_MODE === '1'.`);let i;if(r)try{i=(0,hot_swap_registries_1.supersededSince)(r),Z((0,hot_swap_registries_1.pruneRegistries)(i,"build"),"at the swap")}catch(S){console.error("[child] could not release superseded class registrations:",S)}const f=global.__lensmcpBeginWorkGeneration,y=typeof f=="function"?f():void 0;let E;try{E=await global.createChildApp()}catch(S){throw _=!1,b=Date.now(),S}const x=$;return i?.size&&(x.app?x.superseded=i:Z((0,hot_swap_registries_1.pruneRegistries)(i,"serve"),"at the swap")),g=E,$={app:E,handler:E.handler,inflight:0,...y===void 0?{}:{workGen:y}},console.log("[child] Swapped to fresh app (previous draining in background)"),W(),x.app&&(le+=1,de(x).catch(S=>console.error("[child] background close error:",S)),setTimeout(()=>F("30s after a hot swap"),3e4).unref?.()),_=!1,b=Date.now(),j?(j=!1,M()):E}h(M,"V"),s(M,"swapNow");const re=debounce(async()=>{try{console.log("[child] /webpack/reload"),await M()}catch(r){console.error("[child] reload failed:",r)}},250),G=http.createServer((r,i)=>{if(r.method==="POST"&&r.url==="/webpack/reload"){i.writeHead(200,{"content-type":"application/json"}),i.end(JSON.stringify({ok:!0})),re();return}if(r.method==="GET"&&r.url==="/__lensmcp/alive"){const y=r.headers["x-internal-token"],E=process.env.LENSMCP_SERVICE_KEY;if(E&&y!==E){i.writeHead(404,{"content-type":"text/plain"}),i.end("not found");return}const x=Y();i.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),i.end(JSON.stringify({ok:!0,pid:process.pid,swapping:_,inflight:$.inflight,oldestInflightAt:$.oldestAt,activeWork:x.count,oldestWorkAt:x.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...B?{watcher:B}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const f=$;f.inflight+=1,f.oldestAt===void 0&&(f.oldestAt=Date.now()),i.on("close",()=>{f.inflight-=1,f.inflight===0&&(f.oldestAt=void 0)}),f.handler(r,i)});G.keepAliveTimeout=0;const I=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(I),process.exit(0)});try{const r=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);if((0,schema_first_instance_1.schemaPrewarmEnabled)())try{const i=(0,schema_first_instance_1.prewarmZodFor)(r);i?.created&&console.log(`[child] zod${i.version?` ${i.version}`:""}: built ${i.created} throwaway schemas first, so none of the bundle's own is the first of its class`)}catch{}if((0,redis_disconnect_fix_1.redisDisconnectFixEnabled)())try{const i=(0,redis_disconnect_fix_1.finishStrandedDisconnectsFor)(r);i?.patched&&console.log(`[child] ioredis${i.versions.length?` ${i.versions.join(", ")}`:""}: a disconnect() between reconnect attempts now ends the client \u2014 it used to leave a queue worker's close() waiting for good`)}catch{}await M(),P.enabled&&P.heapFactor>0&&(oe=m()),console.log(`[child] Using bundle: ${r}`),claimSock(I),G.listen(I,()=>{console.log(`[child] Listening on ${I}`),process.send&&process.send({type:"ready",socketPath:I})})}catch(r){console.error("[child] Startup error:",r),process.send&&process.send({type:"boot-error",error:String(r?.stack||r)}),process.exitCode=1;return}process.on("message",async r=>{if(!(!r||typeof r!="object")){if(r.type==="debug-open"){const i=Number(r.port);o(Number.isInteger(i)&&i>1024?i:0);return}if(r.type==="watcher"){const i=r.testimony;i&&typeof i=="object"&&(B=i);return}if(r.type==="recycle-declined"){K=!1;return}if(r.type==="retire"){U();return}if(r.type==="reload"){if(V)return;try{await M(),process.send&&process.send({type:"reloaded",socketPath:I})}catch(i){console.error("[child] reload error:",i),process.send&&process.send({type:"reload-error",error:String(i?.stack||i)})}}}});async function U(){if(V)return;V=!0,console.log("[child] retiring \u2014 a fresh pod has taken over; finishing in-flight work, then exiting");try{fs.writeFileSync(sockRetiringPath(I),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(i=>{setTimeout(i,ge).unref?.()}),cleanupSock(I),G.close();const r=Date.now();for(;Date.now()-r<ee&&(G.closeIdleConnections?.(),!($.inflight===0&&Y().count===0));)await new Promise(i=>{setTimeout(i,250).unref?.()});setTimeout(()=>process.exit(0),3e4).unref?.();try{await g?.close()}catch(i){console.error("[child] close error while retiring:",i)}process.exit(0)}h(U,"ee"),s(U,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const X=s(async r=>{console.log(`[child ${r}] shutting down\u2026`);const i=g?.close().catch(y=>console.error("[child] close error:",y)),f=new Promise(y=>G.close(()=>y()));cleanupSock(I),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([i,f]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(r=>{process.on(r,()=>{inspector?.close?.(),X(r)})})})()}else{let o=h(function(u){C.push(u),C.length>te&&C.splice(0,C.length-te),ee?.write(u.replace(Y,""));for(const m of P)try{const w=m.plain?u.replace(Y,""):u;m.res.write(m.redact?redactInspectorUrls(w):w)}catch{P.delete(m)}},"P");s(o,"emitLogLine");const p=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],g="\x1B[0m",_=Math.max(1,Number(process.env.CHILD_COUNT||1)),B=LENSMCP_WS_KEY||path.basename(process.cwd()),j=s(u=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(B,SERVICE_NAME||"service",u)),"execPathFor");let N=(()=>{const u=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(u)&&u>1024?u:0})();const Z=20;N&&(console.warn("[parent] DEBUGGER ENABLED \u2014 pods and workers open a Node inspector. Node's inspector has NO authentication: the ws:// URL's UUID is the whole credential, and any account on this host that obtains it can run code as you. Do not enable this on a shared machine. (issues/codex-security/06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${N}+slot, workers ${N}+${Z}+i). Any account on this host can connect to 127.0.0.1:${N} and read the session UUID from /json/list. Unset it to use ephemeral ports.`:`[parent] Inspectors bind an EPHEMERAL loopback port; attach via the printed "Debugger listening" URL, or \`lensmcp debug <service>\` (control socket). Set ${exports.DEBUG_FIXED_PORTS_ENV}=1 for fixed ports if your IDE needs a pre-configured attach port \u2014 any account on this host can then reach them.`));const $=process.env.LENSMCP_LOG_FILE||"",ee=$?(()=>{try{fs.mkdirSync(path.dirname($),{recursive:!0});const u=fs.createWriteStream($,{flags:"a",mode:384});try{fs.chmodSync($,384)}catch{}return u}catch(u){return console.warn(`[parent] cannot open log file ${$}:`,u.message),null}})():null,Y=/\x1b\[[0-9;]*m/g,te=2e3,C=[],P=new Set;for(const u of["log","info","warn","error"]){const m=console[u].bind(console);console[u]=(...w)=>{m(...w),o(w.map(O=>typeof O=="string"?O:util.inspect(O)).join(" ")+`
13
- `)}}const oe=s(u=>p[(u-1)%p.length],"colorFor"),le=s(u=>{if(_===1)return"";const m=`[child#${u}] `;return`${oe(u)}${m}${g}`},"tagFor"),ge=s(u=>u.startsWith("{")&&u.endsWith("}"),"isJsonLog"),K=s((u,m,w)=>{const O=prettyFactory({sync:!0,colorize:!0,crlf:!0,messageKey:"message",errorLikeObjectKeys:["err","error"],errorProps:"type,message,stack",ignore:["logContext","context","hostname","req","res","err.driverError","module","cloudEnvironment","frontegg-application-id","frontegg-tenant-id","frontegg-trace-id","frontegg-vendor-id","host","service","version","err","error"].join(","),messageFormat:"{if module}[{module}] {end}{if context}[{context}] {end}{if logContext}[{logContext}] {end}{message}",customPrettifiers:{stack:s(b=>`
14
- `+String(b),"stack")}}),W=s((b,T)=>{const A=b==="stdout"?process.stdout:process.stderr;try{w?.(T)}catch{}const R=ge(T)?O(T):`${T}
15
- `;A.write(`${m}${R}`),o(`${m}${R}`)},"write"),D=s((b,T)=>{if(!b)return;const A=readline.createInterface({input:b});A.on("line",R=>W(T,R)),A.on("close",()=>{})},"attach");D(u.stdout,"stdout"),D(u.stderr,"stderr")},"wireProcLogging"),V=s(u=>K(u.proc,le(u.id),m=>u.tail.push(m)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const u=httpProxy.createProxyServer({});let m=Number(process.env.PORT)||0;m||(m=9090);const w=[],O=new Set;function W(){let e=0;for(;O.has(e);)e++;return O.add(e),e}h(W,"z"),s(W,"allocDebugSlot");let D=1,b=0,T=!1,A=_,R=[],ne=0;const F=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),J=s(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),de=s(e=>e.booting&&!J(e)&&Date.now()-e.spawnedAt<F,"stillBooting");function M(){return w.filter(e=>e.healthy&&typeof e.socketPath=="string")}h(M,"V"),s(M,"healthyChildren");let re;function G(e){if(!e||typeof e!="object")return;const t=e;if(t.schemaVersion===1){re=t;for(const d of w)try{d.proc.send?.({type:"watcher",testimony:t})}catch{}}}h(G,"U"),s(G,"recordWatcherTestimony");function I(e,t=256*1024){return new Promise(d=>{let a="",c=!1;e.on("data",n=>{c||(a+=n,a.length>t&&(c=!0))}),e.on("end",()=>{if(c||!a)return d(void 0);try{d(JSON.parse(a))}catch{d(void 0)}}),e.on("error",()=>d(void 0))})}h(I,"I"),s(I,"readJsonBody");function U(){const e=M();if(e.length===0)return null;const t=b%e.length;return b=(b+1)%e.length,e[t]}h(U,"ee"),s(U,"pickChild");async function X(){const e=D++,t=childSockPath(e);cleanupSock(t);const d=["--enable-source-maps"];__filename.endsWith(".ts")&&d.unshift("--require","@swc-node/register");const a=N?W():-1,c=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:t,...a>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(N,a))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:d,execPath:j("pod")}),n={id:e,proc:c,healthy:!1,lastError:null,debugSlot:a,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:ne};return c.on("message",k=>{if(!k||typeof k!="object")return;const l=k;if(l.type==="ready"){if(n.socketPath=String(l.socketPath),n.healthy=!0,n.booting=!1,console.log(`[parent] Child#${n.id} ready on socket ${n.socketPath}`),re)try{c.send?.({type:"watcher",testimony:re})}catch{}if(ne!==n.bootReloadSeq)try{c.send?.({type:"reload"})}catch{}}else l.type==="recycle-request"?L(n,String(l.reason||"it has outgrown its bound")):l.type==="inspector-url"?(n.inspectorUrl=String(l.url),console.log(`[parent] Child#${n.id} Debugger listening on ${l.url}`)):l.type==="reloaded"?(n.socketPath=String(l.socketPath),n.healthy=!0,console.log(`[parent] Child#${n.id} hot-swapped on socket ${n.socketPath}`)):l.type==="reload-error"?(n.lastError=String(l.error||"unknown reload error"),console.error(`[parent] Child#${n.id} reload error: ${n.lastError}`),n.healthy=!1):l.type==="boot-error"&&(n.lastError=String(l.error||"unknown boot error"),console.error(`[parent] Child#${n.id} boot error: ${n.lastError}`),n.healthy=!1,n.booting=!1)}),c.on("exit",(k,l)=>{n.booting=!1,n.debugSlot>=0&&(O.delete(n.debugSlot),n.debugSlot=-1);const v=l==="SIGINT"||l==="SIGTERM"||k===0;if(v?console.log(`[parent] Child#${n.id} exited cleanly (code=${k}, signal=${l??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${n.id} crashed (code=${k}, signal=${l??"none"}, pid=${n.proc.pid}, ${n.healthy?"was serving":"was not ready"})`,k,l,n.tail.snapshot())),n.lastError||(n.lastError=`Child exited abnormally (code=${k}, signal=${l??"none"})`)),n.healthy=!1,cleanupSock(n.socketPath,n.proc.pid),n.socketPath=void 0,!v&&!T){const q=w.indexOf(n);q>=0&&w.splice(q,1);const ae=Date.now();if(R=R.filter(Q=>ae-Q<3e4),R.length>=5){console.error(`[parent] crash-loop detected (${R.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}R.push(ae),setTimeout(()=>{const Q=w.filter($e=>$e.proc.killed===!1).length;!T&&Q<A&&(console.log(`[parent] respawning a crashed pod (${Q}/${A} live)`),X().catch(()=>{}))},1500).unref?.()}}),w.push(n),V(n),n}h(X,"te"),s(X,"spawnChild");async function r(e){A=Math.max(A,e);const t=w.filter(a=>a.proc.killed===!1),d=e-t.length;for(let a=0;a<d;a++)await X()}h(r,"r"),s(r,"ensurePoolSize");async function i(){const e=w.filter(t=>(!t.healthy||typeof t.socketPath!="string")&&!de(t));await Promise.all(e.map(async t=>{try{t.proc.kill("SIGTERM")}catch{}cleanupSock(t.socketPath,t.proc.pid),t.socketPath=void 0;const d=w.indexOf(t);d>=0&&w.splice(d,1),await X()}))}h(i,"c"),s(i,"respawnUnhealthy");const f=new Set;let y=!1,E=0;const x=3e4,S=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function L(e,t){const d=s(c=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${t}) \u2014 not now: ${c}`);try{e.proc.send?.({type:"recycle-declined"})}catch{}},"decline");if(T||!w.includes(e))return;if(y)return d("another pod is already being replaced");const a=Date.now()-E;if(a<x)return d(`a pod was replaced ${Math.round(a/1e3)}s ago`);y=!0,console.log(`[parent] Child#${e.id} asked for a fresh pod \u2014 ${t}. Starting one; Child#${e.id} keeps serving until it is ready.`);try{const c=await X(),n=Date.now()+F;for(;!c.healthy&&!c.lastError&&!J(c)&&Date.now()<n;)await new Promise(v=>{setTimeout(v,100).unref?.()});if(T)return;if(!c.healthy){const v=w.indexOf(c);v>=0&&w.splice(v,1);try{c.proc.kill("SIGTERM")}catch{}return d(`its successor Child#${c.id} did not come up (${c.lastError??"no answer in time"})`)}const k=w.indexOf(e);k>=0&&w.splice(k,1),e.healthy=!1,f.add(e);const l=setTimeout(()=>{if(!J(e)){console.error(`[parent] Child#${e.id} was still running ${S}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},S);l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),f.delete(e)});try{e.proc.send?.({type:"retire"})}catch{try{e.proc.kill("SIGTERM")}catch{}}E=Date.now(),console.log(`[parent] Child#${c.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${c.proc.pid})`)}catch(c){d(`could not start a successor: ${c?.message??String(c)}`)}finally{y=!1}}h(L,"D"),s(L,"recyclePod");const H=[];function ye(e){const t=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),d=path.join(t,`${e}.js`);if(!fs.existsSync(d))return console.error(`[parent] Worker "${e}" bundle not found: ${d}`),null;const a=["--enable-source-maps"];__filename.endsWith(".ts")&&a.unshift("--require","@swc-node/register");try{a.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const c=inspectorPortFor(N||9229,Z+Math.max(0,WORKER_NAMES.indexOf(e)));a.push(N?`--inspect=127.0.0.1:${c}`:`--inspect-port=127.0.0.1:${c}`);const n=(0,node_child_process_1.fork)(d,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:a,execPath:j(`w-${e}`)}),k={name:e,proc:n,debugPort:c};return H.push(k),K(n,`\x1B[35m[worker:${e}] \x1B[0m`,l=>{const v=/Debugger listening on (wss?:\/\/\S+)/.exec(l);v&&(k.inspectorUrl=v[1])}),n.on("exit",(l,v)=>{v==="SIGINT"||v==="SIGTERM"||l===0||console.error(`[parent] Worker "${e}" crashed (code=${l}, signal=${v??"none"})`);const q=H.indexOf(k);q>=0&&H.splice(q,1)}),console.log(`[parent] Worker "${e}" started (pid=${n.pid})`),k}h(ye,"we"),s(ye,"spawnWorkerProcess");const Ee=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),Se=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),se=new Set,Pe=s(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function pe(e,t){if(se.delete(e),!Pe(e)){console.error(`[parent] Worker "${e.name}" (pid=${e.proc.pid}) ${t} \u2014 killing it. A worker that returns from its SIGTERM handler but leaves a handle open (a Redis client handed to BullMQ is not closed by worker.close()) never exits on its own: close what it opened, or call process.exit().`);try{e.proc.kill("SIGKILL")}catch{}}}h(pe,"fe"),s(pe,"killDrainingWorker");function ke(e){try{e.proc.kill("SIGTERM")}catch{}if(Pe(e))return;const t=[...se].filter(a=>a.name===e.name);for(;t.length>=Se;)pe(t.shift(),`was still running when a later reload replaced its successor (cap ${Se})`);se.add(e);const d=setTimeout(()=>pe(e,`was still running ${Ee}ms after SIGTERM`),Ee);d.unref?.(),e.proc.once("exit",()=>{clearTimeout(d),se.delete(e)})}h(ke,"ke"),s(ke,"retireWorker");function be(){for(const e of[...H])ke(e);H.length=0;for(const e of WORKER_NAMES)ye(e)}h(be,"Ae"),s(be,"restartWorkers");const Re=debounce(async()=>{ne+=1;const e=M();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const t of e)t.proc.send?.({type:"reload"})}await i(),WORKER_NAMES.length>0&&be()},200),ie=s(async(e,t)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){if(!mayDriveThePool(e))return refusePoolCommand(e,t);t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0})),I(e).then(c=>G(c?.watcher)),Re();return}if(e.method==="POST"&&e.url==="/webpack/watching"){if(!mayDriveThePool(e))return refusePoolCommand(e,t);const c=await I(e);G(c),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,relayed:re!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){if(!mayDriveThePool(e))return refusePoolCommand(e,t);let c="";e.on("data",n=>{c+=n}),e.on("end",async()=>{let n=0;try{n=Number(JSON.parse(c||"{}").pods)||0}catch{}n=Math.max(1,Math.min(n,16)),await r(n),t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,pods:n}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const c=new URL(e.url,"http://localhost").searchParams,n=Math.min(Math.max(Number(c.get("tail")??"100")||0,0),te),k=c.get("follow")!=="0",l=c.get("plain")==="1",v=!isOwnerOnlyTransport(e);t.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const q=n===0?"":C.slice(-n).join(""),ae=l?q.replace(Y,""):q;if(t.write(v?redactInspectorUrls(ae):ae),!k){t.end();return}const Q={res:t,plain:l,redact:v};P.add(Q),e.on("close",()=>P.delete(Q));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!isOwnerOnlyTransport(e)){t.writeHead(404,{"content-type":"application/json"}),t.end(JSON.stringify({error:"not found"}));return}const c=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(c)&&c>1024&&(N=c),N||(N=9229);const n=w.filter(l=>l.proc.exitCode===null&&l.proc.signalCode===null);for(const l of n){l.debugSlot<0&&(l.debugSlot=W());try{l.proc.send?.({type:"debug-open",port:inspectorPortFor(N,l.debugSlot)})}catch{}}for(const l of H)try{l.proc.kill("SIGUSR1")}catch{}const k=Date.now()+2e3;for(;Date.now()<k&&n.some(l=>!l.inspectorUrl);)await new Promise(l=>setTimeout(l,50));t.writeHead(200,{"content-type":"application/json"}),t.end(JSON.stringify({ok:!0,basePort:N,fixedPorts:usesFixedInspectorPorts(),pods:n.map(l=>({id:l.id,inspectorUrl:l.inspectorUrl??null})),workers:H.map(l=>({name:l.name,port:l.debugPort||inspectorPortOf(l.inspectorUrl),inspectorUrl:l.inspectorUrl??null}))},null,2)+`
16
- `);return}const d=matchGatewayRoute(e);if(d?.prependPrefix&&!e.url?.startsWith(d.prependPrefix)&&(e.url=d.prependPrefix+(e.url??"/")),d?.target){applyGatewayMiddleware(e),u.web(e,t,{target:d.target,autoRewrite:typeof d.target=="string",changeOrigin:!0},c=>{console.error(`[parent] proxy error to ${d.target}:`,c),t.statusCode=502,t.setHeader("content-type","text/plain; charset=utf-8"),t.end(`Upstream error from ${d.target}.`)});return}if(SERVICE_PREFIX){if(!e.url?.startsWith(SERVICE_PREFIX)){t.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),t.end(`Not found. This service is mounted at ${SERVICE_PREFIX}/`);return}e.url=e.url.slice(SERVICE_PREFIX.length)||"/"}applyGatewayMiddleware(e);const a=U();if(!a){t.statusCode=503,t.setHeader("content-type","text/plain; charset=utf-8");const c=w.map(n=>`#${n.id}: ${n.lastError??"no error recorded"}`).join(`
17
- `);t.end(`No healthy children available. POST /webpack/reload to recover.
12
+ })()`),(0,bundle_parent_1.routeBundleRequires)(requested,resolved);const exports=eval(`require(${JSON.stringify(resolved)})`);try{const mod=eval("require.cache")[resolved];lastImportedModule=mod&&typeof mod=="object"?mod:void 0}catch{}return exports}h(importFresh,"importFresh"),n(importFresh,"importFresh");function debounce(s,u){let f=null;return(...S)=>{f&&clearTimeout(f),f=setTimeout(()=>s(...S),u)}}if(h(debounce,"debounce"),n(debounce,"debounce"),process.env.APP_RUNNER==="1"){(0,stdio_guard_1.guardBrokenStdio)();const s=n(f=>{if(!inspector.url())try{inspector.open(f,"127.0.0.1",!1)}catch(G){console.warn(`[child] inspector.open(${f}) failed (port taken by another devserver?) \u2014 falling back to a random port:`,G.message);try{inspector.open(0,"127.0.0.1",!1)}catch{}}const S=inspector.url();S&&tell(process,{type:"inspector-url",url:S})},"openInspector"),u=process.env.CHILD_DEBUG_PORT;if(u!==void 0&&u!==""){const f=Number(u);Number.isInteger(f)&&(f===0||f>1024)&&s(f)}try{require("@lensmcp/node-instrumentation/register")}catch{}(async()=>{let f=null,S=!1,G,J=!1;const De=(0,hot_swap_registries_1.registryPruningEnabled)(),te=n((t,i)=>{const p=(0,hot_swap_registries_1.describePrune)(t,i);p&&console.log(p)},"logPrune");let F={app:null,handler:n((t,i)=>{i.statusCode=503,i.end("starting")},"handler"),inflight:0};const re=(()=>{const t=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(t)&&t>=0?t:15*6e4})(),R=n(t=>{const i=globalThis.__lensmcpActiveWork;if(typeof i!="function")return{count:0};try{const p=i(t);return{count:Number(p?.count??0),...p?.oldestStartedAt?{oldestStartedAt:p.oldestStartedAt}:{}}}catch{return{count:0}}},"activeWork"),ge=n(t=>{const i=globalThis.__lensmcpStopWorkIntake;if(typeof i=="function")try{return i(t)}catch{return}},"stopWorkIntake"),oe=n(t=>{const i=globalThis.__lensmcpWorkIntakeReport;if(!(t===void 0||typeof i!="function"))try{return i(t)??void 0}catch{return}},"workIntakeReport"),be=(()=>{const t=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(t)&&t>=1?Math.floor(t):1})(),q=new Set,A=(0,swap_recycle_1.swapRecyclePolicy)();let L,j=0;const Ae=(()=>{const t=Number(process.env.LENSMCP_POD_RETIRE_NOTICE_MS);return Number.isFinite(t)&&t>=0?t:3500})();let ne=!1,X=!1;const xe=(0,pod_memory_1.podMemoryPolicy)();let Ne=0;const me=new Set;let y=!1;const v=n(()=>{if(!y)return;let t=0;for(const i of me)i.deref()===void 0?me.delete(i):t+=1;return t},"liveContainers"),_=(0,pod_memory_1.createPodMemoryMonitor)({policy:xe,state:n(()=>({gen:F.gen??0,swaps:j,...L===void 0?{}:{baseline:L},busy:S||J||N.size>0,lastBusyAt:T}),"state"),send:n(t=>{tell(process,t)},"send"),containers:v,counter:(0,pod_memory_1.createLiveObjectCounter)(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH)),onRetained:n(()=>ie(),"onRetained")}),W=n(()=>((0,pod_memory_1.fullGc)()&&_.tracker.noteGc(),process.memoryUsage().heapUsed),"liveHeap"),$=(()=>{if(!(0,error_stack_release_1.errorFrameReleaseEnabled)())return null;try{const t=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH))+path.sep,i=`${path.sep}node_modules${path.sep}`,p=new Set(Object.keys(require.cache));return(0,error_stack_release_1.createErrorFrameReleaser)(require.cache,{skip:n(g=>p.has(g)||g.startsWith(t)&&!g.slice(t.length).includes(i)&&!g.slice(t.length).startsWith(`node_modules${path.sep}`),"skip")})}catch{return null}})();let se=!1;function x(){if(!(!$||se))try{const t=$();t.errors>0&&console.log(`[child] read the stack of ${t.errors} Error object(s) that libraries created while loading and keep for good \u2014 unread, each held the functions that were on its stack, this bundle's included`)}catch(t){console.error("[child] could not release library error frames:",t),se=!0}}h(x,"C"),n(x,"releaseLibraryErrorFrames");const N=new Map;let T=Date.now();const C=n(()=>A.settleMs<=0||!S&&!J&&N.size===0&&Date.now()-T>=A.settleMs,"settled");let V;function he(t){if(!(!A.enabled||ne||X)){if(A.settleMs<=0){z(t);return}V&&clearTimeout(V),V=setTimeout(()=>{V=void 0,C()?z(t):he(t)},A.settleMs),V.unref?.()}}h(he,"_e"),n(he,"recycleCheckWhenSettled");const fe=n(()=>{if(A.stuckCloseMs<=0)return 0;const t=Date.now();let i=0;for(const p of N.values())t-p>=A.stuckCloseMs&&(i+=1);return i},"stuckCloses");function z(t){if(!A.enabled||ne||X||!process.send)return;const i=v8.getHeapStatistics().heap_size_limit,p=n(m=>{const E=_.oldestRetained();return{swaps:j,usedBytes:m,limitBytes:i,stuckCloses:fe(),settled:C(),...L===void 0?{}:{baselineBytes:L},...E?{retained:{generation:E.gen,ageMs:Date.now()-E.closedAt,gcs:E.gcs}}:{}}},"state");if(!(0,swap_recycle_1.recycleReason)(A,p(process.memoryUsage().heapUsed)))return;const g=(0,swap_recycle_1.recycleReason)(A,p(W()));g&&(ne=!0,console.log(`[child] asking for a fresh pod ${t} \u2014 ${g}. This pod keeps serving until its successor is ready. Size the bound with LENSMCP_SWAP_RECYCLE_HEAP_FACTOR / _HEAP_RATIO / _MIN_MB / _RETAINED_MS, or turn it off with LENSMCP_SWAP_RECYCLE=0.`),tell(process,{type:"recycle-request",reason:g}))}h(z,"ee"),n(z,"considerRecycle");let Y;function ie(){if(!A.enabled||A.retainedMs<=0||ne||X)return;const t=_.oldestRetained();if(!t)return;const i=t.closedAt+A.retainedMs-Date.now();if(i<=0){z("with a closed generation still in memory");return}Y||(Y=setTimeout(()=>{Y=void 0,_.measure("retention-check").catch(()=>{})},i),Y.unref?.())}h(ie,"ae"),n(ie,"retainedRecycleCheck");function ae(t,i){const p={};i&&(p.module=i),t&&(p.app=t);try{const g=t?.app;if(g&&typeof g=="object"&&(p.nestApp=g,g.container&&typeof g.container=="object")){p.container=g.container;const m=rootModuleOf(g.container);m&&(p.rootModule=m)}}catch{}return p}h(ae,"ye"),n(ae,"generationTargets");async function ce(t,i){if(t.closed||(t.closed=!0,q.delete(t),i&&console.error(`[child] ${i}`),!t.app))return;N.set(t,Date.now());const p=A.enabled&&A.stuckCloseMs>0?setTimeout(()=>{N.has(t)&&(console.error(`[child] a previous app has been closing for ${Math.round(A.stuckCloseMs/1e3)}s and its close() has not returned \u2014 one of its shutdown hooks is waiting on something that does not end (a queue worker, a connection, a stream). It stays in memory until this pod is replaced.`),z("with a previous app stuck in close()"))},A.stuckCloseMs):void 0;p?.unref?.();const g=t.app;try{await t.app.close()}catch(m){console.error("[child] error closing a previous app:",m)}finally{N.delete(t),T=Date.now(),p&&clearTimeout(p),t.app=null,t.handler=(m,E)=>{E.statusCode=503,E.end("closed")},t.gen!==void 0&&_.closed(t.gen,j,ae(g,t.module)),t.module=void 0}if(t.superseded){try{te((0,hot_swap_registries_1.pruneRegistries)(t.superseded,"serve"),"after closing the previous app")}catch(m){console.error("[child] could not release the previous app's class registrations:",m)}t.superseded=void 0}he("after closing the previous app")}h(ce,"ge"),n(ce,"closeGeneration");async function Q(t){for(;q.size>=be;){const E=q.values().next().value;if(!E)break;await ce(E,`closing a still-draining generation early \u2014 ${q.size} were pending and the cap is ${be} (each holds a whole app: DI container, DB pool, queue workers). Raise LENSMCP_SWAP_MAX_DRAINING if this pod legitimately needs more.`)}q.add(t);const i=Date.now(),p=n(()=>({inflight:t.inflight,work:R(t.workGen).count}),"busy");for(;re>0&&Date.now()-i<re;){if(t.closed)return;const E=p();if(E.inflight===0&&E.work===0)break;await new Promise(H=>{setTimeout(H,250).unref?.()})}if(t.closed)return;const g=p(),m=oe(t.workGen);g.inflight>0||g.work>0?console.error(`[child] swap drain expired after ${Date.now()-i}ms \u2014 closing the previous app with ${g.inflight} in-flight request(s) and ${g.work} background job(s) still running. `+(m?`Previous app still processing ${m.running} queue job(s); ${m.takenAfterStop} were taken after the swap. `:"")+`A queue job killed here keeps its lock until the TTL lapses. Raise LENSMCP_SWAP_DRAIN_MS (currently ${re}ms) if this is legitimate work.`):(Date.now()-i>250||m?.runningAtStop||m?.takenAfterStop)&&console.log(`[child] previous app drained in ${Date.now()-i}ms \u2014 closing it`+(m?` (it finished ${m.finishedAfterStop} queue job(s) after the swap; ${m.takenAfterStop} were taken after the swap)`:"")),await ce(t)}h(Q,"ce"),n(Q,"drainThenClose");async function Z(){if(S)return J=!0,console.log("[child] Reload deferred \u2014 will re-swap after current swap completes"),f;S=!0,T=Date.now(),process.removeAllListeners("uncaughtException"),process.removeAllListeners("unhandledRejection"),process.on("uncaughtException",(b,U)=>{console.error(`[child] uncaughtException (${U}):`,b)}),process.on("unhandledRejection",b=>{const U=b instanceof Error?b.stack||b.message:String(b);U.includes("FronteggContextScope")||U.includes("populateLoggerMetadata")||console.error("[child] unhandledRejection:",b)});let t;if(De)try{t=(0,hot_swap_registries_1.captureRegistries)()}catch{t=void 0}const i=global.__lensmcpBeginWorkGeneration,p=typeof i=="function"?i():void 0;try{try{await importFresh(BUNDLE_PATH)}catch(b){if(!(b instanceof SyntaxError))throw b;console.warn(`[child] bundle was unreadable mid-write (${b.message}) \u2014 retrying the swap once`),await new Promise(U=>setTimeout(U,400)),await importFresh(BUNDLE_PATH)}}catch(b){throw S=!1,T=Date.now(),b}if(typeof global.createChildApp!="function")throw S=!1,T=Date.now(),new Error(`Bundle '${BUNDLE_PATH}' does not export createChildApp() and the zero-touch bridge is not active. Either install @lensmcp/node-instrumentation (a plain NestFactory.create + app.listen main.ts then just works) or set global.createChildApp in main.ts when DEVSERVER_MODE === '1'.`);let g;if(t)try{g=(0,hot_swap_registries_1.supersededSince)(t),te((0,hot_swap_registries_1.pruneRegistries)(g,"build"),"at the swap")}catch(b){console.error("[child] could not release superseded class registrations:",b)}let m;try{m=await global.createChildApp()}catch(b){throw S=!1,T=Date.now(),takeImportedModule(),b}const E=F;g?.size&&(E.app?E.superseded=g:te((0,hot_swap_registries_1.pruneRegistries)(g,"serve"),"at the swap")),f=m;const H=takeImportedModule();F={app:m,handler:m.handler,inflight:0,gen:++Ne,...H?{module:H}:{},...p===void 0?{}:{workGen:p}};const de=ae(m,void 0).container;de&&(me.add(new WeakRef(de)),y=!0);const K=E.app&&p!==void 0?ge(p):void 0;return console.log("[child] Swapped to fresh app (previous draining in background)"+(K?.workers?`; its ${K.workers} queue worker(s) stopped taking new jobs, previous app still processing ${K.runningAtStop} job(s)`:"")),x(),E.app&&(j+=1,Q(E).catch(b=>console.error("[child] background close error:",b)),setTimeout(()=>z("30s after a hot swap"),3e4).unref?.(),_.swapped(E.gen,()=>E.closed===!0&&!N.has(E))),S=!1,T=Date.now(),J?(J=!1,Z()):m}h(Z,"le"),n(Z,"swapNow");const ye=debounce(async()=>{try{console.log("[child] /webpack/reload"),await Z()}catch(t){console.error("[child] reload failed:",t)}},250),le=http.createServer((t,i)=>{if(t.method==="POST"&&t.url==="/webpack/reload"){i.writeHead(200,{"content-type":"application/json"}),i.end(JSON.stringify({ok:!0})),ye();return}if(t.method==="GET"&&t.url==="/__lensmcp/alive"){const g=t.headers["x-internal-token"],m=process.env.LENSMCP_SERVICE_KEY;if(m&&g!==m){i.writeHead(404,{"content-type":"text/plain"}),i.end("not found");return}const E=R();i.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),i.end(JSON.stringify({ok:!0,pid:process.pid,swapping:S,inflight:F.inflight,oldestInflightAt:F.oldestAt,activeWork:E.count,oldestWorkAt:E.oldestStartedAt,keyFingerprint:serviceKeyFingerprint(),...G?{watcher:G}:{},...Number.isFinite(Number(process.env.LENSMCP_POD_GENERATION))&&process.env.LENSMCP_POD_GENERATION?{podGeneration:Number(process.env.LENSMCP_POD_GENERATION)}:{}}));return}const p=F;p.inflight+=1,p.oldestAt===void 0&&(p.oldestAt=Date.now()),i.on("close",()=>{p.inflight-=1,p.inflight===0&&(p.oldestAt=void 0),_.activity()}),p.handler(t,i)});le.keepAliveTimeout=0;const I=process.env.CHILD_SOCK_PATH;process.on("disconnect",()=>{cleanupSock(I),process.exit(0)});try{const t=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.join(__dirname,BUNDLE_PATH);if((0,schema_first_instance_1.schemaPrewarmEnabled)())try{const i=(0,schema_first_instance_1.prewarmZodFor)(t);i?.created&&console.log(`[child] zod${i.version?` ${i.version}`:""}: built ${i.created} throwaway schemas first, so none of the bundle's own is the first of its class`)}catch{}if((0,redis_disconnect_fix_1.redisDisconnectFixEnabled)())try{const i=(0,redis_disconnect_fix_1.finishStrandedDisconnectsFor)(t);i?.patched&&console.log(`[child] ioredis${i.versions.length?` ${i.versions.join(", ")}`:""}: a disconnect() between reconnect attempts now ends the client \u2014 it used to leave a queue worker's close() waiting for good`)}catch{}await Z(),L=(await _.booted()).live,console.log(`[child] Using bundle: ${t}`),claimSock(I),le.listen(I,()=>{console.log(`[child] Listening on ${I}`),tell(process,{type:"ready",socketPath:I})})}catch(t){console.error("[child] Startup error:",t),tell(process,{type:"boot-error",error:String(t?.stack||t)}),process.exitCode=1;return}process.on("message",async t=>{if(!(!t||typeof t!="object")){if(t.type==="debug-open"){const i=Number(t.port);s(Number.isInteger(i)&&i>1024?i:0);return}if(t.type==="watcher"){const i=t.testimony;i&&typeof i=="object"&&(G=i);return}if(t.type==="heap-snapshot"){_e(t);return}if(t.type==="recycle-declined"){ne=!1;return}if(t.type==="retire"){ee();return}if(t.type==="reload"){if(X)return;try{await Z(),tell(process,{type:"reloaded",socketPath:I})}catch(i){console.error("[child] reload error:",i),tell(process,{type:"reload-error",error:String(i?.stack||i)})}}}});let pe=!1;async function _e(t){const i=n(p=>{tell(process,{type:"heap-snapshot-result",id:t.id,...p})},"reply");if(pe)return i({ok:!1,refused:"a snapshot of this pod is already being written"});pe=!0;try{const p=W(),g=_.tracker.views().map(de=>de.gen),m=(0,heap_snapshot_1.heapSnapshotPolicy)();t.maxHeapBytes&&(m.maxHeapBytes=t.maxHeapBytes);const E=(0,heap_snapshot_1.heapSnapshotRefusal)(m,{live:p,...t.available?{available:t.available}:{}});if(E)return i({ok:!1,refused:E,live:p});console.log(`[child] writing a heap snapshot (${Math.round(p/1048576)} MB live) \u2014 the pod pauses while V8 serialises it`);const H=await(0,heap_snapshot_1.writeHeapSnapshot)(t.file);i({ok:!0,file:t.file,bytes:H.bytes,ms:H.ms,live:p,gen:F.gen??0,closedAlive:g})}catch(p){i({ok:!1,error:p?.message??String(p)})}finally{pe=!1}}h(_e,"De"),n(_e,"takeHeapSnapshot");async function ee(){if(X)return;X=!0,_.stop(),console.log("[child] retiring \u2014 a fresh pod has taken over; finishing in-flight work, then exiting");const t=ge(Number.POSITIVE_INFINITY);t?.workers&&console.log(`[child] its ${t.workers} queue worker(s) stopped taking new jobs, still processing ${t.runningAtStop} job(s)`);try{fs.writeFileSync(sockRetiringPath(I),String(process.pid),{flag:"w",mode:384})}catch{}await new Promise(p=>{setTimeout(p,Ae).unref?.()}),cleanupSock(I),le.close();const i=Date.now();for(;Date.now()-i<re&&(le.closeIdleConnections?.(),!(F.inflight===0&&R().count===0));)await new Promise(p=>{setTimeout(p,250).unref?.()});setTimeout(()=>process.exit(0),3e4).unref?.();try{await f?.close()}catch(p){console.error("[child] close error while retiring:",p)}process.exit(0)}h(ee,"re"),n(ee,"retire"),process.on("beforeExit",()=>{try{inspector?.close?.()}catch{}});const we=n(async t=>{console.log(`[child ${t}] shutting down\u2026`);const i=f?.close().catch(g=>console.error("[child] close error:",g)),p=new Promise(g=>le.close(()=>g()));cleanupSock(I),setTimeout(()=>process.exit(0),700).unref(),await Promise.all([i,p]),process.exit(0)},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(t=>{process.on(t,()=>{inspector?.close?.(),we(t)})})})()}else{let s=h(function(y){L.push(y),L.length>A&&L.splice(0,L.length-A),be?.write((0,inspector_secret_1.redactInspectorUrls)(y.replace(q,"")));for(const v of j)try{const _=v.plain?y.replace(q,""):y;v.res.write(v.redact?(0,inspector_secret_1.redactInspectorUrls)(_):_)}catch{j.delete(v)}},"z");n(s,"emitLogLine");const u=12,f=process.env.LENSMCP_PROJECT||SERVICE_NAME||"service",S=(0,pod_memory_events_1.createPodMemoryPublisher)(process.env.LENSMCP_EVENT_FILE),G=n(y=>({project:f,service:SERVICE_NAME||f,pod:y.id,pid:y.proc.pid??0,podGeneration:POD_GENERATION}),"podIdentity"),J=["\x1B[36m","\x1B[33m","\x1B[35m","\x1B[32m","\x1B[34m","\x1B[31m"],De="\x1B[0m",te=Math.max(1,Number(process.env.CHILD_COUNT||1)),F=LENSMCP_WS_KEY||path.basename(process.cwd()),re=n(y=>(0,process_name_1.namedExecPath)((0,process_name_1.processName)(F,SERVICE_NAME||"service",y)),"execPathFor");let R=(()=>{const y=Number(process.env.LENSMCP_DEBUG_PORT||"");return Number.isInteger(y)&&y>1024?y:0})();const ge=20;R&&(console.warn("[parent] DEBUGGER ENABLED \u2014 pods and workers open a Node inspector. Node's inspector has NO authentication: the ws:// URL's UUID is the whole credential, and any account on this host that obtains it can run code as you. Do not enable this on a shared machine. (issues/closed/codex-security-06)"),console.warn(usesFixedInspectorPorts()?`[parent] ${exports.DEBUG_FIXED_PORTS_ENV} is set \u2014 inspectors bind PREDICTABLE ports (${R}+slot, workers ${R}+${ge}+i). Any account on this host can connect to 127.0.0.1:${R} and read the session UUID from /json/list. Unset it to use ephemeral ports.`:`[parent] Inspectors bind an EPHEMERAL loopback port; attach via the printed "Debugger listening" URL, or \`lensmcp debug <service>\` (control socket). Set ${exports.DEBUG_FIXED_PORTS_ENV}=1 for fixed ports if your IDE needs a pre-configured attach port \u2014 any account on this host can then reach them.`));const oe=process.env.LENSMCP_LOG_FILE||"",be=oe?(()=>{try{fs.mkdirSync(path.dirname(oe),{recursive:!0});const y=fs.createWriteStream(oe,{flags:"a",mode:384});try{fs.chmodSync(oe,384)}catch{}return y}catch(y){return console.warn(`[parent] cannot open log file ${oe}:`,y.message),null}})():null,q=/\x1b\[[0-9;]*m/g,A=2e3,L=[],j=new Set,Ae=gatewayManaged();for(const y of["log","info","warn","error"]){const v=console[y].bind(console),_=y==="log"||y==="info"?process.stdout:process.stderr;console[y]=(...W)=>{v(...W.map($=>typeof $=="string"?(0,inspector_secret_1.forStream)(_,Ae,$):$)),s(W.map($=>typeof $=="string"?$:util.inspect($)).join(" ")+`
13
+ `)}}const ne=n(y=>J[(y-1)%J.length],"colorFor"),X=n(y=>{if(te===1)return"";const v=`[child#${y}] `;return`${ne(y)}${v}${De}`},"tagFor"),xe=n(y=>y.startsWith("{")&&y.endsWith("}"),"isJsonLog"),Ne=n((y,v,_)=>{const W=prettyFactory({sync:!0,colorize:!0,crlf:!0,messageKey:"message",errorLikeObjectKeys:["err","error"],errorProps:"type,message,stack",ignore:["logContext","context","hostname","req","res","err.driverError","module","cloudEnvironment","frontegg-application-id","frontegg-tenant-id","frontegg-trace-id","frontegg-vendor-id","host","service","version","err","error"].join(","),messageFormat:"{if module}[{module}] {end}{if context}[{context}] {end}{if logContext}[{logContext}] {end}{message}",customPrettifiers:{stack:n(x=>`
14
+ `+String(x),"stack")}}),$=n((x,N)=>{const T=x==="stdout"?process.stdout:process.stderr;try{_?.(N)}catch{}const C=xe(N)?W(N):`${N}
15
+ `;T.write((0,inspector_secret_1.forStream)(T,Ae,`${v}${C}`)),s(`${v}${C}`)},"write"),se=n((x,N)=>{if(!x)return;const T=readline.createInterface({input:x});T.on("line",C=>$(N,C)),T.on("close",()=>{})},"attach");se(y.stdout,"stdout"),se(y.stderr,"stderr")},"wireProcLogging"),me=n(y=>Ne(y.proc,X(y.id),v=>y.tail.push(v)),"wireChildLogging");(async()=>{ensureOwnerOnlyDir(SOCK_DIR);const y=httpProxy.createProxyServer({});let v=Number(process.env.PORT)||0;v||(v=9090);const _=[],W=new Set;function $(){let e=0;for(;W.has(e);)e++;return W.add(e),e}h($,"I"),n($,"allocDebugSlot");let se=1,x=0,N=!1,T=te,C=[],V=0;const he=(()=>{const e=Number(process.env.LENSMCP_POD_BOOT_GRACE_MS);return Number.isFinite(e)&&e>=0?e:12e4})(),fe=n(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"childExited"),z=n(e=>e.booting&&!fe(e)&&Date.now()-e.spawnedAt<he,"stillBooting");function Y(){return _.filter(e=>e.healthy&&typeof e.socketPath=="string")}h(Y,"J"),n(Y,"healthyChildren");let ie;function ae(e){if(!e||typeof e!="object")return;const o=e;if(o.schemaVersion===1){ie=o;for(const l of _)tell(l.proc,{type:"watcher",testimony:o})}}h(ae,"ye"),n(ae,"recordWatcherTestimony");function ce(e,o=256*1024){return new Promise(l=>{let d="",a=!1;e.on("data",r=>{a||(d+=r,d.length>o&&(a=!0))}),e.on("end",()=>{if(a||!d)return l(void 0);try{l(JSON.parse(d))}catch{l(void 0)}}),e.on("error",()=>l(void 0))})}h(ce,"ge"),n(ce,"readJsonBody");function Q(){const e=Y();if(e.length===0)return null;const o=x%e.length;return x=(x+1)%e.length,e[o]}h(Q,"ce"),n(Q,"pickChild");function Z(e,o){const l=`Child#${e.id}`,d=SERVICE_NAME||f,a=Date.now();if(o.type==="pod-memory"){const r=o.report;if(!r||typeof r.live!="number")return;e.memory.latest=r,e.memory.history.push(r),e.memory.history.length>12&&e.memory.history.splice(0,e.memory.history.length-12),console.log(`[parent] ${(0,pod_memory_1.describePodMemory)(l,r)}`),S.publish((0,pod_memory_events_1.buildPodMemoryEvent)(G(e),r))}else if(o.type==="pod-memory-retained"){const r=o.retained;if(!r||typeof r.gen!="number")return;e.memory.retained.set(r.gen,{...r,at:a}),console.warn(`[parent] ${(0,pod_memory_1.describeRetained)(l,d,r)}`),S.publish((0,pod_memory_events_1.buildPodRetainedEvent)(G(e),r,a))}else{const r=o.released;if(!r||typeof r.gen!="number")return;e.memory.retained.delete(r.gen),console.log(`[parent] ${(0,pod_memory_1.describeReleased)(l,d,r)}`),S.publish((0,pod_memory_events_1.buildPodReleasedEvent)(G(e),r,a))}}h(Z,"le"),n(Z,"recordPodMemory");const ye=new Map;let le=0;async function I(){const e=se++,o=childSockPath(e);cleanupSock(o);const l=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&l.unshift("--require","@swc-node/register"),l.push(...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const d=R?$():-1,a=(0,node_child_process_1.fork)(__filename,{env:{...process.env,APP_RUNNER:"1",CHILD_SOCK_PATH:o,...d>=0&&{CHILD_DEBUG_PORT:String(inspectorPortFor(R,d))}},stdio:["inherit","pipe","pipe","ipc"],execArgv:l,execPath:re("pod")}),r={id:e,proc:a,healthy:!1,lastError:null,debugSlot:d,tail:new exit_reason_1.TailBuffer,booting:!0,spawnedAt:Date.now(),bootReloadSeq:V,memory:{history:[],retained:new Map}};return a.on("error",w=>console.error(`[parent] Child#${e}: ${w.message}`)),a.on("message",w=>{if(!w||typeof w!="object")return;const c=w;c.type==="ready"?(r.socketPath=String(c.socketPath),r.healthy=!0,r.booting=!1,console.log(`[parent] Child#${r.id} ready on socket ${r.socketPath}`),ie&&tell(a,{type:"watcher",testimony:ie}),V!==r.bootReloadSeq&&tell(a,{type:"reload"})):c.type==="recycle-request"?g(r,String(c.reason||"it has outgrown its bound")):c.type==="inspector-url"?(r.inspectorUrl=String(c.url),console.log(`[parent] Child#${r.id} Debugger listening on ${c.url}`)):c.type==="reloaded"?(r.socketPath=String(c.socketPath),r.healthy=!0,console.log(`[parent] Child#${r.id} hot-swapped on socket ${r.socketPath}`)):c.type==="reload-error"?(r.lastError=String(c.error||"unknown reload error"),console.error(`[parent] Child#${r.id} reload error: ${r.lastError}`),r.healthy=!1):c.type==="boot-error"?(r.lastError=String(c.error||"unknown boot error"),console.error(`[parent] Child#${r.id} boot error: ${r.lastError}`),r.healthy=!1,r.booting=!1):c.type==="pod-memory"||c.type==="pod-memory-retained"||c.type==="pod-memory-released"?Z(r,c):c.type==="heap-snapshot-result"&&ye.get(Number(c.id))?.(c)}),a.on("exit",(w,c)=>{r.booting=!1;for(const M of r.memory.retained.values()){const O={gen:M.gen,closedAt:M.closedAt,ageMs:Date.now()-M.closedAt,reason:"pod-exited"};console.log(`[parent] ${(0,pod_memory_1.describeReleased)(`Child#${r.id}`,SERVICE_NAME||f,O)}`),S.publish((0,pod_memory_events_1.buildPodReleasedEvent)(G(r),O,Date.now()))}r.memory.retained.clear(),r.memory.latest&&S.publish((0,pod_memory_events_1.buildPodExitedEvent)(G(r),{code:w,signal:c},Date.now())),r.debugSlot>=0&&(W.delete(r.debugSlot),r.debugSlot=-1);const P=c==="SIGINT"||c==="SIGTERM"||w===0;if(P?console.log(`[parent] Child#${r.id} exited cleanly (code=${w}, signal=${c??"none"})`):(console.error((0,exit_reason_1.crashReport)(`[parent] Child#${r.id} crashed (code=${w}, signal=${c??"none"}, pid=${r.proc.pid}, ${r.healthy?"was serving":"was not ready"})`,w,c,r.tail.snapshot())),r.lastError||(r.lastError=`Child exited abnormally (code=${w}, signal=${c??"none"})`)),r.healthy=!1,cleanupSock(r.socketPath,r.proc.pid),r.socketPath=void 0,!P&&!N){const M=_.indexOf(r);M>=0&&_.splice(M,1);const O=Date.now();if(C=C.filter(B=>O-B<3e4),C.length>=5){console.error(`[parent] crash-loop detected (${C.length} respawns/30s) \u2014 pausing auto-respawn; POST /webpack/reload to retry`);return}C.push(O),setTimeout(()=>{const B=_.filter(Pe=>Pe.proc.killed===!1).length;!N&&B<T&&(console.log(`[parent] respawning a crashed pod (${B}/${T} live)`),I().catch(()=>{}))},1500).unref?.()}}),_.push(r),me(r),r}h(I,"M"),n(I,"spawnChild");async function pe(e){T=Math.max(T,e);const o=_.filter(d=>d.proc.killed===!1),l=e-o.length;for(let d=0;d<l;d++)await I()}h(pe,"pe"),n(pe,"ensurePoolSize");async function _e(){const e=_.filter(o=>(!o.healthy||typeof o.socketPath!="string")&&!z(o));await Promise.all(e.map(async o=>{try{o.proc.kill("SIGTERM")}catch{}cleanupSock(o.socketPath,o.proc.pid),o.socketPath=void 0;const l=_.indexOf(o);l>=0&&_.splice(l,1),await I()}))}h(_e,"De"),n(_e,"respawnUnhealthy");const ee=new Set;let we=!1,t=0;const i=3e4,p=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return(Number.isFinite(e)&&e>=0?e:15*6e4)+6e4})();async function g(e,o){const l=n(a=>{console.log(`[parent] Child#${e.id} asked for a fresh pod (${o}) \u2014 not now: ${a}`),tell(e.proc,{type:"recycle-declined"})},"decline");if(N||!_.includes(e))return;if(we)return l("another pod is already being replaced");const d=Date.now()-t;if(d<i)return l(`a pod was replaced ${Math.round(d/1e3)}s ago`);we=!0,console.log(`[parent] Child#${e.id} asked for a fresh pod \u2014 ${o}. Starting one; Child#${e.id} keeps serving until it is ready.`);try{const a=await I(),r=Date.now()+he;for(;!a.healthy&&!a.lastError&&!fe(a)&&Date.now()<r;)await new Promise(P=>{setTimeout(P,100).unref?.()});if(N)return;if(!a.healthy){const P=_.indexOf(a);P>=0&&_.splice(P,1);try{a.proc.kill("SIGTERM")}catch{}return l(`its successor Child#${a.id} did not come up (${a.lastError??"no answer in time"})`)}const w=_.indexOf(e);w>=0&&_.splice(w,1),e.healthy=!1,ee.add(e);const c=setTimeout(()=>{if(!fe(e)){console.error(`[parent] Child#${e.id} was still running ${p}ms after it was retired \u2014 killing it`);try{e.proc.kill("SIGKILL")}catch{}}},p);if(c.unref?.(),e.proc.once("exit",()=>{clearTimeout(c),ee.delete(e)}),!tell(e.proc,{type:"retire"}))try{e.proc.kill("SIGTERM")}catch{}t=Date.now(),console.log(`[parent] Child#${a.id} took over from Child#${e.id} (pid ${e.proc.pid} \u2192 ${a.proc.pid})`)}catch(a){l(`could not start a successor: ${a?.message??String(a)}`)}finally{we=!1}}h(g,"_"),n(g,"recyclePod");const m=[];function E(e){const o=path.dirname(path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH)),l=path.join(o,`${e}.js`);if(!fs.existsSync(l))return console.error(`[parent] Worker "${e}" bundle not found: ${l}`),null;const d=(0,dev_devtool_1.sourceMapExecArgv)();__filename.endsWith(".ts")&&d.unshift("--require","@swc-node/register");try{d.push("--require",require.resolve("@lensmcp/node-instrumentation/register"))}catch{}const a=inspectorPortFor(R||9229,ge+Math.max(0,WORKER_NAMES.indexOf(e)));d.push(R?`--inspect=127.0.0.1:${a}`:`--inspect-port=127.0.0.1:${a}`,...(0,inspector_secret_1.inspectorPublishExecArgv)(usesFixedInspectorPorts()));const r=(0,node_child_process_1.fork)(l,{env:process.env,stdio:["inherit","pipe","pipe","ipc"],execArgv:d,execPath:re(`w-${e}`)}),w={name:e,proc:r,debugPort:a};return m.push(w),r.on("error",c=>console.error(`[parent] Worker "${e}": ${c.message}`)),Ne(r,`\x1B[35m[worker:${e}] \x1B[0m`,c=>{const P=/Debugger listening on (wss?:\/\/\S+)/.exec(c);P&&(w.inspectorUrl=P[1])}),r.on("exit",(c,P)=>{P==="SIGINT"||P==="SIGTERM"||c===0||console.error(`[parent] Worker "${e}" crashed (code=${c}, signal=${P??"none"})`);const M=m.indexOf(w);M>=0&&m.splice(M,1)}),console.log(`[parent] Worker "${e}" started (pid=${r.pid})`),w}h(E,"E"),n(E,"spawnWorkerProcess");const H=(()=>{const e=Number(process.env.LENSMCP_SWAP_DRAIN_MS);return Number.isFinite(e)&&e>=0?e:15*6e4})(),de=(()=>{const e=Number(process.env.LENSMCP_SWAP_MAX_DRAINING);return Number.isFinite(e)&&e>=1?Math.floor(e):1})(),K=new Set,b=n(e=>e.proc.exitCode!==null||e.proc.signalCode!==null,"workerExited");function U(e,o){if(K.delete(e),!b(e)){console.error(`[parent] Worker "${e.name}" (pid=${e.proc.pid}) ${o} \u2014 killing it. A worker that returns from its SIGTERM handler but leaves a handle open (a Redis client handed to BullMQ is not closed by worker.close()) never exits on its own: close what it opened, or call process.exit().`);try{e.proc.kill("SIGKILL")}catch{}}}h(U,"x"),n(U,"killDrainingWorker");function Ge(e){try{e.proc.kill("SIGTERM")}catch{}if(b(e))return;const o=[...K].filter(d=>d.name===e.name);for(;o.length>=de;)U(o.shift(),`was still running when a later reload replaced its successor (cap ${de})`);K.add(e);const l=setTimeout(()=>U(e,`was still running ${H}ms after SIGTERM`),H);l.unref?.(),e.proc.once("exit",()=>{clearTimeout(l),K.delete(e)})}h(Ge,"Ue"),n(Ge,"retireWorker");function Le(){for(const e of[...m])Ge(e);m.length=0;for(const e of WORKER_NAMES)E(e)}h(Le,"He"),n(Le,"restartWorkers");const Be=debounce(async()=>{V+=1;const e=Y();if(e.length>0){console.log(`[parent] Forwarding reload to ${e.length} child(ren)`);for(const o of e)tell(o.proc,{type:"reload"})}await _e(),WORKER_NAMES.length>0&&Le()},200),Ee=n(async(e,o)=>{if(e.method==="POST"&&e.url==="/webpack/reload"){if(!mayDriveThePool(e))return refusePoolCommand(e,o);o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0})),ce(e).then(a=>ae(a?.watcher)),Be();return}if(e.method==="POST"&&e.url==="/webpack/watching"){if(!mayDriveThePool(e))return refusePoolCommand(e,o);const a=await ce(e);ae(a),o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,relayed:ie!==void 0}));return}if(e.method==="POST"&&e.url==="/webpack/scale"){if(!mayDriveThePool(e))return refusePoolCommand(e,o);let a="";e.on("data",r=>{a+=r}),e.on("end",async()=>{let r=0;try{r=Number(JSON.parse(a||"{}").pods)||0}catch{}r=Math.max(1,Math.min(r,16)),await pe(r),o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,pods:r}))});return}if(e.method==="GET"&&e.url?.startsWith("/webpack/logs")){const a=new URL(e.url,"http://localhost").searchParams,r=Math.min(Math.max(Number(a.get("tail")??"100")||0,0),A),w=a.get("follow")!=="0",c=a.get("plain")==="1",P=!isOwnerOnlyTransport(e);o.writeHead(200,{"content-type":"text/plain; charset=utf-8","cache-control":"no-cache"});const M=r===0?"":L.slice(-r).join(""),O=c?M.replace(q,""):M;if(o.write(P?(0,inspector_secret_1.redactInspectorUrls)(O):O),!w){o.end();return}const B={res:o,plain:c,redact:P};j.add(B),e.on("close",()=>j.delete(B));return}if(e.method==="POST"&&e.url?.startsWith("/webpack/debug")){if(!isOwnerOnlyTransport(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=Number(new URL(e.url,"http://localhost").searchParams.get("port")||"");Number.isInteger(a)&&a>1024&&(R=a),R||(R=9229);const r=_.filter(c=>c.proc.exitCode===null&&c.proc.signalCode===null);for(const c of r)c.debugSlot<0&&(c.debugSlot=$()),tell(c.proc,{type:"debug-open",port:inspectorPortFor(R,c.debugSlot)});for(const c of m)try{c.proc.kill("SIGUSR1")}catch{}const w=Date.now()+2e3;for(;Date.now()<w&&r.some(c=>!c.inspectorUrl);)await new Promise(c=>setTimeout(c,50));o.writeHead(200,{"content-type":"application/json"}),o.end(JSON.stringify({ok:!0,basePort:R,fixedPorts:usesFixedInspectorPorts(),pods:r.map(c=>({id:c.id,inspectorUrl:c.inspectorUrl??null})),workers:m.map(c=>({name:c.name,port:c.debugPort||inspectorPortOf(c.inspectorUrl),inspectorUrl:c.inspectorUrl??null}))},null,2)+`
16
+ `);return}if(e.method==="GET"&&e.url==="/webpack/memory"){if(!isOwnerOnlyTransport(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),r=[..._,...ee].map(w=>({id:w.id,pid:w.proc.pid??0,podGeneration:POD_GENERATION,healthy:w.healthy,retiring:ee.has(w),latest:w.memory.latest??null,history:w.memory.history,retained:[...w.memory.retained.values()]}));o.writeHead(200,{"content-type":"application/json","cache-control":"no-store"}),o.end(JSON.stringify({ok:!0,service:SERVICE_NAME,project:f,bundle:{path:a},pods:r},null,2)+`
17
+ `);return}if(e.method==="POST"&&e.url?.startsWith("/webpack/heap")){if(!isOwnerOnlyTransport(e)){o.writeHead(404,{"content-type":"application/json"}),o.end(JSON.stringify({error:"not found"}));return}const a=n((D,ke)=>{o.writeHead(D,{"content-type":"application/json"}),o.end(JSON.stringify(ke,null,2)+`
18
+ `)},"reply"),r=new URL(e.url,"http://localhost").searchParams,w=Number(r.get("pod")||""),c=r.has("maxMb")?Number(r.get("maxMb")):void 0;if(c!==void 0&&!(Number.isFinite(c)&&c>0)){a(400,{ok:!1,error:"maxMb takes a positive number of MB"});return}const P=Number.isInteger(w)&&w>0?_.find(D=>D.id===w):Y()[0];if(!P||fe(P)){a(404,{ok:!1,error:w?`no pod #${w} \u2014 running: ${_.map(D=>`#${D.id}`).join(", ")||"none"}`:"no pod is serving"});return}if(ye.size>0){a(409,{ok:!1,refused:"a heap snapshot is already being written for this service"});return}const M=(0,heap_snapshot_1.heapSnapshotPolicy)(),O=(0,heap_snapshot_1.heapSnapshotDir)();let B;try{(0,heap_snapshot_1.ensureHeapDir)(O);const D=(0,heap_snapshot_1.pruneHeapSnapshots)(O,M);D.length&&console.log(`[parent] removed ${D.length} older heap snapshot(s) from ${O}`),B=path.join(O,(0,heap_snapshot_1.heapSnapshotFileName)(SERVICE_NAME||f,P.id,P.proc.pid??0,P.memory.latest?.gen??0,Date.now()))}catch(D){a(500,{ok:!1,error:`cannot prepare ${O}: ${D.message}`});return}const Pe=++le,Oe=(0,heap_snapshot_1.availableSystemMemory)();let ve;const k=await new Promise(D=>{const ke=setTimeout(()=>D(void 0),6e5);ke.unref?.(),ye.set(Pe,Ye=>{clearTimeout(ke),D(Ye)}),ve=n(()=>{clearTimeout(ke),D(void 0)},"onExit"),P.proc.once("exit",ve);const je={type:"heap-snapshot",id:Pe,file:B,...Oe?{available:Oe}:{},...c!==void 0?{maxHeapBytes:Math.floor(c*1048576)}:{}};tell(P.proc,je)||ve()}).finally(()=>{ye.delete(Pe),ve&&P.proc.off("exit",ve)});if(!k){a(504,{ok:!1,pod:P.id,error:"the pod did not finish the snapshot (it exited, or took longer than 10 minutes)"});return}k.ok?console.log(`[parent] Child#${P.id} wrote a ${Math.round((k.bytes??0)/1048576)} MB heap snapshot in ${k.ms} ms \u2192 ${k.file}`):console.warn(`[parent] Child#${P.id} heap snapshot not taken: ${k.refused??k.error}`),a(k.ok?200:k.refused?409:500,{ok:k.ok,pod:P.id,pid:P.proc.pid??0,service:SERVICE_NAME,bundle:path.isAbsolute(BUNDLE_PATH)?BUNDLE_PATH:path.resolve(BUNDLE_PATH),...Oe?{available:Oe}:{},...k.file?{file:k.file,bytes:k.bytes,ms:k.ms}:{},...k.live!==void 0?{live:k.live}:{},...k.gen!==void 0?{gen:k.gen,closedAlive:k.closedAlive??[]}:{},...k.refused?{refused:k.refused}:{},...k.error?{error:k.error}:{}});return}const l=matchGatewayRoute(e);if(l?.prependPrefix&&!e.url?.startsWith(l.prependPrefix)&&(e.url=l.prependPrefix+(e.url??"/")),l?.target){applyGatewayMiddleware(e),y.web(e,o,{target:l.target,autoRewrite:typeof l.target=="string",changeOrigin:!0},a=>{console.error(`[parent] proxy error to ${l.target}:`,a),o.statusCode=502,o.setHeader("content-type","text/plain; charset=utf-8"),o.end(`Upstream error from ${l.target}.`)});return}if(SERVICE_PREFIX){if(!e.url?.startsWith(SERVICE_PREFIX)){o.writeHead(404,{"content-type":"text/plain; charset=utf-8"}),o.end(`Not found. This service is mounted at ${SERVICE_PREFIX}/`);return}e.url=e.url.slice(SERVICE_PREFIX.length)||"/"}applyGatewayMiddleware(e);const d=Q();if(!d){o.statusCode=503,o.setHeader("content-type","text/plain; charset=utf-8");const a=_.map(r=>`#${r.id}: ${r.lastError??"no error recorded"}`).join(`
19
+ `);o.end(`No healthy children available. POST /webpack/reload to recover.
18
20
 
19
21
  Last errors:
20
- ${c}`);return}u.web(e,t,{target:{socketPath:a.socketPath},headers:{"x-proxy-child-id":String(a.id)}},c=>{console.error(`[parent] proxy error to Child#${a.id}:`,c),a.healthy=!1,a.lastError=String(c?.stack||c),t.statusCode=502,t.setHeader("content-type","text/plain; charset=utf-8"),t.end(`Upstream error from Child#${a.id}. Try /webpack/reload.
21
- ${a.lastError}`)})},"requestHandler");let z,ce,we;const ue=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),t=GATEWAY_ROUTES.map(a=>a.host).filter(a=>!!a),d=path.join(process.cwd(),"node_modules",".cache","davnx-webpack");ce=e.getCertificateSync(d,SERVICE_NAME||"lensmcp.dev",t),we=e.caCertPath(),z=require("node:https").createServer({key:ce,cert:ce},ie)}else z=http.createServer(ie);const ve=s((e,t,d)=>{const a=matchGatewayRoute(e);if(a?.prependPrefix&&!e.url?.startsWith(a.prependPrefix)&&(e.url=a.prependPrefix+(e.url??"/")),a?.target){u.ws(e,t,d,{target:a.target,changeOrigin:!0},()=>t.destroy());return}const c=U();if(!c){t.destroy();return}u.ws(e,t,d,{target:{socketPath:c.socketPath}},()=>t.destroy())},"upgradeHandler");z.on("upgrade",ve);const Te=2e3;let Ne=!1;z.on("error",e=>{if(e.code==="EADDRINUSE"){Ne||(Ne=!0,console.warn(`[parent] public port ${m} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${Te/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!T&&!z.listening&&z.listen(m)},Te).unref?.();return}console.error("[parent] public server error:",e)}),await r(_);for(const e of WORKER_NAMES)ye(e);z.listen(m,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${m}`),SERVICE_PREFIX&&console.log(`[parent] Service prefix: "${SERVICE_PREFIX}" (enforced \u2014 requests without it will get 404)`),gatewayMiddleware&&console.log(`[parent] Gateway middleware: ACTIVE (${GATEWAY_MIDDLEWARE_PATH})`),GATEWAY_ROUTES.length>0&&console.log(`[parent] Gateway routes: ${GATEWAY_ROUTES.map(t=>`${t.host??"*"}${t.prefix??"/"}\u2192${t.target??"children"}`).join(" ")}`),we&&(console.log("[parent] HTTPS dev CA (trust ONCE, then rotations/new hostnames stay green):"),console.log(`[parent] sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${we}`)),console.log(`POST ${e}://localhost:${m}/webpack/reload to trigger rolling swap/respawn`)});const he=Number(process.env.INTERNAL_PORT||0);if(he>0){const e=s((d,a)=>{const c=U();if(!c){a.statusCode=503,a.setHeader("content-type","text/plain; charset=utf-8"),a.end("No healthy children available.");return}u.web(d,a,{target:{socketPath:c.socketPath}},n=>{c.healthy=!1,c.lastError=String(n?.stack||n),a.statusCode=502,a.end(`Upstream error from Child#${c.id}.`)})},"internalHandler"),t=http.createServer(e);t.on("upgrade",(d,a,c)=>{const n=U();if(!n){a.destroy();return}u.ws(d,a,c,{target:{socketPath:n.socketPath}},()=>a.destroy())}),t.on("error",d=>{console.warn(`[parent] internal port ${he} unavailable (${d.code}).`)}),t.listen(he,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${he}`)}),ue.push(t)}const Ie=(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of Ie){if(!Number.isInteger(e)||e===Number(m))continue;const t=GATEWAY_HTTPS?require("node:https").createServer({key:ce,cert:ce},ie):http.createServer(ie);t.on("upgrade",ve),t.on("error",d=>{console.warn(`[parent] extra port ${e} unavailable (${d.code}) \u2014 main port still up.`)}),t.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),ue.push(t)}const _e=path.join(SOCK_DIR,"parent.sock");claimSock(_e);const me=http.createServer(ie);me.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),me.listen(_e),ue.push(me);const Ae=s(async e=>{T=!0,console.log(`[${e}] [parent] shutting down\u2026`),cleanupSock(_e);for(const t of ue)try{t.close()}catch{}for(const t of H)try{t.proc.kill("SIGTERM")}catch{}for(const t of[...se])pe(t,"was still draining when its devserver shut down");for(const t of f)try{t.proc.kill("SIGTERM")}catch{}for(const t of w){try{t.proc.kill("SIGTERM")}catch{}t.socketPath&&cleanupSock(t.socketPath,t.proc.pid)}try{fs.rmdirSync(SOCK_DIR)}catch{}setTimeout(()=>process.exit(0),700).unref()},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(e=>process.on(e,()=>{Ae(e)}));const fe=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(fe)&&fe>0){const e=setInterval(()=>{try{process.kill(fe,0)}catch(t){if(t.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${fe}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),Ae("gateway-gone")}},5e3);e.unref()}})()}
22
+ ${a}`);return}y.web(e,o,{target:{socketPath:d.socketPath},headers:{"x-proxy-child-id":String(d.id)}},a=>{console.error(`[parent] proxy error to Child#${d.id}:`,a),d.healthy=!1,d.lastError=String(a?.stack||a),o.statusCode=502,o.setHeader("content-type","text/plain; charset=utf-8"),o.end(`Upstream error from Child#${d.id}. Try /webpack/reload.
23
+ ${d.lastError}`)})},"requestHandler");let ue,Se,Ce;const Te=[];if(GATEWAY_HTTPS){const e=require("./basic-ssl"),o=GATEWAY_ROUTES.map(d=>d.host).filter(d=>!!d),l=path.join(process.cwd(),"node_modules",".cache","davnx-webpack");Se=e.getCertificateSync(l,SERVICE_NAME||"lensmcp.dev",o),Ce=e.caCertPath(),ue=require("node:https").createServer({key:Se,cert:Se},Ee)}else ue=http.createServer(Ee);const We=n((e,o,l)=>{const d=matchGatewayRoute(e);if(d?.prependPrefix&&!e.url?.startsWith(d.prependPrefix)&&(e.url=d.prependPrefix+(e.url??"/")),d?.target){y.ws(e,o,l,{target:d.target,changeOrigin:!0},()=>o.destroy());return}const a=Q();if(!a){o.destroy();return}y.ws(e,o,l,{target:{socketPath:a.socketPath}},()=>o.destroy())},"upgradeHandler");ue.on("upgrade",We);const Ue=2e3;let Fe=!1;ue.on("error",e=>{if(e.code==="EADDRINUSE"){Fe||(Fe=!0,console.warn(`[parent] public port ${v} already in use \u2014 continuing on the unix-socket pool only (gateway routing is unaffected) and retrying the bind every ${Ue/1e3}s. Give this service a distinct cluster.port if this is not a rolling handoff.`)),setTimeout(()=>{!N&&!ue.listening&&ue.listen(v)},Ue).unref?.();return}console.error("[parent] public server error:",e)}),await pe(te);for(const e of WORKER_NAMES)E(e);ue.listen(v,()=>{const e=GATEWAY_HTTPS?"https":"http";console.log(`[parent] Listening on ${e}://localhost:${v}`),SERVICE_PREFIX&&console.log(`[parent] Service prefix: "${SERVICE_PREFIX}" (enforced \u2014 requests without it will get 404)`),gatewayMiddleware&&console.log(`[parent] Gateway middleware: ACTIVE (${GATEWAY_MIDDLEWARE_PATH})`),GATEWAY_ROUTES.length>0&&console.log(`[parent] Gateway routes: ${GATEWAY_ROUTES.map(o=>`${o.host??"*"}${o.prefix??"/"}\u2192${o.target??"children"}`).join(" ")}`),Ce&&(console.log("[parent] HTTPS dev CA (trust ONCE, then rotations/new hostnames stay green):"),console.log(`[parent] sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ${Ce}`)),console.log(`POST ${e}://localhost:${v}/webpack/reload to trigger rolling swap/respawn`)});const Re=Number(process.env.INTERNAL_PORT||0);if(Re>0){const e=n((l,d)=>{const a=Q();if(!a){d.statusCode=503,d.setHeader("content-type","text/plain; charset=utf-8"),d.end("No healthy children available.");return}y.web(l,d,{target:{socketPath:a.socketPath}},r=>{a.healthy=!1,a.lastError=String(r?.stack||r),d.statusCode=502,d.end(`Upstream error from Child#${a.id}.`)})},"internalHandler"),o=http.createServer(e);o.on("upgrade",(l,d,a)=>{const r=Q();if(!r){d.destroy();return}y.ws(l,d,a,{target:{socketPath:r.socketPath}},()=>d.destroy())}),o.on("error",l=>{console.warn(`[parent] internal port ${Re} unavailable (${l.code}).`)}),o.listen(Re,()=>{console.log(`[parent] Internal (service-to-service, no gateway middleware) on http://localhost:${Re}`)}),Te.push(o)}const qe=(()=>{try{return JSON.parse(process.env.GATEWAY_EXTRA_PORTS||"[]")}catch{return[]}})();for(const e of qe){if(!Number.isInteger(e)||e===Number(v))continue;const o=GATEWAY_HTTPS?require("node:https").createServer({key:Se,cert:Se},Ee):http.createServer(Ee);o.on("upgrade",We),o.on("error",l=>{console.warn(`[parent] extra port ${e} unavailable (${l.code}) \u2014 main port still up.`)}),o.listen(e,()=>{console.log(`[parent] Also listening on ${GATEWAY_HTTPS?"https":"http"}://localhost:${e}`)}),Te.push(o)}const $e=path.join(SOCK_DIR,"parent.sock");claimSock($e);const Ie=http.createServer(Ee);Ie.on("error",e=>{console.warn(`[parent] control socket unavailable (${e.code}) \u2014 lensmcp logs/debug attach disabled.`)}),Ie.listen($e),Te.push(Ie);const He=n(async e=>{N=!0,console.log(`[${e}] [parent] shutting down\u2026`);const o=sockOwnedByAnother($e);cleanupSock($e);for(const l of Te)if(!(l===Ie&&o))try{l.close()}catch{}for(const l of m)try{l.proc.kill("SIGTERM")}catch{}for(const l of[...K])U(l,"was still draining when its devserver shut down");for(const l of ee)try{l.proc.kill("SIGTERM")}catch{}for(const l of _){try{l.proc.kill("SIGTERM")}catch{}l.socketPath&&cleanupSock(l.socketPath,l.proc.pid)}try{fs.rmdirSync(SOCK_DIR)}catch{}setTimeout(()=>process.exit(0),700).unref()},"shutdown");["SIGINT","SIGTERM","SIGHUP","SIGQUIT","SIGUSR2"].forEach(e=>process.on(e,()=>{He(e)}));const Me=Number(process.env.LENSMCP_GATEWAY_PID??0);if(Number.isInteger(Me)&&Me>0){const e=setInterval(()=>{try{process.kill(Me,0)}catch(o){if(o.code==="EPERM")return;clearInterval(e),console.error(`[parent] gateway (pid ${Me}) is gone \u2014 shutting the pod down to avoid an orphan tree.`),He("gateway-gone")}},5e3);e.unref()}})()}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensmcp/cluster",
3
- "version": "1.23.13",
3
+ "version": "1.24.1",
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",
@@ -57,9 +57,9 @@
57
57
  }
58
58
  },
59
59
  "dependencies": {
60
- "@lensmcp/core": "1.23.13",
61
- "@lensmcp/node-instrumentation": "1.23.13",
62
- "@lensmcp/nx-plugin": "1.23.13",
60
+ "@lensmcp/core": "1.24.1",
61
+ "@lensmcp/node-instrumentation": "1.24.1",
62
+ "@lensmcp/nx-plugin": "1.24.1",
63
63
  "fastify": "^5.8.5",
64
64
  "@fastify/reply-from": "^12.6.2",
65
65
  "@typescript/native-preview": "^7.0.0-dev.20260707.2",