authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-17 23:26:53+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-17 23:26:53+02:00
log24f9c2a08e7b72a8f02b92aba229c34d35f72e30
tree0ef67f68c6f51e55a9a766f52fb4d8e22a756fb2
parent31ca9e553112ba336bfb3f6c04951f1086f37b0c
signature Signed by PGP key B5690EEEBB952194

Share MCP OAuth locks across Codex shadow homes (#4104)


2 files changed, 65 insertions(+), 10 deletions(-)

apps/server/src/provider/Drivers/CodexHomeLayout.test.ts+36
......@@ -114,6 +114,9 @@ it.layer(NodeServices.layer)("CodexHomeLayout", (it) => {
114114
115115 const sessionsTarget = yield* fileSystem.readLink(path.join(shadowHome, "sessions"));
116116 const configTarget = yield* fileSystem.readLink(path.join(shadowHome, "config.toml"));
117 const mcpOauthLocksTarget = yield* fileSystem.readLink(
118 path.join(shadowHome, "mcp-oauth-locks"),
119 );
117120 const modelsCacheExists = yield* fileSystem.exists(
118121 path.join(shadowHome, "models_cache.json"),
119122 );
......@@ -124,12 +127,45 @@ it.layer(NodeServices.layer)("CodexHomeLayout", (it) => {
124127
125128 expect(sessionsTarget).toBe(path.join(sharedHome, "sessions"));
126129 expect(configTarget).toBe(path.join(sharedHome, "config.toml"));
130 expect(mcpOauthLocksTarget).toBe(path.join(sharedHome, "mcp-oauth-locks"));
127131 expect(modelsCacheExists).toBe(false);
128132 expect(authLinkResult._tag).toBe("Failure");
129133 expect(authContents).toContain("shadow");
130134 }),
131135 );
132136
137 it.effect("replaces Codex-created local MCP OAuth locks with the shared lock directory", () =>
138 Effect.gen(function* () {
139 const fileSystem = yield* FileSystem.FileSystem;
140 const path = yield* Path.Path;
141 const sharedHome = yield* makeTempDir("t3code-codex-shared-");
142 const shadowRoot = yield* makeTempDir("t3code-codex-shadow-root-");
143 const shadowHome = path.join(shadowRoot, "shadow");
144 const sharedLocks = path.join(sharedHome, "mcp-oauth-locks");
145 const shadowLocks = path.join(shadowHome, "mcp-oauth-locks");
146
147 yield* writeTextFile(path.join(sharedLocks, "file-store.lock"), "");
148 yield* writeTextFile(path.join(shadowLocks, "file-store.lock"), "");
149
150 const layout = yield* resolveCodexHomeLayout(
151 decodeCodexSettings({
152 homePath: sharedHome,
153 shadowHomePath: shadowHome,
154 }),
155 );
156
157 yield* materializeCodexShadowHome(layout);
158
159 const locksTarget = yield* fileSystem.readLink(shadowLocks);
160 const sharedLockExists = yield* fileSystem.exists(
161 path.join(sharedLocks, "file-store.lock"),
162 );
163
164 expect(locksTarget).toBe(sharedLocks);
165 expect(sharedLockExists).toBe(true);
166 }),
167 );
168
133169 it.effect("accepts Codex-created shadow-local runtime directories", () =>
134170 Effect.gen(function* () {
135171 const fileSystem = yield* FileSystem.FileSystem;
apps/server/src/provider/Drivers/CodexHomeLayout.ts+29-10
......@@ -26,10 +26,12 @@ const KNOWN_SHARED_DIRECTORIES = [
2626 "plugins",
2727 "cache",
2828 "logs",
29 "mcp-oauth-locks",
2930] as const;
3031
3132const PRIVATE_ENTRY_NAMES = new Set(["auth.json", "models_cache.json"]);
3233const SHADOW_LOCAL_ENTRY_NAMES = new Set(["log", "memories", "tmp"]);
34const REPLACEABLE_SHARED_RUNTIME_DIRECTORIES = new Set(["mcp-oauth-locks"]);
3335
3436function resolveHomePath(path: Path.Path, value: string | undefined): string {
3537 const expanded =
......@@ -225,16 +227,6 @@ const ensureSymlink = Effect.fn("CodexHomeLayout.ensureSymlink")(function* (inpu
225227 linkPath: link,
226228 });
227229
228 if (state._tag === "NotSymlink") {
229 return yield* new CodexShadowHomeEntryConflictError({
230 sharedHomePath: input.sharedHomePath,
231 effectiveHomePath: input.effectiveHomePath,
232 entryName: input.entryName,
233 linkPath: link,
234 targetPath: target,
235 });
236 }
237
238230 const createLink = input.fileSystem.symlink(target, link).pipe(
239231 Effect.catchTags({
240232 PlatformError: (cause) =>
......@@ -250,6 +242,33 @@ const ensureSymlink = Effect.fn("CodexHomeLayout.ensureSymlink")(function* (inpu
250242 }),
251243 );
252244
245 if (state._tag === "NotSymlink") {
246 if (!REPLACEABLE_SHARED_RUNTIME_DIRECTORIES.has(input.entryName)) {
247 return yield* new CodexShadowHomeEntryConflictError({
248 sharedHomePath: input.sharedHomePath,
249 effectiveHomePath: input.effectiveHomePath,
250 entryName: input.entryName,
251 linkPath: link,
252 targetPath: target,
253 });
254 }
255
256 yield* input.fileSystem.remove(link, { recursive: true }).pipe(
257 Effect.catchTags({
258 PlatformError: (cause) =>
259 new CodexShadowHomeFileSystemError({
260 sharedHomePath: input.sharedHomePath,
261 effectiveHomePath: input.effectiveHomePath,
262 operation: "remove",
263 path: link,
264 entryName: input.entryName,
265 cause,
266 }),
267 }),
268 );
269 return yield* createLink;
270 }
271
253272 if (state._tag === "Missing") {
254273 return yield* createLink;
255274 }