From 11eca5965243e4a024f84995d9a1c46ba96018c1 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 17 Jul 2026 22:33:08 +0200 Subject: [PATCH] Share MCP OAuth locks across Codex shadow homes - Replace Codex-created local MCP OAuth lock directories with shared symlinks - Add coverage for preserving and linking shared lock files --- .../provider/Drivers/CodexHomeLayout.test.ts | 36 +++++++++++++++++ .../src/provider/Drivers/CodexHomeLayout.ts | 39 ++++++++++++++----- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/apps/server/src/provider/Drivers/CodexHomeLayout.test.ts b/apps/server/src/provider/Drivers/CodexHomeLayout.test.ts index ec78b1665ef..03c717abb14 100644 --- a/apps/server/src/provider/Drivers/CodexHomeLayout.test.ts +++ b/apps/server/src/provider/Drivers/CodexHomeLayout.test.ts @@ -114,6 +114,9 @@ it.layer(NodeServices.layer)("CodexHomeLayout", (it) => { const sessionsTarget = yield* fileSystem.readLink(path.join(shadowHome, "sessions")); const configTarget = yield* fileSystem.readLink(path.join(shadowHome, "config.toml")); + const mcpOauthLocksTarget = yield* fileSystem.readLink( + path.join(shadowHome, "mcp-oauth-locks"), + ); const modelsCacheExists = yield* fileSystem.exists( path.join(shadowHome, "models_cache.json"), ); @@ -124,12 +127,45 @@ it.layer(NodeServices.layer)("CodexHomeLayout", (it) => { expect(sessionsTarget).toBe(path.join(sharedHome, "sessions")); expect(configTarget).toBe(path.join(sharedHome, "config.toml")); + expect(mcpOauthLocksTarget).toBe(path.join(sharedHome, "mcp-oauth-locks")); expect(modelsCacheExists).toBe(false); expect(authLinkResult._tag).toBe("Failure"); expect(authContents).toContain("shadow"); }), ); + it.effect("replaces Codex-created local MCP OAuth locks with the shared lock directory", () => + Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const sharedHome = yield* makeTempDir("t3code-codex-shared-"); + const shadowRoot = yield* makeTempDir("t3code-codex-shadow-root-"); + const shadowHome = path.join(shadowRoot, "shadow"); + const sharedLocks = path.join(sharedHome, "mcp-oauth-locks"); + const shadowLocks = path.join(shadowHome, "mcp-oauth-locks"); + + yield* writeTextFile(path.join(sharedLocks, "file-store.lock"), ""); + yield* writeTextFile(path.join(shadowLocks, "file-store.lock"), ""); + + const layout = yield* resolveCodexHomeLayout( + decodeCodexSettings({ + homePath: sharedHome, + shadowHomePath: shadowHome, + }), + ); + + yield* materializeCodexShadowHome(layout); + + const locksTarget = yield* fileSystem.readLink(shadowLocks); + const sharedLockExists = yield* fileSystem.exists( + path.join(sharedLocks, "file-store.lock"), + ); + + expect(locksTarget).toBe(sharedLocks); + expect(sharedLockExists).toBe(true); + }), + ); + it.effect("accepts Codex-created shadow-local runtime directories", () => Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; diff --git a/apps/server/src/provider/Drivers/CodexHomeLayout.ts b/apps/server/src/provider/Drivers/CodexHomeLayout.ts index d2d09e9d844..92e923c0b66 100644 --- a/apps/server/src/provider/Drivers/CodexHomeLayout.ts +++ b/apps/server/src/provider/Drivers/CodexHomeLayout.ts @@ -26,10 +26,12 @@ const KNOWN_SHARED_DIRECTORIES = [ "plugins", "cache", "logs", + "mcp-oauth-locks", ] as const; const PRIVATE_ENTRY_NAMES = new Set(["auth.json", "models_cache.json"]); const SHADOW_LOCAL_ENTRY_NAMES = new Set(["log", "memories", "tmp"]); +const REPLACEABLE_SHARED_RUNTIME_DIRECTORIES = new Set(["mcp-oauth-locks"]); function resolveHomePath(path: Path.Path, value: string | undefined): string { const expanded = @@ -225,16 +227,6 @@ const ensureSymlink = Effect.fn("CodexHomeLayout.ensureSymlink")(function* (inpu linkPath: link, }); - if (state._tag === "NotSymlink") { - return yield* new CodexShadowHomeEntryConflictError({ - sharedHomePath: input.sharedHomePath, - effectiveHomePath: input.effectiveHomePath, - entryName: input.entryName, - linkPath: link, - targetPath: target, - }); - } - const createLink = input.fileSystem.symlink(target, link).pipe( Effect.catchTags({ PlatformError: (cause) => @@ -250,6 +242,33 @@ const ensureSymlink = Effect.fn("CodexHomeLayout.ensureSymlink")(function* (inpu }), ); + if (state._tag === "NotSymlink") { + if (!REPLACEABLE_SHARED_RUNTIME_DIRECTORIES.has(input.entryName)) { + return yield* new CodexShadowHomeEntryConflictError({ + sharedHomePath: input.sharedHomePath, + effectiveHomePath: input.effectiveHomePath, + entryName: input.entryName, + linkPath: link, + targetPath: target, + }); + } + + yield* input.fileSystem.remove(link, { recursive: true }).pipe( + Effect.catchTags({ + PlatformError: (cause) => + new CodexShadowHomeFileSystemError({ + sharedHomePath: input.sharedHomePath, + effectiveHomePath: input.effectiveHomePath, + operation: "remove", + path: link, + entryName: input.entryName, + cause, + }), + }), + ); + return yield* createLink; + } + if (state._tag === "Missing") { return yield* createLink; }