@axiomatic-labs/claudeflow 2.32.22 → 2.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -27,9 +27,6 @@ const SUBCOMMANDS = new Set([
27
27
  "help",
28
28
  "--help",
29
29
  "-h",
30
- "doctor",
31
- "panel",
32
- "preview",
33
30
  ]);
34
31
 
35
32
  function inClaudeflowProject(startDir) {
@@ -58,21 +55,6 @@ async function runSubcommand(command) {
58
55
  await version();
59
56
  return;
60
57
  }
61
- case "doctor": {
62
- const doctor = require("../lib/doctor.js");
63
- const code = await doctor(process.argv.slice(3));
64
- process.exit(code || 0);
65
- }
66
- case "panel": {
67
- const panel = require("../lib/panel.js");
68
- await panel(process.argv.slice(3));
69
- return;
70
- }
71
- case "preview": {
72
- const preview = require("../lib/preview.js");
73
- await preview(process.argv.slice(3));
74
- return;
75
- }
76
58
  case "help":
77
59
  case "--help":
78
60
  case "-h":
@@ -85,15 +67,6 @@ async function runSubcommand(command) {
85
67
  console.log(
86
68
  ` ${ui.CYAN}install${ui.RESET} Install or update Claudeflow in the current project`,
87
69
  );
88
- console.log(
89
- ` ${ui.CYAN}doctor${ui.RESET} Diagnose local issues (CDP port, stale lockfiles); add --fix to repair`,
90
- );
91
- console.log(
92
- ` ${ui.CYAN}panel${ui.RESET} Open the local web dashboard for hooks, CLAUDE.md, and run state`,
93
- );
94
- console.log(
95
- ` ${ui.CYAN}preview${ui.RESET} Live-reload server for prototypes (.claudeflow/prototypes/) — run once, leave it`,
96
- );
97
70
  console.log(` ${ui.CYAN}version${ui.RESET} Show version info`);
98
71
  console.log(` ${ui.CYAN}help${ui.RESET} Show this message`);
99
72
  console.log("");
package/lib/doctor.js CHANGED
@@ -146,43 +146,9 @@ function checkStaleLockfiles(cwd) {
146
146
  // Detection strategy (in order of preference):
147
147
  // 1. error-observer.json exists → read its `port` + `pid_path`.
148
148
  // 2. If pidfile exists and PID is alive → running=true.
149
- // 3. Else, TCP-probe the observer port with a short `net.connect` (via a
150
- // one-shot `node -e` child, so the synchronous status path stays sync).
149
+ // 3. Else, TCP probe the observer port via `lsof -i :PORT -sTCP:LISTEN -t`.
151
150
  // The pidfile may be missing (race) but if SOMETHING is listening on
152
- // the configured port, the daemon is up. This replaces a prior `lsof`
153
- // probe that was slow on macOS (often >1.5s — the timeout — so a live
154
- // observer read as stopped) and absent on Windows (no `lsof` → the
155
- // fallback never ran). `net.connect` is a Node built-in: fast and
156
- // cross-platform. Trade-off: it proves a listener EXISTS but cannot
157
- // attribute the owning PID, so a probe hit returns `pid: null`.
158
- //
159
- // Synchronous TCP liveness check WITHOUT lsof. Node has no sync net.connect,
160
- // so we run the connect in a one-shot `node -e` child via spawnSync — keeping
161
- // readObserverState (and its sync callers: getMcpInfo/getObserverInfo/
162
- // collectStatus) synchronous. Probes both IPv4 and IPv6 loopback (the daemon
163
- // may bind either); exit 0 = a listener answered. Reached only when the
164
- // pidfile path already failed, so the ~node-startup cost is off the hot path.
165
- function portHasListenerSync(port) {
166
- if (!Number.isFinite(port) || port <= 0) return false;
167
- const script =
168
- 'const net=require("net");' +
169
- 'const port=' + Number(port) + ';' +
170
- 'const hosts=["127.0.0.1","::1"];' +
171
- 'let pending=hosts.length,done=false;' +
172
- 'const finish=(ok)=>{if(done)return;if(ok){done=true;process.exit(0);}if(--pending===0){done=true;process.exit(1);}};' +
173
- 'for(const h of hosts){const s=net.connect({host:h,port});s.setTimeout(250);' +
174
- 's.once("connect",()=>{s.destroy();finish(true);});' +
175
- 's.once("error",()=>finish(false));' +
176
- 's.once("timeout",()=>{s.destroy();finish(false);});}';
177
- try {
178
- const { spawnSync } = require('child_process');
179
- const r = spawnSync(process.execPath, ['-e', script], { timeout: 1000 });
180
- return r.status === 0;
181
- } catch {
182
- return false;
183
- }
184
- }
185
-
151
+ // the configured port, the daemon is up.
186
152
  function readObserverState(cwd) {
187
153
  const observerJsonPath = path.join(cwd, '.claudeflow', 'tmp', 'error-observer.json');
188
154
  // Defense-in-depth retry: even though the daemon now writes atomically
@@ -234,12 +200,18 @@ function readObserverState(cwd) {
234
200
  if (alive) return { running: true, pid, port, source: 'pidfile', observerJsonPath };
235
201
  }
236
202
 
237
- // Fallback: cross-platform net.connect probe. Catches the case where the
238
- // pidfile is missing (race / external start) but the daemon is still
239
- // listening on the port. Can't attribute the PID, so report pid: null.
240
- if (portHasListenerSync(port)) {
241
- return { running: true, pid: null, port, source: 'tcp-probe', observerJsonPath };
242
- }
203
+ // Fallback: TCP probe via lsof. Catches the case where pidfile is missing
204
+ // but the daemon process is still listening on the port.
205
+ try {
206
+ const { spawnSync } = require('child_process');
207
+ const r = spawnSync('lsof', ['-i', `:${port}`, '-sTCP:LISTEN', '-t'], { encoding: 'utf8', timeout: 1500 });
208
+ if (r.status === 0 && r.stdout && r.stdout.trim()) {
209
+ const tcpPid = parseInt(r.stdout.trim().split('\n')[0], 10);
210
+ if (Number.isFinite(tcpPid) && tcpPid > 0) {
211
+ return { running: true, pid: tcpPid, port, source: 'tcp-probe', observerJsonPath };
212
+ }
213
+ }
214
+ } catch {}
243
215
 
244
216
  return { running: false, pid: pid, port, source: 'no-listener', observerJsonPath };
245
217
  }