@axiomatic-labs/claudeflow 2.13.55 → 2.13.56
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/lib/doctor.js +51 -0
- package/lib/panel.js +21 -16
- package/package.json +1 -1
package/lib/doctor.js
CHANGED
|
@@ -129,6 +129,54 @@ function checkStaleLockfiles(cwd) {
|
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
// Read the browser-error-daemon observer state from its pidfile.
|
|
133
|
+
// Returns { running, pid, lockfile } — running=true only when the pid is
|
|
134
|
+
// alive AND the lockfile exists. Used by both doctor and panel.
|
|
135
|
+
function readObserverState(cwd, cdpPort) {
|
|
136
|
+
const lockfile = path.join(cwd, '.claudeflow', 'tmp', `.browser-cdp-${cdpPort}.pid`);
|
|
137
|
+
let pid = null;
|
|
138
|
+
try {
|
|
139
|
+
pid = parseInt(fs.readFileSync(lockfile, 'utf8').trim(), 10);
|
|
140
|
+
} catch {
|
|
141
|
+
return { running: false, pid: null, lockfile };
|
|
142
|
+
}
|
|
143
|
+
if (!Number.isFinite(pid) || pid <= 0) return { running: false, pid, lockfile };
|
|
144
|
+
let alive = false;
|
|
145
|
+
try { process.kill(pid, 0); alive = true; } catch (e) { alive = e.code === 'EPERM'; }
|
|
146
|
+
return { running: alive, pid, lockfile };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Surface as a doctor/panel issue when the browser-error-daemon observer
|
|
150
|
+
// is stopped while Playwright is configured. The observer collects console
|
|
151
|
+
// errors + network failures during headed browser verification — when it's
|
|
152
|
+
// down, those signals are silently lost, so the agent may verify a flow
|
|
153
|
+
// that has console errors and not know it.
|
|
154
|
+
function checkObserverState(cwd) {
|
|
155
|
+
const mcpPath = path.join(cwd, '.mcp.json');
|
|
156
|
+
let cfg;
|
|
157
|
+
try { cfg = JSON.parse(fs.readFileSync(mcpPath, 'utf8')); }
|
|
158
|
+
catch { return { id: 'observer-state', severity: 'info', message: 'No .mcp.json — observer not applicable.' }; }
|
|
159
|
+
const hasPlaywright = cfg.mcpServers
|
|
160
|
+
&& Object.keys(cfg.mcpServers).some((s) => s.toLowerCase().includes('playwright'));
|
|
161
|
+
if (!hasPlaywright) {
|
|
162
|
+
return { id: 'observer-state', severity: 'info', message: 'Playwright not configured — observer not applicable.' };
|
|
163
|
+
}
|
|
164
|
+
const observer = readObserverState(cwd, deriveCdpPort(cwd));
|
|
165
|
+
if (observer.running) {
|
|
166
|
+
return {
|
|
167
|
+
id: 'observer-state',
|
|
168
|
+
severity: 'ok',
|
|
169
|
+
message: `browser-error-daemon running (pid ${observer.pid}).`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
id: 'observer-state',
|
|
174
|
+
severity: 'mismatch',
|
|
175
|
+
message: 'browser-error-daemon is STOPPED while Playwright is configured. Console errors / network failures from headed sessions are NOT being collected — the agent may verify a flow with console errors and miss them. The observer should auto-start at SessionStart via `.claude/hooks/SessionStart/browser-error-daemon.js`; if it keeps stopping, inspect that hook for crashes or check the pidfile at `.claudeflow/tmp/.browser-cdp-<port>.pid`.',
|
|
176
|
+
detail: [{ lockfile: observer.lockfile, pid: observer.pid }],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
132
180
|
function applyStaleLockfileFix(check) {
|
|
133
181
|
for (const { file } of check.fix.stale) {
|
|
134
182
|
try { fs.unlinkSync(file); } catch {}
|
|
@@ -163,6 +211,7 @@ async function run(argv = []) {
|
|
|
163
211
|
const checks = [
|
|
164
212
|
checkCdpPortMismatch(cwd),
|
|
165
213
|
checkStaleLockfiles(cwd),
|
|
214
|
+
checkObserverState(cwd),
|
|
166
215
|
];
|
|
167
216
|
|
|
168
217
|
for (const check of checks) printCheck(check);
|
|
@@ -202,6 +251,8 @@ module.exports = run;
|
|
|
202
251
|
module.exports.deriveCdpPort = deriveCdpPort;
|
|
203
252
|
module.exports.checkCdpPortMismatch = checkCdpPortMismatch;
|
|
204
253
|
module.exports.checkStaleLockfiles = checkStaleLockfiles;
|
|
254
|
+
module.exports.checkObserverState = checkObserverState;
|
|
255
|
+
module.exports.readObserverState = readObserverState;
|
|
205
256
|
module.exports.readPlaywrightCdpEndpoint = readPlaywrightCdpEndpoint;
|
|
206
257
|
module.exports.applyCdpPortFix = applyCdpPortFix;
|
|
207
258
|
module.exports.applyStaleLockfileFix = applyStaleLockfileFix;
|
package/lib/panel.js
CHANGED
|
@@ -19,6 +19,8 @@ const {
|
|
|
19
19
|
deriveCdpPort,
|
|
20
20
|
checkCdpPortMismatch,
|
|
21
21
|
checkStaleLockfiles,
|
|
22
|
+
checkObserverState,
|
|
23
|
+
readObserverState: readObserverStateFromDoctor,
|
|
22
24
|
readPlaywrightCdpEndpoint,
|
|
23
25
|
} = require('./doctor.js');
|
|
24
26
|
const {
|
|
@@ -211,19 +213,10 @@ function getMcpInfo(cwd) {
|
|
|
211
213
|
};
|
|
212
214
|
}
|
|
213
215
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
pid = parseInt(fs.readFileSync(lockfile, 'utf8').trim(), 10);
|
|
219
|
-
} catch {
|
|
220
|
-
return { running: false, pid: null, lockfile };
|
|
221
|
-
}
|
|
222
|
-
if (!Number.isFinite(pid) || pid <= 0) return { running: false, pid, lockfile };
|
|
223
|
-
let alive = false;
|
|
224
|
-
try { process.kill(pid, 0); alive = true; } catch (e) { alive = e.code === 'EPERM'; }
|
|
225
|
-
return { running: alive, pid, lockfile };
|
|
226
|
-
}
|
|
216
|
+
// Re-export the canonical helper from doctor.js so panel callers keep the
|
|
217
|
+
// same name. The implementation moved to doctor.js so both surfaces (CLI
|
|
218
|
+
// doctor + panel) share a single source of truth.
|
|
219
|
+
const readObserverState = readObserverStateFromDoctor;
|
|
227
220
|
|
|
228
221
|
function getSetupContextInfo(cwd) {
|
|
229
222
|
const p = path.join(cwd, '.claudeflow', 'config', 'setup-context.json');
|
|
@@ -524,7 +517,11 @@ function getLogsInfo(cwd) {
|
|
|
524
517
|
}
|
|
525
518
|
|
|
526
519
|
function getDoctorInfo(cwd) {
|
|
527
|
-
const checks = [
|
|
520
|
+
const checks = [
|
|
521
|
+
checkCdpPortMismatch(cwd),
|
|
522
|
+
checkStaleLockfiles(cwd),
|
|
523
|
+
checkObserverState(cwd),
|
|
524
|
+
];
|
|
528
525
|
const issues = checks.filter((c) => c.severity !== 'ok' && c.severity !== 'info');
|
|
529
526
|
return {
|
|
530
527
|
issueCount: issues.length,
|
|
@@ -872,7 +869,15 @@ function severityFor(id) {
|
|
|
872
869
|
if (s.logs.latestReminderStatus === 'heavy') return 'warn';
|
|
873
870
|
return s.logs.total > 0 ? 'ok' : 'info';
|
|
874
871
|
}
|
|
875
|
-
case 'mcp':
|
|
872
|
+
case 'mcp': {
|
|
873
|
+
if (!s.mcp.configFound) return 'info';
|
|
874
|
+
const playwrightOk = s.mcp.playwright.match && s.mcp.staleLockfiles.length === 0;
|
|
875
|
+
// Observer stopped while Playwright is configured = warn. Console errors
|
|
876
|
+
// and network failures from headed sessions are silently lost when the
|
|
877
|
+
// observer is down.
|
|
878
|
+
const observerOk = !s.mcp.playwright.match || (s.mcp.observer && s.mcp.observer.running);
|
|
879
|
+
return playwrightOk && observerOk ? 'ok' : 'warn';
|
|
880
|
+
}
|
|
876
881
|
case 'setupContext': return !s.setupContext.exists ? 'err' : (s.setupContext.toolingComplete ? 'ok' : 'warn');
|
|
877
882
|
case 'activeRun': return s.activeRun.active ? 'info' : 'info';
|
|
878
883
|
case 'doctor': return s.doctor.issueCount > 0 ? 'warn' : 'ok';
|
|
@@ -1294,7 +1299,7 @@ function renderMcp() {
|
|
|
1294
1299
|
\${row('Playwright --cdp-endpoint port', String(m.playwright.configured || 'n/a'))}
|
|
1295
1300
|
\${row('Computed port (from path)', String(m.playwright.computed))}
|
|
1296
1301
|
\${row('Match', m.playwright.match ? 'YES' : 'NO', { kind: m.playwright.match ? 'ok' : 'err', text: m.playwright.match ? '✓' : '✗' })}
|
|
1297
|
-
\${row('Observer', m.observer.running ? 'running (pid '+m.observer.pid+')' : '
|
|
1302
|
+
\${row('Observer', m.observer.running ? 'running (pid '+m.observer.pid+')' : 'STOPPED — browser errors NOT being collected', { kind: m.observer.running ? 'ok' : (m.playwright.match ? 'warn' : 'info'), text: m.observer.running ? '✓' : (m.playwright.match ? '⚠' : '·') })}
|
|
1298
1303
|
</div>
|
|
1299
1304
|
<h2 style="margin-top:18px">Stale lockfiles</h2>
|
|
1300
1305
|
\${lockfiles}\`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiomatic-labs/claudeflow",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.56",
|
|
4
4
|
"description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"claudeflow": "./bin/cli.js"
|