@axiomatic-labs/claudeflow 2.13.56 → 2.13.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/panel.js +110 -0
  2. package/package.json +1 -1
package/lib/panel.js CHANGED
@@ -22,6 +22,7 @@ const {
22
22
  checkObserverState,
23
23
  readObserverState: readObserverStateFromDoctor,
24
24
  readPlaywrightCdpEndpoint,
25
+ applyCdpPortFix,
25
26
  } = require('./doctor.js');
26
27
  const {
27
28
  readOverrides,
@@ -205,6 +206,10 @@ function getMcpInfo(cwd) {
205
206
  configured: reading.state === 'ok' ? reading.port : null,
206
207
  computed: deriveCdpPort(cwd),
207
208
  match: cdp.severity === 'ok',
209
+ // `fixable` is true only for the "wrong port" mismatch case — the
210
+ // applyCdpPortFix routine can rewrite the args entry. Other cases
211
+ // (invalid JSON, unparseable value) can't be auto-fixed by the panel.
212
+ fixable: cdp.severity === 'mismatch',
208
213
  state: reading.state,
209
214
  message: cdp.message,
210
215
  },
@@ -658,6 +663,9 @@ details[open] summary { padding-bottom: 8px; border-bottom: 1px solid var(--bord
658
663
  .logs-controls { display: flex; gap: 12px; align-items: center; margin-bottom: 14px; }
659
664
  .logs-controls select, .logs-controls button { background: var(--panel); border: 1px solid var(--border); color: var(--fg); padding: 6px 10px; border-radius: 6px; font: inherit; }
660
665
  .logs-controls button:hover { border-color: var(--accent); cursor: pointer; }
666
+ .btn-warn { background: var(--panel); border: 1px solid rgba(245,194,52,0.5); color: var(--warn, #f5c234); padding: 6px 12px; border-radius: 6px; cursor: pointer; font: inherit; }
667
+ .btn-warn:hover { border-color: var(--warn, #f5c234); background: rgba(245,194,52,0.08); }
668
+ .btn-warn:disabled { opacity: 0.4; cursor: not-allowed; }
661
669
  .logs-clear-btn { margin-left: auto; color: var(--err); border-color: rgba(248,81,73,0.4); }
662
670
  .logs-clear-btn:hover { border-color: var(--err); background: rgba(248,81,73,0.08); }
663
671
  .logs-clear-btn:disabled { opacity: 0.4; cursor: not-allowed; color: var(--muted); border-color: var(--border); }
@@ -1299,7 +1307,9 @@ function renderMcp() {
1299
1307
  \${row('Playwright --cdp-endpoint port', String(m.playwright.configured || 'n/a'))}
1300
1308
  \${row('Computed port (from path)', String(m.playwright.computed))}
1301
1309
  \${row('Match', m.playwright.match ? 'YES' : 'NO', { kind: m.playwright.match ? 'ok' : 'err', text: m.playwright.match ? '✓' : '✗' })}
1310
+ \${m.playwright.fixable ? '<div style="padding:10px 14px;border-top:1px solid var(--border);"><button id="fix-cdp-port" class="btn-warn">Fix CDP port</button> <span class="muted" style="margin-left:10px;font-size:12px;">Rewrites .mcp.json --cdp-endpoint to localhost:'+m.playwright.computed+' (the port this machine derives from the cwd).</span></div>' : ''}
1302
1311
  \${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 ? '⚠' : '·') })}
1312
+ \${!m.observer.running && m.playwright.match ? '<div style="padding:10px 14px;border-top:1px solid var(--border);"><button id="restart-observer" class="btn-warn">Restart observer</button> <span class="muted" style="margin-left:10px;font-size:12px;">Re-runs SessionStart/browser-error-daemon.js — spawns Chrome with --cdp-endpoint and registers the daemon pidfile.</span></div>' : ''}
1303
1313
  </div>
1304
1314
  <h2 style="margin-top:18px">Stale lockfiles</h2>
1305
1315
  \${lockfiles}\`;
@@ -1409,8 +1419,56 @@ document.addEventListener('click', (e) => {
1409
1419
  if (!confirm('Delete ' + total + ' captured log event(s) and truncate .claude/tmp/hooks.log?\\n\\nThis cannot be undone. (Future events will be captured normally.)')) return;
1410
1420
  return clearLogsAction();
1411
1421
  }
1422
+ if (t.id === 'restart-observer') {
1423
+ return restartObserverAction(t);
1424
+ }
1425
+ if (t.id === 'fix-cdp-port') {
1426
+ return fixCdpPortAction(t);
1427
+ }
1412
1428
  });
1413
1429
 
1430
+ async function fixCdpPortAction(btn) {
1431
+ const originalLabel = btn.textContent;
1432
+ btn.disabled = true;
1433
+ btn.textContent = 'Fixing…';
1434
+ try {
1435
+ const r = await fetch('/api/cdp-port/fix', { method: 'POST' });
1436
+ const result = await r.json();
1437
+ if (r.ok && result.ok) {
1438
+ showToast('CDP port fixed — .mcp.json rewritten. ' + (result.after && result.after.message || ''), 'ok');
1439
+ } else {
1440
+ showToast('Fix failed: ' + (result.error || 'unknown'), 'err');
1441
+ }
1442
+ } catch (e) {
1443
+ showToast('Network error: ' + e.message, 'err');
1444
+ } finally {
1445
+ btn.disabled = false;
1446
+ btn.textContent = originalLabel;
1447
+ await refresh();
1448
+ }
1449
+ }
1450
+
1451
+ async function restartObserverAction(btn) {
1452
+ const originalLabel = btn.textContent;
1453
+ btn.disabled = true;
1454
+ btn.textContent = 'Restarting…';
1455
+ try {
1456
+ const r = await fetch('/api/observer/restart', { method: 'POST' });
1457
+ const result = await r.json();
1458
+ if (r.ok && result.ok) {
1459
+ showToast('Observer restarted — pid ' + (result.observer && result.observer.pid) + '. Refreshing…', 'ok');
1460
+ } else {
1461
+ showToast('Restart failed: ' + (result.error || result.script_stderr || 'unknown'), 'err');
1462
+ }
1463
+ } catch (e) {
1464
+ showToast('Network error: ' + e.message, 'err');
1465
+ } finally {
1466
+ btn.disabled = false;
1467
+ btn.textContent = originalLabel;
1468
+ await refresh();
1469
+ }
1470
+ }
1471
+
1414
1472
  async function clearLogsAction() {
1415
1473
  try {
1416
1474
  const r = await fetch('/api/logs', { method: 'DELETE' });
@@ -1661,6 +1719,58 @@ function handler(cwd) {
1661
1719
  return send(status, JSON.stringify(result), 'application/json');
1662
1720
  }
1663
1721
 
1722
+ if (req.method === 'POST' && route === '/api/cdp-port/fix') {
1723
+ // Re-run checkCdpPortMismatch to get fresh state (the file may have
1724
+ // changed since the panel last fetched). Only apply the fix if the
1725
+ // check still reports severity='mismatch' (i.e. the wrong-port case
1726
+ // — other failure modes like invalid JSON cannot be auto-fixed).
1727
+ const check = checkCdpPortMismatch(cwd);
1728
+ if (check.severity !== 'mismatch') {
1729
+ return send(409, JSON.stringify({
1730
+ ok: false,
1731
+ error: `cdp-port check is "${check.severity}" — only "mismatch" is auto-fixable. Current message: ${check.message}`,
1732
+ check_severity: check.severity,
1733
+ }), 'application/json');
1734
+ }
1735
+ try {
1736
+ applyCdpPortFix(check);
1737
+ } catch (e) {
1738
+ return send(500, JSON.stringify({ ok: false, error: `applyCdpPortFix failed: ${e.message}` }), 'application/json');
1739
+ }
1740
+ const after = checkCdpPortMismatch(cwd);
1741
+ return send(200, JSON.stringify({
1742
+ ok: after.severity === 'ok',
1743
+ before: { severity: check.severity, message: check.message },
1744
+ after: { severity: after.severity, message: after.message },
1745
+ }), 'application/json');
1746
+ }
1747
+
1748
+ if (req.method === 'POST' && route === '/api/observer/restart') {
1749
+ // Re-run the SessionStart/browser-error-daemon.js hook script
1750
+ // synchronously. The script calls ensureObserver() which spawns
1751
+ // Chrome with --cdp-endpoint as a detached process. Once the
1752
+ // script exits, the daemon is up (or the script logged the failure).
1753
+ const daemonScript = path.join(cwd, '.claude', 'hooks', 'SessionStart', 'browser-error-daemon.js');
1754
+ if (!fs.existsSync(daemonScript)) {
1755
+ return send(404, JSON.stringify({ ok: false, error: 'daemon script not found at .claude/hooks/SessionStart/browser-error-daemon.js' }), 'application/json');
1756
+ }
1757
+ const { spawnSync } = require('child_process');
1758
+ const r = spawnSync(process.execPath, [daemonScript], {
1759
+ cwd,
1760
+ env: { ...process.env, CLAUDE_PROJECT_DIR: cwd },
1761
+ encoding: 'utf8',
1762
+ timeout: 15000,
1763
+ });
1764
+ const observerState = readObserverState(cwd, deriveCdpPort(cwd));
1765
+ const ok = observerState.running;
1766
+ return send(ok ? 200 : 500, JSON.stringify({
1767
+ ok,
1768
+ observer: observerState,
1769
+ script_exit: r.status,
1770
+ script_stderr: (r.stderr || '').slice(0, 500),
1771
+ }), 'application/json');
1772
+ }
1773
+
1664
1774
  if (req.method === 'POST' && route === '/api/reminders/toggle') {
1665
1775
  const raw = await readRequestBody(req);
1666
1776
  let payload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.13.56",
3
+ "version": "2.13.58",
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"