Centralize CLI self-version discovery without widening the early boot import graph. - Reuse one bounded manifest resolver for the CLI binary, updater, and plugin scaffold. - Add regression coverage for manifest lookup bounds, malformed manifests, and scaffold version fallback. - Preserve update command test isolation after the core i18n module relocation. Files changed: packages/cli/src/__tests__/cli-version.test.ts | 136 +++++++++++++++++++++ .../plugin-scaffold-caret-fallback.test.ts | 91 ++++++++++++++ packages/cli/src/bin.ts | 45 +------ packages/cli/src/cli-version.ts | 51 ++++++++ packages/cli/src/commands/__tests__/update.test.ts | 2 +- packages/cli/src/commands/plugin-scaffold.ts | 40 +----- packages/cli/src/commands/update.ts | 37 +----- 7 files changed, 292 insertions(+), 110 deletions(-) Fusion-Task-Id: FN-8985 Fusion-Task-Lineage: dd95b651-3578-4c5f-957a-5af7b85129d7 Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
import { existsSync, readFileSync } from "node:fs";
|
|
import { dirname, resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
export const CLI_PACKAGE_NAME = "@runfusion/fusion";
|
|
export const MAX_PACKAGE_LOOKUP_DEPTH = 8;
|
|
|
|
/**
|
|
* FNXC:CliVersionResolution 2026-08-11-08:34:
|
|
* The CLI bin, update command, and plugin scaffold formerly kept byte-equivalent
|
|
* self-version walkers. Centralizing them prevents drift while each caller passes
|
|
* its own import.meta.url so its resolution start directory remains unchanged.
|
|
*
|
|
* This must remain a built-ins-only leaf: bin resolves --version before app imports
|
|
* so the dashboard CLI Binary probe cannot bootstrap the application. Its node:fs
|
|
* surface is intentionally limited to existsSync/readFileSync because update tests
|
|
* replace that module with only those APIs. The eight-level bound is pinned in both
|
|
* directions by plugin-scaffold-caret-fallback.test.ts. Do not merge the dashboard
|
|
* resolver or self-extension resolver: they have richer and different contracts.
|
|
*/
|
|
export function readOwnCliVersion(importMetaUrl: string = import.meta.url): string | undefined {
|
|
let currentDir: string;
|
|
try {
|
|
currentDir = dirname(fileURLToPath(importMetaUrl));
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
|
|
for (let i = 0; i < MAX_PACKAGE_LOOKUP_DEPTH; i += 1) {
|
|
const pkgPath = resolve(currentDir, "package.json");
|
|
if (existsSync(pkgPath)) {
|
|
try {
|
|
const parsed = JSON.parse(readFileSync(pkgPath, "utf-8")) as {
|
|
name?: string;
|
|
version?: string;
|
|
};
|
|
if (parsed.name === CLI_PACKAGE_NAME && typeof parsed.version === "string") {
|
|
return parsed.version;
|
|
}
|
|
} catch {
|
|
// Ignore malformed manifest and keep walking.
|
|
}
|
|
}
|
|
|
|
const parentDir = resolve(currentDir, "..");
|
|
if (parentDir === currentDir) break;
|
|
currentDir = parentDir;
|
|
}
|
|
|
|
return undefined;
|
|
}
|