Files
fusion/packages/cli/src/cli-version.ts
gsxdsm 62979d8796 FN-8985: centralize CLI version resolution
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>
2026-08-11 01:51:22 -07:00

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;
}