diff --git a/apps/server/src/codexAppServerManager.test.ts b/apps/server/src/codexAppServerManager.test.ts index 80323c744119..4442215f9899 100644 --- a/apps/server/src/codexAppServerManager.test.ts +++ b/apps/server/src/codexAppServerManager.test.ts @@ -356,59 +356,6 @@ describe("startSession", () => { manager.stopAll(); } }); - - it("fails fast with an upgrade message when codex is below the minimum supported version", async () => { - const manager = new CodexAppServerManager(); - const events: Array<{ method: string; kind: string; message?: string }> = []; - manager.on("event", (event) => { - events.push({ - method: event.method, - kind: event.kind, - ...(event.message ? { message: event.message } : {}), - }); - }); - - const versionCheck = vi - .spyOn( - manager as unknown as { - assertSupportedCodexCliVersion: (input: { - binaryPath: string; - cwd: string; - homePath?: string; - }) => void; - }, - "assertSupportedCodexCliVersion", - ) - .mockImplementation(() => { - throw new Error( - "Codex CLI v0.36.0 is too old for T3 Code. Upgrade to v0.37.0 or newer and restart T3 Code.", - ); - }); - - try { - await expect( - manager.startSession({ - threadId: asThreadId("thread-1"), - provider: "codex", - runtimeMode: "full-access", - }), - ).rejects.toThrow( - "Codex CLI v0.36.0 is too old for T3 Code. Upgrade to v0.37.0 or newer and restart T3 Code.", - ); - expect(versionCheck).toHaveBeenCalledTimes(1); - expect(events).toEqual([ - { - method: "session/startFailed", - kind: "error", - message: - "Codex CLI v0.36.0 is too old for T3 Code. Upgrade to v0.37.0 or newer and restart T3 Code.", - }, - ]); - } finally { - versionCheck.mockRestore(); - manager.stopAll(); - } - }); }); describe("sendTurn", () => { diff --git a/apps/server/src/codexAppServerManager.ts b/apps/server/src/codexAppServerManager.ts index 0ac37db3e8a9..a4943cc24d5f 100644 --- a/apps/server/src/codexAppServerManager.ts +++ b/apps/server/src/codexAppServerManager.ts @@ -22,12 +22,6 @@ import { import { normalizeModelSlug } from "@t3tools/shared/model"; import { Effect, ServiceMap } from "effect"; -import { - formatCodexCliUpgradeMessage, - isCodexCliVersionSupported, - parseCodexCliVersion, -} from "./provider/codexCliVersion"; - type PendingRequestKey = string; interface PendingRequest { @@ -145,8 +139,6 @@ export interface CodexThreadSnapshot { turns: CodexThreadTurnSnapshot[]; } -const CODEX_VERSION_CHECK_TIMEOUT_MS = 4_000; - const ANSI_ESCAPE_CHAR = String.fromCharCode(27); const ANSI_ESCAPE_REGEX = new RegExp(`${ANSI_ESCAPE_CHAR}\\[[0-9;]*m`, "g"); const CODEX_STDERR_LOG_REGEX = @@ -544,11 +536,6 @@ export class CodexAppServerManager extends EventEmitter): void { context.session = { ...context.session, @@ -1605,51 +1584,6 @@ function readCodexProviderOptions(input: CodexAppServerStartSessionInput): { }; } -function assertSupportedCodexCliVersion(input: { - readonly binaryPath: string; - readonly cwd: string; - readonly homePath?: string; -}): void { - const result = spawnSync(input.binaryPath, ["--version"], { - cwd: input.cwd, - env: { - ...process.env, - ...(input.homePath ? { CODEX_HOME: input.homePath } : {}), - }, - encoding: "utf8", - shell: process.platform === "win32", - stdio: ["ignore", "pipe", "pipe"], - timeout: CODEX_VERSION_CHECK_TIMEOUT_MS, - maxBuffer: 1024 * 1024, - }); - - if (result.error) { - const lower = result.error.message.toLowerCase(); - if ( - lower.includes("enoent") || - lower.includes("command not found") || - lower.includes("not found") - ) { - throw new Error(`Codex CLI (${input.binaryPath}) is not installed or not executable.`); - } - throw new Error( - `Failed to execute Codex CLI version check: ${result.error.message || String(result.error)}`, - ); - } - - const stdout = result.stdout ?? ""; - const stderr = result.stderr ?? ""; - if (result.status !== 0) { - const detail = stderr.trim() || stdout.trim() || `Command exited with code ${result.status}.`; - throw new Error(`Codex CLI version check failed. ${detail}`); - } - - const parsedVersion = parseCodexCliVersion(`${stdout}\n${stderr}`); - if (parsedVersion && !isCodexCliVersionSupported(parsedVersion)) { - throw new Error(formatCodexCliUpgradeMessage(parsedVersion)); - } -} - function readResumeCursorThreadId(resumeCursor: unknown): string | undefined { if (!resumeCursor || typeof resumeCursor !== "object" || Array.isArray(resumeCursor)) { return undefined; diff --git a/apps/server/src/provider/Layers/ProviderHealth.test.ts b/apps/server/src/provider/Layers/ProviderHealth.test.ts index e24f07bcfa4a..7d3af1ca4312 100644 --- a/apps/server/src/provider/Layers/ProviderHealth.test.ts +++ b/apps/server/src/provider/Layers/ProviderHealth.test.ts @@ -136,14 +136,14 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => { }).pipe(Effect.provide(failingSpawnerLayer("spawn codex ENOENT"))), ); - it.effect("returns unavailable when codex is below the minimum supported version", () => + it.effect("returns a warning when codex is below the minimum supported version", () => Effect.gen(function* () { yield* withTempCodexHome(); const status = yield* checkCodexProviderStatus; assert.strictEqual(status.provider, "codex"); - assert.strictEqual(status.status, "error"); - assert.strictEqual(status.available, false); - assert.strictEqual(status.authStatus, "unknown"); + assert.strictEqual(status.status, "warning"); + assert.strictEqual(status.available, true); + assert.strictEqual(status.authStatus, "authenticated"); assert.strictEqual( status.message, "Codex CLI v0.36.0 is too old for T3 Code. Upgrade to v0.37.0 or newer and restart T3 Code.", @@ -153,6 +153,7 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => { mockSpawnerLayer((args) => { const joined = args.join(" "); if (joined === "--version") return { stdout: "codex 0.36.0\n", stderr: "", code: 0 }; + if (joined === "login status") return { stdout: "Logged in\n", stderr: "", code: 0 }; throw new Error(`Unexpected args: ${joined}`); }), ), diff --git a/apps/server/src/provider/Layers/ProviderHealth.ts b/apps/server/src/provider/Layers/ProviderHealth.ts index cbb97a807ec3..2c222fad71e6 100644 --- a/apps/server/src/provider/Layers/ProviderHealth.ts +++ b/apps/server/src/provider/Layers/ProviderHealth.ts @@ -337,16 +337,10 @@ export const checkCodexProviderStatus: Effect.Effect< } const parsedVersion = parseCodexCliVersion(`${version.stdout}\n${version.stderr}`); - if (parsedVersion && !isCodexCliVersionSupported(parsedVersion)) { - return { - provider: CODEX_PROVIDER, - status: "error" as const, - available: false, - authStatus: "unknown" as const, - checkedAt, - message: formatCodexCliUpgradeMessage(parsedVersion), - }; - } + const versionWarningMessage = + parsedVersion && !isCodexCliVersionSupported(parsedVersion) + ? formatCodexCliUpgradeMessage(parsedVersion) + : null; // Probe 2: `codex login status` — is the user authenticated? // @@ -397,13 +391,14 @@ export const checkCodexProviderStatus: Effect.Effect< } const parsed = parseAuthStatusFromOutput(authProbe.success.value); + const message = parsed.message ?? versionWarningMessage ?? undefined; return { provider: CODEX_PROVIDER, - status: parsed.status, + status: versionWarningMessage && parsed.status === "ready" ? "warning" : parsed.status, available: true, authStatus: parsed.authStatus, checkedAt, - ...(parsed.message ? { message: parsed.message } : {}), + ...(message ? { message } : {}), } satisfies ServerProviderStatus; });