@axiomatic-labs/claudeflow 2.13.60 → 2.13.62

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/doctor.js +29 -6
  2. package/package.json +1 -1
package/lib/doctor.js CHANGED
@@ -149,13 +149,36 @@ function checkStaleLockfiles(cwd) {
149
149
  // the configured port, the daemon is up.
150
150
  function readObserverState(cwd) {
151
151
  const observerJsonPath = path.join(cwd, '.claudeflow', 'tmp', 'error-observer.json');
152
- let meta;
153
- try {
154
- meta = JSON.parse(fs.readFileSync(observerJsonPath, 'utf8'));
155
- } catch {
156
- return { running: false, pid: null, port: null, source: 'no-observer-json', observerJsonPath };
152
+ // Defense-in-depth retry: even though the daemon now writes atomically
153
+ // (rename-after-write), older daemon versions and any future non-atomic
154
+ // writer would expose a 0-byte window during overwrite. We retry up to
155
+ // 3 times with a tiny pause between attempts so a transient empty read
156
+ // doesn't flip the panel UI between running/stopped every refresh.
157
+ let meta = null;
158
+ let lastErr = null;
159
+ for (let attempt = 0; attempt < 3; attempt += 1) {
160
+ try {
161
+ const raw = fs.readFileSync(observerJsonPath, 'utf8');
162
+ if (raw.trim()) {
163
+ meta = JSON.parse(raw);
164
+ break;
165
+ }
166
+ lastErr = new Error('empty file');
167
+ } catch (e) {
168
+ lastErr = e;
169
+ if (e.code === 'ENOENT') break; // file genuinely missing — no retry helps
170
+ }
171
+ if (attempt < 2) {
172
+ // Sync sleep (~30ms) via spawnSync — sufficient to outlast a writeFile race.
173
+ const { spawnSync } = require('child_process');
174
+ spawnSync('sleep', ['0.03']);
175
+ }
176
+ }
177
+ if (!meta) {
178
+ const source = (lastErr && lastErr.code === 'ENOENT') ? 'no-observer-json' : 'observer-json-malformed';
179
+ return { running: false, pid: null, port: null, source, observerJsonPath };
157
180
  }
158
- if (!meta || typeof meta.port !== 'number') {
181
+ if (typeof meta.port !== 'number') {
159
182
  return { running: false, pid: null, port: null, source: 'observer-json-malformed', observerJsonPath };
160
183
  }
161
184
  const port = meta.port;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiomatic-labs/claudeflow",
3
- "version": "2.13.60",
3
+ "version": "2.13.62",
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"