diff --git a/src/cli/doctor/checks/system-loaded-version.test.ts b/src/cli/doctor/checks/system-loaded-version.test.ts index de4c9391f..f20b4a797 100644 --- a/src/cli/doctor/checks/system-loaded-version.test.ts +++ b/src/cli/doctor/checks/system-loaded-version.test.ts @@ -4,6 +4,7 @@ import { tmpdir } from "node:os" import { dirname, join } from "node:path" import { PACKAGE_NAME } from "../constants" +import { PLUGIN_NAME } from "../../../shared/plugin-identity" import { resolveSymlink } from "../../../shared/file-utils" const systemLoadedVersionModulePath = "./system-loaded-version?system-loaded-version-test" @@ -106,6 +107,28 @@ describe("system loaded version", () => { expect(loadedVersion.loadedVersion).toBe("2.3.4") }) + it("detects installs published under the canonical plugin name", () => { + //#given + const configDir = createTemporaryDirectory("omo-config-") + + process.env.OPENCODE_CONFIG_DIR = configDir + + writeJson(join(configDir, "package.json"), { + dependencies: { [PLUGIN_NAME]: "5.6.7" }, + }) + writeJson(join(configDir, "node_modules", PLUGIN_NAME, "package.json"), { + version: "5.6.7", + }) + + //#when + const loadedVersion = getLoadedPluginVersion() + + //#then + expect(loadedVersion.installedPackagePath).toBe(join(configDir, "node_modules", PLUGIN_NAME, "package.json")) + expect(loadedVersion.expectedVersion).toBe("5.6.7") + expect(loadedVersion.loadedVersion).toBe("5.6.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 04e4a87d1..25d7baccf 100644 --- a/src/cli/doctor/checks/system-loaded-version.ts +++ b/src/cli/doctor/checks/system-loaded-version.ts @@ -5,13 +5,24 @@ import { resolveSymlink } from "../../../shared/file-utils" import { getLatestVersion } from "../../../hooks/auto-update-checker/checker" import { extractChannel } from "../../../hooks/auto-update-checker" import { PACKAGE_NAME } from "../constants" -import { getOpenCodeCacheDir, getOpenCodeConfigPaths, parseJsonc } from "../../../shared" +import { ACCEPTED_PACKAGE_NAMES, getOpenCodeCacheDir, getOpenCodeConfigPaths, parseJsonc } from "../../../shared" interface PackageJsonShape { version?: string dependencies?: Record } +interface PackageCandidate { + packageName: string + installedPackagePath: string +} + +interface InstallCandidate { + cacheDir: string + cachePackagePath: string + packageCandidates: PackageCandidate[] +} + export interface LoadedVersionInfo { cacheDir: string cachePackagePath: string @@ -58,31 +69,51 @@ function normalizeVersion(value: string | undefined): string | null { return match?.[0] ?? null } +function createPackageCandidates(rootDir: string): PackageCandidate[] { + return ACCEPTED_PACKAGE_NAMES.map((packageName) => ({ + packageName, + installedPackagePath: join(rootDir, "node_modules", packageName, "package.json"), + })) +} + +function selectInstalledPackage(candidate: InstallCandidate): PackageCandidate { + return candidate.packageCandidates.find((packageCandidate) => existsSync(packageCandidate.installedPackagePath)) + ?? candidate.packageCandidates[0] +} + +function getExpectedVersion(cachePackage: PackageJsonShape | null, packageName: string): string | null { + return normalizeVersion(cachePackage?.dependencies?.[packageName]) + ?? normalizeVersion(cachePackage?.dependencies?.[PACKAGE_NAME]) +} + export function getLoadedPluginVersion(): LoadedVersionInfo { const configPaths = getOpenCodeConfigPaths({ binary: "opencode" }) const configDir = resolveExistingDir(configPaths.configDir) const cacheDir = resolveExistingDir(resolveOpenCodeCacheDir()) - const candidates = [ + const candidates: InstallCandidate[] = [ { cacheDir: configDir, cachePackagePath: join(configDir, "package.json"), - installedPackagePath: join(configDir, "node_modules", PACKAGE_NAME, "package.json"), + packageCandidates: createPackageCandidates(configDir), }, { cacheDir, cachePackagePath: join(cacheDir, "package.json"), - installedPackagePath: join(cacheDir, "node_modules", PACKAGE_NAME, "package.json"), + packageCandidates: createPackageCandidates(cacheDir), }, ] - const selectedCandidate = candidates.find((candidate) => existsSync(candidate.installedPackagePath)) ?? candidates[0] + const selectedCandidate = candidates.find((candidate) => candidate.packageCandidates.some((packageCandidate) => existsSync(packageCandidate.installedPackagePath))) + ?? candidates[0] - const { cacheDir: selectedDir, cachePackagePath, installedPackagePath } = selectedCandidate + const { cacheDir: selectedDir, cachePackagePath } = selectedCandidate + const selectedPackage = selectInstalledPackage(selectedCandidate) + const installedPackagePath = selectedPackage.installedPackagePath const cachePackage = readPackageJson(cachePackagePath) const installedPackage = readPackageJson(installedPackagePath) - const expectedVersion = normalizeVersion(cachePackage?.dependencies?.[PACKAGE_NAME]) + const expectedVersion = getExpectedVersion(cachePackage, selectedPackage.packageName) const loadedVersion = normalizeVersion(installedPackage?.version) return {