diff --git a/devlog/_plan/260829_restart_codex_shim_backup/000_repro_and_root_cause.md b/devlog/_plan/260829_restart_codex_shim_backup/000_repro_and_root_cause.md new file mode 100644 index 0000000000..3d587cd755 --- /dev/null +++ b/devlog/_plan/260829_restart_codex_shim_backup/000_repro_and_root_cause.md @@ -0,0 +1,26 @@ +# 000 — `--restart-codex` misses `codex.opencodex-real` shim backup binaries + +## The report + +On remote hosts where Codex autostart shim is installed (`~/.local/bin/codex`), running `ocx sync --restart-codex` +reports zero app-server processes stopped, leaving long-lived app-servers holding stale in-memory model catalogs. + +## What is actually running there + +`ps -ef` on the affected host: + +```text +ubuntu 3600609 1 0 04:35 ? /home/ubuntu/.local/bin/codex.opencodex-real -c features.code_mode_host=true app-server --listen unix:// +``` + +## The defect + +`isCodexExecutableToken` in `src/codex/app-server-processes.ts` checked only `codex`, `codex.exe`, `codex.cmd`, +and Rust target triples. When the autostart shim moves the original launcher to `codex.opencodex-real`, +the process command line starts with `codex.opencodex-real` and fails `isCodexExecutableToken`, so `--restart-codex` +ignores it entirely. + +## The fix + +Admit `codex.opencodex-real`, `codex.opencodex-real.exe`, and `codex.opencodex-real.cmd` in `isCodexExecutableToken` +and in `WINDOWS_CODEX_BASENAME_CANDIDATE_RE`. diff --git a/src/codex/app-server-processes.ts b/src/codex/app-server-processes.ts index fbce428278..e5180f457a 100644 --- a/src/codex/app-server-processes.ts +++ b/src/codex/app-server-processes.ts @@ -46,7 +46,7 @@ const CODEX_TARGET_TRIPLE_BODY = "[a-z0-9_]+-[a-z0-9_]+-[a-z0-9_]+(?:-[a-z0-9_]+ * `codex-x86_64-pc-windows-msvc.exe`. */ export const WINDOWS_CODEX_BASENAME_CANDIDATE_RE = new RegExp( - `(^|[/\\\\\\s'"=])codex(-${CODEX_TARGET_TRIPLE_BODY})?([.]exe|[.]cmd)?['"]?(\\s|$)`, + `(^|[/\\\\\\s'"=])codex([.]opencodex-real)?(-${CODEX_TARGET_TRIPLE_BODY})?([.]exe|[.]cmd)?['"]?(\\s|$)`, "i", ); @@ -159,6 +159,8 @@ function tokenBasename(token: string): string { function isCodexExecutableToken(token: string): boolean { const base = tokenBasename(token); return base === "codex" || base === "codex.exe" || base === "codex.cmd" + || base === "codex.opencodex-real" || base === "codex.opencodex-real.exe" + || base === "codex.opencodex-real.cmd" || CODEX_TARGET_TRIPLE_BASENAME_RE.test(base); } diff --git a/tests/codex-app-server-processes.test.ts b/tests/codex-app-server-processes.test.ts index da8d0b522a..518e986f7c 100644 --- a/tests/codex-app-server-processes.test.ts +++ b/tests/codex-app-server-processes.test.ts @@ -398,6 +398,18 @@ describe("Codex app-server process matching (#476)", () => { }); + test("matches codex.opencodex-real launcher backup binaries and wrappers", () => { + expect(isCodexAppServerCommandLine("/home/ubuntu/.local/bin/codex.opencodex-real app-server proxy")).toBe(true); + expect(isCodexAppServerCommandLine( + "/home/ubuntu/.local/bin/codex.opencodex-real -c features.code_mode_host=true app-server --listen unix://", + )).toBe(true); + expect(isCodexAppServerCommandLine("C:\\Users\\a\\AppData\\codex.opencodex-real.exe app-server --listen pipe")).toBe(true); + expect(isCodexAppServerCommandLine("\"C:\\Program Files\\nodejs\\codex.opencodex-real.cmd\" app-server")).toBe(true); + expect(isCodexAppServerCommandLine("node /usr/local/bin/codex.opencodex-real app-server proxy")).toBe(true); + expect(isCodexAppServerCommandLine("codex.opencodex-real exec 'hello'")).toBe(false); + expect(isCodexAppServerCommandLine("node worker.js codex.opencodex-real app-server")).toBe(false); + }); + test("matches the npm wrapper that supervises the native app-server", () => { // The shape that made `ocx sync --restart-codex` report a survivor on Linux. An // npm-installed Codex runs as a PAIR: `node /usr/local/bin/codex app-server` and @@ -492,6 +504,12 @@ describe("Codex app-server process matching (#476)", () => { expect(isWindowsCodexCandidateCommandLine( "C:\\Users\\a\\.codex\\bin\\codex-aarch64-pc-windows-msvc.exe app-server", )).toBe(true); + expect(isWindowsCodexCandidateCommandLine( + "C:\\Users\\a\\.local\\bin\\codex.opencodex-real.exe app-server", + )).toBe(true); + expect(isWindowsCodexCandidateCommandLine( + "\"C:\\Program Files\\Codex\\codex.opencodex-real.cmd\" app-server", + )).toBe(true); // Stay narrow: incidental "opencodex" paths must not pay GetOwner. expect(isWindowsCodexCandidateCommandLine( "node C:\\Users\\a\\opencodex\\src\\cli\\index.ts start",