claude-flow 3.7.0-alpha.80 → 3.7.0-alpha.81

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.7.0-alpha.80",
3
+ "version": "3.7.0-alpha.81",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -208,7 +208,20 @@ export declare class HeadlessWorkerExecutor extends EventEmitter {
208
208
  private claudeCodeVersion;
209
209
  constructor(projectRoot: string, options?: HeadlessExecutorConfig);
210
210
  /**
211
- * Check if Claude Code CLI is available
211
+ * Check if Claude Code CLI is available.
212
+ *
213
+ * #2110 fix — three issues addressed:
214
+ * 1. Cache only `true`, never `false`. A transient failure (WSL2 cold
215
+ * start, AV scanner, slow shell init) used to set
216
+ * `claudeCodeAvailable = false` for the rest of the daemon
217
+ * lifetime, so the daemon kept running local stubs even after the
218
+ * user fixed `claude auth login`. Now: false results re-probe on
219
+ * the next call.
220
+ * 2. Log the actual error from the catch block instead of silently
221
+ * swallowing it. Operators couldn't distinguish timeout / ENOENT /
222
+ * auth-failure / exit-code without this.
223
+ * 3. Honour `CLAUDE_CODE_AVAILABILITY_TIMEOUT_MS` for WSL2 / slow
224
+ * systems where `claude --version` can take >5s on first invoke.
212
225
  */
213
226
  isAvailable(): Promise<boolean>;
214
227
  /**
@@ -387,17 +387,33 @@ export class HeadlessWorkerExecutor extends EventEmitter {
387
387
  // Public API
388
388
  // ============================================
389
389
  /**
390
- * Check if Claude Code CLI is available
390
+ * Check if Claude Code CLI is available.
391
+ *
392
+ * #2110 fix — three issues addressed:
393
+ * 1. Cache only `true`, never `false`. A transient failure (WSL2 cold
394
+ * start, AV scanner, slow shell init) used to set
395
+ * `claudeCodeAvailable = false` for the rest of the daemon
396
+ * lifetime, so the daemon kept running local stubs even after the
397
+ * user fixed `claude auth login`. Now: false results re-probe on
398
+ * the next call.
399
+ * 2. Log the actual error from the catch block instead of silently
400
+ * swallowing it. Operators couldn't distinguish timeout / ENOENT /
401
+ * auth-failure / exit-code without this.
402
+ * 3. Honour `CLAUDE_CODE_AVAILABILITY_TIMEOUT_MS` for WSL2 / slow
403
+ * systems where `claude --version` can take >5s on first invoke.
391
404
  */
392
405
  async isAvailable() {
393
- if (this.claudeCodeAvailable !== null) {
394
- return this.claudeCodeAvailable;
406
+ // Only the `true` result is cached — `false` is re-probed every call
407
+ // so a transient failure doesn't poison the rest of the daemon's life.
408
+ if (this.claudeCodeAvailable === true) {
409
+ return true;
395
410
  }
411
+ const timeoutMs = Number.parseInt(process.env.CLAUDE_CODE_AVAILABILITY_TIMEOUT_MS || '', 10) || 5000;
396
412
  try {
397
413
  const output = execSync('claude --version', {
398
414
  encoding: 'utf-8',
399
415
  stdio: 'pipe',
400
- timeout: 5000,
416
+ timeout: timeoutMs,
401
417
  windowsHide: true, // Prevent phantom console windows on Windows
402
418
  });
403
419
  this.claudeCodeAvailable = true;
@@ -405,9 +421,14 @@ export class HeadlessWorkerExecutor extends EventEmitter {
405
421
  this.emit('status', { available: true, version: this.claudeCodeVersion });
406
422
  return true;
407
423
  }
408
- catch {
409
- this.claudeCodeAvailable = false;
410
- this.emit('status', { available: false });
424
+ catch (err) {
425
+ // Don't cache false — let the next call retry. Surface the actual
426
+ // error via emit so operators can diagnose timeout / ENOENT / auth.
427
+ this.claudeCodeAvailable = null;
428
+ const reason = err instanceof Error
429
+ ? `${err.name}: ${err.message}`.slice(0, 200)
430
+ : String(err).slice(0, 200);
431
+ this.emit('status', { available: false, reason });
411
432
  return false;
412
433
  }
413
434
  }
@@ -94,6 +94,18 @@ export declare class WorkerDaemon extends EventEmitter {
94
94
  * cgroup v2 / v1 quota files first so the maxCpuLoad threshold stays
95
95
  * meaningful under resource-limited containers.
96
96
  */
97
+ /**
98
+ * #2110 — detect WSL2 / WSL1 so the CPU-load gate can use a sane
99
+ * default. `/proc/loadavg` on WSL maps in Windows-side process counts
100
+ * and routinely reports values 100-1000x larger than real Linux load.
101
+ *
102
+ * Detection order:
103
+ * 1. `WSL_DISTRO_NAME` env var (set by Microsoft's WSL launcher)
104
+ * 2. `WSL_INTEROP` env var (set by recent WSL2)
105
+ * 3. `/proc/sys/kernel/osrelease` contains "microsoft" or "WSL"
106
+ * (kernel build marker; survives env stripping)
107
+ */
108
+ static isWslEnvironment(): boolean;
97
109
  static getEffectiveCpuCount(): number;
98
110
  /**
99
111
  * Read daemon-specific config from .claude-flow/config.{json,yaml,yml}.
@@ -57,7 +57,18 @@ export class WorkerDaemon extends EventEmitter {
57
57
  const fileConfig = this.readDaemonConfigFromFile(claudeFlowDir);
58
58
  // CPU-proportional smart default instead of hardcoded 2.0
59
59
  const cpuCount = WorkerDaemon.getEffectiveCpuCount();
60
- const smartMaxCpuLoad = Math.max(cpuCount * 0.8, 2.0); // Floor of 2.0 for single-CPU machines
60
+ let smartMaxCpuLoad = Math.max(cpuCount * 0.8, 2.0); // Floor of 2.0 for single-CPU machines
61
+ // #2110 — WSL2 reports `/proc/loadavg` values that include Windows-side
62
+ // process counts mapped into the Linux kernel. Real load on a 4-CPU
63
+ // WSL2 host can be 200-400 even when the Linux side is idle. The
64
+ // default gate of `cpuCount * 0.8` always trips, deferring every
65
+ // worker as "CPU load too high" while the daemon reports healthy.
66
+ // Bump the floor to 1000 when WSL is detected so the gate is
67
+ // effectively disabled (real load on Linux side rarely exceeds 100
68
+ // even under heavy contention).
69
+ if (WorkerDaemon.isWslEnvironment()) {
70
+ smartMaxCpuLoad = Math.max(smartMaxCpuLoad, 1000);
71
+ }
61
72
  // Platform-aware default: macOS os.freemem() excludes reclaimable file cache,
62
73
  // so reported "free" is much lower than actually available memory.
63
74
  // Linux reports available memory (including reclaimable cache) more accurately.
@@ -155,6 +166,28 @@ export class WorkerDaemon extends EventEmitter {
155
166
  * cgroup v2 / v1 quota files first so the maxCpuLoad threshold stays
156
167
  * meaningful under resource-limited containers.
157
168
  */
169
+ /**
170
+ * #2110 — detect WSL2 / WSL1 so the CPU-load gate can use a sane
171
+ * default. `/proc/loadavg` on WSL maps in Windows-side process counts
172
+ * and routinely reports values 100-1000x larger than real Linux load.
173
+ *
174
+ * Detection order:
175
+ * 1. `WSL_DISTRO_NAME` env var (set by Microsoft's WSL launcher)
176
+ * 2. `WSL_INTEROP` env var (set by recent WSL2)
177
+ * 3. `/proc/sys/kernel/osrelease` contains "microsoft" or "WSL"
178
+ * (kernel build marker; survives env stripping)
179
+ */
180
+ static isWslEnvironment() {
181
+ if (process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP)
182
+ return true;
183
+ try {
184
+ const osrelease = readFileSync('/proc/sys/kernel/osrelease', 'utf8').toLowerCase();
185
+ if (osrelease.includes('microsoft') || osrelease.includes('wsl'))
186
+ return true;
187
+ }
188
+ catch { /* not on Linux or /proc inaccessible */ }
189
+ return false;
190
+ }
158
191
  static getEffectiveCpuCount() {
159
192
  // 1. Try cgroup v2: /sys/fs/cgroup/cpu.max
160
193
  try {
@@ -909,21 +942,42 @@ export class WorkerDaemon extends EventEmitter {
909
942
  try {
910
943
  this.log('info', `Running ${workerConfig.type} in headless mode (Claude Code AI)`);
911
944
  const result = await this.headlessExecutor.execute(workerConfig.type);
912
- // #1793: persist the headless result to the same metrics files the
913
- // local workers write to. Without this, AI-mode runs produced rich
914
- // parsedOutput that lived only in `.claude-flow/logs/headless/*` and
915
- // never reached `.claude-flow/metrics/<name>.json` `memory stats`
916
- // and downstream consumers saw nothing despite successful runs.
917
- try {
918
- this.persistHeadlessResult(workerConfig.type, result);
945
+ // #2110 `HeadlessWorkerExecutor.execute()` returns
946
+ // `createErrorResult(...)` with `success: false` when
947
+ // `isAvailable()` is false, instead of throwing. The previous
948
+ // try/catch never fired in that path, and the result was
949
+ // persisted as mode:"headless" despite being a stub. Downstream
950
+ // dashboards / `memory stats` couldn't distinguish a real AI
951
+ // run from a fallback. Treat falsy success the same as throw.
952
+ const ok = result?.success === true;
953
+ if (!ok) {
954
+ const reason = result?.error ||
955
+ result?.note ||
956
+ 'headless executor reported success=false';
957
+ this.log('warn', `Headless ${workerConfig.type} returned success=false (${String(reason).slice(0, 200)}); falling back to local mode`);
958
+ this.emit('headless:fallback', {
959
+ type: workerConfig.type,
960
+ error: String(reason).slice(0, 500),
961
+ });
962
+ // Fall through to local switch.
919
963
  }
920
- catch (persistError) {
921
- this.log('warn', `Failed to persist headless result for ${workerConfig.type}: ${persistError.message}`);
964
+ else {
965
+ // #1793: persist the headless result to the same metrics files the
966
+ // local workers write to. Without this, AI-mode runs produced rich
967
+ // parsedOutput that lived only in `.claude-flow/logs/headless/*` and
968
+ // never reached `.claude-flow/metrics/<name>.json` — `memory stats`
969
+ // and downstream consumers saw nothing despite successful runs.
970
+ try {
971
+ this.persistHeadlessResult(workerConfig.type, result);
972
+ }
973
+ catch (persistError) {
974
+ this.log('warn', `Failed to persist headless result for ${workerConfig.type}: ${persistError.message}`);
975
+ }
976
+ return {
977
+ mode: 'headless',
978
+ ...result,
979
+ };
922
980
  }
923
- return {
924
- mode: 'headless',
925
- ...result,
926
- };
927
981
  }
928
982
  catch (error) {
929
983
  this.log('warn', `Headless execution failed for ${workerConfig.type}, falling back to local mode`);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.7.0-alpha.80",
3
+ "version": "3.7.0-alpha.81",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",