diff --git a/src/cli/doctor/checks/system-loaded-version.test.ts b/src/cli/doctor/checks/system-loaded-version.test.ts index f20b4a797..aeefa3b2f 100644 --- a/src/cli/doctor/checks/system-loaded-version.test.ts +++ b/src/cli/doctor/checks/system-loaded-version.test.ts @@ -129,6 +129,46 @@ describe("system loaded version", () => { expect(loadedVersion.loadedVersion).toBe("5.6.7") }) + it("falls back to require.resolve when neither config nor cache directory has an install", () => { + //#given + const configDir = createTemporaryDirectory("omo-config-") + const cacheHome = createTemporaryDirectory("omo-cache-") + + process.env.OPENCODE_CONFIG_DIR = configDir + process.env.XDG_CACHE_HOME = cacheHome + + //#when + const loadedVersion = getLoadedPluginVersion() + + //#then + expect(loadedVersion.loadedVersion).not.toBeNull() + expect(loadedVersion.loadedVersion).toMatch(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/) + expect(loadedVersion.installedPackagePath).toContain("package.json") + }) + + it("prefers candidate install path over require.resolve fallback when candidate exists", () => { + //#given + const configDir = createTemporaryDirectory("omo-config-") + const cacheHome = createTemporaryDirectory("omo-cache-") + + process.env.OPENCODE_CONFIG_DIR = configDir + process.env.XDG_CACHE_HOME = cacheHome + + writeJson(join(configDir, "package.json"), { + dependencies: { [PACKAGE_NAME]: "7.7.7" }, + }) + writeJson(join(configDir, "node_modules", PACKAGE_NAME, "package.json"), { + version: "7.7.7", + }) + + //#when + const loadedVersion = getLoadedPluginVersion() + + //#then + expect(loadedVersion.installedPackagePath).toBe(join(configDir, "node_modules", PACKAGE_NAME, "package.json")) + expect(loadedVersion.loadedVersion).toBe("7.7.7") + }) + it("resolves symlinked config directories before selecting install path", () => { //#given const realConfigDir = createTemporaryDirectory("omo-real-config-") diff --git a/src/cli/doctor/checks/system-loaded-version.ts b/src/cli/doctor/checks/system-loaded-version.ts index 25d7baccf..80f38982f 100644 --- a/src/cli/doctor/checks/system-loaded-version.ts +++ b/src/cli/doctor/checks/system-loaded-version.ts @@ -1,4 +1,5 @@ import { existsSync, readFileSync } from "node:fs" +import { createRequire } from "node:module" import { homedir } from "node:os" import { join } from "node:path" import { resolveSymlink } from "../../../shared/file-utils" @@ -86,6 +87,25 @@ function getExpectedVersion(cachePackage: PackageJsonShape | null, packageName: ?? normalizeVersion(cachePackage?.dependencies?.[PACKAGE_NAME]) } +function resolveInstalledPackageJsonPath(): { packageName: string; packageJsonPath: string } | null { + try { + const require = createRequire(import.meta.url) + for (const packageName of ACCEPTED_PACKAGE_NAMES) { + try { + const packageJsonPath = require.resolve(`${packageName}/package.json`) + if (existsSync(packageJsonPath)) { + return { packageName, packageJsonPath } + } + } catch { + continue + } + } + } catch { + return null + } + return null +} + export function getLoadedPluginVersion(): LoadedVersionInfo { const configPaths = getOpenCodeConfigPaths({ binary: "opencode" }) const configDir = resolveExistingDir(configPaths.configDir) @@ -108,12 +128,17 @@ export function getLoadedPluginVersion(): LoadedVersionInfo { const { cacheDir: selectedDir, cachePackagePath } = selectedCandidate const selectedPackage = selectInstalledPackage(selectedCandidate) - const installedPackagePath = selectedPackage.installedPackagePath + const candidateInstalledPath = selectedPackage.installedPackagePath + const candidateExists = existsSync(candidateInstalledPath) + + const resolvedFallback = candidateExists ? null : resolveInstalledPackageJsonPath() + const installedPackagePath = resolvedFallback?.packageJsonPath ?? candidateInstalledPath + const resolvedPackageName = resolvedFallback?.packageName ?? selectedPackage.packageName const cachePackage = readPackageJson(cachePackagePath) const installedPackage = readPackageJson(installedPackagePath) - const expectedVersion = getExpectedVersion(cachePackage, selectedPackage.packageName) + const expectedVersion = getExpectedVersion(cachePackage, resolvedPackageName) const loadedVersion = normalizeVersion(installedPackage?.version) return {