lensmcp 1.20.0 → 1.20.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.
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "lensmcp-marketplace",
3
+ "interface": {
4
+ "displayName": "LensMCP Marketplace"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "lensmcp",
9
+ "source": {
10
+ "source": "local",
11
+ "path": "./plugins/lensmcp"
12
+ },
13
+ "policy": {
14
+ "installation": "AVAILABLE",
15
+ "authentication": "ON_INSTALL"
16
+ },
17
+ "category": "Developer Tools"
18
+ }
19
+ ]
20
+ }
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > The observability lens for coding agents — run the MCP server your agent connects to and watch your apps live.
4
4
 
5
- **LensMCP** gives coding agents (and you) eyes on running applications. It captures traces, flows, and logs from your app and exposes them through an [MCP](https://modelcontextprotocol.io) server, so an agent can ask *"what just happened?"* and get a real answer — request flows, state changes, errors, and browser activity — instead of guessing from source code alone.
5
+ **LensMCP** gives coding agents (and you) eyes on running applications. It captures traces, flows, and logs from your app and exposes them through an [MCP](https://modelcontextprotocol.io) server, so an agent can ask _"what just happened?"_ and get a real answer — request flows, state changes, errors, and browser activity — instead of guessing from source code alone.
6
6
 
7
7
  `lensmcp` is the CLI. It runs the LensMCP MCP server (over stdio or HTTP) that agents discover and connect to, and it serves a local **dashboard** where you can browse Flows, Cluster, Logs, and Resources captured from your running apps. It also ships helpers to wire LensMCP into your workspace (`install`) and to diagnose your setup (`doctor`).
8
8
 
@@ -20,15 +20,15 @@ Requires **Node 18+** (Node 20+ recommended; this workspace pins Node 26 via `.n
20
20
  lensmcp <command> [options]
21
21
  ```
22
22
 
23
- | Command | Description |
24
- | --- | --- |
25
- | `lensmcp mcp` | Launch the LensMCP MCP server that agents connect to. Defaults to **stdio**; set `--transport http` (or `LENSMCP_TRANSPORT=http`) to serve over HTTP on `--port` (default **3000**). |
26
- | `lensmcp dashboard` | Serve the live web view of the lens — Flows, Cluster, Logs, Resources — on `--port` (default **4321**). |
27
- | `lensmcp install` | Install LensMCP into the host Nx workspace (delegates to `nx g @lensmcp/nx-plugin:init`). Wires `.mcp.json` so agents auto-discover the server, plus `.gitignore` and config. Idempotent. |
28
- | `lensmcp doctor` | Diagnose the install: Node version, workspace + plugin registration, `.mcp.json` entry, MCP bundle, per-project Vite/Nest wiring, `agent-dev` targets, and Chrome for browser capture. |
29
- | `lensmcp rollout` | Edit the gateway rollout config (`@lensmcp/cluster`) — `add-cohort`, `set-weight`, `promote`, `abort`, `add-rule`, `remove-rule`, `status`. |
30
- | `lensmcp --version` | Print the installed `lensmcp` version. |
31
- | `lensmcp --help` | Show usage. |
23
+ | Command | Description |
24
+ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
25
+ | `lensmcp mcp` | Launch the LensMCP MCP server that agents connect to. Defaults to **stdio**; set `--transport http` (or `LENSMCP_TRANSPORT=http`) to serve over HTTP on `--port` (default **3000**). |
26
+ | `lensmcp dashboard` | Serve the live web view of the lens — Flows, Cluster, Logs, Resources — on `--port` (default **4321**). |
27
+ | `lensmcp install` | Install LensMCP into the host Nx workspace (delegates to `nx g @lensmcp/nx-plugin:init`). Wires `.mcp.json` so agents auto-discover the server, plus `.gitignore` and config. Idempotent. |
28
+ | `lensmcp doctor` | Diagnose the install: Node version, workspace + plugin registration, `.mcp.json` entry, MCP bundle, per-project Vite/Nest wiring, `agent-dev` targets, and Chrome for browser capture. |
29
+ | `lensmcp rollout` | Edit the gateway rollout config (`@lensmcp/cluster`) — `add-cohort`, `set-weight`, `promote`, `abort`, `add-rule`, `remove-rule`, `status`. |
30
+ | `lensmcp --version` | Print the installed `lensmcp` version. |
31
+ | `lensmcp --help` | Show usage. |
32
32
 
33
33
  ### `lensmcp mcp`
34
34
 
@@ -36,11 +36,11 @@ lensmcp <command> [options]
36
36
  lensmcp mcp [--cwd <dir>] [--transport stdio|http] [--port <n>]
37
37
  ```
38
38
 
39
- | Flag | Default | Description |
40
- | --- | --- | --- |
41
- | `--cwd <dir>` | current directory | Workspace root the server runs against. |
42
- | `--transport <stdio\|http>` | `stdio` | Transport agents connect over. `stdio` for local agents (Claude Code, Cursor); `http` to expose an HTTP endpoint. |
43
- | `--port <n>` | `3000` | HTTP port (used when transport is `http`). |
39
+ | Flag | Default | Description |
40
+ | --------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------- |
41
+ | `--cwd <dir>` | current directory | Workspace root the server runs against. |
42
+ | `--transport <stdio\|http>` | `stdio` | Transport agents connect over. `stdio` for local agents (Claude Code, Cursor); `http` to expose an HTTP endpoint. |
43
+ | `--port <n>` | `3000` | HTTP port (used when transport is `http`). |
44
44
 
45
45
  Reads/writes the cross-process event file at `<cwd>/.lensmcp/events.jsonl` (override with `LENSMCP_EVENT_FILE`) — the same file your instrumented apps write to, so ingest and producers rendezvous with zero extra config.
46
46
 
@@ -50,10 +50,10 @@ Reads/writes the cross-process event file at `<cwd>/.lensmcp/events.jsonl` (over
50
50
  lensmcp dashboard [--cwd <dir>] [--port <n>]
51
51
  ```
52
52
 
53
- | Flag | Default | Description |
54
- | --- | --- | --- |
55
- | `--cwd <dir>` | current directory | Workspace root to read events from. |
56
- | `--port <n>` | `4321` | Port to serve the dashboard on (`http://localhost:<port>`). |
53
+ | Flag | Default | Description |
54
+ | ------------- | ----------------- | ----------------------------------------------------------- |
55
+ | `--cwd <dir>` | current directory | Workspace root to read events from. |
56
+ | `--port <n>` | `4321` | Port to serve the dashboard on (`http://localhost:<port>`). |
57
57
 
58
58
  Tails the same `.lensmcp/events.jsonl` the MCP server uses, so the dashboard shows the identical live flows, signals, and resources your agent sees.
59
59
 
@@ -83,13 +83,13 @@ Edits a gateway rollout config with validated, atomic writes that the production
83
83
 
84
84
  ## Environment variables
85
85
 
86
- | Variable | Used by | Default |
87
- | --- | --- | --- |
88
- | `LENSMCP_TRANSPORT` | `mcp` | `stdio` (overridden by `--transport`) |
89
- | `LENSMCP_PORT` | `mcp` | `3000` (overridden by `--port`) |
90
- | `LENSMCP_DASHBOARD_PORT` | `dashboard` | `4321` (overridden by `--port`) |
91
- | `LENSMCP_EVENT_FILE` | `mcp`, `dashboard` | `<cwd>/.lensmcp/events.jsonl` |
92
- | `CHROME_PATH` | `doctor` | auto-detected (browser capture only) |
86
+ | Variable | Used by | Default |
87
+ | ------------------------ | ------------------ | ------------------------------------- |
88
+ | `LENSMCP_TRANSPORT` | `mcp` | `stdio` (overridden by `--transport`) |
89
+ | `LENSMCP_PORT` | `mcp` | `3000` (overridden by `--port`) |
90
+ | `LENSMCP_DASHBOARD_PORT` | `dashboard` | `4321` (overridden by `--port`) |
91
+ | `LENSMCP_EVENT_FILE` | `mcp`, `dashboard` | `<cwd>/.lensmcp/events.jsonl` |
92
+ | `CHROME_PATH` | `doctor` | auto-detected (browser capture only) |
93
93
 
94
94
  ## Quick start
95
95
 
@@ -113,6 +113,19 @@ lensmcp mcp --transport http --port 3000 # or expose over HTTP
113
113
 
114
114
  Most local agents (Claude Code, Cursor, VS Code) read `.mcp.json` and launch `lensmcp mcp` over stdio automatically after `lensmcp install` — no manual wiring needed.
115
115
 
116
+ ### Codex plugin
117
+
118
+ The npm package also ships a native Codex plugin with LensMCP skills and a project-local MCP
119
+ server. After installing and setting up LensMCP, add the package as a Codex marketplace:
120
+
121
+ ```bash
122
+ codex plugin marketplace add ./node_modules/lensmcp
123
+ codex plugin add lensmcp@lensmcp-marketplace
124
+ ```
125
+
126
+ Start a new Codex thread after installation. The existing Claude Code plugin remains available
127
+ separately and is unchanged.
128
+
116
129
  ## How it fits
117
130
 
118
131
  `lensmcp` is the entry point of the LensMCP toolchain. [`@lensmcp/nx-plugin`](https://github.com/kiwiapps-ltd/lensmcp) wires LensMCP into your workspace and projects; the instrumentation libraries (`@lensmcp/vite-plugin`, `@lensmcp/nest-instrumentation`, `@lensmcp/react-instrumentation`, and friends) capture flows, state, and logs from running apps into `.lensmcp/events.jsonl`; and [`@lensmcp/cluster`](https://github.com/kiwiapps-ltd/lensmcp) powers the gateway and targeted rollouts driven by `lensmcp rollout`.
package/bin.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- "use strict";import{runCli as e}from"./lib/cli.js";e({cwd:process.cwd(),argv:process.argv.slice(2)}).then(s=>{process.exit(s.exitCode)},s=>{console.error(s instanceof Error?s.stack??s.message:String(s)),process.exit(1)});
2
+ "use strict";import{runCli as r}from"./lib/cli.js";r({cwd:process.cwd(),argv:process.argv.slice(2)}).then(e=>{process.exit(e.exitCode)},e=>{console.error(e instanceof Error?e.stack??e.message:String(e)),process.exit(1)});
package/lib/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var yt=Object.defineProperty;var d=(t,n)=>yt(t,"name",{value:n,configurable:!0});var bt=Object.defineProperty,l=d((t,n)=>bt(t,"name",{value:n,configurable:!0}),"d"),kt=Object.defineProperty,p=l((t,n)=>kt(t,"name",{value:n,configurable:!0}),"d"),Ct=Object.defineProperty,c=p((t,n)=>Ct(t,"name",{value:n,configurable:!0}),"d");import{spawn as St,spawnSync as x}from"node:child_process";import{existsSync as k,mkdirSync as oe,openSync as $t,readFileSync as B,readdirSync as it,rmSync as xt,statSync as jt,writeFileSync as K}from"node:fs";import{request as ut}from"node:http";import{homedir as Nt,tmpdir as Pt}from"node:os";import{basename as we,dirname as L,isAbsolute as Mt,join as v,relative as dt,resolve as N}from"node:path";import{fileURLToPath as Et}from"node:url";import{ensureLensConfig as ee}from"./workspace-scope.js";import{remember as Tt,shouldAnnounce as Ot}from"./hook-memory.js";import{isMcpMode as lt}from"./mcp-mode.js";import{effectiveMcpMode as Lt,LENS_CONFIG as _t,probeSharedMcp as pt,probeSharedMcpSync as At,readMcpWiring as at,wireMcpMode as Rt}from"./mcp-wiring.js";import{sweepLensRuntimeFiles as It}from"./sweep.js";import{candidateSignals as Dt,findingLine as Ft,findingsDigest as Gt,formatContext as Ht,hookPayload as ht,isHookEvent as ft,select as Wt}from"./status-policy.js";import{DEFAULT_BUDGET_MS as Jt,readStatus as Bt}from"./status-client.js";import{detectSkew as Vt,formatSkewContext as gt,readVersions as mt,spawnRunner as Kt,syncPlugin as Ut,updateCommands as qt}from"./plugin-version.js";const wt=`lensmcp \u2014 CLI for LensMCP (FrontMCP-based observability for coding agents)
1
+ "use strict";var wt=Object.defineProperty;var d=(t,n)=>wt(t,"name",{value:n,configurable:!0});var vt=Object.defineProperty,c=d((t,n)=>vt(t,"name",{value:n,configurable:!0}),"d");import{spawn as yt,spawnSync as C}from"node:child_process";import{existsSync as y,mkdirSync as K,openSync as kt,readFileSync as I,readdirSync as oe,rmSync as bt,statSync as St,writeFileSync as B}from"node:fs";import{request as ye}from"node:http";import{homedir as Ct,tmpdir as xt}from"node:os";import{basename as ee,dirname as M,isAbsolute as $t,join as m,relative as ke,resolve as $}from"node:path";import{fileURLToPath as jt}from"node:url";import{ensureLensConfig as V}from"./workspace-scope.js";import{remember as Nt,shouldAnnounce as Pt}from"./hook-memory.js";import{isMcpMode as be}from"./mcp-mode.js";import{effectiveMcpMode as Et,LENS_CONFIG as Mt,probeSharedMcp as Se,probeSharedMcpSync as Tt,readMcpWiring as ie,wireMcpMode as Ot}from"./mcp-wiring.js";import{sweepLensRuntimeFiles as Lt}from"./sweep.js";import{candidateSignals as _t,findingLine as At,findingsDigest as Rt,formatContext as Dt,hookPayload as Ce,isHookEvent as xe,select as It}from"./status-policy.js";import{DEFAULT_BUDGET_MS as Ft,readStatus as Gt}from"./status-client.js";import{detectSkew as Ht,formatSkewContext as $e,readVersions as je,spawnRunner as Wt,syncPlugin as Jt,updateCommands as Bt}from"./plugin-version.js";const Ne=`lensmcp \u2014 CLI for LensMCP (FrontMCP-based observability for coding agents)
2
2
 
3
3
  Usage:
4
4
  lensmcp <command> [options]
@@ -122,28 +122,28 @@ Commands:
122
122
  listening" link in \`lensmcp logs <service>\`.
123
123
  --version Print the lensmcp version.
124
124
  --help Show this help.
125
- `;export async function runCli(t){const n=t.out??(o=>console.log(o)),e=t.err??(o=>console.error(o)),[s,...r]=t.argv;if(!s||s==="--help"||s==="-h")return n(wt),{exitCode:0};if(s==="--version"||s==="-V")return n(X()),{exitCode:0};switch(s){case"setup":return Re(t,r,n,e);case"mcp":return ve(t,r,n,e);case"dashboard":return ye(t,r,n,e);case"gateway":return $e(t,r,n,e);case"bridge":return Oe(t,r,n,e);case"install":return de(t,r,n,e);case"trust":return le(t,r,n,e);case"status":return Ie(t,r,n);case"version-check":return De(t,r,n);case"doctor":return Fe(t,r,n);case"rollout":return qe(t,r,e);case"logs":return rt(t,r,n,e);case"debug":return ot(t,r,n,e);default:return e(`Unknown command: ${s}`),e(wt),{exitCode:2,message:`Unknown command: ${s}`}}}d(runCli,"runCli"),l(runCli,"runCli"),p(runCli,"runCli"),c(runCli,"runCli");function ve(t,n,e,s){const r=P(n,{string:["--cwd","--transport","--port","--mcp"]}),o=N(y(r.flags["--cwd"])??t.cwd),a=ee(o),i=y(r.flags["--transport"])??t.env?.LENSMCP_TRANSPORT??"stdio",h=y(r.flags["--port"])??t.env?.LENSMCP_PORT??String(a.ports.mcpHttp),u=Lt(o,y(r.flags["--mcp"]),{...process.env,...t.env??{}})==="shared"&&i==="stdio",f=u?Ye(o)??se(o):se(o);if(!f)return s(`Could not locate the lensmcp-mcp server bundle.
125
+ `;export async function runCli(t){const n=t.out??(o=>console.log(o)),e=t.err??(o=>console.error(o)),[s,...r]=t.argv;if(!s||s==="--help"||s==="-h")return n(Ne),{exitCode:0};if(s==="--version"||s==="-V")return n(X()),{exitCode:0};switch(s){case"setup":return Ke(t,r,n,e);case"mcp":return Pe(t,r,n,e);case"dashboard":return Ee(t,r,n,e);case"gateway":return _e(t,r,n,e);case"bridge":return We(t,r,n,e);case"install":return le(t,r,n,e);case"trust":return pe(t,r,n,e);case"status":return Ue(t,r,n);case"version-check":return qe(t,r,n);case"doctor":return ze(t,r,n);case"rollout":return rt(t,r,e);case"logs":return ft(t,r,n,e);case"debug":return gt(t,r,n,e);default:return e(`Unknown command: ${s}`),e(Ne),{exitCode:2,message:`Unknown command: ${s}`}}}d(runCli,"runCli"),c(runCli,"runCli");function Pe(t,n,e,s){const r=T(n,{string:["--cwd","--transport","--port","--mcp"]}),o=$(w(r.flags["--cwd"])??t.cwd),a=V(o),i=w(r.flags["--transport"])??t.env?.LENSMCP_TRANSPORT??"stdio",l=w(r.flags["--port"])??t.env?.LENSMCP_PORT??String(a.ports.mcpHttp),u=Et(o,w(r.flags["--mcp"]),{...process.env,...t.env??{}})==="shared"&&i==="stdio",p=u?it(o)??se(o):se(o);if(!p)return s(`Could not locate the lensmcp-mcp server bundle.
126
126
  Looked in (in order):
127
127
  \u2022 <cli-package>/bundled/main.js (published bin)
128
128
  \u2022 <workspace>/apps/lensmcp-mcp/dist/main.js (dev build)
129
- For dev: \`yarn nx build @lensmcp/lensmcp-mcp\`.`),{exitCode:1};Z(o);const g=t.env?.LENSMCP_EVENT_FILE??process.env.LENSMCP_EVENT_FILE??v(o,".lensmcp","events.jsonl"),b={...process.env,...t.env??{},LENSMCP_TRANSPORT:i,LENSMCP_PORT:h,LENSMCP_EVENT_FILE:g},m=u?[f,"--cwd",o]:[f];return{exitCode:x(process.execPath,m,{cwd:o,stdio:"inherit",env:b}).status??1}}d(ve,"ye"),l(ve,"Pe"),p(ve,"tt"),c(ve,"runMcp");function ye(t,n,e,s){const r=P(n,{string:["--cwd","--port","--base"]}),o=N(y(r.flags["--cwd"])??t.cwd),a=ee(o),i=y(r.flags["--port"])??t.env?.LENSMCP_DASHBOARD_PORT??String(a.ports.dashboard),h=y(r.flags["--base"])??t.env?.LENSMCP_DASHBOARD_BASE??"",u=ne(o,"dashboard.js");if(!u)return s(`Could not locate the lensmcp dashboard bundle.
129
+ For dev: \`yarn nx build @lensmcp/lensmcp-mcp\`.`),{exitCode:1};Z(o);const h=t.env?.LENSMCP_EVENT_FILE??process.env.LENSMCP_EVENT_FILE??m(o,".lensmcp","events.jsonl"),v={...process.env,...t.env??{},LENSMCP_TRANSPORT:i,LENSMCP_PORT:l,LENSMCP_EVENT_FILE:h},f=u?[p,"--cwd",o]:[p];return{exitCode:C(process.execPath,f,{cwd:o,stdio:"inherit",env:v}).status??1}}d(Pe,"tt"),c(Pe,"runMcp");function Ee(t,n,e,s){const r=T(n,{string:["--cwd","--port","--base"]}),o=$(w(r.flags["--cwd"])??t.cwd),a=V(o),i=w(r.flags["--port"])??t.env?.LENSMCP_DASHBOARD_PORT??String(a.ports.dashboard),l=w(r.flags["--base"])??t.env?.LENSMCP_DASHBOARD_BASE??"",u=ne(o,"dashboard.js");if(!u)return s(`Could not locate the lensmcp dashboard bundle.
130
130
  Looked in (in order):
131
131
  \u2022 <cli-package>/bundled/dashboard.js (published bin)
132
132
  \u2022 <workspace>/servers/lensmcp-mcp/dist/dashboard.js (dev build)
133
- For dev: \`yarn nx build @lensmcp/lensmcp-mcp\`.`),{exitCode:1};Z(o);const f=t.env?.LENSMCP_EVENT_FILE??process.env.LENSMCP_EVENT_FILE??v(o,".lensmcp","events.jsonl");return e(`lensmcp dashboard \u2192 http://localhost:${i}${h||"/"} (reading ${f})`),{exitCode:x(process.execPath,[u],{cwd:o,stdio:"inherit",env:{...process.env,...t.env??{},LENSMCP_DASHBOARD_PORT:i,LENSMCP_DASHBOARD_BASE:h,LENSMCP_EVENT_FILE:f}}).status??1}}d(ye,"ke"),l(ye,"Ee"),p(ye,"nt"),c(ye,"runDashboard");function be(){return v(process.env.LENSMCP_HOME||Nt(),".lensmcp","control.sock")}d(be,"be"),l(be,"Me"),p(be,"st"),c(be,"controlSocketPath");function _(t,n,e){const s=be();if(!k(s))return null;const r=["-s","-o","-","-w",`
134
- %{http_code}`,"--unix-socket",s,"-X",t,`http://localhost${n}`];e!==void 0&&r.push("-H","content-type: application/json","-d",JSON.stringify(e));const o=x("curl",r,{encoding:"utf8",timeout:5e3});if(o.status!==0||typeof o.stdout!="string"||o.stdout.length===0)return null;const a=o.stdout.split(`
135
- `),i=Number(a.pop());let h=null;try{const u=a.join(`
136
- `).trim();h=u?JSON.parse(u):null}catch{}return Number.isFinite(i)?{status:i,json:h}:null}d(_,"L"),l(_,"O"),p(_,"L"),c(_,"daemonRequest");function ke(){const t=_("GET","/status");if(t?.status!==200||!t.json||typeof t.json!="object")return;const n=t.json.daemon;return typeof n?.wsKey=="string"&&n.wsKey.length>0?n.wsKey:void 0}d(ke,"Ce"),l(ke,"Te"),p(ke,"rt"),c(ke,"daemonWsKey");function Ce(t){const n={};for(const e of F(t,s=>s==="project.json"))try{const s=JSON.parse(B(e,"utf8")),r=L(e);n[s.name??we(r)]={root:dt(t,r)||"."}}catch{}return n}d(Ce,"Se"),l(Ce,"Oe"),p(Ce,"ot"),c(Ce,"buildProjectsMap");function Se(t){const n=v(t,"node_modules",".bin",process.platform==="win32"?"nx.cmd":"nx");if(!k(n))return;const e=x(n,["graph","--print"],{cwd:t,encoding:"utf8",timeout:15e3,maxBuffer:32*1024*1024});if(!(e.status!==0||typeof e.stdout!="string"))try{const s=JSON.parse(e.stdout).graph?.dependencies;return s?Object.fromEntries(Object.entries(s).map(([r,o])=>[r,o.flatMap(a=>typeof a.target=="string"?[a.target]:[])])):void 0}catch{return}}d(Se,"$e"),l(Se,"Le"),p(Se,"it"),c(Se,"buildProjectDependencies");function ie(t,n){const e=Se(t);return{wsKey:n,root:t,projects:Ce(t),...e?{projectDependencies:e}:{}}}d(ie,"ie"),l(ie,"ae"),p(ie,"me"),c(ie,"gatewayRegistrationBody");function $e(t,n,e,s){const r=P(n,{string:["--cwd","--project","--target"]}),o=(r.positional[0]??"status").toLowerCase(),a=n.includes("--json"),i=N(y(r.flags["--cwd"])??t.cwd),h=ee(i),u=v(i,".lensmcp","gateway.pid"),f=v(i,".lensmcp","gateway.log"),g=v(i,".lensmcp","registered.json"),b=`https://lensmcp.local${h.dashboardBasePath}/`,m=c(()=>{const{pid:w,healed:V}=z(u,i);if(w!==void 0&&q(w)){const O=classifyAliveGateway({oursOn443:R(i)!==void 0,port443Held:A().length>0,pidFileAgeMs:ce(u)});if(O==="serving")return V&&e(`healed: adopted a running gateway (pid ${w}) the pid file had lost \u2014 no duplicate started.`),e(`gateway already running (pid ${w}).`),e(` dashboard \u2192 ${b}`),{exitCode:0};if(O==="booting")return e(`gateway starting (pid ${w}) \u2014 :443 not bound yet.`),e(` dashboard \u2192 ${b}`),e(` logs \u2192 ${f}`),{exitCode:0};Y(w,"SIGKILL"),I(u),U(i,t.env,()=>{}),e(`reaped a dead gateway (pid ${w} was alive but nothing served :443 for this workspace).`)}const M=A()[0];if(M!==void 0){const O=_("GET","/list");if(O!==null){if(O.status===200&&O.json&&typeof O.json=="object"&&(O.json.workspaces??[]).some(J=>J.key===h.key))return oe(L(g),{recursive:!0}),K(g,JSON.stringify({wsKey:h.key})),e(`already registered into the shared gateway daemon (pid ${M}).`),e(` dashboard \u2192 ${b}`),{exitCode:0};const C=_("POST","/register",ie(i,h.key));return C&&C.status===200?(oe(L(g),{recursive:!0}),K(g,JSON.stringify({wsKey:h.key})),e(`registered '${h.key}' into the shared gateway daemon (pid ${M}).`),e(` dashboard \u2192 ${b}`),e(" the daemon hosts this workspace's services + dashboard; `lensmcp gateway stop` unregisters."),{exitCode:0}):(s(`failed to register into the gateway daemon: ${C?JSON.stringify(C.json):"no response from the control socket"}`),{exitCode:1})}return s(`port :443 is held by pid ${M}, which is not this workspace's gateway.
137
- Free it first (stop that process), then \`lensmcp gateway start\`.`),{exitCode:1}}const T=xe(i,y(r.flags["--project"]),y(r.flags["--target"]));if(!T)return s(`This workspace ('${h.key}') has no gateway target \u2014 no project declares the`),s("`@lensmcp/cluster:gateway` executor, so there is nothing here to launch on :443."),s(""),s(" \u2022 Joining a shared gateway? Start it in the workspace that OWNS it, then run"),s(" `lensmcp gateway start` here \u2014 this workspace registers into it (no local target needed)."),s(" \u2022 Should this workspace host its own gateway? Scaffold it: `lensmcp install`"),s(" (or `lensmcp setup` for the full install + trust bootstrap)."),s(" \u2022 Already have a differently-named target? `lensmcp gateway start --project <p> --target <t>`."),{exitCode:1};const E=Q(i);if(!E)return s("Could not find the `nx` binary in the workspace. Install Nx first: `yarn add -D nx`."),{exitCode:1};It(i,f);const G=$t(f,"a"),W=St(E,["run",`${T.project}:${T.target}`],{cwd:i,detached:!0,stdio:["ignore",G,G],env:{...process.env,...t.env??{}}});return W.unref(),W.pid&&K(u,String(W.pid)),e(`gateway starting \u2192 ${T.project}:${T.target} (pid ${W.pid??"?"})`),e(` dashboard \u2192 ${b}`),e(` logs \u2192 ${f}`),e(" note: binding :443 needs privilege \u2014 if it fails, run the gateway target with sudo,"),e(" and run `nx run gateway:trust` once for the *.local hosts + TLS."),{exitCode:0}},"start"),$=c(()=>{if(k(g)&&R(i)===void 0){let E=h.key;try{E=JSON.parse(B(g,"utf8")).wsKey??h.key}catch{}const G=_("POST","/unregister",{wsKey:E});return I(g),e(G&&G.status===200?`unregistered '${E}' from the shared gateway daemon.`:"unregister sent (the daemon may already be gone)."),{exitCode:0}}I(g);const{pid:w,healed:V}=z(u,i);if(w===void 0)return e("gateway not running."),I(u),{exitCode:0};V&&e(`healed: found an orphaned gateway (pid ${w}) the pid file had lost \u2014 stopping it.`),Y(w);let M=!1;if(!ue(6e3)){M=!0,Y(w,"SIGKILL");for(const E of A())try{process.kill(E,"SIGKILL")}catch{}ue(3e3)}const T=R(i);return T!==void 0&&T!==w&&(Y(T,"SIGKILL"),M=!0),I(u),M&&U(i,t.env,()=>{}),e(`gateway stopped (pid ${w})${M?" \u2014 forced (a child ignored SIGTERM)":""}.`),{exitCode:0}},"stop"),j=c(()=>{const{pid:w,healed:V}=z(u,i),M=w!==void 0&&q(w)?classifyAliveGateway({oursOn443:R(i)!==void 0,port443Held:A().length>0,pidFileAgeMs:ce(u)}):void 0,T=M==="serving"||M==="booting";if(a){const C=_("GET","/status"),J=C?.status===200&&C.json&&typeof C.json=="object"?C.json:null,ct=k(g),vt=T?"daemon":J&&ct?"guest":J?"other":"stopped";return e(JSON.stringify({thisWorkspace:h.key,role:vt,dashboardUrl:b,chooserUrl:"https://lensmcp.local/",logFile:f,mcp:pe(i,t.env??process.env),daemon:J?.daemon??null,workspaces:J?.workspaces??[],services:J?.services??[]})),{exitCode:T||J&&ct?0:1}}const E=_("GET","/list"),G=E?.status===200&&E.json&&typeof E.json=="object"?E.json.workspaces??[]:[],W=c(()=>{if(G.length!==0){e(` workspaces (${G.length} registered):`);for(const C of G)e(` \u2022 ${C.key} (${C.routes??0} routes, ${C.services??0} services)${C.key===h.key?" \u2190 this workspace":""}`)}},"printWorkspaces"),O=c(()=>{const C=pe(i,t.env??process.env);e(C.mode==="shared"?` mcp \u2192 shared ${C.url} ${C.healthy?"(up)":`(DOWN \u2014 ${C.error??"no answer"})`}`:" mcp \u2192 stdio (one server per agent session)")},"printMcp");if(T)return e(M==="booting"?`gateway: starting (pid ${w}) \u2014 :443 not bound yet`:`gateway: running (pid ${w}) \u2014 DAEMON on :443${V?" \u2014 recovered a stale pid file":""}`),e(` workspace \u2192 ${h.key}`),e(` dashboard \u2192 ${b}`),e(" chooser \u2192 https://lensmcp.local/"),e(` logs \u2192 ${f}`),O(),W(),{exitCode:0};if(k(g)&&E?.status===200){const C=A()[0];return e(`gateway: registered into a shared daemon${C!==void 0?` (pid ${C})`:""} \u2014 this workspace is a guest`),e(` workspace \u2192 ${h.key}`),e(` dashboard \u2192 ${b}`),e(" stop \u2192 `lensmcp gateway stop` (unregisters; the daemon keeps running)"),O(),W(),{exitCode:0}}return M==="zombie"?(e(`gateway: NOT serving \u2014 tracked pid ${w} is alive but nothing holds :443 for this workspace`),e(" (a crashed gateway under a wedged nx wrapper) \u2014 run `lensmcp gateway restart` to recover."),e(` workspace \u2192 ${h.key}`),e(` logs \u2192 ${f}`),O(),W(),{exitCode:1}):(e("gateway: stopped"),e(` workspace \u2192 ${h.key}`),e(` dashboard \u2192 ${b}`),O(),{exitCode:1})},"status");switch(o){case"start":return m();case"stop":return $();case"restart":return $(),U(i,t.env,e),m();case"status":return j();default:return s(`Unknown gateway subcommand: ${o} (use start | stop | status | restart)`),{exitCode:2}}}d($e,"xe"),l($e,"_e"),p($e,"at"),c($e,"runGateway");function U(t,n,e){const s=Q(t);if(!s){e(" (graph refresh skipped \u2014 no nx binary found in the workspace)");return}const r={...process.env,...n??{}},o=c(a=>x(s,a,{cwd:t,env:r,stdio:"ignore"}).status===0,"tryNx");if(o(["reset","--only-daemon"])||o(["daemon","--stop"])){e(" nx project graph refreshed \u2014 a lib/app added since boot will now resolve.");return}e(" (graph refresh best-effort failed \u2014 if a new project does not resolve, run `nx reset`)")}d(U,"U"),l(U,"U"),p(U,"q"),c(U,"refreshNxGraph");function ae(t,n,e,s){if(e&&s)return{project:e,target:s};const r=F(t,o=>o==="project.json");for(const o of r){let a;try{a=JSON.parse(D(o))}catch{continue}const i=a.name??we(L(o));for(const[h,u]of Object.entries(a.targets??{}))if(u.executor===n)return{project:e??i,target:s??h}}}d(ae,"ae"),l(ae,"ce"),p(ae,"we"),c(ae,"scanExecutorTarget");function xe(t,n,e){return ae(t,"@lensmcp/cluster:gateway",n,e)||(n||e?{project:n??"gateway",target:e??"serve"}:void 0)}d(xe,"je"),l(xe,"Ae"),p(xe,"ct"),c(xe,"findGatewayTarget");function je(t,n,e){return ae(t,"@lensmcp/cluster:trust",n,e)||(n?{project:n,target:e??"trust"}:Ne(t,"gateway")?{project:"gateway",target:e??"trust"}:void 0)}d(je,"Ne"),l(je,"Re"),p(je,"dt"),c(je,"findTrustTarget");function Ne(t,n){for(const e of F(t,s=>s==="project.json"))try{if((JSON.parse(D(e)).name??we(L(e)))===n)return!0}catch{}return!1}d(Ne,"Pe"),l(Ne,"De"),p(Ne,"lt"),c(Ne,"projectExists");function Pe(t){try{const n=Number(B(t,"utf8").trim());return Number.isInteger(n)&&n>0?n:void 0}catch{return}}d(Pe,"Ee"),l(Pe,"Ie"),p(Pe,"ut"),c(Pe,"readPid");function q(t){try{return process.kill(t,0),!0}catch(n){return n.code==="EPERM"}}d(q,"q"),l(q,"q"),p(q,"z"),c(q,"isAlive");function A(){try{const t=x("lsof",["-nP","-iTCP:443","-sTCP:LISTEN","-t"],{encoding:"utf8"});return t.status!==0||!t.stdout?[]:[...new Set(t.stdout.split(/\s+/).map(Number).filter(n=>Number.isInteger(n)&&n>0))]}catch{return[]}}d(A,"A"),l(A,"_"),p(A,"I"),c(A,"pidsOnPort443");function Me(t){try{return(x("ps",["-o","command=","-p",String(t)],{encoding:"utf8"}).stdout??"").trim()}catch{return""}}d(Me,"Me"),l(Me,"Fe"),p(Me,"pt"),c(Me,"processCmd");function R(t){const n=v(t,"node_modules","nx","dist","bin","run-executor.js"),e=v(t,"node_modules","@lensmcp","cluster");for(const s of A()){const r=Me(s);if(r.includes(n)||r.includes(e))return s}}d(R,"R"),l(R,"F"),p(R,"G"),c(R,"gatewayOnPort443");function ce(t){try{return Date.now()-jt(t).mtimeMs}catch{return}}d(ce,"ce"),l(ce,"ue"),p(ce,"ye"),c(ce,"pidFileAgeMs");const zt=18e4;export function classifyAliveGateway(t){if(t.oursOn443)return"serving";const n=t.pidFileAgeMs!==void 0&&t.pidFileAgeMs<(t.bootGraceMs??zt);return!t.port443Held&&n?"booting":"zombie"}d(classifyAliveGateway,"classifyAliveGateway"),l(classifyAliveGateway,"classifyAliveGateway"),p(classifyAliveGateway,"classifyAliveGateway"),c(classifyAliveGateway,"classifyAliveGateway");function z(t,n){const e=Pe(t);if(e!==void 0&&q(e))return{pid:e,healed:!1};const s=R(n);if(s!==void 0){try{K(t,String(s))}catch{}return{pid:s,healed:!0}}return{healed:!1}}d(z,"z"),l(z,"z"),p(z,"Y"),c(z,"reconcileGateway");function Y(t,n="SIGTERM"){try{process.kill(-t,n)}catch{try{process.kill(t,n)}catch{}}}d(Y,"Y"),l(Y,"Y"),p(Y,"X"),c(Y,"killGatewayTree");function ue(t){const n=Date.now()+t;for(;;){if(A().length===0)return!0;if(Date.now()>=n)return!1;x("sleep",["0.15"])}}d(ue,"ue"),l(ue,"de"),p(ue,"ve"),c(ue,"waitForPortFree");function Ee(t){const n=x("curl",["-s","-o","/dev/null","-m","8","-w","%{ssl_verify_result}",`https://${t}/`],{encoding:"utf8"}),e=(n.stdout??"").trim();return n.status===0&&e==="0"?"trusted":e&&e!=="0"?"untrusted":"unknown"}d(Ee,"Te"),l(Ee,"Ge"),p(Ee,"ht"),c(Ee,"probeHostTrust");function Te(t){for(const n of F(t,e=>e==="project.json"))try{const e=JSON.parse(B(n,"utf8")),s=(e.cluster??e.davnx)?.host;if(s&&!s.includes("*"))return s}catch{}}d(Te,"Oe"),l(Te,"He"),p(Te,"gt"),c(Te,"firstClusterHost");function I(t){try{xt(t,{force:!0})}catch{}}d(I,"I"),l(I,"G"),p(I,"H"),c(I,"rmFile");function Oe(t,n,e,s){const r=P(n,{string:["--cwd","--host","--port"]}),o=N(y(r.flags["--cwd"])??t.cwd),a=ee(o),i=y(r.flags["--host"])??t.env?.LENSMCP_WS_HOST??process.env.LENSMCP_WS_HOST,h=y(r.flags["--port"])??t.env?.LENSMCP_WS_PORT??process.env.LENSMCP_WS_PORT??String(a.ports.bridge),u=Xe(o);if(!u)return s(`Could not locate the lensmcp bridge bundle.
133
+ For dev: \`yarn nx build @lensmcp/lensmcp-mcp\`.`),{exitCode:1};Z(o);const p=t.env?.LENSMCP_EVENT_FILE??process.env.LENSMCP_EVENT_FILE??m(o,".lensmcp","events.jsonl");return e(`lensmcp dashboard \u2192 http://localhost:${i}${l||"/"} (reading ${p})`),{exitCode:C(process.execPath,[u],{cwd:o,stdio:"inherit",env:{...process.env,...t.env??{},LENSMCP_DASHBOARD_PORT:i,LENSMCP_DASHBOARD_BASE:l,LENSMCP_EVENT_FILE:p}}).status??1}}d(Ee,"nt"),c(Ee,"runDashboard");function Me(){return m(process.env.LENSMCP_HOME||Ct(),".lensmcp","control.sock")}d(Me,"st"),c(Me,"controlSocketPath");function O(t,n,e){const s=Me();if(!y(s))return null;const r=["-s","-o","-","-w",`
134
+ %{http_code}`,"--unix-socket",s,"-X",t,`http://localhost${n}`];e!==void 0&&r.push("-H","content-type: application/json","-d",JSON.stringify(e));const o=C("curl",r,{encoding:"utf8",timeout:5e3});if(o.status!==0||typeof o.stdout!="string"||o.stdout.length===0)return null;const a=o.stdout.split(`
135
+ `),i=Number(a.pop());let l=null;try{const u=a.join(`
136
+ `).trim();l=u?JSON.parse(u):null}catch{}return Number.isFinite(i)?{status:i,json:l}:null}d(O,"L"),c(O,"daemonRequest");function Te(){const t=O("GET","/status");if(t?.status!==200||!t.json||typeof t.json!="object")return;const n=t.json.daemon;return typeof n?.wsKey=="string"&&n.wsKey.length>0?n.wsKey:void 0}d(Te,"rt"),c(Te,"daemonWsKey");function Oe(t){const n={};for(const e of W(t,s=>s==="project.json"))try{const s=JSON.parse(I(e,"utf8")),r=M(e);n[s.name??ee(r)]={root:ke(t,r)||"."}}catch{}return n}d(Oe,"ot"),c(Oe,"buildProjectsMap");function Le(t){const n=m(t,"node_modules",".bin",process.platform==="win32"?"nx.cmd":"nx");if(!y(n))return;const e=C(n,["graph","--print"],{cwd:t,encoding:"utf8",timeout:15e3,maxBuffer:32*1024*1024});if(!(e.status!==0||typeof e.stdout!="string"))try{const s=JSON.parse(e.stdout).graph?.dependencies;return s?Object.fromEntries(Object.entries(s).map(([r,o])=>[r,o.flatMap(a=>typeof a.target=="string"?[a.target]:[])])):void 0}catch{return}}d(Le,"it"),c(Le,"buildProjectDependencies");function ae(t,n){const e=Le(t);return{wsKey:n,root:t,projects:Oe(t),...e?{projectDependencies:e}:{}}}d(ae,"me"),c(ae,"gatewayRegistrationBody");function _e(t,n,e,s){const r=T(n,{string:["--cwd","--project","--target"]}),o=(r.positional[0]??"status").toLowerCase(),a=n.includes("--json"),i=$(w(r.flags["--cwd"])??t.cwd),l=V(i),u=m(i,".lensmcp","gateway.pid"),p=m(i,".lensmcp","gateway.log"),h=m(i,".lensmcp","registered.json"),v=`https://lensmcp.local${l.dashboardBasePath}/`,f=c(()=>{const{pid:g,healed:J}=z(u,i);if(g!==void 0&&q(g)){const E=classifyAliveGateway({oursOn443:F(i)!==void 0,port443Held:_().length>0,pidFileAgeMs:ue(u)});if(E==="serving")return J&&e(`healed: adopted a running gateway (pid ${g}) the pid file had lost \u2014 no duplicate started.`),e(`gateway already running (pid ${g}).`),e(` dashboard \u2192 ${v}`),{exitCode:0};if(E==="booting")return e(`gateway starting (pid ${g}) \u2014 :443 not bound yet.`),e(` dashboard \u2192 ${v}`),e(` logs \u2192 ${p}`),{exitCode:0};Y(g,"SIGKILL"),G(u),U(i,t.env,()=>{}),e(`reaped a dead gateway (pid ${g} was alive but nothing served :443 for this workspace).`)}const j=_()[0];if(j!==void 0){const E=O("GET","/list");if(E!==null){if(E.status===200&&E.json&&typeof E.json=="object"&&(E.json.workspaces??[]).some(D=>D.key===l.key))return K(M(h),{recursive:!0}),B(h,JSON.stringify({wsKey:l.key})),e(`already registered into the shared gateway daemon (pid ${j}).`),e(` dashboard \u2192 ${v}`),{exitCode:0};const k=O("POST","/register",ae(i,l.key));return k&&k.status===200?(K(M(h),{recursive:!0}),B(h,JSON.stringify({wsKey:l.key})),e(`registered '${l.key}' into the shared gateway daemon (pid ${j}).`),e(` dashboard \u2192 ${v}`),e(" the daemon hosts this workspace's services + dashboard; `lensmcp gateway stop` unregisters."),{exitCode:0}):(s(`failed to register into the gateway daemon: ${k?JSON.stringify(k.json):"no response from the control socket"}`),{exitCode:1})}return s(`port :443 is held by pid ${j}, which is not this workspace's gateway.
137
+ Free it first (stop that process), then \`lensmcp gateway start\`.`),{exitCode:1}}const P=Ae(i,w(r.flags["--project"]),w(r.flags["--target"]));if(!P)return s(`This workspace ('${l.key}') has no gateway target \u2014 no project declares the`),s("`@lensmcp/cluster:gateway` executor, so there is nothing here to launch on :443."),s(""),s(" \u2022 Joining a shared gateway? Start it in the workspace that OWNS it, then run"),s(" `lensmcp gateway start` here \u2014 this workspace registers into it (no local target needed)."),s(" \u2022 Should this workspace host its own gateway? Scaffold it: `lensmcp install`"),s(" (or `lensmcp setup` for the full install + trust bootstrap)."),s(" \u2022 Already have a differently-named target? `lensmcp gateway start --project <p> --target <t>`."),{exitCode:1};const N=Q(i);if(!N)return s("Could not find the `nx` binary in the workspace. Install Nx first: `yarn add -D nx`."),{exitCode:1};Lt(i,p);const L=kt(p,"a"),R=yt(N,["run",`${P.project}:${P.target}`],{cwd:i,detached:!0,stdio:["ignore",L,L],env:{...process.env,...t.env??{}}});return R.unref(),R.pid&&B(u,String(R.pid)),e(`gateway starting \u2192 ${P.project}:${P.target} (pid ${R.pid??"?"})`),e(` dashboard \u2192 ${v}`),e(` logs \u2192 ${p}`),e(" note: binding :443 needs privilege \u2014 if it fails, run the gateway target with sudo,"),e(" and run `nx run gateway:trust` once for the *.local hosts + TLS."),{exitCode:0}},"start"),b=c(()=>{if(y(h)&&F(i)===void 0){let N=l.key;try{N=JSON.parse(I(h,"utf8")).wsKey??l.key}catch{}const L=O("POST","/unregister",{wsKey:N});return G(h),e(L&&L.status===200?`unregistered '${N}' from the shared gateway daemon.`:"unregister sent (the daemon may already be gone)."),{exitCode:0}}G(h);const{pid:g,healed:J}=z(u,i);if(g===void 0)return e("gateway not running."),G(u),{exitCode:0};J&&e(`healed: found an orphaned gateway (pid ${g}) the pid file had lost \u2014 stopping it.`),Y(g);let j=!1;if(!de(6e3)){j=!0,Y(g,"SIGKILL");for(const N of _())try{process.kill(N,"SIGKILL")}catch{}de(3e3)}const P=F(i);return P!==void 0&&P!==g&&(Y(P,"SIGKILL"),j=!0),G(u),j&&U(i,t.env,()=>{}),e(`gateway stopped (pid ${g})${j?" \u2014 forced (a child ignored SIGTERM)":""}.`),{exitCode:0}},"stop"),x=c(()=>{const{pid:g,healed:J}=z(u,i),j=g!==void 0&&q(g)?classifyAliveGateway({oursOn443:F(i)!==void 0,port443Held:_().length>0,pidFileAgeMs:ue(u)}):void 0,P=j==="serving"||j==="booting";if(a){const k=O("GET","/status"),D=k?.status===200&&k.json&&typeof k.json=="object"?k.json:null,ve=y(h),mt=P?"daemon":D&&ve?"guest":D?"other":"stopped";return e(JSON.stringify({thisWorkspace:l.key,role:mt,dashboardUrl:v,chooserUrl:"https://lensmcp.local/",logFile:p,mcp:he(i,t.env??process.env),daemon:D?.daemon??null,workspaces:D?.workspaces??[],services:D?.services??[]})),{exitCode:P||D&&ve?0:1}}const N=O("GET","/list"),L=N?.status===200&&N.json&&typeof N.json=="object"?N.json.workspaces??[]:[],R=c(()=>{if(L.length!==0){e(` workspaces (${L.length} registered):`);for(const k of L)e(` \u2022 ${k.key} (${k.routes??0} routes, ${k.services??0} services)${k.key===l.key?" \u2190 this workspace":""}`)}},"printWorkspaces"),E=c(()=>{const k=he(i,t.env??process.env);e(k.mode==="shared"?` mcp \u2192 shared ${k.url} ${k.healthy?"(up)":`(DOWN \u2014 ${k.error??"no answer"})`}`:" mcp \u2192 stdio (one server per agent session)")},"printMcp");if(P)return e(j==="booting"?`gateway: starting (pid ${g}) \u2014 :443 not bound yet`:`gateway: running (pid ${g}) \u2014 DAEMON on :443${J?" \u2014 recovered a stale pid file":""}`),e(` workspace \u2192 ${l.key}`),e(` dashboard \u2192 ${v}`),e(" chooser \u2192 https://lensmcp.local/"),e(` logs \u2192 ${p}`),E(),R(),{exitCode:0};if(y(h)&&N?.status===200){const k=_()[0];return e(`gateway: registered into a shared daemon${k!==void 0?` (pid ${k})`:""} \u2014 this workspace is a guest`),e(` workspace \u2192 ${l.key}`),e(` dashboard \u2192 ${v}`),e(" stop \u2192 `lensmcp gateway stop` (unregisters; the daemon keeps running)"),E(),R(),{exitCode:0}}return j==="zombie"?(e(`gateway: NOT serving \u2014 tracked pid ${g} is alive but nothing holds :443 for this workspace`),e(" (a crashed gateway under a wedged nx wrapper) \u2014 run `lensmcp gateway restart` to recover."),e(` workspace \u2192 ${l.key}`),e(` logs \u2192 ${p}`),E(),R(),{exitCode:1}):(e("gateway: stopped"),e(` workspace \u2192 ${l.key}`),e(` dashboard \u2192 ${v}`),E(),{exitCode:1})},"status");switch(o){case"start":return f();case"stop":return b();case"restart":return b(),U(i,t.env,e),f();case"status":return x();default:return s(`Unknown gateway subcommand: ${o} (use start | stop | status | restart)`),{exitCode:2}}}d(_e,"at"),c(_e,"runGateway");function U(t,n,e){const s=Q(t);if(!s){e(" (graph refresh skipped \u2014 no nx binary found in the workspace)");return}const r={...process.env,...n??{}},o=c(a=>C(s,a,{cwd:t,env:r,stdio:"ignore"}).status===0,"tryNx");if(o(["reset","--only-daemon"])||o(["daemon","--stop"])){e(" nx project graph refreshed \u2014 a lib/app added since boot will now resolve.");return}e(" (graph refresh best-effort failed \u2014 if a new project does not resolve, run `nx reset`)")}d(U,"q"),c(U,"refreshNxGraph");function ce(t,n,e,s){if(e&&s)return{project:e,target:s};const r=W(t,o=>o==="project.json");for(const o of r){let a;try{a=JSON.parse(H(o))}catch{continue}const i=a.name??ee(M(o));for(const[l,u]of Object.entries(a.targets??{}))if(u.executor===n)return{project:e??i,target:s??l}}}d(ce,"we"),c(ce,"scanExecutorTarget");function Ae(t,n,e){return ce(t,"@lensmcp/cluster:gateway",n,e)||(n||e?{project:n??"gateway",target:e??"serve"}:void 0)}d(Ae,"ct"),c(Ae,"findGatewayTarget");function Re(t,n,e){return ce(t,"@lensmcp/cluster:trust",n,e)||(n?{project:n,target:e??"trust"}:De(t,"gateway")?{project:"gateway",target:e??"trust"}:void 0)}d(Re,"dt"),c(Re,"findTrustTarget");function De(t,n){for(const e of W(t,s=>s==="project.json"))try{if((JSON.parse(H(e)).name??ee(M(e)))===n)return!0}catch{}return!1}d(De,"lt"),c(De,"projectExists");function Ie(t){try{const n=Number(I(t,"utf8").trim());return Number.isInteger(n)&&n>0?n:void 0}catch{return}}d(Ie,"ut"),c(Ie,"readPid");function q(t){try{return process.kill(t,0),!0}catch(n){return n.code==="EPERM"}}d(q,"z"),c(q,"isAlive");function _(){try{const t=C("lsof",["-nP","-iTCP:443","-sTCP:LISTEN","-t"],{encoding:"utf8"});return t.status!==0||!t.stdout?[]:[...new Set(t.stdout.split(/\s+/).map(Number).filter(n=>Number.isInteger(n)&&n>0))]}catch{return[]}}d(_,"I"),c(_,"pidsOnPort443");function Fe(t){try{return(C("ps",["-o","command=","-p",String(t)],{encoding:"utf8"}).stdout??"").trim()}catch{return""}}d(Fe,"pt"),c(Fe,"processCmd");function F(t){const n=m(t,"node_modules","nx","dist","bin","run-executor.js"),e=m(t,"node_modules","@lensmcp","cluster");for(const s of _()){const r=Fe(s);if(r.includes(n)||r.includes(e))return s}}d(F,"G"),c(F,"gatewayOnPort443");function ue(t){try{return Date.now()-St(t).mtimeMs}catch{return}}d(ue,"ye"),c(ue,"pidFileAgeMs");const Vt=18e4;export function classifyAliveGateway(t){if(t.oursOn443)return"serving";const n=t.pidFileAgeMs!==void 0&&t.pidFileAgeMs<(t.bootGraceMs??Vt);return!t.port443Held&&n?"booting":"zombie"}d(classifyAliveGateway,"classifyAliveGateway"),c(classifyAliveGateway,"classifyAliveGateway");function z(t,n){const e=Ie(t);if(e!==void 0&&q(e))return{pid:e,healed:!1};const s=F(n);if(s!==void 0){try{B(t,String(s))}catch{}return{pid:s,healed:!0}}return{healed:!1}}d(z,"Y"),c(z,"reconcileGateway");function Y(t,n="SIGTERM"){try{process.kill(-t,n)}catch{try{process.kill(t,n)}catch{}}}d(Y,"X"),c(Y,"killGatewayTree");function de(t){const n=Date.now()+t;for(;;){if(_().length===0)return!0;if(Date.now()>=n)return!1;C("sleep",["0.15"])}}d(de,"ve"),c(de,"waitForPortFree");function Ge(t){const n=C("curl",["-s","-o","/dev/null","-m","8","-w","%{ssl_verify_result}",`https://${t}/`],{encoding:"utf8"}),e=(n.stdout??"").trim();return n.status===0&&e==="0"?"trusted":e&&e!=="0"?"untrusted":"unknown"}d(Ge,"ht"),c(Ge,"probeHostTrust");function He(t){for(const n of W(t,e=>e==="project.json"))try{const e=JSON.parse(I(n,"utf8")),s=(e.cluster??e.davnx)?.host;if(s&&!s.includes("*"))return s}catch{}}d(He,"gt"),c(He,"firstClusterHost");function G(t){try{bt(t,{force:!0})}catch{}}d(G,"H"),c(G,"rmFile");function We(t,n,e,s){const r=T(n,{string:["--cwd","--host","--port"]}),o=$(w(r.flags["--cwd"])??t.cwd),a=V(o),i=w(r.flags["--host"])??t.env?.LENSMCP_WS_HOST??process.env.LENSMCP_WS_HOST,l=w(r.flags["--port"])??t.env?.LENSMCP_WS_PORT??process.env.LENSMCP_WS_PORT??String(a.ports.bridge),u=at(o);if(!u)return s(`Could not locate the lensmcp bridge bundle.
138
138
  Looked in (in order):
139
139
  \u2022 <cli-package>/bundled/bridge.js (published bin)
140
140
  \u2022 <workspace>/libs/bridge/dist/main.js (dev build)
141
- For dev: \`yarn nx build @lensmcp/bridge\`.`),{exitCode:1};Z(o);const f=t.env?.LENSMCP_EVENT_FILE??process.env.LENSMCP_EVENT_FILE??v(o,".lensmcp","events.jsonl"),g={...process.env,...t.env??{},LENSMCP_EVENT_FILE:f};return i&&(g.LENSMCP_WS_HOST=i),h&&(g.LENSMCP_WS_PORT=h),e(`lensmcp bridge \u2192 ws://${i??"127.0.0.1"}:${h??"5747"} (forwarding to ${f})`),{exitCode:x(process.execPath,[u],{cwd:o,stdio:"inherit",env:g}).status??1}}d(Oe,"Le"),l(Oe,"We"),p(Oe,"mt"),c(Oe,"runBridge");function de(t,n,e,s){const r=P(n,{string:["--cwd","--register-host-config"],boolean:["--skip-format","--yes"]}),o=N(y(r.flags["--cwd"])??t.cwd);if(!k(v(o,"nx.json")))return s(`No nx.json found at ${o}.`),s("Run `lensmcp install` from inside a Nx workspace."),{exitCode:1};const a=Q(o);if(!a)return s("Could not find the `nx` binary in the host workspace. Install Nx first: `yarn add -D nx`."),{exitCode:1};const i=["g","@lensmcp/nx-plugin:init","--no-interactive"];return r.flags["--skip-format"]===!0&&i.push("--skipFormat"),r.flags["--register-host-config"]&&i.push(`--registerHostConfig=${String(r.flags["--register-host-config"])}`),e(`> ${a} ${i.join(" ")}`),{exitCode:x(a,i,{cwd:o,stdio:"inherit",env:{...process.env,...t.env??{}}}).status??1}}d(de,"de"),l(de,"le"),p(de,"be"),c(de,"runInstall");function le(t,n,e,s){const r=P(n,{string:["--cwd","--project","--target"]}),o=N(y(r.flags["--cwd"])??t.cwd);if(!k(v(o,"nx.json")))return s(`No nx.json found at ${o}.`),s("Run `lensmcp trust` from inside a Nx workspace (after `lensmcp install`)."),{exitCode:1};const a=Q(o);if(!a)return s("Could not find the `nx` binary in the workspace. Install Nx first: `yarn add -D nx`."),{exitCode:1};const i=je(o,y(r.flags["--project"]),y(r.flags["--target"])),h=_("GET","/list")?.status===200&&R(o)===void 0;if(!i){if(s("This workspace has no trust target \u2014 no project declares the `@lensmcp/cluster:trust` executor."),s(""),h){const f=ke();s(` A shared gateway daemon${f?` (owned by '${f}')`:""} already serves this workspace, and`),s(" TLS rides ITS certificate authority. Run `lensmcp trust` in THAT workspace \u2014 it owns the CA \u2014"),s(" and this workspace is trusted too (restart the browser once if it cached a cert error).")}else s(" \u2022 Should this workspace own its gateway (and its CA)? Scaffold it first: `lensmcp install`,"),s(" then re-run `lensmcp trust`."),s(" \u2022 Joining a shared gateway? Run `lensmcp trust` in the workspace that owns the daemon.");return s(" \u2022 Already have a differently-named target? `lensmcp trust --project <p> --target <t>`."),{exitCode:1}}Qe()||e(" note: trust runs `sudo` for the CA + /etc/hosts \u2014 run it in a real terminal if a step is needed."),h&&(e(" a shared gateway daemon serves this workspace \u2014 TLS rides ITS (already-trusted) CA, not a separate"),e(" one here. Setting up /etc/hosts only (trusting the CA is the DAEMON workspace's job).")),e(`> ${a} run ${i.project}:${i.target} (sudo prompts inline for ${h?"/etc/hosts":"the CA + /etc/hosts"})`);const u=x(a,["run",`${i.project}:${i.target}`],{cwd:o,stdio:"inherit",env:{...process.env,...t.env??{},...h?{LENSMCP_TRUST_HOSTS_ONLY:"1"}:{}}});if(h&&u.status===0){const f=Te(o);if(f){const g=Ee(f);e(""),e(g==="trusted"?` \u2713 ${f} \u2192 served by the daemon with a TRUSTED cert \u2014 you're set (restart the browser once if it cached an error).`:g==="untrusted"?` \u2717 ${f} \u2192 the daemon's cert is NOT trusted yet. Run \`lensmcp trust\` in the DAEMON workspace (it owns the CA), then restart your browser.`:` ? ${f} \u2192 couldn't reach it to verify (is the daemon running?). /etc/hosts is set; TLS rides the daemon's CA.`)}}return{exitCode:u.status??1}}d(le,"le"),l(le,"pe"),p(le,"ke"),c(le,"runTrust");function Le(t){Z(t);const n={version:X(),completedAt:new Date().toISOString(),steps:{install:!0,trust:!0}};try{K(v(t,".lensmcp","setup-state.json"),`${JSON.stringify(n,null,2)}
142
- `)}catch{}}d(Le,"Ae"),l(Le,"Je"),p(Le,"wt"),c(Le,"writeSetupState");async function _e(t,n,e,s){const r=lt(n)?n:at(t,e).mode,o=Rt(t,r);if(s(""),s(`[mcp] mode \u2192 ${r}`),o.wrote&&s(` wrote ${_t}`),r==="embedded"){s(" every agent session builds its OWN MCP server, and keeps serving"),s(" the bundle it started with until that session exits."),s(" one shared server for the workspace: `lensmcp setup --mcp shared`");return}s(` sessions proxy into ONE shared server at ${o.url}`),s(" .mcp.json is unchanged \u2014 `lensmcp mcp` becomes a thin stdio shim.");const a=await pt(o.url);s(a.ok?` \u2713 already up \u2014 ${a.server??"MCP"} (protocol ${a.protocolVersion??"?"})`:` \xB7 not up yet (${a.error??"unknown"}) \u2014 the next session starts it on demand`)}d(_e,"_e"),l(_e,"Be"),p(_e,"yt"),c(_e,"applyMcpModeStep");function Ae(t,n,e){const s=mt({cwd:t,cliVersion:X()}),r=Ut({run:Kt(n),facts:s});if(!(r.outcome==="current"||r.outcome==="unknown")){if(e(""),r.outcome==="updated"){e(`[plugin] ${r.from} \u2192 ${r.to}`),e(" restart Claude Code for the new plugin to load.");return}e(`[plugin] the Claude Code plugin is ${r.from}, this lensmcp is ${r.to}.`),e(` could not update it automatically (${r.detail??"unknown reason"}). Run:`);for(const o of r.commands??[])e(` ${o}`);e(" \u2026then restart Claude Code.")}}d(Ae,"Re"),l(Ae,"Ve"),p(Ae,"vt"),c(Ae,"applyPluginSyncStep");async function Re(t,n,e,s){const r=P(n,{string:["--cwd","--mcp"]}),o=N(y(r.flags["--cwd"])??t.cwd),a={...t,cwd:o},i=y(r.flags["--mcp"]);if(i!==void 0&&!lt(i))return s(`setup: --mcp must be "embedded" or "shared" (got "${i}").`),{exitCode:1};e("lensmcp setup \u2014 first-time bootstrap (install \u2192 trust \u2192 pods)"),e(""),e("[1/3] install \u2014 plugin, .mcp.json, nx config, vite/nest wiring");const h=de(a,n,e,s);if(h.exitCode!==0)return s(""),s("setup: the install step failed \u2014 fix the above and re-run `lensmcp setup`."),h;e(""),e("[2/3] trust \u2014 dev CA \u2192 System keychain, /etc/hosts, DNS flush (sudo)");const u=le(a,n,e,s);if(u.exitCode!==0)return s(""),s("setup: the trust step failed \u2014 run `lensmcp setup` directly in a terminal so sudo can prompt."),u;e(""),e("[3/3] pods \u2014 recycle this workspace's devservers, reconcile the gateway");const f=ee(o).key,g=st(o,f,e);nt(a,o,f,e),await _e(o,i,t.env??process.env,e);try{Ae(o,{...process.env,...t.env??{}},e)}catch{}return Le(o),e(""),e("\u2713 setup complete."),(g.killed>0||g.stale>0)&&e(` recycled ${g.killed} pod${g.killed===1?"":"s"}`+(g.stale>0?` (+${g.stale} stale socket${g.stale===1?"":"s"} cleared)`:"")+" \u2014 they come back on the new wiring."),e(" next: lensmcp gateway start (the :443 front door \u2014 needs sudo)"),e(" verify: lensmcp doctor"),{exitCode:0}}d(Re,"Ie"),l(Re,"Ke"),p(Re,"bt"),c(Re,"runSetup");async function Ie(t,n,e){const s=P(n,{string:["--cwd","--hook","--timeout","--session"],boolean:["--json","--quiet"]}),r=N(y(s.flags["--cwd"])??t.cwd),o=y(s.flags["--hook"]),a=ft(o)?o:void 0,i=y(s.flags["--session"]);if(o!==void 0&&a===void 0)return{exitCode:0};const h=s.flags["--json"]===!0,u=s.flags["--quiet"]===!0||o!==void 0,f=Number(y(s.flags["--timeout"])??""),g=Number.isFinite(f)&&f>0?f:Jt,b={...process.env,...t.env??{}};try{const m=await Bt({cwd:r,env:b,budgetMs:g,verify:c(w=>Dt(w).map(V=>V.resource),"verify")});if(!m.available)return h&&!u?e(JSON.stringify({available:!1,workspace:m.source.workspace,mode:m.source.mode,reason:m.reason,elapsedMs:m.elapsedMs})):u||e(`lensmcp status \u2014 ${m.source.workspace}: unavailable (${m.reason})`),{exitCode:0};const $=Wt(m.status,m.evidence,Date.now()),j=Ht($,{workspace:m.source.workspace,includeUnmonitored:a==="SessionStart"||!u});if(h)return u&&j===void 0?{exitCode:0}:(e(JSON.stringify({available:!0,workspace:m.source.workspace,mode:m.source.mode,url:m.source.url,aggregate:m.status.status??"unknown",elapsedMs:m.elapsedMs,findings:$.findings,suppressed:$.suppressed,unmonitored:$.unmonitored})),{exitCode:0});if(a){const w=Gt($.findings);return Ot(i,w)?(j!==void 0&&e(ht(a,j)),i&&Tt(i,w),{exitCode:0}):{exitCode:0}}if(u)return j!==void 0&&e(j),{exitCode:0};if(e(`lensmcp status \u2014 ${m.source.workspace} (${m.source.mode}, ${m.source.url})`),e(` aggregate says: ${m.status.status??"unknown"} (read in ${m.elapsedMs}ms)`),$.findings.length===0)e(" nothing actionable that this command can vouch for.");else for(const w of $.findings)e(` \u2022 ${Ft(w)}`);for(const w of $.suppressed)e(` \xB7 not reported \u2014 ${w.kind}: ${w.reason}`);return $.unmonitored.length>0&&e(` \xB7 unmonitored (unknown, NOT clean): ${$.unmonitored.join(", ")}`),{exitCode:0}}catch(m){return u||e(`lensmcp status: unavailable (${m instanceof Error?m.message:String(m)})`),{exitCode:0}}}d(Ie,"De"),l(Ie,"Ue"),p(Ie,"kt"),c(Ie,"runStatus");function De(t,n,e){const s=P(n,{string:["--cwd","--hook","--plugin-root"],boolean:["--json","--quiet"]}),r=N(y(s.flags["--cwd"])??t.cwd),o=y(s.flags["--hook"]),a=ft(o)?o:void 0;if(o!==void 0&&a===void 0)return{exitCode:0};const i=s.flags["--json"]===!0,h=s.flags["--quiet"]===!0||o!==void 0,u=y(s.flags["--plugin-root"]);try{const f=mt({cwd:r,cliVersion:X(),...u?{pluginRoot:u}:{}}),g=Vt(f);if(i)return h&&!g?{exitCode:0}:(e(JSON.stringify({...f,skew:g??null})),{exitCode:0});if(a)return g&&e(ht(a,gt(g))),{exitCode:0};if(h)return g&&e(gt(g)),{exitCode:0};if(e(`lensmcp version-check \u2014 ${f.pluginId??"lensmcp plugin"}`),e(` plugin ${f.plugin??"unknown"}${f.pluginSource?` (${f.pluginSource})`:""}`),e(` project ${f.project??"unknown"} (node_modules/lensmcp)`),e(` cli ${f.cli??"unknown"} (the binary running now)`),!g)return e(f.plugin&&(f.project??f.cli)?" \u2713 in step.":" ? cannot compare \u2014 one of the versions is unreadable (staying silent)."),{exitCode:0};e(` \u2717 the plugin is ${g.direction} \u2014 update it, then restart Claude Code:`);for(const b of qt(g))e(` ${b}`);return{exitCode:0}}catch(f){return h||e(`lensmcp version-check: unavailable (${f instanceof Error?f.message:String(f)})`),{exitCode:0}}}d(De,"Fe"),l(De,"qe"),p(De,"St"),c(De,"runVersionCheck");async function Fe(t,n,e){const s=P(n,{string:["--cwd"],boolean:["--json"]}),r=N(y(s.flags["--cwd"])??t.cwd),o=s.flags["--json"]===!0,a=[];Ge(a),He(r,a),We(r,a),await Je(r,t.env??process.env,a),Be(r,a),Ve(r,a),Ke(r,a),Ue(a);const i=a.some(u=>u.status==="fail"),h=a.some(u=>u.status==="warn");if(o)return e(JSON.stringify({ok:!i,summary:{total:a.length,failed:a.filter(u=>u.status==="fail").length,warnings:a.filter(u=>u.status==="warn").length},checks:a},null,2)),{exitCode:i?1:0};for(const u of a){const f=u.status==="pass"?"\u2713":u.status==="warn"?"!":"\u2717";e(`${f} ${u.label}${u.detail?` \u2014 ${u.detail}`:""}`),u.status!=="pass"&&u.hint&&e(` \u21B3 ${u.hint}`)}return e(""),e(i?"doctor: issues found (see \u2717 above).":h?"doctor: ok, with warnings (!).":"doctor: all checks passed."),{exitCode:i?1:0}}d(Fe,"Ge"),l(Fe,"ze"),p(Fe,"Ct"),c(Fe,"runDoctor");function S(t,n,e,s,r){t.push({status:n,label:e,detail:s,hint:r})}d(S,"C"),l(S,"S"),p(S,"b"),c(S,"add");function Ge(t){const n=Number(process.versions.node.split(".")[0]);n>=20?S(t,"pass",`Node ${process.versions.node}`):n>=18?S(t,"warn",`Node ${process.versions.node}`,"older than recommended","upgrade to Node 20+ for best results"):S(t,"fail",`Node ${process.versions.node}`,"too old","LensMCP needs Node 18+ (20+ recommended)")}d(Ge,"He"),l(Ge,"Ye"),p(Ge,"$t"),c(Ge,"checkNode");function He(t,n){const e=v(t,"nx.json");if(!k(e)){S(n,"fail","nx.json at workspace root","missing","run `lensmcp` from inside a Nx workspace");return}let s;try{s=JSON.parse(B(e,"utf8"))}catch(o){S(n,"fail","nx.json parses",o.message,"fix the JSON syntax in nx.json");return}const r=(s.plugins??[]).some(o=>typeof o=="string"?o==="@lensmcp/nx-plugin":!!o&&typeof o=="object"&&o.plugin==="@lensmcp/nx-plugin");S(n,r?"pass":"fail","@lensmcp/nx-plugin registered in nx.json#plugins",r?void 0:"not registered",r?void 0:"run `lensmcp install`"),S(n,s.lensmcp?.schemaVersion===1?"pass":"warn","lensmcp config block in nx.json",s.lensmcp?.schemaVersion===1?void 0:"missing",s.lensmcp?.schemaVersion===1?void 0:"run `lensmcp install` to write the config defaults")}d(He,"Je"),l(He,"Xe"),p(He,"Nt"),c(He,"checkWorkspace");function We(t,n){const e=v(t,".mcp.json");if(!k(e)){S(n,"warn",".mcp.json with a lensmcp server","missing","run `lensmcp install` so agents auto-discover the MCP server");return}try{const s=JSON.parse(B(e,"utf8")),r=!!s.mcpServers&&!!s.mcpServers.lensmcp;S(n,r?"pass":"warn",".mcp.json registers the lensmcp MCP server",r?void 0:"no lensmcp entry",r?void 0:"run `lensmcp install`")}catch(s){S(n,"warn",".mcp.json parses",s.message,"fix the JSON syntax in .mcp.json")}}d(We,"We"),l(We,"Qe"),p(We,"jt"),c(We,"checkMcpConfig");async function Je(t,n,e){const s=at(t,n);if(S(e,"pass","MCP mode",s.mode==="shared"?`shared \u2014 sessions proxy into ${s.url}`:"embedded \u2014 one MCP server per agent session",s.mode==="shared"?void 0:"one shared server: `lensmcp setup --mcp shared`"),s.mode!=="shared")return;const r=await pt(s.url);S(e,r.ok?"pass":"warn","shared MCP server",r.ok?`${r.server??"MCP"} @ ${s.url} (protocol ${r.protocolVersion??"?"})`:`not running at ${s.url} \u2014 ${r.error??"no answer"}`,r.ok?void 0:"the next agent session starts it on demand; `lensmcp gateway start` starts it now")}d(Je,"Be"),l(Je,"Ze"),p(Je,"xt"),c(Je,"checkMcpMode");function pe(t,n){const e=at(t,n);if(e.mode!=="shared")return{mode:e.mode,url:e.url,port:e.port,healthy:!1};const s=At(e.url);return{mode:e.mode,url:e.url,port:e.port,healthy:s.ok,...s.server?{server:s.server}:{},...s.error?{error:s.error}:{}}}d(pe,"pe"),l(pe,"he"),p(pe,"Se"),c(pe,"mcpStatusBlock");function Be(t,n){const e=se(t);S(n,e?"pass":"fail","lensmcp-mcp server bundle reachable",e??"missing",e?void 0:"dev: `yarn nx build @lensmcp/lensmcp-mcp`")}d(Be,"Ve"),l(Be,"et"),p(Be,"Pt"),c(Be,"checkServerBundle");function Ve(t,n){const e=v(t,".lensmcp");S(n,k(e)?"pass":"warn",".lensmcp/ runtime directory",k(e)?void 0:"not created yet",k(e)?void 0:"created automatically on first `lensmcp mcp` run");const s=v(t,".gitignore"),r=(k(s)?D(s):"").split(`
143
- `).some(o=>o.trim()===".lensmcp/"||o.trim()===".lensmcp");S(n,r?"pass":"warn",".gitignore excludes .lensmcp/",r?void 0:"not ignored",r?void 0:"add `.lensmcp/` to .gitignore (lensmcp install does this)")}d(Ve,"Ke"),l(Ve,"tt"),p(Ve,"Et"),c(Ve,"checkRuntimeDir");function Ke(t,n){const e=F(t,i=>/^vite\.config\.[cm]?[jt]s$/.test(i)),s=e.filter(i=>/lensmcpVitePlugin|@lensmcp\/vite-plugin/.test(D(i)));if(e.length>0){const i=s.length===e.length;S(n,i?"pass":"warn",`Vite wiring: ${s.length}/${e.length} config(s) use the LensMCP plugin`,void 0,i?void 0:"wire each: `nx g @lensmcp/nx-plugin:setup-vite <project>`")}const r=F(t,i=>i==="main.ts").filter(i=>/NestFactory\.create|createLensmcpNestApp/.test(D(i))),o=r.filter(i=>/createLensmcpNestApp/.test(D(i)));if(r.length>0){const i=o.length===r.length;S(n,i?"pass":"warn",`Nest wiring: ${o.length}/${r.length} bootstrap(s) use createLensmcpNestApp`,void 0,i?void 0:"wire each: `nx g @lensmcp/nx-plugin:setup-nest <project>`")}const a=F(t,i=>i==="project.json"||i==="package.json").filter(i=>/"agent-dev"/.test(D(i)));S(n,a.length>0?"pass":"warn",`agent-dev targets: ${a.length} project(s)`,void 0,a.length>0?void 0:"no project wired \u2014 run setup-vite / setup-nest")}d(Ke,"Ue"),l(Ke,"st"),p(Ke,"Mt"),c(Ke,"checkProjectWiring");function Ue(t){const n=process.env.CHROME_PATH,e=process.platform==="darwin"?["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Chromium.app/Contents/MacOS/Chromium"]:process.platform==="win32"?["C:/Program Files/Google/Chrome/Application/chrome.exe","C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"]:["/usr/bin/google-chrome","/usr/bin/chromium-browser","/usr/bin/chromium","/usr/bin/google-chrome-stable"],s=(n&&k(n)?n:void 0)??e.find(r=>k(r));S(t,s?"pass":"warn","Chrome for browser:// capture",s?we(s):"not found",s?void 0:"install Chrome or set CHROME_PATH (only needed for browser capture)")}d(Ue,"qe"),l(Ue,"nt"),p(Ue,"Tt"),c(Ue,"checkChrome");function D(t){try{return B(t,"utf8")}catch{return""}}d(D,"D"),l(D,"H"),p(D,"J"),c(D,"safeRead");function F(t,n,e=6,s=6e3){const r=new Set(["node_modules","dist","build",".git",".nx",".lensmcp","coverage","tmp",".cache",".yarn"]),o=[],a=[{dir:t,depth:0}];let i=0;for(;a.length>0;){const{dir:h,depth:u}=a.pop();let f;try{f=it(h,{withFileTypes:!0})}catch{continue}for(const g of f){if(i++>s)return o;if(g.isDirectory()){if(r.has(g.name)||g.name.startsWith("."))continue;u<e&&a.push({dir:v(h,g.name),depth:u+1})}else g.isFile()&&n(g.name)&&o.push(v(h,g.name))}}return o}d(F,"F"),l(F,"W"),p(F,"W"),c(F,"walkProjectFiles");function te(){let t=L(Et(import.meta.url));for(let n=0;n<4;n++){if(k(v(t,"package.json")))return t;const e=L(t);if(e===t)break;t=e}return t}d(te,"te"),l(te,"te"),p(te,"se"),c(te,"cliPackageRoot");function X(){try{return JSON.parse(B(v(te(),"package.json"),"utf8")).version}catch{return"0.0.0"}}d(X,"X"),l(X,"X"),p(X,"Q"),c(X,"readCliVersion");function qe(t,n,e){const s=N(t.cwd),r=ze(s);return r?{exitCode:x(process.execPath,[r,...n],{stdio:"inherit",cwd:s,env:{...process.env,...t.env}}).status??1}:(e(`Could not locate the lensmcp rollout helper (@lensmcp/cluster).
141
+ For dev: \`yarn nx build @lensmcp/bridge\`.`),{exitCode:1};Z(o);const p=t.env?.LENSMCP_EVENT_FILE??process.env.LENSMCP_EVENT_FILE??m(o,".lensmcp","events.jsonl"),h={...process.env,...t.env??{},LENSMCP_EVENT_FILE:p};return i&&(h.LENSMCP_WS_HOST=i),l&&(h.LENSMCP_WS_PORT=l),e(`lensmcp bridge \u2192 ws://${i??"127.0.0.1"}:${l??"5747"} (forwarding to ${p})`),{exitCode:C(process.execPath,[u],{cwd:o,stdio:"inherit",env:h}).status??1}}d(We,"mt"),c(We,"runBridge");function le(t,n,e,s){const r=T(n,{string:["--cwd","--register-host-config"],boolean:["--skip-format","--yes"]}),o=$(w(r.flags["--cwd"])??t.cwd);if(!y(m(o,"nx.json")))return s(`No nx.json found at ${o}.`),s("Run `lensmcp install` from inside a Nx workspace."),{exitCode:1};const a=Q(o);if(!a)return s("Could not find the `nx` binary in the host workspace. Install Nx first: `yarn add -D nx`."),{exitCode:1};const i=["g","@lensmcp/nx-plugin:init","--no-interactive"];return r.flags["--skip-format"]===!0&&i.push("--skipFormat"),r.flags["--register-host-config"]&&i.push(`--registerHostConfig=${String(r.flags["--register-host-config"])}`),e(`> ${a} ${i.join(" ")}`),{exitCode:C(a,i,{cwd:o,stdio:"inherit",env:{...process.env,...t.env??{}}}).status??1}}d(le,"be"),c(le,"runInstall");function pe(t,n,e,s){const r=T(n,{string:["--cwd","--project","--target"]}),o=$(w(r.flags["--cwd"])??t.cwd);if(!y(m(o,"nx.json")))return s(`No nx.json found at ${o}.`),s("Run `lensmcp trust` from inside a Nx workspace (after `lensmcp install`)."),{exitCode:1};const a=Q(o);if(!a)return s("Could not find the `nx` binary in the workspace. Install Nx first: `yarn add -D nx`."),{exitCode:1};const i=Re(o,w(r.flags["--project"]),w(r.flags["--target"])),l=O("GET","/list")?.status===200&&F(o)===void 0;if(!i){if(s("This workspace has no trust target \u2014 no project declares the `@lensmcp/cluster:trust` executor."),s(""),l){const p=Te();s(` A shared gateway daemon${p?` (owned by '${p}')`:""} already serves this workspace, and`),s(" TLS rides ITS certificate authority. Run `lensmcp trust` in THAT workspace \u2014 it owns the CA \u2014"),s(" and this workspace is trusted too (restart the browser once if it cached a cert error).")}else s(" \u2022 Should this workspace own its gateway (and its CA)? Scaffold it first: `lensmcp install`,"),s(" then re-run `lensmcp trust`."),s(" \u2022 Joining a shared gateway? Run `lensmcp trust` in the workspace that owns the daemon.");return s(" \u2022 Already have a differently-named target? `lensmcp trust --project <p> --target <t>`."),{exitCode:1}}ct()||e(" note: trust runs `sudo` for the CA + /etc/hosts \u2014 run it in a real terminal if a step is needed."),l&&(e(" a shared gateway daemon serves this workspace \u2014 TLS rides ITS (already-trusted) CA, not a separate"),e(" one here. Setting up /etc/hosts only (trusting the CA is the DAEMON workspace's job).")),e(`> ${a} run ${i.project}:${i.target} (sudo prompts inline for ${l?"/etc/hosts":"the CA + /etc/hosts"})`);const u=C(a,["run",`${i.project}:${i.target}`],{cwd:o,stdio:"inherit",env:{...process.env,...t.env??{},...l?{LENSMCP_TRUST_HOSTS_ONLY:"1"}:{}}});if(l&&u.status===0){const p=He(o);if(p){const h=Ge(p);e(""),e(h==="trusted"?` \u2713 ${p} \u2192 served by the daemon with a TRUSTED cert \u2014 you're set (restart the browser once if it cached an error).`:h==="untrusted"?` \u2717 ${p} \u2192 the daemon's cert is NOT trusted yet. Run \`lensmcp trust\` in the DAEMON workspace (it owns the CA), then restart your browser.`:` ? ${p} \u2192 couldn't reach it to verify (is the daemon running?). /etc/hosts is set; TLS rides the daemon's CA.`)}}return{exitCode:u.status??1}}d(pe,"ke"),c(pe,"runTrust");function Je(t){Z(t);const n={version:X(),completedAt:new Date().toISOString(),steps:{install:!0,trust:!0}};try{B(m(t,".lensmcp","setup-state.json"),`${JSON.stringify(n,null,2)}
142
+ `)}catch{}}d(Je,"wt"),c(Je,"writeSetupState");async function Be(t,n,e,s){const r=be(n)?n:ie(t,e).mode,o=Ot(t,r);if(s(""),s(`[mcp] mode \u2192 ${r}`),o.wrote&&s(` wrote ${Mt}`),r==="embedded"){s(" every agent session builds its OWN MCP server, and keeps serving"),s(" the bundle it started with until that session exits."),s(" one shared server for the workspace: `lensmcp setup --mcp shared`");return}s(` sessions proxy into ONE shared server at ${o.url}`),s(" .mcp.json is unchanged \u2014 `lensmcp mcp` becomes a thin stdio shim.");const a=await Se(o.url);s(a.ok?` \u2713 already up \u2014 ${a.server??"MCP"} (protocol ${a.protocolVersion??"?"})`:` \xB7 not up yet (${a.error??"unknown"}) \u2014 the next session starts it on demand`)}d(Be,"yt"),c(Be,"applyMcpModeStep");function Ve(t,n,e){const s=je({cwd:t,cliVersion:X()}),r=Jt({run:Wt(n),facts:s});if(!(r.outcome==="current"||r.outcome==="unknown")){if(e(""),r.outcome==="updated"){e(`[plugin] ${r.from} \u2192 ${r.to}`),e(" restart Claude Code for the new plugin to load.");return}e(`[plugin] the Claude Code plugin is ${r.from}, this lensmcp is ${r.to}.`),e(` could not update it automatically (${r.detail??"unknown reason"}). Run:`);for(const o of r.commands??[])e(` ${o}`);e(" \u2026then restart Claude Code.")}}d(Ve,"vt"),c(Ve,"applyPluginSyncStep");async function Ke(t,n,e,s){const r=T(n,{string:["--cwd","--mcp"]}),o=$(w(r.flags["--cwd"])??t.cwd),a={...t,cwd:o},i=w(r.flags["--mcp"]);if(i!==void 0&&!be(i))return s(`setup: --mcp must be "embedded" or "shared" (got "${i}").`),{exitCode:1};e("lensmcp setup \u2014 first-time bootstrap (install \u2192 trust \u2192 pods)"),e(""),e("[1/3] install \u2014 plugin, .mcp.json, nx config, vite/nest wiring");const l=le(a,n,e,s);if(l.exitCode!==0)return s(""),s("setup: the install step failed \u2014 fix the above and re-run `lensmcp setup`."),l;e(""),e("[2/3] trust \u2014 dev CA \u2192 System keychain, /etc/hosts, DNS flush (sudo)");const u=pe(a,n,e,s);if(u.exitCode!==0)return s(""),s("setup: the trust step failed \u2014 run `lensmcp setup` directly in a terminal so sudo can prompt."),u;e(""),e("[3/3] pods \u2014 recycle this workspace's devservers, reconcile the gateway");const p=V(o).key,h=pt(o,p,e);ht(a,o,p,e),await Be(o,i,t.env??process.env,e);try{Ve(o,{...process.env,...t.env??{}},e)}catch{}return Je(o),e(""),e("\u2713 setup complete."),(h.killed>0||h.stale>0)&&e(` recycled ${h.killed} pod${h.killed===1?"":"s"}`+(h.stale>0?` (+${h.stale} stale socket${h.stale===1?"":"s"} cleared)`:"")+" \u2014 they come back on the new wiring."),e(" next: lensmcp gateway start (the :443 front door \u2014 needs sudo)"),e(" verify: lensmcp doctor"),{exitCode:0}}d(Ke,"bt"),c(Ke,"runSetup");async function Ue(t,n,e){const s=T(n,{string:["--cwd","--hook","--timeout","--session"],boolean:["--json","--quiet"]}),r=$(w(s.flags["--cwd"])??t.cwd),o=w(s.flags["--hook"]),a=xe(o)?o:void 0,i=w(s.flags["--session"]);if(o!==void 0&&a===void 0)return{exitCode:0};const l=s.flags["--json"]===!0,u=s.flags["--quiet"]===!0||o!==void 0,p=Number(w(s.flags["--timeout"])??""),h=Number.isFinite(p)&&p>0?p:Ft,v={...process.env,...t.env??{}};try{const f=await Gt({cwd:r,env:v,budgetMs:h,verify:c(g=>_t(g).map(J=>J.resource),"verify")});if(!f.available)return l&&!u?e(JSON.stringify({available:!1,workspace:f.source.workspace,mode:f.source.mode,reason:f.reason,elapsedMs:f.elapsedMs})):u||e(`lensmcp status \u2014 ${f.source.workspace}: unavailable (${f.reason})`),{exitCode:0};const b=It(f.status,f.evidence,Date.now()),x=Dt(b,{workspace:f.source.workspace,includeUnmonitored:a==="SessionStart"||!u});if(l)return u&&x===void 0?{exitCode:0}:(e(JSON.stringify({available:!0,workspace:f.source.workspace,mode:f.source.mode,url:f.source.url,aggregate:f.status.status??"unknown",elapsedMs:f.elapsedMs,findings:b.findings,suppressed:b.suppressed,unmonitored:b.unmonitored})),{exitCode:0});if(a){const g=Rt(b.findings);return Pt(i,g)?(x!==void 0&&e(Ce(a,x)),i&&Nt(i,g),{exitCode:0}):{exitCode:0}}if(u)return x!==void 0&&e(x),{exitCode:0};if(e(`lensmcp status \u2014 ${f.source.workspace} (${f.source.mode}, ${f.source.url})`),e(` aggregate says: ${f.status.status??"unknown"} (read in ${f.elapsedMs}ms)`),b.findings.length===0)e(" nothing actionable that this command can vouch for.");else for(const g of b.findings)e(` \u2022 ${At(g)}`);for(const g of b.suppressed)e(` \xB7 not reported \u2014 ${g.kind}: ${g.reason}`);return b.unmonitored.length>0&&e(` \xB7 unmonitored (unknown, NOT clean): ${b.unmonitored.join(", ")}`),{exitCode:0}}catch(f){return u||e(`lensmcp status: unavailable (${f instanceof Error?f.message:String(f)})`),{exitCode:0}}}d(Ue,"kt"),c(Ue,"runStatus");function qe(t,n,e){const s=T(n,{string:["--cwd","--hook","--plugin-root"],boolean:["--json","--quiet"]}),r=$(w(s.flags["--cwd"])??t.cwd),o=w(s.flags["--hook"]),a=xe(o)?o:void 0;if(o!==void 0&&a===void 0)return{exitCode:0};const i=s.flags["--json"]===!0,l=s.flags["--quiet"]===!0||o!==void 0,u=w(s.flags["--plugin-root"]);try{const p=je({cwd:r,cliVersion:X(),...u?{pluginRoot:u}:{}}),h=Ht(p);if(i)return l&&!h?{exitCode:0}:(e(JSON.stringify({...p,skew:h??null})),{exitCode:0});if(a)return h&&e(Ce(a,$e(h))),{exitCode:0};if(l)return h&&e($e(h)),{exitCode:0};if(e(`lensmcp version-check \u2014 ${p.pluginId??"lensmcp plugin"}`),e(` plugin ${p.plugin??"unknown"}${p.pluginSource?` (${p.pluginSource})`:""}`),e(` project ${p.project??"unknown"} (node_modules/lensmcp)`),e(` cli ${p.cli??"unknown"} (the binary running now)`),!h)return e(p.plugin&&(p.project??p.cli)?" \u2713 in step.":" ? cannot compare \u2014 one of the versions is unreadable (staying silent)."),{exitCode:0};e(` \u2717 the plugin is ${h.direction} \u2014 update it, then restart Claude Code:`);for(const v of Bt(h))e(` ${v}`);return{exitCode:0}}catch(p){return l||e(`lensmcp version-check: unavailable (${p instanceof Error?p.message:String(p)})`),{exitCode:0}}}d(qe,"St"),c(qe,"runVersionCheck");async function ze(t,n,e){const s=T(n,{string:["--cwd"],boolean:["--json"]}),r=$(w(s.flags["--cwd"])??t.cwd),o=s.flags["--json"]===!0,a=[];Ye(a),Xe(r,a),Qe(r,a),await Ze(r,t.env??process.env,a),et(r,a),tt(r,a),st(r,a),nt(a);const i=a.some(u=>u.status==="fail"),l=a.some(u=>u.status==="warn");if(o)return e(JSON.stringify({ok:!i,summary:{total:a.length,failed:a.filter(u=>u.status==="fail").length,warnings:a.filter(u=>u.status==="warn").length},checks:a},null,2)),{exitCode:i?1:0};for(const u of a){const p=u.status==="pass"?"\u2713":u.status==="warn"?"!":"\u2717";e(`${p} ${u.label}${u.detail?` \u2014 ${u.detail}`:""}`),u.status!=="pass"&&u.hint&&e(` \u21B3 ${u.hint}`)}return e(""),e(i?"doctor: issues found (see \u2717 above).":l?"doctor: ok, with warnings (!).":"doctor: all checks passed."),{exitCode:i?1:0}}d(ze,"Ct"),c(ze,"runDoctor");function S(t,n,e,s,r){t.push({status:n,label:e,detail:s,hint:r})}d(S,"b"),c(S,"add");function Ye(t){const n=Number(process.versions.node.split(".")[0]);n>=20?S(t,"pass",`Node ${process.versions.node}`):n>=18?S(t,"warn",`Node ${process.versions.node}`,"older than recommended","upgrade to Node 20+ for best results"):S(t,"fail",`Node ${process.versions.node}`,"too old","LensMCP needs Node 18+ (20+ recommended)")}d(Ye,"$t"),c(Ye,"checkNode");function Xe(t,n){const e=m(t,"nx.json");if(!y(e)){S(n,"fail","nx.json at workspace root","missing","run `lensmcp` from inside a Nx workspace");return}let s;try{s=JSON.parse(I(e,"utf8"))}catch(o){S(n,"fail","nx.json parses",o.message,"fix the JSON syntax in nx.json");return}const r=(s.plugins??[]).some(o=>typeof o=="string"?o==="@lensmcp/nx-plugin":!!o&&typeof o=="object"&&o.plugin==="@lensmcp/nx-plugin");S(n,r?"pass":"fail","@lensmcp/nx-plugin registered in nx.json#plugins",r?void 0:"not registered",r?void 0:"run `lensmcp install`"),S(n,s.lensmcp?.schemaVersion===1?"pass":"warn","lensmcp config block in nx.json",s.lensmcp?.schemaVersion===1?void 0:"missing",s.lensmcp?.schemaVersion===1?void 0:"run `lensmcp install` to write the config defaults")}d(Xe,"Nt"),c(Xe,"checkWorkspace");function Qe(t,n){const e=m(t,".mcp.json");if(!y(e)){S(n,"warn",".mcp.json with a lensmcp server","missing","run `lensmcp install` so agents auto-discover the MCP server");return}try{const s=JSON.parse(I(e,"utf8")),r=!!s.mcpServers&&!!s.mcpServers.lensmcp;S(n,r?"pass":"warn",".mcp.json registers the lensmcp MCP server",r?void 0:"no lensmcp entry",r?void 0:"run `lensmcp install`")}catch(s){S(n,"warn",".mcp.json parses",s.message,"fix the JSON syntax in .mcp.json")}}d(Qe,"jt"),c(Qe,"checkMcpConfig");async function Ze(t,n,e){const s=ie(t,n);if(S(e,"pass","MCP mode",s.mode==="shared"?`shared \u2014 sessions proxy into ${s.url}`:"embedded \u2014 one MCP server per agent session",s.mode==="shared"?void 0:"one shared server: `lensmcp setup --mcp shared`"),s.mode!=="shared")return;const r=await Se(s.url);S(e,r.ok?"pass":"warn","shared MCP server",r.ok?`${r.server??"MCP"} @ ${s.url} (protocol ${r.protocolVersion??"?"})`:`not running at ${s.url} \u2014 ${r.error??"no answer"}`,r.ok?void 0:"the next agent session starts it on demand; `lensmcp gateway start` starts it now")}d(Ze,"xt"),c(Ze,"checkMcpMode");function he(t,n){const e=ie(t,n);if(e.mode!=="shared")return{mode:e.mode,url:e.url,port:e.port,healthy:!1};const s=Tt(e.url);return{mode:e.mode,url:e.url,port:e.port,healthy:s.ok,...s.server?{server:s.server}:{},...s.error?{error:s.error}:{}}}d(he,"Se"),c(he,"mcpStatusBlock");function et(t,n){const e=se(t);S(n,e?"pass":"fail","lensmcp-mcp server bundle reachable",e??"missing",e?void 0:"dev: `yarn nx build @lensmcp/lensmcp-mcp`")}d(et,"Pt"),c(et,"checkServerBundle");function tt(t,n){const e=m(t,".lensmcp");S(n,y(e)?"pass":"warn",".lensmcp/ runtime directory",y(e)?void 0:"not created yet",y(e)?void 0:"created automatically on first `lensmcp mcp` run");const s=m(t,".gitignore"),r=(y(s)?H(s):"").split(`
143
+ `).some(o=>o.trim()===".lensmcp/"||o.trim()===".lensmcp");S(n,r?"pass":"warn",".gitignore excludes .lensmcp/",r?void 0:"not ignored",r?void 0:"add `.lensmcp/` to .gitignore (lensmcp install does this)")}d(tt,"Et"),c(tt,"checkRuntimeDir");function st(t,n){const e=W(t,i=>/^vite\.config\.[cm]?[jt]s$/.test(i)),s=e.filter(i=>/lensmcpVitePlugin|@lensmcp\/vite-plugin/.test(H(i)));if(e.length>0){const i=s.length===e.length;S(n,i?"pass":"warn",`Vite wiring: ${s.length}/${e.length} config(s) use the LensMCP plugin`,void 0,i?void 0:"wire each: `nx g @lensmcp/nx-plugin:setup-vite <project>`")}const r=W(t,i=>i==="main.ts").filter(i=>/NestFactory\.create|createLensmcpNestApp/.test(H(i))),o=r.filter(i=>/createLensmcpNestApp/.test(H(i)));if(r.length>0){const i=o.length===r.length;S(n,i?"pass":"warn",`Nest wiring: ${o.length}/${r.length} bootstrap(s) use createLensmcpNestApp`,void 0,i?void 0:"wire each: `nx g @lensmcp/nx-plugin:setup-nest <project>`")}const a=W(t,i=>i==="project.json"||i==="package.json").filter(i=>/"agent-dev"/.test(H(i)));S(n,a.length>0?"pass":"warn",`agent-dev targets: ${a.length} project(s)`,void 0,a.length>0?void 0:"no project wired \u2014 run setup-vite / setup-nest")}d(st,"Mt"),c(st,"checkProjectWiring");function nt(t){const n=process.env.CHROME_PATH,e=process.platform==="darwin"?["/Applications/Google Chrome.app/Contents/MacOS/Google Chrome","/Applications/Chromium.app/Contents/MacOS/Chromium"]:process.platform==="win32"?["C:/Program Files/Google/Chrome/Application/chrome.exe","C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"]:["/usr/bin/google-chrome","/usr/bin/chromium-browser","/usr/bin/chromium","/usr/bin/google-chrome-stable"],s=(n&&y(n)?n:void 0)??e.find(r=>y(r));S(t,s?"pass":"warn","Chrome for browser:// capture",s?ee(s):"not found",s?void 0:"install Chrome or set CHROME_PATH (only needed for browser capture)")}d(nt,"Tt"),c(nt,"checkChrome");function H(t){try{return I(t,"utf8")}catch{return""}}d(H,"J"),c(H,"safeRead");function W(t,n,e=6,s=6e3){const r=new Set(["node_modules","dist","build",".git",".nx",".lensmcp","coverage","tmp",".cache",".yarn"]),o=[],a=[{dir:t,depth:0}];let i=0;for(;a.length>0;){const{dir:l,depth:u}=a.pop();let p;try{p=oe(l,{withFileTypes:!0})}catch{continue}for(const h of p){if(i++>s)return o;if(h.isDirectory()){if(r.has(h.name)||h.name.startsWith("."))continue;u<e&&a.push({dir:m(l,h.name),depth:u+1})}else h.isFile()&&n(h.name)&&o.push(m(l,h.name))}}return o}d(W,"W"),c(W,"walkProjectFiles");function te(){let t=M(jt(import.meta.url));for(let n=0;n<4;n++){if(y(m(t,"package.json")))return t;const e=M(t);if(e===t)break;t=e}return t}d(te,"se"),c(te,"cliPackageRoot");function X(){try{return JSON.parse(I(m(te(),"package.json"),"utf8")).version}catch{return"0.0.0"}}d(X,"Q"),c(X,"readCliVersion");function rt(t,n,e){const s=$(t.cwd),r=ot(s);return r?{exitCode:C(process.execPath,[r,...n],{stdio:"inherit",cwd:s,env:{...process.env,...t.env}}).status??1}:(e(`Could not locate the lensmcp rollout helper (@lensmcp/cluster).
144
144
  Looked for main.rollout.js (walking up from cwd):
145
145
  \u2022 <dir>/node_modules/@lensmcp/cluster/executors/gateway/main.rollout.js
146
146
  \u2022 <dir>/dist/libs/cluster/executors/gateway/main.rollout.js
147
- Install @lensmcp/cluster, or \`yarn nx build @lensmcp/cluster\` in the workspace.`),{exitCode:1})}d(qe,"ze"),l(qe,"rt"),p(qe,"Ot"),c(qe,"runRollout");function ze(t){let n=t;for(let e=0;e<6;e++){for(const r of[v(n,"node_modules","@lensmcp","cluster","executors","gateway","main.rollout.js"),v(n,"dist","libs","cluster","executors","gateway","main.rollout.js")])if(k(r))return r;const s=L(n);if(s===n)break;n=s}}d(ze,"Ye"),l(ze,"ot"),p(ze,"Lt"),c(ze,"findRolloutBundle");function se(t){return ne(t,"main.js")}d(se,"se"),l(se,"se"),p(se,"re"),c(se,"findMcpBinary");function Ye(t){return ne(t,"shim.js")}d(Ye,"Xe"),l(Ye,"it"),p(Ye,"_t"),c(Ye,"findShimBinary");function ne(t,n){const e=v(te(),"bundled",n);if(k(e))return e;const s=v(t,"servers","lensmcp-mcp","dist",n);if(k(s))return s;let r=t;for(let o=0;o<5;o++){const a=v(r,"servers","lensmcp-mcp","dist",n);if(k(a))return a;const i=L(r);if(i===r)break;r=i}}d(ne,"ne"),l(ne,"ne"),p(ne,"oe"),c(ne,"findServerBundle");function Xe(t){const n=v(te(),"bundled","bridge.js");if(k(n))return n;let e=t;for(let s=0;s<5;s++){const r=v(e,"libs","bridge","dist","main.js");if(k(r))return r;const o=L(e);if(o===e)break;e=o}}d(Xe,"Qe"),l(Xe,"at"),p(Xe,"Rt"),c(Xe,"findBridgeBundle");function Q(t){let n=t;for(let e=0;e<5;e++){const s=v(n,"node_modules",".bin","nx");if(k(s))return s;const r=L(n);if(r===n)break;n=r}}d(Q,"Q"),l(Q,"Q"),p(Q,"Z"),c(Q,"findNxBinary");function Z(t){const n=v(t,".lensmcp");k(n)||oe(n,{recursive:!0})}d(Z,"Z"),l(Z,"Z"),p(Z,"ee"),c(Z,"ensureLensmcpDir");function Qe(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}d(Qe,"Ze"),l(Qe,"ct"),p(Qe,"At"),c(Qe,"isInteractive");function re(){const t=Pt(),n=[],e=c((s,r)=>{try{for(const o of it(s,{withFileTypes:!0})){if(!o.isDirectory()||!o.name.endsWith("-devserver"))continue;const a=v(s,o.name,"parent.sock");k(a)&&n.push({service:o.name.slice(0,-10),wsKey:r,sock:a})}}catch{}},"scan");e(t,"");try{for(const s of it(t,{withFileTypes:!0}))s.isDirectory()&&/^[a-z0-9][a-z0-9-]*$/.test(s.name)&&!s.name.endsWith("-devserver")&&e(v(t,s.name),s.name)}catch{}return n.sort((s,r)=>`${s.wsKey}/${s.service}`.localeCompare(`${r.wsKey}/${r.service}`))}d(re,"re"),l(re,"re"),p(re,"ie"),c(re,"discoverDevserverSocks");const H=c(t=>t.wsKey?`${t.wsKey}/${t.service}`:t.service,"fullServiceName");function he(t){try{const n=x("lsof",["-t",t],{encoding:"utf8"}),e=Number((n.stdout??"").split(`
148
- `)[0]?.trim());return Number.isInteger(e)&&e>0?e:void 0}catch{return}}d(he,"he"),l(he,"fe"),p(he,"Ce"),c(he,"pidOnSocket");function fe(t){try{const n=x("ps",["-o","pgid=","-p",String(t)],{encoding:"utf8"}),e=Number((n.stdout??"").trim());return Number.isInteger(e)&&e>1?e:void 0}catch{return}}d(fe,"fe"),l(fe,"ge"),p(fe,"$e"),c(fe,"pgidOf");function Ze(t){try{const n=(x("lsof",["-a","-p",String(t),"-d","cwd","-Fn"],{encoding:"utf8"}).stdout??"").split(`
149
- `).find(e=>e.startsWith("n"));return n&&n.slice(1).trim()||void 0}catch{return}}d(Ze,"et"),l(Ze,"ut"),p(Ze,"It"),c(Ze,"cwdOfPid");function et(t,n){const e=dt(N(t),N(n));return e===""||!e.startsWith("..")&&!Mt(e)}d(et,"tt"),l(et,"dt"),p(et,"Dt"),c(et,"isInside");function tt(t,n){return re().filter(e=>{if(e.wsKey)return e.wsKey===n;const s=he(e.sock),r=s!==void 0?Ze(s):void 0;return r!==void 0&&et(t,r)})}d(tt,"st"),l(tt,"lt"),p(tt,"Ft"),c(tt,"workspaceDevservers");function st(t,n,e){const s=tt(t,n);if(s.length===0)return e(" no devserver pods running for this workspace \u2014 nothing to recycle."),{killed:0,stale:0};const r=new Set;for(const i of[process.pid,...A()]){const h=fe(i);h!==void 0&&r.add(h)}let o=0,a=0;for(const i of s){const h=H(i),u=he(i.sock);if(u===void 0){I(i.sock),e(` \u2713 ${h} \u2014 stale socket removed (no process held it)`),a++;continue}const f=fe(u),g=f!==void 0&&!r.has(f),b=c(m=>{try{g?process.kill(-f,m):process.kill(u,m)}catch{}},"signal");b("SIGTERM"),ge(u,5e3)||(b("SIGKILL"),ge(u,2e3)),I(i.sock),e(` \u2713 ${h} \u2014 stopped (pid ${u}${g?`, group ${f}`:", parent only"})`),o++}return{killed:o,stale:a}}d(st,"nt"),l(st,"pt"),p(st,"Gt"),c(st,"reapWorkspacePods");function ge(t,n){const e=Date.now()+n;for(;;){if(!q(t))return!0;if(Date.now()>=e)return!1;x("sleep",["0.15"])}}d(ge,"ge"),l(ge,"me"),p(ge,"Ne"),c(ge,"waitForPidGone");function nt(t,n,e,s){const r=v(n,".lensmcp","gateway.pid"),o=v(n,".lensmcp","registered.json"),{pid:a,healed:i}=z(r,n);if(i&&s(` healed: adopted a running gateway (pid ${a}) the pid file had lost.`),U(n,t.env,f=>s(` ${f}`)),R(n)!==void 0){s(` gateway: this workspace owns the :443 daemon (pid ${a??"?"}) \u2014 left running, pods respawn fresh.`);return}const h=_("GET","/list");if(h?.status!==200){s(" gateway: not running. Start it when you are ready: lensmcp gateway start");return}if(h.json&&typeof h.json=="object"&&(h.json.workspaces??[]).some(f=>f.key===e)){oe(L(o),{recursive:!0}),K(o,JSON.stringify({wsKey:e})),s(` gateway: '${e}' is already registered into the shared daemon \u2014 left serving.`);return}const u=_("POST","/register",ie(n,e));if(u&&u.status===200){oe(L(o),{recursive:!0}),K(o,JSON.stringify({wsKey:e})),s(` gateway: registered '${e}' into the shared daemon \u2014 it now hosts this workspace.`);return}s(" gateway: a shared daemon is up but registering this workspace failed \u2014 run `lensmcp gateway start`.")}d(nt,"rt"),l(nt,"ht"),p(nt,"Ht"),c(nt,"reconcileGatewayAfterSetup");function me(t,n,e,s){let r=n.filter(o=>o.service===t||H(o)===t);if(r.length>1)try{const o=ee(e).key,a=r.filter(i=>i.wsKey===o);a.length===1&&(r=a)}catch{}return r.length===0?(s(`No running service named "${t}".`),n.length>0&&s(`Running: ${n.map(H).join(", ")}`),null):r.length>1?(s(`"${t}" is ambiguous \u2014 use one of: ${r.map(H).join(", ")}`),null):r[0]}d(me,"me"),l(me,"we"),p(me,"je"),c(me,"resolveServiceSock");async function rt(t,n,e,s){const r=P(n,{string:["--tail","--cwd"],boolean:["--no-follow","--plain","--color"]}),o=r.positional[0],a=re();if(!o){if(a.length===0)e("No running services found (no devserver control sockets under $TMPDIR)."),e("Start one with `yarn nx serve <project>` or `lensmcp gateway start`.");else{e("Running services \u2014 attach with `lensmcp logs <service>`:");for(const m of a)e(` ${H(m)}`)}return{exitCode:0}}const i=me(o,a,N(y(r.flags["--cwd"])??t.cwd),s);if(!i)return{exitCode:1};const h=r.flags["--no-follow"]!==!0,u=Number(y(r.flags["--tail"])??NaN),f=Number.isInteger(u)&&u>=0?u:100,g=r.flags["--plain"]===!0||r.flags["--color"]!==!0&&!process.stdout.isTTY,b=`/webpack/logs?tail=${f}&follow=${h?1:0}${g?"&plain=1":""}`;return new Promise(m=>{const $=ut({socketPath:i.sock,path:b,method:"GET"},j=>{j.on("data",w=>process.stdout.write(w)),j.on("end",()=>m({exitCode:0})),j.on("error",()=>m({exitCode:1}))});$.on("error",j=>{s(`Cannot attach to ${H(i)}: ${j.message}`),s("The devserver may have crashed leaving a stale socket \u2014 restart the service and retry."),m({exitCode:1})}),$.end()})}d(rt,"ot"),l(rt,"ft"),p(rt,"Jt"),c(rt,"runLogs");async function ot(t,n,e,s){const r=P(n,{string:["--port","--cwd"]}),o=r.positional[0],a=re();if(!o)return s("Usage: lensmcp debug <service> [--port <base>]"),a.length>0&&s(`Running: ${a.map(H).join(", ")}`),{exitCode:2};const i=me(o,a,N(y(r.flags["--cwd"])??t.cwd),s);if(!i)return{exitCode:1};const h=Number(y(r.flags["--port"])??"")||void 0,u=`/webpack/debug${h?`?port=${h}`:""}`,f=await new Promise(b=>{const m=ut({socketPath:i.sock,path:u,method:"POST"},$=>{let j="";$.on("data",w=>j+=w),$.on("end",()=>b(j)),$.on("error",()=>b(null))});m.on("error",()=>b(null)),m.end()});if(f===null)return s(`Cannot reach ${H(i)} \u2014 the devserver may have crashed leaving a stale socket.`),{exitCode:1};let g;try{g=JSON.parse(f)}catch{return s(`Unexpected response from the devserver: ${f.slice(0,200)}`),{exitCode:1}}e(`Inspectors open on ${H(i)} \u2014 in place, no restart:`);for(const b of g.pods??[])e(` pod #${b.id} ${b.inspectorUrl??"(no url reported \u2014 check lensmcp logs)"}`);for(const b of g.workers??[])e(` worker ${b.name} 127.0.0.1:${b.port}`);return e(`Attach WebStorm (Run \u2192 Attach to Node.js/Chrome) to 127.0.0.1:${g.basePort??"?"} \u2014 or click the "Debugger listening" link in \`lensmcp logs ${o}\`.`),{exitCode:0}}d(ot,"it"),l(ot,"gt"),p(ot,"Wt"),c(ot,"runDebug");function y(t){if(t!==void 0)return typeof t=="string"?t:void 0}d(y,"v"),l(y,"w"),p(y,"w"),c(y,"stringFlag");function P(t,n={}){const e=new Set(n.string??[]),s=new Set(n.boolean??[]),r={},o=[];for(let a=0;a<t.length;a++){const i=t[a];if(i.startsWith("--")){const h=i.indexOf("="),u=h===-1?i:i.slice(0,h);if(s.has(u))r[u]=!0;else if(e.has(u))if(h!==-1)r[u]=i.slice(h+1);else{const f=t[a+1];f===void 0||f.startsWith("--")?r[u]="":(r[u]=f,a++)}else r[u]=h===-1?!0:i.slice(h+1)}else o.push(i)}return{flags:r,positional:o}}d(P,"E"),l(P,"T"),p(P,"M"),c(P,"parseFlags");
147
+ Install @lensmcp/cluster, or \`yarn nx build @lensmcp/cluster\` in the workspace.`),{exitCode:1})}d(rt,"Ot"),c(rt,"runRollout");function ot(t){let n=t;for(let e=0;e<6;e++){for(const r of[m(n,"node_modules","@lensmcp","cluster","executors","gateway","main.rollout.js"),m(n,"dist","libs","cluster","executors","gateway","main.rollout.js")])if(y(r))return r;const s=M(n);if(s===n)break;n=s}}d(ot,"Lt"),c(ot,"findRolloutBundle");function se(t){return ne(t,"main.js")}d(se,"re"),c(se,"findMcpBinary");function it(t){return ne(t,"shim.js")}d(it,"_t"),c(it,"findShimBinary");function ne(t,n){const e=m(te(),"bundled",n);if(y(e))return e;const s=m(t,"servers","lensmcp-mcp","dist",n);if(y(s))return s;let r=t;for(let o=0;o<5;o++){const a=m(r,"servers","lensmcp-mcp","dist",n);if(y(a))return a;const i=M(r);if(i===r)break;r=i}}d(ne,"oe"),c(ne,"findServerBundle");function at(t){const n=m(te(),"bundled","bridge.js");if(y(n))return n;let e=t;for(let s=0;s<5;s++){const r=m(e,"libs","bridge","dist","main.js");if(y(r))return r;const o=M(e);if(o===e)break;e=o}}d(at,"Rt"),c(at,"findBridgeBundle");function Q(t){let n=t;for(let e=0;e<5;e++){const s=m(n,"node_modules",".bin","nx");if(y(s))return s;const r=M(n);if(r===n)break;n=r}}d(Q,"Z"),c(Q,"findNxBinary");function Z(t){const n=m(t,".lensmcp");y(n)||K(n,{recursive:!0})}d(Z,"ee"),c(Z,"ensureLensmcpDir");function ct(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}d(ct,"At"),c(ct,"isInteractive");function re(){const t=xt(),n=[],e=c((s,r)=>{try{for(const o of oe(s,{withFileTypes:!0})){if(!o.isDirectory()||!o.name.endsWith("-devserver"))continue;const a=m(s,o.name,"parent.sock");y(a)&&n.push({service:o.name.slice(0,-10),wsKey:r,sock:a})}}catch{}},"scan");e(t,"");try{for(const s of oe(t,{withFileTypes:!0}))s.isDirectory()&&/^[a-z0-9][a-z0-9-]*$/.test(s.name)&&!s.name.endsWith("-devserver")&&e(m(t,s.name),s.name)}catch{}return n.sort((s,r)=>`${s.wsKey}/${s.service}`.localeCompare(`${r.wsKey}/${r.service}`))}d(re,"ie"),c(re,"discoverDevserverSocks");const A=c(t=>t.wsKey?`${t.wsKey}/${t.service}`:t.service,"fullServiceName");function fe(t){try{const n=C("lsof",["-t",t],{encoding:"utf8"}),e=Number((n.stdout??"").split(`
148
+ `)[0]?.trim());return Number.isInteger(e)&&e>0?e:void 0}catch{return}}d(fe,"Ce"),c(fe,"pidOnSocket");function ge(t){try{const n=C("ps",["-o","pgid=","-p",String(t)],{encoding:"utf8"}),e=Number((n.stdout??"").trim());return Number.isInteger(e)&&e>1?e:void 0}catch{return}}d(ge,"$e"),c(ge,"pgidOf");function ut(t){try{const n=(C("lsof",["-a","-p",String(t),"-d","cwd","-Fn"],{encoding:"utf8"}).stdout??"").split(`
149
+ `).find(e=>e.startsWith("n"));return n&&n.slice(1).trim()||void 0}catch{return}}d(ut,"It"),c(ut,"cwdOfPid");function dt(t,n){const e=ke($(t),$(n));return e===""||!e.startsWith("..")&&!$t(e)}d(dt,"Dt"),c(dt,"isInside");function lt(t,n){return re().filter(e=>{if(e.wsKey)return e.wsKey===n;const s=fe(e.sock),r=s!==void 0?ut(s):void 0;return r!==void 0&&dt(t,r)})}d(lt,"Ft"),c(lt,"workspaceDevservers");function pt(t,n,e){const s=lt(t,n);if(s.length===0)return e(" no devserver pods running for this workspace \u2014 nothing to recycle."),{killed:0,stale:0};const r=new Set;for(const i of[process.pid,..._()]){const l=ge(i);l!==void 0&&r.add(l)}let o=0,a=0;for(const i of s){const l=A(i),u=fe(i.sock);if(u===void 0){G(i.sock),e(` \u2713 ${l} \u2014 stale socket removed (no process held it)`),a++;continue}const p=ge(u),h=p!==void 0&&!r.has(p),v=c(f=>{try{h?process.kill(-p,f):process.kill(u,f)}catch{}},"signal");v("SIGTERM"),me(u,5e3)||(v("SIGKILL"),me(u,2e3)),G(i.sock),e(` \u2713 ${l} \u2014 stopped (pid ${u}${h?`, group ${p}`:", parent only"})`),o++}return{killed:o,stale:a}}d(pt,"Gt"),c(pt,"reapWorkspacePods");function me(t,n){const e=Date.now()+n;for(;;){if(!q(t))return!0;if(Date.now()>=e)return!1;C("sleep",["0.15"])}}d(me,"Ne"),c(me,"waitForPidGone");function ht(t,n,e,s){const r=m(n,".lensmcp","gateway.pid"),o=m(n,".lensmcp","registered.json"),{pid:a,healed:i}=z(r,n);if(i&&s(` healed: adopted a running gateway (pid ${a}) the pid file had lost.`),U(n,t.env,p=>s(` ${p}`)),F(n)!==void 0){s(` gateway: this workspace owns the :443 daemon (pid ${a??"?"}) \u2014 left running, pods respawn fresh.`);return}const l=O("GET","/list");if(l?.status!==200){s(" gateway: not running. Start it when you are ready: lensmcp gateway start");return}if(l.json&&typeof l.json=="object"&&(l.json.workspaces??[]).some(p=>p.key===e)){K(M(o),{recursive:!0}),B(o,JSON.stringify({wsKey:e})),s(` gateway: '${e}' is already registered into the shared daemon \u2014 left serving.`);return}const u=O("POST","/register",ae(n,e));if(u&&u.status===200){K(M(o),{recursive:!0}),B(o,JSON.stringify({wsKey:e})),s(` gateway: registered '${e}' into the shared daemon \u2014 it now hosts this workspace.`);return}s(" gateway: a shared daemon is up but registering this workspace failed \u2014 run `lensmcp gateway start`.")}d(ht,"Ht"),c(ht,"reconcileGatewayAfterSetup");function we(t,n,e,s){let r=n.filter(o=>o.service===t||A(o)===t);if(r.length>1)try{const o=V(e).key,a=r.filter(i=>i.wsKey===o);a.length===1&&(r=a)}catch{}return r.length===0?(s(`No running service named "${t}".`),n.length>0&&s(`Running: ${n.map(A).join(", ")}`),null):r.length>1?(s(`"${t}" is ambiguous \u2014 use one of: ${r.map(A).join(", ")}`),null):r[0]}d(we,"je"),c(we,"resolveServiceSock");async function ft(t,n,e,s){const r=T(n,{string:["--tail","--cwd"],boolean:["--no-follow","--plain","--color"]}),o=r.positional[0],a=re();if(!o){if(a.length===0)e("No running services found (no devserver control sockets under $TMPDIR)."),e("Start one with `yarn nx serve <project>` or `lensmcp gateway start`.");else{e("Running services \u2014 attach with `lensmcp logs <service>`:");for(const f of a)e(` ${A(f)}`)}return{exitCode:0}}const i=we(o,a,$(w(r.flags["--cwd"])??t.cwd),s);if(!i)return{exitCode:1};const l=r.flags["--no-follow"]!==!0,u=Number(w(r.flags["--tail"])??NaN),p=Number.isInteger(u)&&u>=0?u:100,h=r.flags["--plain"]===!0||r.flags["--color"]!==!0&&!process.stdout.isTTY,v=`/webpack/logs?tail=${p}&follow=${l?1:0}${h?"&plain=1":""}`;return new Promise(f=>{const b=ye({socketPath:i.sock,path:v,method:"GET"},x=>{x.on("data",g=>process.stdout.write(g)),x.on("end",()=>f({exitCode:0})),x.on("error",()=>f({exitCode:1}))});b.on("error",x=>{s(`Cannot attach to ${A(i)}: ${x.message}`),s("The devserver may have crashed leaving a stale socket \u2014 restart the service and retry."),f({exitCode:1})}),b.end()})}d(ft,"Jt"),c(ft,"runLogs");async function gt(t,n,e,s){const r=T(n,{string:["--port","--cwd"]}),o=r.positional[0],a=re();if(!o)return s("Usage: lensmcp debug <service> [--port <base>]"),a.length>0&&s(`Running: ${a.map(A).join(", ")}`),{exitCode:2};const i=we(o,a,$(w(r.flags["--cwd"])??t.cwd),s);if(!i)return{exitCode:1};const l=Number(w(r.flags["--port"])??"")||void 0,u=`/webpack/debug${l?`?port=${l}`:""}`,p=await new Promise(v=>{const f=ye({socketPath:i.sock,path:u,method:"POST"},b=>{let x="";b.on("data",g=>x+=g),b.on("end",()=>v(x)),b.on("error",()=>v(null))});f.on("error",()=>v(null)),f.end()});if(p===null)return s(`Cannot reach ${A(i)} \u2014 the devserver may have crashed leaving a stale socket.`),{exitCode:1};let h;try{h=JSON.parse(p)}catch{return s(`Unexpected response from the devserver: ${p.slice(0,200)}`),{exitCode:1}}e(`Inspectors open on ${A(i)} \u2014 in place, no restart:`);for(const v of h.pods??[])e(` pod #${v.id} ${v.inspectorUrl??"(no url reported \u2014 check lensmcp logs)"}`);for(const v of h.workers??[])e(` worker ${v.name} 127.0.0.1:${v.port}`);return e(`Attach WebStorm (Run \u2192 Attach to Node.js/Chrome) to 127.0.0.1:${h.basePort??"?"} \u2014 or click the "Debugger listening" link in \`lensmcp logs ${o}\`.`),{exitCode:0}}d(gt,"Wt"),c(gt,"runDebug");function w(t){if(t!==void 0)return typeof t=="string"?t:void 0}d(w,"w"),c(w,"stringFlag");function T(t,n={}){const e=new Set(n.string??[]),s=new Set(n.boolean??[]),r={},o=[];for(let a=0;a<t.length;a++){const i=t[a];if(i.startsWith("--")){const l=i.indexOf("="),u=l===-1?i:i.slice(0,l);if(s.has(u))r[u]=!0;else if(e.has(u))if(l!==-1)r[u]=i.slice(l+1);else{const p=t[a+1];p===void 0||p.startsWith("--")?r[u]="":(r[u]=p,a++)}else r[u]=l===-1?!0:i.slice(l+1)}else o.push(i)}return{flags:r,positional:o}}d(T,"M"),c(T,"parseFlags");
@@ -1 +1 @@
1
- "use strict";var s=Object.defineProperty;var r=(e,n)=>s(e,"name",{value:n,configurable:!0});var i=Object.defineProperty,t=r((e,n)=>i(e,"name",{value:n,configurable:!0}),"r"),l=Object.defineProperty,c=t((e,n)=>l(e,"name",{value:n,configurable:!0}),"n"),f=Object.defineProperty,o=c((e,n)=>f(e,"name",{value:n,configurable:!0}),"n");import{mkdirSync as d,readFileSync as p,rmSync as y,writeFileSync as b}from"node:fs";import{tmpdir as h}from"node:os";import{join as m}from"node:path";const A=o(e=>e.replace(/[^A-Za-z0-9._-]/g,"_").slice(0,100)||"unknown","safeKey");export const memoryPathFor=o((e,n=h())=>m(n,"lensmcp-hook",`${A(e)}.digest`),"memoryPathFor");export function lastAnnounced(e,n){try{return p(memoryPathFor(e,n),"utf8")}catch{return}}r(lastAnnounced,"lastAnnounced"),t(lastAnnounced,"lastAnnounced"),c(lastAnnounced,"lastAnnounced"),o(lastAnnounced,"lastAnnounced");export function remember(e,n,a){const u=memoryPathFor(e,a);try{if(!n){y(u,{force:!0});return}d(m(u,".."),{recursive:!0}),b(u,n)}catch{}}r(remember,"remember"),t(remember,"remember"),c(remember,"remember"),o(remember,"remember");export function shouldAnnounce(e,n,a){return n?e?lastAnnounced(e,a)!==n:!0:!1}r(shouldAnnounce,"shouldAnnounce"),t(shouldAnnounce,"shouldAnnounce"),c(shouldAnnounce,"shouldAnnounce"),o(shouldAnnounce,"shouldAnnounce");
1
+ "use strict";var i=Object.defineProperty;var n=(r,e)=>i(r,"name",{value:e,configurable:!0});var m=Object.defineProperty,o=n((r,e)=>m(r,"name",{value:e,configurable:!0}),"n");import{mkdirSync as s,readFileSync as u,rmSync as f,writeFileSync as l}from"node:fs";import{tmpdir as p}from"node:os";import{join as a}from"node:path";const y=o(r=>r.replace(/[^A-Za-z0-9._-]/g,"_").slice(0,100)||"unknown","safeKey");export const memoryPathFor=o((r,e=p())=>a(e,"lensmcp-hook",`${y(r)}.digest`),"memoryPathFor");export function lastAnnounced(r,e){try{return u(memoryPathFor(r,e),"utf8")}catch{return}}n(lastAnnounced,"lastAnnounced"),o(lastAnnounced,"lastAnnounced");export function remember(r,e,t){const c=memoryPathFor(r,t);try{if(!e){f(c,{force:!0});return}s(a(c,".."),{recursive:!0}),l(c,e)}catch{}}n(remember,"remember"),o(remember,"remember");export function shouldAnnounce(r,e,t){return e?r?lastAnnounced(r,t)!==e:!0:!1}n(shouldAnnounce,"shouldAnnounce"),o(shouldAnnounce,"shouldAnnounce");
package/lib/mcp-mode.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var s=Object.defineProperty;var o=(e,r)=>s(e,"name",{value:r,configurable:!0});var f=Object.defineProperty,c=o((e,r)=>f(e,"name",{value:r,configurable:!0}),"o"),l=Object.defineProperty,n=c((e,r)=>l(e,"name",{value:r,configurable:!0}),"o"),u=Object.defineProperty,t=n((e,r)=>u(e,"name",{value:r,configurable:!0}),"e");export const DEFAULT_MCP_MODE="embedded",SHARED_MCP_ENTRY_PATH="/mcp",SHARED_MCP_BIND_ADDRESS="127.0.0.1";export function isMcpMode(e){return e==="embedded"||e==="shared"}o(isMcpMode,"isMcpMode"),c(isMcpMode,"isMcpMode"),n(isMcpMode,"isMcpMode"),t(isMcpMode,"isMcpMode");export function sharedMcpUrl(e,r=SHARED_MCP_BIND_ADDRESS){return`http://${r}:${e}${SHARED_MCP_ENTRY_PATH}`}o(sharedMcpUrl,"sharedMcpUrl"),c(sharedMcpUrl,"sharedMcpUrl"),n(sharedMcpUrl,"sharedMcpUrl"),t(sharedMcpUrl,"sharedMcpUrl");export function resolveMcpMode(e={}){for(const r of[e.flag,e.env,e.config])if(isMcpMode(r))return r;return DEFAULT_MCP_MODE}o(resolveMcpMode,"resolveMcpMode"),c(resolveMcpMode,"resolveMcpMode"),n(resolveMcpMode,"resolveMcpMode"),t(resolveMcpMode,"resolveMcpMode");function M(e){if(!e||!e.trim())return{};try{const r=JSON.parse(e);return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}catch{return{}}}o(M,"p"),c(M,"i"),n(M,"i"),t(M,"parseObject");export function applyConfigMcpMode(e,r){const a=M(e),p=a.mcp,d=p&&typeof p=="object"&&!Array.isArray(p)?{...p,mode:r}:{mode:r},i=JSON.stringify({...a,mcp:d},null,2)+`
2
- `;return{json:i,changed:i!==e}}o(applyConfigMcpMode,"applyConfigMcpMode"),c(applyConfigMcpMode,"applyConfigMcpMode"),n(applyConfigMcpMode,"applyConfigMcpMode"),t(applyConfigMcpMode,"applyConfigMcpMode");
1
+ "use strict";var a=Object.defineProperty;var o=(e,r)=>a(e,"name",{value:r,configurable:!0});var d=Object.defineProperty,n=o((e,r)=>d(e,"name",{value:r,configurable:!0}),"e");export const DEFAULT_MCP_MODE="embedded",SHARED_MCP_ENTRY_PATH="/mcp",SHARED_MCP_BIND_ADDRESS="127.0.0.1";export function isMcpMode(e){return e==="embedded"||e==="shared"}o(isMcpMode,"isMcpMode"),n(isMcpMode,"isMcpMode");export function sharedMcpUrl(e,r=SHARED_MCP_BIND_ADDRESS){return`http://${r}:${e}${SHARED_MCP_ENTRY_PATH}`}o(sharedMcpUrl,"sharedMcpUrl"),n(sharedMcpUrl,"sharedMcpUrl");export function resolveMcpMode(e={}){for(const r of[e.flag,e.env,e.config])if(isMcpMode(r))return r;return DEFAULT_MCP_MODE}o(resolveMcpMode,"resolveMcpMode"),n(resolveMcpMode,"resolveMcpMode");function i(e){if(!e||!e.trim())return{};try{const r=JSON.parse(e);return r&&typeof r=="object"&&!Array.isArray(r)?r:{}}catch{return{}}}o(i,"i"),n(i,"parseObject");export function applyConfigMcpMode(e,r){const t=i(e),c=t.mcp,s=c&&typeof c=="object"&&!Array.isArray(c)?{...c,mode:r}:{mode:r},p=JSON.stringify({...t,mcp:s},null,2)+`
2
+ `;return{json:p,changed:p!==e}}o(applyConfigMcpMode,"applyConfigMcpMode"),n(applyConfigMcpMode,"applyConfigMcpMode");
package/lib/mcp-wiring.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var v=Object.defineProperty;var s=(e,r)=>v(e,"name",{value:r,configurable:!0});var b=Object.defineProperty,p=s((e,r)=>b(e,"name",{value:r,configurable:!0}),"i"),j=Object.defineProperty,a=p((e,r)=>j(e,"name",{value:r,configurable:!0}),"i"),x=Object.defineProperty,c=a((e,r)=>x(e,"name",{value:r,configurable:!0}),"s");import{spawnSync as k}from"node:child_process";import{existsSync as w,mkdirSync as O,readFileSync as P,writeFileSync as C}from"node:fs";import{request as E}from"node:http";import{dirname as I,join as g}from"node:path";import{applyConfigMcpMode as z,resolveMcpMode as N,sharedMcpUrl as S}from"./mcp-mode.js";import{readLensConfig as m}from"./workspace-scope.js";export const LENS_CONFIG=g(".lensmcp","config.json");function f(e){try{return P(e,"utf8")}catch{return}}s(f,"m"),p(f,"g"),a(f,"E"),c(f,"read");export function effectiveMcpMode(e,r,o=process.env){return N({flag:r,env:o.LENSMCP_MCP_MODE,config:m(e).mcp?.mode})}s(effectiveMcpMode,"effectiveMcpMode"),p(effectiveMcpMode,"effectiveMcpMode"),a(effectiveMcpMode,"effectiveMcpMode"),c(effectiveMcpMode,"effectiveMcpMode");export function wireMcpMode(e,r){const o=m(e).ports.mcpHttp,t=g(e,LENS_CONFIG),i=z(f(t),r);if(i.changed){const d=I(t);w(d)||O(d,{recursive:!0}),C(t,i.json)}return{mode:r,url:S(o),port:o,wrote:i.changed}}s(wireMcpMode,"wireMcpMode"),p(wireMcpMode,"wireMcpMode"),a(wireMcpMode,"wireMcpMode"),c(wireMcpMode,"wireMcpMode");export function readMcpWiring(e,r=process.env){const o=m(e).ports.mcpHttp;return{mode:effectiveMcpMode(e,void 0,r),port:o,url:S(o)}}s(readMcpWiring,"readMcpWiring"),p(readMcpWiring,"readMcpWiring"),a(readMcpWiring,"readMcpWiring"),c(readMcpWiring,"readMcpWiring");const M=JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"lensmcp-doctor",version:"1"}}});export function parseInitializeResponse(e){const r=e.split(`
2
- `).find(t=>t.startsWith("data:")),o=r?r.slice(5).trim():e.trim();try{const t=JSON.parse(o);return t.result?{ok:!0,...t.result.serverInfo?.name?{server:t.result.serverInfo.name}:{},...t.result.protocolVersion?{protocolVersion:t.result.protocolVersion}:{}}:{ok:!1,error:"no initialize result"}}catch{return{ok:!1,error:"unreadable initialize response"}}}s(parseInitializeResponse,"parseInitializeResponse"),p(parseInitializeResponse,"parseInitializeResponse"),a(parseInitializeResponse,"parseInitializeResponse"),c(parseInitializeResponse,"parseInitializeResponse");export function probeSharedMcpSync(e,r=2){const o=k("curl",["-s","--max-time",String(r),"-X","POST",e,"-H","content-type: application/json","-H","accept: application/json, text/event-stream","-d",M],{encoding:"utf8"});if(o.error||o.status!==0)return{ok:!1,error:"nothing listening"};const t=(o.stdout??"").trim();return t?parseInitializeResponse(t):{ok:!1,error:"empty response"}}s(probeSharedMcpSync,"probeSharedMcpSync"),p(probeSharedMcpSync,"probeSharedMcpSync"),a(probeSharedMcpSync,"probeSharedMcpSync"),c(probeSharedMcpSync,"probeSharedMcpSync");export function probeSharedMcp(e,r=2500){return new Promise(o=>{let t=!1;const i=c(n=>{t||(t=!0,o(n))},"finish");let d;try{d=new URL(e)}catch{i({ok:!1,error:`not a URL: ${e}`});return}const l=E({hostname:d.hostname,port:d.port,path:d.pathname,method:"POST",timeout:r,headers:{"content-type":"application/json",accept:"application/json, text/event-stream","content-length":Buffer.byteLength(M)}},n=>{let u="";n.setEncoding("utf8"),n.on("data",y=>{u+=y,(u.includes('"result"')||u.length>64e3)&&n.destroy()});const h=c(()=>{if((n.statusCode??0)>=400){i({ok:!1,error:`HTTP ${n.statusCode}`});return}i(parseInitializeResponse(u))},"settle");n.on("end",h),n.on("close",h)});l.on("timeout",()=>{l.destroy(),i({ok:!1,error:`no response within ${r}ms`})}),l.on("error",n=>{i({ok:!1,error:n.code==="ECONNREFUSED"?"nothing listening":n.message??"request failed"})}),l.end(M)})}s(probeSharedMcp,"probeSharedMcp"),p(probeSharedMcp,"probeSharedMcp"),a(probeSharedMcp,"probeSharedMcp"),c(probeSharedMcp,"probeSharedMcp");
1
+ "use strict";var y=Object.defineProperty;var i=(e,r)=>y(e,"name",{value:r,configurable:!0});var S=Object.defineProperty,c=i((e,r)=>S(e,"name",{value:r,configurable:!0}),"s");import{spawnSync as v}from"node:child_process";import{existsSync as x,mkdirSync as k,readFileSync as j,writeFileSync as b}from"node:fs";import{request as C}from"node:http";import{dirname as w,join as f}from"node:path";import{applyConfigMcpMode as E,resolveMcpMode as O,sharedMcpUrl as h}from"./mcp-mode.js";import{readLensConfig as d}from"./workspace-scope.js";export const LENS_CONFIG=f(".lensmcp","config.json");function g(e){try{return j(e,"utf8")}catch{return}}i(g,"E"),c(g,"read");export function effectiveMcpMode(e,r,o=process.env){return O({flag:r,env:o.LENSMCP_MCP_MODE,config:d(e).mcp?.mode})}i(effectiveMcpMode,"effectiveMcpMode"),c(effectiveMcpMode,"effectiveMcpMode");export function wireMcpMode(e,r){const o=d(e).ports.mcpHttp,t=f(e,LENS_CONFIG),s=E(g(t),r);if(s.changed){const a=w(t);x(a)||k(a,{recursive:!0}),b(t,s.json)}return{mode:r,url:h(o),port:o,wrote:s.changed}}i(wireMcpMode,"wireMcpMode"),c(wireMcpMode,"wireMcpMode");export function readMcpWiring(e,r=process.env){const o=d(e).ports.mcpHttp;return{mode:effectiveMcpMode(e,void 0,r),port:o,url:h(o)}}i(readMcpWiring,"readMcpWiring"),c(readMcpWiring,"readMcpWiring");const u=JSON.stringify({jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"lensmcp-doctor",version:"1"}}});export function parseInitializeResponse(e){const r=e.split(`
2
+ `).find(t=>t.startsWith("data:")),o=r?r.slice(5).trim():e.trim();try{const t=JSON.parse(o);return t.result?{ok:!0,...t.result.serverInfo?.name?{server:t.result.serverInfo.name}:{},...t.result.protocolVersion?{protocolVersion:t.result.protocolVersion}:{}}:{ok:!1,error:"no initialize result"}}catch{return{ok:!1,error:"unreadable initialize response"}}}i(parseInitializeResponse,"parseInitializeResponse"),c(parseInitializeResponse,"parseInitializeResponse");export function probeSharedMcpSync(e,r=2){const o=v("curl",["-s","--max-time",String(r),"-X","POST",e,"-H","content-type: application/json","-H","accept: application/json, text/event-stream","-d",u],{encoding:"utf8"});if(o.error||o.status!==0)return{ok:!1,error:"nothing listening"};const t=(o.stdout??"").trim();return t?parseInitializeResponse(t):{ok:!1,error:"empty response"}}i(probeSharedMcpSync,"probeSharedMcpSync"),c(probeSharedMcpSync,"probeSharedMcpSync");export function probeSharedMcp(e,r=2500){return new Promise(o=>{let t=!1;const s=c(n=>{t||(t=!0,o(n))},"finish");let a;try{a=new URL(e)}catch{s({ok:!1,error:`not a URL: ${e}`});return}const p=C({hostname:a.hostname,port:a.port,path:a.pathname,method:"POST",timeout:r,headers:{"content-type":"application/json",accept:"application/json, text/event-stream","content-length":Buffer.byteLength(u)}},n=>{let l="";n.setEncoding("utf8"),n.on("data",M=>{l+=M,(l.includes('"result"')||l.length>64e3)&&n.destroy()});const m=c(()=>{if((n.statusCode??0)>=400){s({ok:!1,error:`HTTP ${n.statusCode}`});return}s(parseInitializeResponse(l))},"settle");n.on("end",m),n.on("close",m)});p.on("timeout",()=>{p.destroy(),s({ok:!1,error:`no response within ${r}ms`})}),p.on("error",n=>{s({ok:!1,error:n.code==="ECONNREFUSED"?"nothing listening":n.message??"request failed"})}),p.end(u)})}i(probeSharedMcp,"probeSharedMcp"),c(probeSharedMcp,"probeSharedMcp");
@@ -1,3 +1,3 @@
1
- "use strict";var j=Object.defineProperty;var p=(e,n)=>j(e,"name",{value:n,configurable:!0});var k=Object.defineProperty,u=p((e,n)=>k(e,"name",{value:n,configurable:!0}),"p"),v=Object.defineProperty,l=u((e,n)=>v(e,"name",{value:n,configurable:!0}),"p"),x=Object.defineProperty,i=l((e,n)=>x(e,"name",{value:n,configurable:!0}),"u");import{spawnSync as w}from"node:child_process";import{existsSync as S,readFileSync as P}from"node:fs";import{homedir as y}from"node:os";import{join as h}from"node:path";export const PLUGIN_NAME="lensmcp";function f(e){try{return JSON.parse(P(e,"utf8"))}catch{return}}p(f,"f"),u(f,"f"),l(f,"a"),i(f,"readJson");function m(e){const n=f(e)?.version;return typeof n=="string"&&n.length>0?n:void 0}p(m,"m"),u(m,"g"),l(m,"g"),i(m,"readVersionField");export function readInstalledPlugin(e,n=y()){const t=f(h(n,".claude","plugins","installed_plugins.json"))?.plugins;if(!t||typeof t!="object")return;const r=[];for(const[o,c]of Object.entries(t)){const[s,g]=o.split("@");if(!(s!==PLUGIN_NAME||!g))for(const d of Array.isArray(c)?c:[c]){if(typeof d!="object"||d===null)continue;const a=d;typeof a.version=="string"&&r.push({id:o,marketplace:g,version:a.version,...typeof a.scope=="string"?{scope:a.scope}:{},...typeof a.projectPath=="string"?{projectPath:a.projectPath}:{}})}}if(r.length!==0)return r.find(o=>o.projectPath===e)??r.find(o=>o.scope==="user")??r[0]}p(readInstalledPlugin,"readInstalledPlugin"),u(readInstalledPlugin,"readInstalledPlugin"),l(readInstalledPlugin,"readInstalledPlugin"),i(readInstalledPlugin,"readInstalledPlugin");export function readVersions(e){const n={};if(e.pluginRoot){const o=m(h(e.pluginRoot,".claude-plugin","plugin.json"));o&&(n.plugin=o,n.pluginSource="plugin-root")}const t=readInstalledPlugin(e.cwd,e.home);t&&(n.pluginId=t.id,n.pluginMarketplace=t.marketplace,t.scope&&(n.pluginScope=t.scope),n.plugin||(n.plugin=t.version,n.pluginSource="install-index"));const r=m(h(e.cwd,"node_modules","lensmcp","package.json"));return r&&(n.project=r),e.cliVersion&&e.cliVersion!=="0.0.0"&&(n.cli=e.cliVersion),n}p(readVersions,"readVersions"),u(readVersions,"readVersions"),l(readVersions,"readVersions"),i(readVersions,"readVersions");export function compareVersions(e,n){const t=i(c=>{const s=c.trim().replace(/^v/,"").split(/[-+]/)[0];if(s===void 0)return;const g=s.split(".");if(g.length===0||g.length>3)return;const d=g.map(a=>/^\d+$/.test(a)?Number(a):Number.NaN);return d.some(Number.isNaN)?void 0:[d[0]??0,d[1]??0,d[2]??0]},"parse"),r=t(e),o=t(n);if(!(!r||!o)){for(let c=0;c<3;c++){const s=(r[c]??0)-(o[c]??0);if(s!==0)return s<0?-1:1}return 0}}p(compareVersions,"compareVersions"),u(compareVersions,"compareVersions"),l(compareVersions,"compareVersions"),i(compareVersions,"compareVersions");export function detectSkew(e){const n=e.plugin,t=e.project??e.cli;if(!n||!t||n===t)return;const r=compareVersions(n,t);if(r===void 0||r===0)return;const o=e.cli&&e.project&&e.cli!==e.project?{cli:e.cli,project:e.project}:void 0;return{mismatch:!0,plugin:n,expected:t,expectedSource:e.project?"project":"cli",direction:r<0?"behind":"ahead",...e.pluginId?{pluginId:e.pluginId}:{},...e.pluginMarketplace?{marketplace:e.pluginMarketplace}:{},...e.pluginScope?{scope:e.pluginScope}:{},...o?{cliSkew:o}:{}}}p(detectSkew,"detectSkew"),u(detectSkew,"detectSkew"),l(detectSkew,"detectSkew"),i(detectSkew,"detectSkew");export function updateCommands(e){const n=e.pluginId??PLUGIN_NAME,t=e.scope&&e.scope!=="user"?` --scope ${e.scope}`:"";return[...e.marketplace?[`claude plugin marketplace update ${e.marketplace}`]:[],`claude plugin update ${n}${t}`]}p(updateCommands,"updateCommands"),u(updateCommands,"updateCommands"),l(updateCommands,"updateCommands"),i(updateCommands,"updateCommands");export function formatSkewContext(e){const n=[`LensMCP plugin is out of step: plugin ${e.plugin} vs lensmcp ${e.expected} (${e.expectedSource==="project"?"this project\u2019s node_modules":"the running CLI"}). The plugin\u2019s skills, hooks and wiring are from ${e.plugin} \u2014 update before relying on them:`,...updateCommands(e).map(t=>` ${t}`)," \u2026then restart Claude Code for the new plugin to load."];return e.cliSkew&&n.push(` (also: the lensmcp CLI on PATH is ${e.cliSkew.cli} while this project depends on ${e.cliSkew.project}.)`),n.join(`
2
- `)}p(formatSkewContext,"formatSkewContext"),u(formatSkewContext,"formatSkewContext"),l(formatSkewContext,"formatSkewContext"),i(formatSkewContext,"formatSkewContext");export function syncPlugin(e){const n=detectSkew(e.facts);if(!n)return{outcome:e.facts.plugin!==void 0&&(e.facts.project??e.facts.cli)!==void 0?"current":"unknown",...e.facts.plugin?{from:e.facts.plugin}:{}};const t=updateCommands(n);for(const r of t){const[o,...c]=r.split(" "),s=o===void 0?void 0:e.run(o,c);if(!s)return{outcome:"manual",from:n.plugin,to:n.expected,commands:t,detail:`\`${o??"claude"}\` is not on PATH`};if(s.code!==0)return{outcome:"manual",from:n.plugin,to:n.expected,commands:t,detail:(s.stderr??"").trim().split(`
3
- `)[0]||`\`${r}\` exited ${s.code}`}}return{outcome:"updated",from:n.plugin,to:n.expected,commands:t}}p(syncPlugin,"syncPlugin"),u(syncPlugin,"syncPlugin"),l(syncPlugin,"syncPlugin"),i(syncPlugin,"syncPlugin");export function spawnRunner(e,n=6e4){return(t,r)=>{try{const o=w(t,r,{encoding:"utf8",timeout:n,env:e,stdio:["ignore","pipe","pipe"]});return o.error&&o.error.code==="ENOENT"?void 0:{code:o.status??1,...o.stderr?{stderr:o.stderr}:{}}}catch{return}}}p(spawnRunner,"spawnRunner"),u(spawnRunner,"spawnRunner"),l(spawnRunner,"spawnRunner"),i(spawnRunner,"spawnRunner");export const pluginManifestPath=i(e=>h(e,".claude-plugin","plugin.json"),"pluginManifestPath"),isPluginRoot=i(e=>S(pluginManifestPath(e)),"isPluginRoot");
1
+ "use strict";var m=Object.defineProperty;var p=(t,n)=>m(t,"name",{value:n,configurable:!0});var h=Object.defineProperty,i=p((t,n)=>h(t,"name",{value:n,configurable:!0}),"u");import{spawnSync as j}from"node:child_process";import{existsSync as x,readFileSync as k}from"node:fs";import{homedir as v}from"node:os";import{join as d}from"node:path";export const PLUGIN_NAME="lensmcp";function f(t){try{return JSON.parse(k(t,"utf8"))}catch{return}}p(f,"a"),i(f,"readJson");function g(t){const n=f(t)?.version;return typeof n=="string"&&n.length>0?n:void 0}p(g,"g"),i(g,"readVersionField");export function readInstalledPlugin(t,n=v()){const e=f(d(n,".claude","plugins","installed_plugins.json"))?.plugins;if(!e||typeof e!="object")return;const r=[];for(const[o,c]of Object.entries(e)){const[s,a]=o.split("@");if(!(s!==PLUGIN_NAME||!a))for(const l of Array.isArray(c)?c:[c]){if(typeof l!="object"||l===null)continue;const u=l;typeof u.version=="string"&&r.push({id:o,marketplace:a,version:u.version,...typeof u.scope=="string"?{scope:u.scope}:{},...typeof u.projectPath=="string"?{projectPath:u.projectPath}:{}})}}if(r.length!==0)return r.find(o=>o.projectPath===t)??r.find(o=>o.scope==="user")??r[0]}p(readInstalledPlugin,"readInstalledPlugin"),i(readInstalledPlugin,"readInstalledPlugin");export function readVersions(t){const n={};if(t.pluginRoot){const o=g(d(t.pluginRoot,".claude-plugin","plugin.json"));o&&(n.plugin=o,n.pluginSource="plugin-root")}const e=readInstalledPlugin(t.cwd,t.home);e&&(n.pluginId=e.id,n.pluginMarketplace=e.marketplace,e.scope&&(n.pluginScope=e.scope),n.plugin||(n.plugin=e.version,n.pluginSource="install-index"));const r=g(d(t.cwd,"node_modules","lensmcp","package.json"));return r&&(n.project=r),t.cliVersion&&t.cliVersion!=="0.0.0"&&(n.cli=t.cliVersion),n}p(readVersions,"readVersions"),i(readVersions,"readVersions");export function compareVersions(t,n){const e=i(c=>{const s=c.trim().replace(/^v/,"").split(/[-+]/)[0];if(s===void 0)return;const a=s.split(".");if(a.length===0||a.length>3)return;const l=a.map(u=>/^\d+$/.test(u)?Number(u):Number.NaN);return l.some(Number.isNaN)?void 0:[l[0]??0,l[1]??0,l[2]??0]},"parse"),r=e(t),o=e(n);if(!(!r||!o)){for(let c=0;c<3;c++){const s=(r[c]??0)-(o[c]??0);if(s!==0)return s<0?-1:1}return 0}}p(compareVersions,"compareVersions"),i(compareVersions,"compareVersions");export function detectSkew(t){const n=t.plugin,e=t.project??t.cli;if(!n||!e||n===e)return;const r=compareVersions(n,e);if(r===void 0||r===0)return;const o=t.cli&&t.project&&t.cli!==t.project?{cli:t.cli,project:t.project}:void 0;return{mismatch:!0,plugin:n,expected:e,expectedSource:t.project?"project":"cli",direction:r<0?"behind":"ahead",...t.pluginId?{pluginId:t.pluginId}:{},...t.pluginMarketplace?{marketplace:t.pluginMarketplace}:{},...t.pluginScope?{scope:t.pluginScope}:{},...o?{cliSkew:o}:{}}}p(detectSkew,"detectSkew"),i(detectSkew,"detectSkew");export function updateCommands(t){const n=t.pluginId??PLUGIN_NAME,e=t.scope&&t.scope!=="user"?` --scope ${t.scope}`:"";return[...t.marketplace?[`claude plugin marketplace update ${t.marketplace}`]:[],`claude plugin update ${n}${e}`]}p(updateCommands,"updateCommands"),i(updateCommands,"updateCommands");export function formatSkewContext(t){const n=[`LensMCP plugin is out of step: plugin ${t.plugin} vs lensmcp ${t.expected} (${t.expectedSource==="project"?"this project\u2019s node_modules":"the running CLI"}). The plugin\u2019s skills, hooks and wiring are from ${t.plugin} \u2014 update before relying on them:`,...updateCommands(t).map(e=>` ${e}`)," \u2026then restart Claude Code for the new plugin to load."];return t.cliSkew&&n.push(` (also: the lensmcp CLI on PATH is ${t.cliSkew.cli} while this project depends on ${t.cliSkew.project}.)`),n.join(`
2
+ `)}p(formatSkewContext,"formatSkewContext"),i(formatSkewContext,"formatSkewContext");export function syncPlugin(t){const n=detectSkew(t.facts);if(!n)return{outcome:t.facts.plugin!==void 0&&(t.facts.project??t.facts.cli)!==void 0?"current":"unknown",...t.facts.plugin?{from:t.facts.plugin}:{}};const e=updateCommands(n);for(const r of e){const[o,...c]=r.split(" "),s=o===void 0?void 0:t.run(o,c);if(!s)return{outcome:"manual",from:n.plugin,to:n.expected,commands:e,detail:`\`${o??"claude"}\` is not on PATH`};if(s.code!==0)return{outcome:"manual",from:n.plugin,to:n.expected,commands:e,detail:(s.stderr??"").trim().split(`
3
+ `)[0]||`\`${r}\` exited ${s.code}`}}return{outcome:"updated",from:n.plugin,to:n.expected,commands:e}}p(syncPlugin,"syncPlugin"),i(syncPlugin,"syncPlugin");export function spawnRunner(t,n=6e4){return(e,r)=>{try{const o=j(e,r,{encoding:"utf8",timeout:n,env:t,stdio:["ignore","pipe","pipe"]});return o.error&&o.error.code==="ENOENT"?void 0:{code:o.status??1,...o.stderr?{stderr:o.stderr}:{}}}catch{return}}}p(spawnRunner,"spawnRunner"),i(spawnRunner,"spawnRunner");export const pluginManifestPath=i(t=>d(t,".claude-plugin","plugin.json"),"pluginManifestPath"),isPluginRoot=i(t=>x(pluginManifestPath(t)),"isPluginRoot");
@@ -1,2 +1,2 @@
1
- "use strict";var M=Object.defineProperty;var i=(e,t)=>M(e,"name",{value:t,configurable:!0});var S=Object.defineProperty,c=i((e,t)=>S(e,"name",{value:t,configurable:!0}),"i"),D=Object.defineProperty,u=c((e,t)=>D(e,"name",{value:t,configurable:!0}),"i"),k=Object.defineProperty,a=u((e,t)=>k(e,"name",{value:t,configurable:!0}),"i");import{readLensConfig as T}from"./workspace-scope.js";import{sharedMcpUrl as P}from"./mcp-mode.js";import{effectiveMcpMode as O}from"./mcp-wiring.js";import{freshnessOf as R}from"./status-policy.js";export const IDENTITY_PATH="/lensmcp/identity",IDENTITY_KIND="lensmcp-mcp-identity";export function resolveSource(e,t){const n=T(e),r=n.ports.mcpHttp;return{workspace:n.key,mode:O(e,void 0,t),port:r,url:P(r)}}i(resolveSource,"resolveSource"),c(resolveSource,"resolveSource"),u(resolveSource,"resolveSource"),a(resolveSource,"resolveSource");function h(e){const t=Date.now()+e;return{remaining:a(()=>Math.max(0,t-Date.now()),"remaining"),expired:a(()=>Date.now()>=t,"expired")}}i(h,"v"),c(h,"w"),u(h,"M"),a(h,"deadline");async function p(e,t,n){const r=n.remaining();if(r<=0)return;const s=new AbortController,o=setTimeout(()=>s.abort(),r);try{return await fetch(e,{...t,signal:s.signal})}catch{return}finally{clearTimeout(o)}}i(p,"u"),c(p,"l"),u(p,"l"),a(p,"fetchWithin");async function d(e,t,n,r){const s=await p(e,{method:"POST",headers:{"content-type":"application/json",accept:"application/json, text/event-stream",...t?{"mcp-session-id":t}:{}},body:JSON.stringify(n)},r);if(!s)return;if(s.status===202)return{res:s};const o=await s.text().catch(()=>"");return{res:s,...parseFrame(o)===void 0?{}:{json:parseFrame(o)}}}i(d,"p"),c(d,"f"),u(d,"m"),a(d,"rpc");export function parseFrame(e){const t=e.split(`
2
- `).find(r=>r.startsWith("data:")),n=(t?t.slice(5):e).trim();if(n)try{const r=JSON.parse(n);return typeof r=="object"&&r!==null?r:void 0}catch{return}}i(parseFrame,"parseFrame"),c(parseFrame,"parseFrame"),u(parseFrame,"parseFrame"),a(parseFrame,"parseFrame");export function parseResourceResult(e){const t=e?.result;if(typeof t!="object"||t===null)return;const n=t.contents;if(!Array.isArray(n)||n.length===0)return;const r=n[0];if(typeof r!="object"||r===null)return;const s=r.text;if(typeof s=="string")try{return JSON.parse(s)}catch{return}}i(parseResourceResult,"parseResourceResult"),c(parseResourceResult,"parseResourceResult"),u(parseResourceResult,"parseResourceResult"),a(parseResourceResult,"parseResourceResult");async function v(e,t,n){const r=await p(`http://127.0.0.1:${e}${IDENTITY_PATH}`,{headers:{accept:"application/json"}},n);if(!r)return"no server on this workspace\u2019s MCP port";if(!r.ok)return await r.text().catch(()=>{}),`the process on :${e} does not serve a lensmcp identity`;const s=await r.json().catch(()=>{});if(typeof s!="object"||s===null)return`unreadable identity on :${e}`;const o=s;if(o.kind!==IDENTITY_KIND)return`the process on :${e} is not a lensmcp server`;if(o.key!==t)return`:${e} serves workspace "${String(o.key)}", not "${t}"`}i(v,"w"),c(v,"b"),u(v,"D"),a(v,"verifyIdentity");export const DEFAULT_BUDGET_MS=2e3;export async function readStatus(e){const t=Date.now(),n=resolveSource(e.cwd,e.env),r=h(e.budgetMs??DEFAULT_BUDGET_MS),s=a(f=>({available:!1,source:n,reason:f,elapsedMs:Date.now()-t}),"fail");if(n.mode!=="shared")return s("MCP mode is embedded \u2014 there is no shared server to read (`lensmcp setup --mcp shared`)");const o=await v(n.port,n.workspace,r);if(o)return s(o);const w=await d(n.url,void 0,{jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"lensmcp-status",version:"1"}}},r);if(!w||!w.res.ok)return s("the shared MCP server did not accept a session");const l=w.res.headers.get("mcp-session-id")??void 0,x=a(async()=>{l&&await p(n.url,{method:"DELETE",headers:{"mcp-session-id":l}},r)},"close");try{await d(n.url,l,{jsonrpc:"2.0",method:"notifications/initialized"},r);const f=a(async m=>{const y=await d(n.url,l,{jsonrpc:"2.0",id:2,method:"resources/read",params:{uri:m}},r);return parseResourceResult(y?.json)},"read"),b=await f("agent://current-status");if(typeof b!="object"||b===null)return s("the shared MCP server did not return agent://current-status");const g=b,j=new Map;for(const m of e.verify?.(g)??[]){if(r.expired())break;const y=await f(m);y!==void 0&&j.set(m,R(y))}return{available:!0,source:n,status:g,evidence:j,elapsedMs:Date.now()-t}}finally{await x()}}i(readStatus,"readStatus"),c(readStatus,"readStatus"),u(readStatus,"readStatus"),a(readStatus,"readStatus");
1
+ "use strict";var j=Object.defineProperty;var i=(t,r)=>j(t,"name",{value:r,configurable:!0});var x=Object.defineProperty,o=i((t,r)=>x(t,"name",{value:r,configurable:!0}),"i");import{readLensConfig as M}from"./workspace-scope.js";import{sharedMcpUrl as D}from"./mcp-mode.js";import{effectiveMcpMode as k}from"./mcp-wiring.js";import{freshnessOf as T}from"./status-policy.js";export const IDENTITY_PATH="/lensmcp/identity",IDENTITY_KIND="lensmcp-mcp-identity";export function resolveSource(t,r){const n=M(t),e=n.ports.mcpHttp;return{workspace:n.key,mode:k(t,void 0,r),port:e,url:D(e)}}i(resolveSource,"resolveSource"),o(resolveSource,"resolveSource");function w(t){const r=Date.now()+t;return{remaining:o(()=>Math.max(0,r-Date.now()),"remaining"),expired:o(()=>Date.now()>=r,"expired")}}i(w,"M"),o(w,"deadline");async function l(t,r,n){const e=n.remaining();if(e<=0)return;const s=new AbortController,a=setTimeout(()=>s.abort(),e);try{return await fetch(t,{...r,signal:s.signal})}catch{return}finally{clearTimeout(a)}}i(l,"l"),o(l,"fetchWithin");async function f(t,r,n,e){const s=await l(t,{method:"POST",headers:{"content-type":"application/json",accept:"application/json, text/event-stream",...r?{"mcp-session-id":r}:{}},body:JSON.stringify(n)},e);if(!s)return;if(s.status===202)return{res:s};const a=await s.text().catch(()=>"");return{res:s,...parseFrame(a)===void 0?{}:{json:parseFrame(a)}}}i(f,"m"),o(f,"rpc");export function parseFrame(t){const r=t.split(`
2
+ `).find(e=>e.startsWith("data:")),n=(r?r.slice(5):t).trim();if(n)try{const e=JSON.parse(n);return typeof e=="object"&&e!==null?e:void 0}catch{return}}i(parseFrame,"parseFrame"),o(parseFrame,"parseFrame");export function parseResourceResult(t){const r=t?.result;if(typeof r!="object"||r===null)return;const n=r.contents;if(!Array.isArray(n)||n.length===0)return;const e=n[0];if(typeof e!="object"||e===null)return;const s=e.text;if(typeof s=="string")try{return JSON.parse(s)}catch{return}}i(parseResourceResult,"parseResourceResult"),o(parseResourceResult,"parseResourceResult");async function b(t,r,n){const e=await l(`http://127.0.0.1:${t}${IDENTITY_PATH}`,{headers:{accept:"application/json"}},n);if(!e)return"no server on this workspace\u2019s MCP port";if(!e.ok)return await e.text().catch(()=>{}),`the process on :${t} does not serve a lensmcp identity`;const s=await e.json().catch(()=>{});if(typeof s!="object"||s===null)return`unreadable identity on :${t}`;const a=s;if(a.kind!==IDENTITY_KIND)return`the process on :${t} is not a lensmcp server`;if(a.key!==r)return`:${t} serves workspace "${String(a.key)}", not "${r}"`}i(b,"D"),o(b,"verifyIdentity");export const DEFAULT_BUDGET_MS=2e3;export async function readStatus(t){const r=Date.now(),n=resolveSource(t.cwd,t.env),e=w(t.budgetMs??DEFAULT_BUDGET_MS),s=o(u=>({available:!1,source:n,reason:u,elapsedMs:Date.now()-r}),"fail");if(n.mode!=="shared")return s("MCP mode is embedded \u2014 there is no shared server to read (`lensmcp setup --mcp shared`)");const a=await b(n.port,n.workspace,e);if(a)return s(a);const m=await f(n.url,void 0,{jsonrpc:"2.0",id:1,method:"initialize",params:{protocolVersion:"2025-06-18",capabilities:{},clientInfo:{name:"lensmcp-status",version:"1"}}},e);if(!m||!m.res.ok)return s("the shared MCP server did not accept a session");const c=m.res.headers.get("mcp-session-id")??void 0,g=o(async()=>{c&&await l(n.url,{method:"DELETE",headers:{"mcp-session-id":c}},e)},"close");try{await f(n.url,c,{jsonrpc:"2.0",method:"notifications/initialized"},e);const u=o(async p=>{const d=await f(n.url,c,{jsonrpc:"2.0",id:2,method:"resources/read",params:{uri:p}},e);return parseResourceResult(d?.json)},"read"),h=await u("agent://current-status");if(typeof h!="object"||h===null)return s("the shared MCP server did not return agent://current-status");const y=h,v=new Map;for(const p of t.verify?.(y)??[]){if(e.expired())break;const d=await u(p);d!==void 0&&v.set(p,T(d))}return{available:!0,source:n,status:y,evidence:v,elapsedMs:Date.now()-r}}finally{await g()}}i(readStatus,"readStatus"),o(readStatus,"readStatus");
@@ -1,2 +1,2 @@
1
- "use strict";var b=Object.defineProperty;var i=(n,e)=>b(n,"name",{value:e,configurable:!0});var m=Object.defineProperty,s=i((n,e)=>m(n,"name",{value:e,configurable:!0}),"i"),$=Object.defineProperty,a=s((n,e)=>$(n,"name",{value:e,configurable:!0}),"i"),x=Object.defineProperty,o=a((n,e)=>x(n,"name",{value:e,configurable:!0}),"r");export const HOOK_SIGNAL_CLASS={typecheck:"check",lint:"check",build:"check",runtime:"observation",cluster:"observation"},OBSERVATION_WINDOW_MS=30*6e4;const k=o(n=>typeof n=="object"&&n!==null&&!Array.isArray(n),"isRecord"),h=o(n=>typeof n=="number"&&Number.isFinite(n)?n:void 0,"asFiniteNumber");function f(n,e){if(!Array.isArray(n))return;let t;for(const d of n){if(!k(d))continue;const r=h(d[e]);r!==void 0&&(t===void 0||r>t)&&(t=r)}return t}i(f,"u"),s(f,"f"),a(f,"h"),o(f,"newestIn");export function freshnessOf(n){if(!k(n))return{};const e=typeof n.producer=="string"?n.producer:void 0,t=h(n.lastRunAt)??h(n.lastReportedAt)??f(n.producers,"lastReportedAt")??f(n.producers,"lastRunAt"),d=f(n.entries,"timestamp")??f(n.failures,"timestamp")??f(n.violations,"timestamp");return{...e===void 0?{}:{declaredProducer:e},...n.stale===!0?{declaredStale:!0}:{},...t===void 0?{}:{ranAt:t},...d===void 0?{}:{newestItemAt:d},...h(n.ageMs)===void 0?{}:{ageMs:h(n.ageMs)}}}i(freshnessOf,"freshnessOf"),s(freshnessOf,"freshnessOf"),a(freshnessOf,"freshnessOf"),o(freshnessOf,"freshnessOf");const v=o((n,e)=>n===void 0?void 0:Math.max(0,e-n),"ageFrom");export function candidateSignals(n){return(n.signals??[]).filter(e=>HOOK_SIGNAL_CLASS[e.kind]!==void 0&&(e.state==="failed"||e.state==="warning")&&e.producer==="connected")}i(candidateSignals,"candidateSignals"),s(candidateSignals,"candidateSignals"),a(candidateSignals,"candidateSignals"),o(candidateSignals,"candidateSignals");export function judge(n,e,t,d=OBSERVATION_WINDOW_MS){const r=HOOK_SIGNAL_CLASS[n.kind];if(r===void 0)return{kind:n.kind,reason:"out-of-scope"};if(n.state!=="failed"&&n.state!=="warning")return{kind:n.kind,reason:"not-actionable"};if(n.producer!=="connected"||e?.declaredProducer==="none")return{kind:n.kind,reason:"no-producer"};const l=n.reads?.[0]??n.resource,g={kind:n.kind,state:n.state,...n.count===void 0?{}:{count:n.count},read:l,detail:n.detail};if(n.stale===!0||e?.declaredStale===!0){const p=e?.ageMs??v(e?.ranAt,t);return{...g,stale:!0,...p===void 0?{}:{ageMs:p}}}if(r==="check"){const p=v(e?.ranAt,t);return p===void 0?{kind:n.kind,reason:"no-freshness-evidence"}:{...g,stale:!1,ageMs:p}}const c=e?.newestItemAt;if(c===void 0)return{kind:n.kind,reason:"no-freshness-evidence"};const u=Math.max(0,t-c);return u>d?{kind:n.kind,reason:"stale-observation"}:{...g,stale:!1,ageMs:u}}i(judge,"judge"),s(judge,"judge"),a(judge,"judge"),o(judge,"judge");const A=o(n=>"state"in n,"isFinding");export function select(n,e,t,d=OBSERVATION_WINDOW_MS){const r=[],l=[],g=[];for(const c of n.signals??[]){c.producer==="none"&&HOOK_SIGNAL_CLASS[c.kind]!==void 0&&g.push(c.kind);const u=judge(c,e.get(c.resource),t,d);A(u)?r.push(u):u.reason!=="out-of-scope"&&u.reason!=="not-actionable"&&l.push(u)}return r.sort((c,u)=>c.state===u.state?(u.count??0)-(c.count??0):c.state==="failed"?-1:1),{findings:r,suppressed:l,unmonitored:g}}i(select,"select"),s(select,"select"),a(select,"select"),o(select,"select");export function describeAge(n){return n<6e4?`${Math.max(1,Math.round(n/1e3))}s ago`:n<36e5?`${Math.round(n/6e4)}m ago`:`${Math.round(n/36e5)}h ago`}i(describeAge,"describeAge"),s(describeAge,"describeAge"),a(describeAge,"describeAge"),o(describeAge,"describeAge");export function describeCount(n){const e=n.kind==="runtime"?"browser runtime error":`${n.kind} ${n.state==="failed"?"error":"warning"}`,t=n.count===1?"":"s";return`${n.count===void 0?"":`${n.count} `}${e}${t}`}i(describeCount,"describeCount"),s(describeCount,"describeCount"),a(describeCount,"describeCount"),o(describeCount,"describeCount");export function describeWhen(n){return n.stale?`last checked ${n.ageMs===void 0?"a while ago":describeAge(n.ageMs)}, nothing has re-run since`:n.ageMs===void 0?"recency unknown":describeAge(n.ageMs)}i(describeWhen,"describeWhen"),s(describeWhen,"describeWhen"),a(describeWhen,"describeWhen"),o(describeWhen,"describeWhen");export function findingLine(n){return`${n.kind}: ${describeCount(n)} (${describeWhen(n)}) \u2192 read ${n.read}`}i(findingLine,"findingLine"),s(findingLine,"findingLine"),a(findingLine,"findingLine"),o(findingLine,"findingLine");export function formatContext(n,e={}){const{findings:t,unmonitored:d}=n;if(t.length===0)return;const r=[`LensMCP${e.workspace?` (${e.workspace})`:""}: ${t.length===1?"1 signal needs":`${t.length} signals need`} attention.`,...t.map(l=>` \u2022 ${findingLine(l)}`)];return e.includeUnmonitored&&d.length>0&&r.push(` (unmonitored, so unknown rather than clean: ${d.join(", ")})`),r.push(" Verify before acting \u2014 these are observations, not a fresh build."),r.join(`
2
- `)}i(formatContext,"formatContext"),s(formatContext,"formatContext"),a(formatContext,"formatContext"),o(formatContext,"formatContext");export const HOOK_EVENTS=["Stop","SessionStart","SubagentStop"],isHookEvent=o(n=>typeof n=="string"&&HOOK_EVENTS.includes(n),"isHookEvent");export function hookPayload(n,e){return JSON.stringify({hookSpecificOutput:{hookEventName:n,additionalContext:e}})}i(hookPayload,"hookPayload"),s(hookPayload,"hookPayload"),a(hookPayload,"hookPayload"),o(hookPayload,"hookPayload");export function findingsDigest(n){return n.map(e=>`${e.kind}:${e.state}:${e.count??""}`).sort().join("|")}i(findingsDigest,"findingsDigest"),s(findingsDigest,"findingsDigest"),a(findingsDigest,"findingsDigest"),o(findingsDigest,"findingsDigest");
1
+ "use strict";var k=Object.defineProperty;var i=(n,t)=>k(n,"name",{value:t,configurable:!0});var v=Object.defineProperty,o=i((n,t)=>v(n,"name",{value:t,configurable:!0}),"r");export const HOOK_SIGNAL_CLASS={typecheck:"check",lint:"check",build:"check",runtime:"observation",cluster:"observation"},OBSERVATION_WINDOW_MS=30*6e4;const p=o(n=>typeof n=="object"&&n!==null&&!Array.isArray(n),"isRecord"),g=o(n=>typeof n=="number"&&Number.isFinite(n)?n:void 0,"asFiniteNumber");function f(n,t){if(!Array.isArray(n))return;let e;for(const s of n){if(!p(s))continue;const r=g(s[t]);r!==void 0&&(e===void 0||r>e)&&(e=r)}return e}i(f,"h"),o(f,"newestIn");export function freshnessOf(n){if(!p(n))return{};const t=typeof n.producer=="string"?n.producer:void 0,e=g(n.lastRunAt)??g(n.lastReportedAt)??f(n.producers,"lastReportedAt")??f(n.producers,"lastRunAt"),s=f(n.entries,"timestamp")??f(n.failures,"timestamp")??f(n.violations,"timestamp");return{...t===void 0?{}:{declaredProducer:t},...n.stale===!0?{declaredStale:!0}:{},...e===void 0?{}:{ranAt:e},...s===void 0?{}:{newestItemAt:s},...g(n.ageMs)===void 0?{}:{ageMs:g(n.ageMs)}}}i(freshnessOf,"freshnessOf"),o(freshnessOf,"freshnessOf");const h=o((n,t)=>n===void 0?void 0:Math.max(0,t-n),"ageFrom");export function candidateSignals(n){return(n.signals??[]).filter(t=>HOOK_SIGNAL_CLASS[t.kind]!==void 0&&(t.state==="failed"||t.state==="warning")&&t.producer==="connected")}i(candidateSignals,"candidateSignals"),o(candidateSignals,"candidateSignals");export function judge(n,t,e,s=OBSERVATION_WINDOW_MS){const r=HOOK_SIGNAL_CLASS[n.kind];if(r===void 0)return{kind:n.kind,reason:"out-of-scope"};if(n.state!=="failed"&&n.state!=="warning")return{kind:n.kind,reason:"not-actionable"};if(n.producer!=="connected"||t?.declaredProducer==="none")return{kind:n.kind,reason:"no-producer"};const c=n.reads?.[0]??n.resource,u={kind:n.kind,state:n.state,...n.count===void 0?{}:{count:n.count},read:c,detail:n.detail};if(n.stale===!0||t?.declaredStale===!0){const l=t?.ageMs??h(t?.ranAt,e);return{...u,stale:!0,...l===void 0?{}:{ageMs:l}}}if(r==="check"){const l=h(t?.ranAt,e);return l===void 0?{kind:n.kind,reason:"no-freshness-evidence"}:{...u,stale:!1,ageMs:l}}const a=t?.newestItemAt;if(a===void 0)return{kind:n.kind,reason:"no-freshness-evidence"};const d=Math.max(0,e-a);return d>s?{kind:n.kind,reason:"stale-observation"}:{...u,stale:!1,ageMs:d}}i(judge,"judge"),o(judge,"judge");const m=o(n=>"state"in n,"isFinding");export function select(n,t,e,s=OBSERVATION_WINDOW_MS){const r=[],c=[],u=[];for(const a of n.signals??[]){a.producer==="none"&&HOOK_SIGNAL_CLASS[a.kind]!==void 0&&u.push(a.kind);const d=judge(a,t.get(a.resource),e,s);m(d)?r.push(d):d.reason!=="out-of-scope"&&d.reason!=="not-actionable"&&c.push(d)}return r.sort((a,d)=>a.state===d.state?(d.count??0)-(a.count??0):a.state==="failed"?-1:1),{findings:r,suppressed:c,unmonitored:u}}i(select,"select"),o(select,"select");export function describeAge(n){return n<6e4?`${Math.max(1,Math.round(n/1e3))}s ago`:n<36e5?`${Math.round(n/6e4)}m ago`:`${Math.round(n/36e5)}h ago`}i(describeAge,"describeAge"),o(describeAge,"describeAge");export function describeCount(n){const t=n.kind==="runtime"?"browser runtime error":`${n.kind} ${n.state==="failed"?"error":"warning"}`,e=n.count===1?"":"s";return`${n.count===void 0?"":`${n.count} `}${t}${e}`}i(describeCount,"describeCount"),o(describeCount,"describeCount");export function describeWhen(n){return n.stale?`last checked ${n.ageMs===void 0?"a while ago":describeAge(n.ageMs)}, nothing has re-run since`:n.ageMs===void 0?"recency unknown":describeAge(n.ageMs)}i(describeWhen,"describeWhen"),o(describeWhen,"describeWhen");export function findingLine(n){return`${n.kind}: ${describeCount(n)} (${describeWhen(n)}) \u2192 read ${n.read}`}i(findingLine,"findingLine"),o(findingLine,"findingLine");export function formatContext(n,t={}){const{findings:e,unmonitored:s}=n;if(e.length===0)return;const r=[`LensMCP${t.workspace?` (${t.workspace})`:""}: ${e.length===1?"1 signal needs":`${e.length} signals need`} attention.`,...e.map(c=>` \u2022 ${findingLine(c)}`)];return t.includeUnmonitored&&s.length>0&&r.push(` (unmonitored, so unknown rather than clean: ${s.join(", ")})`),r.push(" Verify before acting \u2014 these are observations, not a fresh build."),r.join(`
2
+ `)}i(formatContext,"formatContext"),o(formatContext,"formatContext");export const HOOK_EVENTS=["Stop","SessionStart","SubagentStop"],isHookEvent=o(n=>typeof n=="string"&&HOOK_EVENTS.includes(n),"isHookEvent");export function hookPayload(n,t){return JSON.stringify({hookSpecificOutput:{hookEventName:n,additionalContext:t}})}i(hookPayload,"hookPayload"),o(hookPayload,"hookPayload");export function findingsDigest(n){return n.map(t=>`${t.kind}:${t.state}:${t.count??""}`).sort().join("|")}i(findingsDigest,"findingsDigest"),o(findingsDigest,"findingsDigest");
package/lib/sweep.js CHANGED
@@ -1 +1 @@
1
- "use strict";var j=Object.defineProperty;var o=(n,e)=>j(n,"name",{value:e,configurable:!0});var F=Object.defineProperty,m=o((n,e)=>F(n,"name",{value:e,configurable:!0}),"o"),S=Object.defineProperty,u=m((n,e)=>S(n,"name",{value:e,configurable:!0}),"r"),h=Object.defineProperty,f=u((n,e)=>h(n,"name",{value:e,configurable:!0}),"r");import{readdirSync as L,renameSync as N,rmSync as w,statSync as p}from"node:fs";import{join as i}from"node:path";const s=f(n=>{try{w(n,{force:!0})}catch{}},"removeQuietly");export function sweepLensRuntimeFiles(n,e){const r=i(n,".lensmcp"),y=i(r,"events.jsonl");s(`${y}.old`),s(`${e}.old`);const v=(()=>{const t=Number(process.env.LENSMCP_EVENT_MAX_FILES);return Number.isFinite(t)&&t>=1&&t<=20?Math.floor(t):5})(),b=64*1024*1024;try{for(const t of L(r)){const a=/^events\.jsonl\.(\d+)$/.exec(t);if(!a)continue;const l=Number(a[1]),c=i(r,t);if(l<1||l>v)s(c);else try{p(c).size>b&&s(c)}catch{}}}catch{}const d=16*1024*1024;try{p(e).size>d&&N(e,`${e}.1`)}catch{}}o(sweepLensRuntimeFiles,"sweepLensRuntimeFiles"),m(sweepLensRuntimeFiles,"sweepLensRuntimeFiles"),u(sweepLensRuntimeFiles,"sweepLensRuntimeFiles"),f(sweepLensRuntimeFiles,"sweepLensRuntimeFiles");
1
+ "use strict";var v=Object.defineProperty;var r=(t,e)=>v(t,"name",{value:e,configurable:!0});var S=Object.defineProperty,l=r((t,e)=>S(t,"name",{value:e,configurable:!0}),"r");import{readdirSync as N,renameSync as b,rmSync as h,statSync as u}from"node:fs";import{join as i}from"node:path";const c=l(t=>{try{h(t,{force:!0})}catch{}},"removeQuietly");export function sweepLensRuntimeFiles(t,e){const n=i(t,".lensmcp"),f=i(n,"events.jsonl");c(`${f}.old`),c(`${e}.old`);const y=(()=>{const s=Number(process.env.LENSMCP_EVENT_MAX_FILES);return Number.isFinite(s)&&s>=1&&s<=20?Math.floor(s):5})(),p=64*1024*1024;try{for(const s of N(n)){const a=/^events\.jsonl\.(\d+)$/.exec(s);if(!a)continue;const m=Number(a[1]),o=i(n,s);if(m<1||m>y)c(o);else try{u(o).size>p&&c(o)}catch{}}}catch{}const d=16*1024*1024;try{u(e).size>d&&b(e,`${e}.1`)}catch{}}r(sweepLensRuntimeFiles,"sweepLensRuntimeFiles"),l(sweepLensRuntimeFiles,"sweepLensRuntimeFiles");
@@ -1,2 +1,2 @@
1
- "use strict";var m=Object.defineProperty;var s=(r,e)=>m(r,"name",{value:e,configurable:!0});var y=Object.defineProperty,t=s((r,e)=>y(r,"name",{value:e,configurable:!0}),"s"),v=Object.defineProperty,a=t((r,e)=>v(r,"name",{value:e,configurable:!0}),"o"),h=Object.defineProperty,i=a((r,e)=>h(r,"name",{value:e,configurable:!0}),"o");import{existsSync as g,mkdirSync as b,readFileSync as l,writeFileSync as k}from"node:fs";import{basename as C,join as f}from"node:path";function u(r,e){let n=2166136261;for(let o=0;o<r.length;o++)n^=r.charCodeAt(o),n=Math.imul(n,16777619);return Math.abs(n|0)%e}s(u,"p"),t(u,"d"),a(u,"l"),i(u,"hashToRange");export function slugify(r){return r.toLowerCase().replace(/^@[^/]+\//,"").replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")||"workspace"}s(slugify,"slugify"),t(slugify,"slugify"),a(slugify,"slugify"),i(slugify,"slugify");export function resolveWorkspaceKey(r){const e=c(f(r,"nx.json"));if(e?.lensmcp?.key)return slugify(e.lensmcp.key);const n=c(f(r,"package.json"));return n?.name?slugify(n.name):e?.name?slugify(e.name):slugify(C(r))}s(resolveWorkspaceKey,"resolveWorkspaceKey"),t(resolveWorkspaceKey,"resolveWorkspaceKey"),a(resolveWorkspaceKey,"resolveWorkspaceKey"),i(resolveWorkspaceKey,"resolveWorkspaceKey");const p={dashboard:4321,mcpHttp:4500,bridge:5747};export function derivePorts(r){const e=u(r,200);return{dashboard:p.dashboard+e,mcpHttp:p.mcpHttp+e,bridge:p.bridge+e}}s(derivePorts,"derivePorts"),t(derivePorts,"derivePorts"),a(derivePorts,"derivePorts"),i(derivePorts,"derivePorts");export function deriveConfig(r){const e=resolveWorkspaceKey(r);return{schemaVersion:1,key:e,ports:derivePorts(e),dashboardBasePath:"/"+e}}s(deriveConfig,"deriveConfig"),t(deriveConfig,"deriveConfig"),a(deriveConfig,"deriveConfig"),i(deriveConfig,"deriveConfig");export function readLensConfig(r){const e=deriveConfig(r),n=c(f(r,".lensmcp","config.json"));if(!n)return e;const o=n.key?slugify(n.key):e.key;return{...n,schemaVersion:1,key:o,ports:{...e.ports,...n.ports??{}},dashboardBasePath:n.dashboardBasePath??"/"+o,...n.mcp?{mcp:{...n.mcp}}:{}}}s(readLensConfig,"readLensConfig"),t(readLensConfig,"readLensConfig"),a(readLensConfig,"readLensConfig"),i(readLensConfig,"readLensConfig");export function ensureLensConfig(r){const e=readLensConfig(r),n=f(r,".lensmcp");g(n)||b(n,{recursive:!0});const o=f(n,"config.json"),d=JSON.stringify(e,null,2)+`
2
- `;return(!g(o)||l(o,"utf8")!==d)&&k(o,d),e}s(ensureLensConfig,"ensureLensConfig"),t(ensureLensConfig,"ensureLensConfig"),a(ensureLensConfig,"ensureLensConfig"),i(ensureLensConfig,"ensureLensConfig");function c(r){try{return JSON.parse(l(r,"utf8"))}catch{return}}s(c,"i"),t(c,"c"),a(c,"a"),i(c,"readJson");
1
+ "use strict";var m=Object.defineProperty;var o=(r,e)=>m(r,"name",{value:e,configurable:!0});var l=Object.defineProperty,s=o((r,e)=>l(r,"name",{value:e,configurable:!0}),"o");import{existsSync as u,mkdirSync as g,readFileSync as f,writeFileSync as h}from"node:fs";import{basename as y,join as a}from"node:path";function d(r,e){let n=2166136261;for(let t=0;t<r.length;t++)n^=r.charCodeAt(t),n=Math.imul(n,16777619);return Math.abs(n|0)%e}o(d,"l"),s(d,"hashToRange");export function slugify(r){return r.toLowerCase().replace(/^@[^/]+\//,"").replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,"")||"workspace"}o(slugify,"slugify"),s(slugify,"slugify");export function resolveWorkspaceKey(r){const e=c(a(r,"nx.json"));if(e?.lensmcp?.key)return slugify(e.lensmcp.key);const n=c(a(r,"package.json"));return n?.name?slugify(n.name):e?.name?slugify(e.name):slugify(y(r))}o(resolveWorkspaceKey,"resolveWorkspaceKey"),s(resolveWorkspaceKey,"resolveWorkspaceKey");const i={dashboard:4321,mcpHttp:4500,bridge:5747};export function derivePorts(r){const e=d(r,200);return{dashboard:i.dashboard+e,mcpHttp:i.mcpHttp+e,bridge:i.bridge+e}}o(derivePorts,"derivePorts"),s(derivePorts,"derivePorts");export function deriveConfig(r){const e=resolveWorkspaceKey(r);return{schemaVersion:1,key:e,ports:derivePorts(e),dashboardBasePath:"/"+e}}o(deriveConfig,"deriveConfig"),s(deriveConfig,"deriveConfig");export function readLensConfig(r){const e=deriveConfig(r),n=c(a(r,".lensmcp","config.json"));if(!n)return e;const t=n.key?slugify(n.key):e.key;return{...n,schemaVersion:1,key:t,ports:{...e.ports,...n.ports??{}},dashboardBasePath:n.dashboardBasePath??"/"+t,...n.mcp?{mcp:{...n.mcp}}:{}}}o(readLensConfig,"readLensConfig"),s(readLensConfig,"readLensConfig");export function ensureLensConfig(r){const e=readLensConfig(r),n=a(r,".lensmcp");u(n)||g(n,{recursive:!0});const t=a(n,"config.json"),p=JSON.stringify(e,null,2)+`
2
+ `;return(!u(t)||f(t,"utf8")!==p)&&h(t,p),e}o(ensureLensConfig,"ensureLensConfig"),s(ensureLensConfig,"ensureLensConfig");function c(r){try{return JSON.parse(f(r,"utf8"))}catch{return}}o(c,"a"),s(c,"readJson");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lensmcp",
3
- "version": "1.20.0",
3
+ "version": "1.20.1",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "module": "./index.js",
@@ -43,6 +43,7 @@
43
43
  "observability",
44
44
  "ai-agents",
45
45
  "claude",
46
+ "codex",
46
47
  "cli"
47
48
  ],
48
49
  "repository": {
@@ -2,7 +2,7 @@
2
2
  "name": "lensmcp",
3
3
  "displayName": "LensMCP",
4
4
  "description": "The observability lens for coding agents. One command brings up the dev cluster gateway (every project.json `cluster` decl → its host on :443), the per-project lens dashboard at https://lensmcp.local/<project>/, and the MCP server your agent connects to — scoped automatically to whatever project you opened Claude Code in.",
5
- "version": "1.20.0",
5
+ "version": "1.20.1",
6
6
  "author": {
7
7
  "name": "David Antoon",
8
8
  "email": "davidmantoon@gmail.com"
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "lensmcp",
3
+ "version": "1.20.1",
4
+ "description": "Observe a running Nx workspace from Codex with runtime flows, build health, cluster failures, and a live dashboard.",
5
+ "author": {
6
+ "name": "David Antoon",
7
+ "email": "davidmantoon@gmail.com",
8
+ "url": "https://github.com/kiwiapps-ltd"
9
+ },
10
+ "homepage": "https://github.com/kiwiapps-ltd/lensmcp",
11
+ "repository": "https://github.com/kiwiapps-ltd/lensmcp",
12
+ "license": "Apache-2.0",
13
+ "keywords": [
14
+ "mcp",
15
+ "observability",
16
+ "nx",
17
+ "vite",
18
+ "nestjs",
19
+ "runtime-debugging"
20
+ ],
21
+ "skills": "./skills/",
22
+ "mcpServers": "./.mcp.json",
23
+ "interface": {
24
+ "displayName": "LensMCP",
25
+ "shortDescription": "Observe and debug running Nx applications",
26
+ "longDescription": "Give Codex a live observability lens over an Nx workspace: current build health, cluster crashes and 502s, browser/runtime errors, interaction flows, render causes, performance, memory, and the LensMCP dashboard.",
27
+ "developerName": "KiwiApps",
28
+ "category": "Developer Tools",
29
+ "capabilities": [
30
+ "Interactive",
31
+ "Read"
32
+ ],
33
+ "websiteURL": "https://github.com/kiwiapps-ltd/lensmcp",
34
+ "defaultPrompt": [
35
+ "Check whether my running workspace is healthy.",
36
+ "Use LensMCP to diagnose this runtime problem.",
37
+ "Open the LensMCP dashboard for this project."
38
+ ],
39
+ "brandColor": "#6D5DFB"
40
+ }
41
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "lensmcp": {
4
+ "command": "node",
5
+ "args": ["node_modules/lensmcp/bin.js", "mcp", "--cwd", "."],
6
+ "startup_timeout_sec": 30
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,38 @@
1
+ # LensMCP for Codex
2
+
3
+ This is the Codex plugin shipped inside the `lensmcp` npm package. It connects Codex to
4
+ the project-local LensMCP server and adds workflows for starting, inspecting, and stopping
5
+ the development cluster.
6
+
7
+ ## Requirements
8
+
9
+ Install `lensmcp` in the workspace where Codex will run:
10
+
11
+ ```bash
12
+ yarn add -D lensmcp
13
+ yarn lensmcp setup
14
+ ```
15
+
16
+ The plugin deliberately starts `node_modules/lensmcp/bin.js` from the active Codex
17
+ workspace. That keeps the MCP server, CLI, configuration, and `.lensmcp/events.jsonl` on
18
+ the same project-local version and prevents events from different workspaces mixing.
19
+
20
+ ## Install from the npm package
21
+
22
+ From the target workspace:
23
+
24
+ ```bash
25
+ codex plugin marketplace add ./node_modules/lensmcp
26
+ codex plugin add lensmcp@lensmcp-marketplace
27
+ ```
28
+
29
+ Start a new Codex thread after installing so the plugin's skills and MCP tools are loaded.
30
+
31
+ ## Development
32
+
33
+ From this repository, add `libs/cli` as the marketplace root, then install the plugin:
34
+
35
+ ```bash
36
+ codex plugin marketplace add ./libs/cli
37
+ codex plugin add lensmcp@lensmcp-marketplace
38
+ ```
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: lensmcp-dashboard
3
+ description: Find and open the current workspace's LensMCP dashboard. Use when the user asks to open or view the LensMCP dashboard, flows, cluster, logs, or live resources UI.
4
+ ---
5
+
6
+ # Open the LensMCP dashboard
7
+
8
+ First get the workspace-specific dashboard URL:
9
+
10
+ ```bash
11
+ node node_modules/lensmcp/bin.js gateway status --cwd .
12
+ ```
13
+
14
+ Open the printed `https://lensmcp.local/<project-key>/` URL with the available browser
15
+ tool, or give the URL to the user when browser opening is unavailable. If the gateway is
16
+ not running, use the `lensmcp-start` workflow first.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: lensmcp-start
3
+ description: Start the LensMCP dev cluster gateway and dashboard for the current workspace. Use when the user asks to start LensMCP, bring the development cluster up, start listening, or make the dashboard available.
4
+ ---
5
+
6
+ # Start LensMCP
7
+
8
+ Run this from the current workspace:
9
+
10
+ ```bash
11
+ node node_modules/lensmcp/bin.js gateway start --cwd .
12
+ ```
13
+
14
+ The command reads the workspace's `project.json` cluster declarations, starts each app or
15
+ service behind the HTTPS gateway, and serves the project dashboard at the URL it prints.
16
+
17
+ Report that URL. If startup reports `EACCES` on port 443, explain that the machine needs
18
+ the one-time `lensmcp setup` or `lensmcp trust` flow and the platform's required elevation.
19
+ Use `.lensmcp/gateway.log` for the concrete failure. Do not start individual services by
20
+ hand; the gateway owns their lifecycle.
@@ -0,0 +1,27 @@
1
+ ---
2
+ name: lensmcp-status
3
+ description: Check LensMCP gateway liveness, installation wiring, and current workspace health. Use when the user asks whether LensMCP is running, whether the workspace is healthy, or why the lens or dashboard is unavailable.
4
+ ---
5
+
6
+ # Check LensMCP status
7
+
8
+ Run the gateway and installation checks from the current workspace:
9
+
10
+ ```bash
11
+ node node_modules/lensmcp/bin.js gateway status --cwd .
12
+ node node_modules/lensmcp/bin.js doctor --cwd .
13
+ ```
14
+
15
+ Summarize gateway liveness, the dashboard URL, and every warning or failure with its
16
+ suggested fix.
17
+
18
+ For source/runtime health, prefer the MCP tool `observe_current_status`. It reports whether
19
+ typecheck, lint, build, runtime, and cluster signals are connected, stale, or unavailable.
20
+ Use `observe_errors` for the individual findings. Never treat `producer: "none"` as a pass;
21
+ it means that signal is not being monitored.
22
+
23
+ For a CLI-only health read, run:
24
+
25
+ ```bash
26
+ node node_modules/lensmcp/bin.js status --cwd .
27
+ ```
@@ -0,0 +1,16 @@
1
+ ---
2
+ name: lensmcp-stop
3
+ description: Stop the LensMCP dev cluster gateway for the current workspace. Use when the user asks to stop LensMCP, stop the gateway or cluster, or shut down the development services managed by LensMCP.
4
+ ---
5
+
6
+ # Stop LensMCP
7
+
8
+ Run this from the current workspace:
9
+
10
+ ```bash
11
+ node node_modules/lensmcp/bin.js gateway stop --cwd .
12
+ ```
13
+
14
+ Report whether the gateway stopped, was already stopped, or healed stale/orphaned gateway
15
+ state. LensMCP reconciles its pid file with the real port owner, so do not manually kill a
16
+ process unless the command identifies a remaining process it could not stop.
@@ -0,0 +1,42 @@
1
+ ---
2
+ name: using-lensmcp
3
+ description: Diagnose a running application with LensMCP runtime flows, build health, cluster errors, render causes, performance, and memory evidence. Use for runtime behavior, 502s, browser errors, unexplained UI actions, re-rendering, slow paths, or workspace health questions.
4
+ ---
5
+
6
+ # Use LensMCP as the runtime evidence source
7
+
8
+ LensMCP observes the running workspace that Codex is open in. The plugin's MCP server is
9
+ started from that workspace and reads its `.lensmcp/events.jsonl`, so projects remain
10
+ isolated from each other.
11
+
12
+ ## Diagnose from evidence
13
+
14
+ For a runtime question, inspect LensMCP before forming a code-only explanation. For a build
15
+ health question, inspect its current status before rerunning checks already watched by the
16
+ cluster.
17
+
18
+ Use these tools:
19
+
20
+ | Question | Tool |
21
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------- |
22
+ | Is anything broken now? | `observe_current_status` |
23
+ | What are the typecheck, lint, build, runtime, or cluster errors? | `observe_errors` with the matching `kind` |
24
+ | Why is a route returning 502 or did a service crash? | `observe_errors` with `kind: "cluster"`, then `process://cluster` |
25
+ | What happened recently? | `flow_list_recent`, then `flow_get` |
26
+ | What backend work followed a click? | `graph_find_backend_work_caused_by_click` or `graph_trace_from_origin` |
27
+ | Why did a component re-render? | `graph_explain_rerender` |
28
+ | What is slow or running in a loop? | `graph_find_slow_path` or `graph_find_db_in_loop` |
29
+ | Which state updates did an action cause? | `graph_find_state_updates_caused_by` |
30
+ | Did memory grow or leak? | `memory_explain_leak` or `memory_find_growth_by_flow` |
31
+ | Compile a readable flow narrative | `story_compile`, then `story_get` |
32
+ | Inspect visual/render output | `visual_capture_frame`, `visual_inspect_node`, or `visual_compare_frames` |
33
+
34
+ Trust a clean result only when its signal has `producer: "connected"` and is not stale.
35
+ `producer: "none"` means unmonitored, not healthy. If the result is stale after source
36
+ edits, run the real check or trigger the relevant producer before making a health claim.
37
+
38
+ If a tool says LensMCP is not ready, ask the user to run `lensmcp setup` once in their
39
+ terminal, then continue. Do not repeatedly retry a readiness-gated tool.
40
+
41
+ The normal loop is: start the LensMCP gateway, interact with the running app, inspect the
42
+ resulting status/flow evidence, and use the dashboard when a human-readable live view helps.