claude-flow 3.10.43 → 3.10.45

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.10.43",
3
+ "version": "3.10.45",
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",
@@ -251,7 +251,16 @@ async function spawnClaudeCodeInstance(swarmId, swarmName, objective, workers, f
251
251
  // HIGH-02: Strict boolean check (=== true) instead of loose truthiness (!== false)
252
252
  // to prevent undefined/null from being treated as "skip permissions".
253
253
  // Behavior change: only explicit --dangerously-skip-permissions flag triggers skip.
254
- const skipPermissions = flags['dangerously-skip-permissions'] === true && !flags['no-auto-permissions'];
254
+ // #2269: the arg parser normalizes kebab-case to camelCase (parser.ts:350,
255
+ // normalizeKey) and stores only the normalized key, so reading
256
+ // flags['dangerously-skip-permissions'] alone is always undefined. Accept
257
+ // both forms — mirroring the isNonInteractive pattern a few lines above.
258
+ // The deny clause must ALSO accept the yargs-style negation the parser
259
+ // produces for `--no-auto-permissions` (stored as `autoPermissions: false`,
260
+ // NOT `noAutoPermissions: true`); without this third clause, the deny half
261
+ // never fires and `--no-auto-permissions` is silently ignored.
262
+ const skipPermissions = (flags['dangerously-skip-permissions'] === true || flags.dangerouslySkipPermissions === true) &&
263
+ !(flags['no-auto-permissions'] || flags.noAutoPermissions || flags.autoPermissions === false);
255
264
  if (skipPermissions) {
256
265
  claudeArgs.push('--dangerously-skip-permissions');
257
266
  if (!isNonInteractive) {
@@ -562,26 +562,35 @@ async function rescueAgentdbEmbedder(agentdb) {
562
562
  // own embed() does the right thing and we should not interpose.
563
563
  if (emb.pipeline)
564
564
  return;
565
- let generateEmbedding = null;
565
+ let localEmbed = null;
566
566
  try {
567
567
  const mod = (await import('./memory-initializer.js'));
568
- generateEmbedding = mod.generateEmbedding ?? null;
568
+ localEmbed = mod.generateLocalEmbedding ?? null;
569
569
  }
570
570
  catch {
571
571
  return; // can't import the rescuer — leave the mock fallback alone
572
572
  }
573
- if (!generateEmbedding)
573
+ if (!localEmbed)
574
574
  return;
575
- const embed = generateEmbedding;
576
- // Probe once to confirm the rescuer actually returns real (non-zero) vectors.
577
- // If our own fallback chain also dead-ends in mock embeddings, leave
578
- // agentdb as-is rather than swapping one mock for another.
575
+ const embed = localEmbed;
576
+ // Probe once to confirm the rescuer actually returns REAL ONNX vectors
577
+ // (#2312: the old probe only checked non-zero, which the deterministic
578
+ // hash fallback also satisfies so it "rescued" agentdb's mock with our
579
+ // own mock and reported it as real). Require backend === 'onnx'.
579
580
  try {
580
581
  const probe = await embed('rescue-probe');
581
582
  const arr = probe?.embedding ? Array.from(probe.embedding) : [];
582
583
  const hasSignal = arr.length > 0 && arr.some((v) => Math.abs(v) > 1e-9);
583
- if (!hasSignal)
584
+ if (!hasSignal || probe.backend !== 'onnx') {
585
+ // Local chain is also degraded — leave agentdb's embedder alone, but
586
+ // tag it so bridgeGenerateEmbedding's AUDIT-#3 isMock check reports
587
+ // backend='mock' truthfully instead of labeling mock vectors 'onnx'.
588
+ try {
589
+ emb.backend = 'mock';
590
+ }
591
+ catch { /* frozen */ }
584
592
  return;
593
+ }
585
594
  }
586
595
  catch {
587
596
  return;
@@ -269,6 +269,27 @@ export declare function generateEmbedding(text: string): Promise<{
269
269
  model: string;
270
270
  backend: 'onnx' | 'mock';
271
271
  }>;
272
+ /**
273
+ * Generate an embedding using ONLY the local model chain (transformers.js /
274
+ * ruvector ONNX / hash fallback) — never the AgentDB bridge.
275
+ *
276
+ * #2312: this MUST stay bridge-free. `memory-bridge.ts` rescues a degraded
277
+ * agentdb embedder by delegating to this module; if that delegation went
278
+ * through `generateEmbedding` (bridge-first), the call would re-enter the
279
+ * patched `agentdb.embedder.embed` and recurse unboundedly:
280
+ *
281
+ * generateEmbedding → bridgeGenerateEmbedding → embedder.embed (patched)
282
+ * → generateEmbedding → … (heap OOM at ~4 GB on CI, no stack overflow
283
+ * because the cycle is async/microtask-driven)
284
+ *
285
+ * Keeping the local chain as its own export breaks that cycle structurally.
286
+ */
287
+ export declare function generateLocalEmbedding(text: string): Promise<{
288
+ embedding: number[];
289
+ dimensions: number;
290
+ model: string;
291
+ backend: 'onnx' | 'mock';
292
+ }>;
272
293
  /**
273
294
  * Generate embeddings for multiple texts
274
295
  * Uses parallel execution for API-based providers (2-4x faster)
@@ -1609,6 +1609,24 @@ export async function generateEmbedding(text) {
1609
1609
  return { ...bridgeResult, backend };
1610
1610
  }
1611
1611
  }
1612
+ return generateLocalEmbedding(text);
1613
+ }
1614
+ /**
1615
+ * Generate an embedding using ONLY the local model chain (transformers.js /
1616
+ * ruvector ONNX / hash fallback) — never the AgentDB bridge.
1617
+ *
1618
+ * #2312: this MUST stay bridge-free. `memory-bridge.ts` rescues a degraded
1619
+ * agentdb embedder by delegating to this module; if that delegation went
1620
+ * through `generateEmbedding` (bridge-first), the call would re-enter the
1621
+ * patched `agentdb.embedder.embed` and recurse unboundedly:
1622
+ *
1623
+ * generateEmbedding → bridgeGenerateEmbedding → embedder.embed (patched)
1624
+ * → generateEmbedding → … (heap OOM at ~4 GB on CI, no stack overflow
1625
+ * because the cycle is async/microtask-driven)
1626
+ *
1627
+ * Keeping the local chain as its own export breaks that cycle structurally.
1628
+ */
1629
+ export async function generateLocalEmbedding(text) {
1612
1630
  // Ensure model is loaded
1613
1631
  if (!embeddingModelState?.loaded) {
1614
1632
  await loadEmbeddingModel();
@@ -8,6 +8,19 @@ import * as path from 'path';
8
8
  import { execFile } from 'child_process';
9
9
  import { promisify } from 'util';
10
10
  const execFileAsync = promisify(execFile);
11
+ // On Windows, `npm` is a shell script (no `.exe`) and `npm.cmd` is a batch
12
+ // wrapper. Since Node 18.20.2 / 20.12.2 (CVE-2024-27980) the runtime refuses
13
+ // to spawn `.cmd`/`.bat` files directly and throws `spawn EINVAL` — the only
14
+ // supported invocation is via a real `.exe` shell. We wrap every npm call
15
+ // through `cmd.exe /d /s /c npm <args>`, which keeps Node's safe array-form
16
+ // argument escaping intact and avoids both ENOENT and EINVAL.
17
+ const isWindows = process.platform === 'win32';
18
+ function runNpm(args, timeoutMs) {
19
+ if (isWindows) {
20
+ return execFileAsync('cmd.exe', ['/d', '/s', '/c', 'npm', ...args], { timeout: timeoutMs });
21
+ }
22
+ return execFileAsync('npm', args, { timeout: timeoutMs });
23
+ }
11
24
  /**
12
25
  * Validate npm package name to prevent shell injection (S-3)
13
26
  */
@@ -105,7 +118,7 @@ export class PluginManager {
105
118
  validatePackageName(versionSpec);
106
119
  // Use npm to install (array form prevents shell injection)
107
120
  console.log(`[PluginManager] Installing ${versionSpec}...`);
108
- await execFileAsync('npm', ['install', '--prefix', this.config.pluginsDir, versionSpec], { timeout: 120000 });
121
+ await runNpm(['install', '--prefix', this.config.pluginsDir, versionSpec], 120000);
109
122
  // Get installed version
110
123
  const packageJsonPath = path.join(installDir, packageName, 'package.json');
111
124
  let installedVersion = version || 'latest';
@@ -210,7 +223,7 @@ export class PluginManager {
210
223
  // For npm-installed plugins, remove from node_modules
211
224
  if (plugin.source === 'npm') {
212
225
  validatePackageName(packageName);
213
- await execFileAsync('npm', ['uninstall', '--prefix', this.config.pluginsDir, packageName], { timeout: 60000 });
226
+ await runNpm(['uninstall', '--prefix', this.config.pluginsDir, packageName], 60000);
214
227
  }
215
228
  // Remove from manifest
216
229
  delete this.manifest.plugins[packageName];
@@ -333,7 +346,7 @@ export class PluginManager {
333
346
  // Validate package name to prevent injection (S-3)
334
347
  validatePackageName(versionSpec);
335
348
  // Reinstall with new version (array form prevents shell injection)
336
- await execFileAsync('npm', ['install', '--prefix', this.config.pluginsDir, versionSpec], { timeout: 120000 });
349
+ await runNpm(['install', '--prefix', this.config.pluginsDir, versionSpec], 120000);
337
350
  // Update manifest
338
351
  const installDir = path.join(this.config.pluginsDir, 'node_modules');
339
352
  const packageJsonPath = path.join(installDir, packageName, 'package.json');
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.10.43",
3
+ "version": "3.10.45",
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",