@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 +0 -27
- package/lib/doctor.js +14 -42
- package/lib/install.js +121 -1359
- package/lib/panel.js +10 -26
- package/package.json +1 -1
- package/lib/hook-overrides.js +0 -262
- package/lib/preview.js +0 -360
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
|
|
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.
|
|
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:
|
|
238
|
-
//
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
}
|