mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-09-14 14:01:28 +08:00
1430b4c76e
Same failure shape as #1147: `npx @claude-flow/cli@latest` crashes with `npm error Invalid Version: ` on npm 11.x / Node 24+. Root cause: @claude-flow/embeddings declared @huggingface/transformers in both `optionalDependencies` AND `peerDependencies` (with peerDependenciesMeta.optional = true). When npm 11.x arborist runs PlaceDep.pruneDedupable → Node.canDedupe → semver.eq, the overlap plus a "-dev." prerelease transitive (onnxruntime-web@1.26.0-dev.…) trips the empty-version crash inside @npmcli/arborist. Fix: - @claude-flow/embeddings: keep only the optional peerDependency, drop the duplicate optionalDependencies entry. Bumped to 3.0.0-alpha.17. - @claude-flow/cli: pin embeddings to ^3.0.0-alpha.17 so existing lockfiles re-resolve to the fixed version. Bumped to 3.7.0-alpha.39. Regression guards (so this can't recur): - scripts/audit-package-dep-overlap.mjs — static check that no package declares the same name in BOTH optionalDependencies and peerDependencies. Wired into v3-ci.yml as part of the plugin-package-audit job. - scripts/smoke-cli-npx-install.mjs — behavioural smoke: pack the CLI tarball, install it into a scratch project with optionals enabled, fail if `npm install` prints "Invalid Version:" or exits non-zero. New v3-ci.yml job `cli-npx-install-smoke` runs on Node 22 + 24 (Node 24 ships npm 11.x, where the bug surfaces) and gates witness-verify. Verified end-to-end: $ npx -y @claude-flow/cli@3.7.0-alpha.39 doctor ✓ MCP Servers, AIDefence, Disk Space, TypeScript … 10 passed
95 lines
3.3 KiB
JavaScript
Executable File
95 lines
3.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* Package dependency overlap audit (regression guard for ruvnet/ruflo#1147 and #2018).
|
|
*
|
|
* Both issues are the same failure shape:
|
|
* `npm error Invalid Version: ` (empty) thrown by `new SemVer('')` inside
|
|
* `@npmcli/arborist`'s `PlaceDep.pruneDedupable → Node.canDedupe → semver.eq`.
|
|
*
|
|
* Root cause: a package declares the SAME dependency name in both
|
|
* `optionalDependencies` and `peerDependencies` (typically with
|
|
* `peerDependenciesMeta[name].optional: true`). Newer npm (>=11) trips on
|
|
* this overlap during the dedupe pass when the dep itself has a "-dev." /
|
|
* prerelease tagged transitive (`onnxruntime-web@1.26.0-dev.…`), producing
|
|
* the user-facing empty-version crash.
|
|
*
|
|
* Fix: any name should appear in EITHER `optionalDependencies` OR as an
|
|
* optional peer — never both. This script scans every package.json under
|
|
* `v3/@claude-flow/*` and `v3/plugins/*` and fails on the overlap.
|
|
*
|
|
* Usage:
|
|
* node scripts/audit-package-dep-overlap.mjs # exit 1 on any overlap
|
|
* node scripts/audit-package-dep-overlap.mjs --json # machine-readable
|
|
*/
|
|
|
|
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
const REPO_ROOT = process.cwd();
|
|
const SCAN_DIRS = [
|
|
join(REPO_ROOT, 'v3', '@claude-flow'),
|
|
join(REPO_ROOT, 'v3', 'plugins'),
|
|
];
|
|
const JSON_OUT = process.argv.includes('--json');
|
|
|
|
const issues = [];
|
|
const note = (pkg, code, message) => issues.push({ pkg, code, message });
|
|
|
|
function listPackages(root) {
|
|
if (!existsSync(root)) return [];
|
|
return readdirSync(root)
|
|
.map((name) => join(root, name))
|
|
.filter((p) => {
|
|
try { return statSync(p).isDirectory() && existsSync(join(p, 'package.json')); }
|
|
catch { return false; }
|
|
});
|
|
}
|
|
|
|
let scanned = 0;
|
|
for (const root of SCAN_DIRS) {
|
|
for (const dir of listPackages(root)) {
|
|
const pkgPath = join(dir, 'package.json');
|
|
let pkg;
|
|
try { pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')); }
|
|
catch (e) { note(dir, 'PARSE', `invalid JSON: ${e.message}`); continue; }
|
|
scanned++;
|
|
const label = pkg.name || dir;
|
|
const opt = pkg.optionalDependencies || {};
|
|
const peers = pkg.peerDependencies || {};
|
|
|
|
// --- Overlap check: same name in optionalDependencies AND peerDependencies.
|
|
// npm 11.x arborist dedupe pass crashes with "Invalid Version: " on this
|
|
// pattern when any transitive carries a prerelease tag — that's #1147 / #2018.
|
|
for (const name of Object.keys(opt)) {
|
|
if (peers[name] !== undefined) {
|
|
note(label, 'OVERLAP',
|
|
`"${name}" is in BOTH optionalDependencies and peerDependencies — ` +
|
|
`npm 11.x arborist crashes with "Invalid Version: " on dedupe. ` +
|
|
`Remove it from optionalDependencies and keep only the optional peer.`);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const report = {
|
|
scanned,
|
|
issueCount: issues.length,
|
|
issues,
|
|
};
|
|
|
|
if (JSON_OUT) {
|
|
console.log(JSON.stringify(report, null, 2));
|
|
} else {
|
|
console.log(`package dep-overlap audit — scanned ${scanned} package(s)`);
|
|
if (issues.length === 0) {
|
|
console.log(' ok: no optionalDependencies/peerDependencies overlaps');
|
|
} else {
|
|
for (const i of issues) {
|
|
console.log(` fail [${i.code}] ${i.pkg}: ${i.message}`);
|
|
}
|
|
console.log(`\n${issues.length} issue(s) — see ruvnet/ruflo#1147 and #2018 for context`);
|
|
}
|
|
}
|
|
|
|
process.exit(issues.length > 0 ? 1 : 0);
|