From 114ece29494f6a4a8a540fed68bb03e7963c0c17 Mon Sep 17 00:00:00 2001 From: luvs01 Date: Thu, 13 Aug 2026 09:35:42 +0900 Subject: [PATCH] fix(service): accept legacy WSL ownership state --- src/service.ts | 15 ++++++++++++--- structure/02_config-and-codex-home.md | 5 +++-- tests/codex-home-wsl.test.ts | 25 ++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/src/service.ts b/src/service.ts index 668f16520..4d8ba9970 100644 --- a/src/service.ts +++ b/src/service.ts @@ -241,6 +241,17 @@ export function serviceHomeMatches(a: string, b: string): boolean { return normalizePathForCompare(a) === normalizePathForCompare(b); } +/** Accept the Linux default written by service versions predating WSL home discovery. */ +export function serviceCodexHomeMatchesInstall(recordedHome: string, deps: CodexHomeDeps = {}): boolean { + const actualHome = currentCodexHome(deps); + if (serviceHomeMatches(recordedHome, actualHome)) return true; + + const env = deps.env ?? process.env; + if (env.CODEX_HOME?.trim() || !isWslRuntime(deps)) return false; + const legacyDefault = join((deps.homedir ?? homedir)(), ".codex"); + return serviceHomeMatches(recordedHome, legacyDefault); +} + /** Single accessor for backend-sensitive service code — v1/legacy state maps to scheduler. */ export function readServiceBackend(): ServiceBackend { return readServiceInstallState()?.backend === "native" ? "native" : "scheduler"; @@ -299,9 +310,7 @@ export function assertServiceEnvironmentMatchesInstall(): void { const state = readServiceInstallState(); if (!state) return; const actualCodexHome = currentCodexHome(); - const expected = normalizePathForCompare(state.codexHome); - const actual = normalizePathForCompare(actualCodexHome); - if (expected !== actual) { + if (!serviceCodexHomeMatchesInstall(state.codexHome)) { throw new ServiceOwnershipError( `Service was installed with CODEX_HOME=${state.codexHome}, but current CODEX_HOME=${actualCodexHome}. ` + "Run the service command from the same Codex home so native Codex restore updates the correct config.", diff --git a/structure/02_config-and-codex-home.md b/structure/02_config-and-codex-home.md index f3346020c..7c6a357fd 100644 --- a/structure/02_config-and-codex-home.md +++ b/structure/02_config-and-codex-home.md @@ -23,8 +23,9 @@ the resolved `CODEX_HOME`. Service install-state ownership uses this same resolver. In WSL, an unset `CODEX_HOME` may resolve to the single discoverable Windows Desktop home; recording Linux `~/.codex` instead would make a later repair or uninstall look foreign even though the service and runtime were started from the -same environment. An explicit `CODEX_HOME` remains authoritative, and existing foreign ownership -records are never migrated implicitly. +same environment. Ownership checks therefore accept that exact legacy Linux-home record when WSL +now discovers a Windows home. An explicit `CODEX_HOME` remains authoritative, and other foreign +ownership records are never migrated implicitly. [Decision Log] - 목적과 의도: Keep service ownership metadata aligned with the Codex home the proxy actually uses. diff --git a/tests/codex-home-wsl.test.ts b/tests/codex-home-wsl.test.ts index 2441776ec..e38a30547 100644 --- a/tests/codex-home-wsl.test.ts +++ b/tests/codex-home-wsl.test.ts @@ -1,7 +1,7 @@ import { describe, expect, test } from "bun:test"; import { wslAutomountRoot, listWslWindowsCodexHomes } from "../src/codex/home"; import { isWindowsInteropDir } from "../src/codex/shim"; -import { currentServiceHomes } from "../src/service"; +import { currentServiceHomes, serviceCodexHomeMatchesInstall } from "../src/service"; describe("wsl.conf automount root", () => { test("defaults to /mnt when wsl.conf is absent or silent", () => { @@ -63,4 +63,27 @@ describe("wsl.conf automount root", () => { expect(homes.codexHome).toBe(windowsCodexHome); expect(homes.codexHome).not.toBe("/home/example/.codex"); }); + + test("service ownership accepts the legacy Linux fallback when WSL now discovers Windows Codex", () => { + const usersRoot = ["/mnt/c", "Users"].join("/"); + const windowsCodexHome = [usersRoot, "windows-user", ".codex"].join("/"); + const deps = { + env: { WSL_DISTRO_NAME: "Ubuntu" }, + platform: "linux", + homedir: () => "/home/example", + usersRoot, + existsSync: (path: string) => path === usersRoot + || path === `${windowsCodexHome}/config.toml`, + readdirSync: () => ["windows-user"], + statSync: (() => ({ isDirectory: () => true })) as never, + realpathSync: (path: string) => path, + }; + + expect(serviceCodexHomeMatchesInstall("/home/example/.codex", deps)).toBe(true); + expect(serviceCodexHomeMatchesInstall("/home/other/.codex", deps)).toBe(false); + expect(serviceCodexHomeMatchesInstall("/home/example/.codex", { + ...deps, + env: { ...deps.env, CODEX_HOME: windowsCodexHome }, + })).toBe(false); + }); });