diff --git a/apps/server/src/provider/Layers/ProviderHealth.test.ts b/apps/server/src/provider/Layers/ProviderHealth.test.ts index 964b45ba49..10bd12a7cf 100644 --- a/apps/server/src/provider/Layers/ProviderHealth.test.ts +++ b/apps/server/src/provider/Layers/ProviderHealth.test.ts @@ -1,10 +1,15 @@ -import assert from "node:assert/strict"; -import { it } from "@effect/vitest"; -import { Effect, Layer, Sink, Stream } from "effect"; +import * as NodeServices from "@effect/platform-node/NodeServices"; +import { describe, it, assert } from "@effect/vitest"; +import { Effect, FileSystem, Layer, Path, Sink, Stream } from "effect"; import * as PlatformError from "effect/PlatformError"; import { ChildProcessSpawner } from "effect/unstable/process"; -import { checkCodexProviderStatus, parseAuthStatusFromOutput } from "./ProviderHealth"; +import { + checkCodexProviderStatus, + hasCustomModelProvider, + parseAuthStatusFromOutput, + readCodexConfigModelProvider, +} from "./ProviderHealth"; // ── Test helpers ──────────────────────────────────────────────────── @@ -53,157 +58,410 @@ function failingSpawnerLayer(description: string) { ); } -// ── Tests ─────────────────────────────────────────────────────────── - -it.effect("returns ready when codex is installed and authenticated", () => - Effect.gen(function* () { - const status = yield* checkCodexProviderStatus; - assert.strictEqual(status.provider, "codex"); - assert.strictEqual(status.status, "ready"); - assert.strictEqual(status.available, true); - assert.strictEqual(status.authStatus, "authenticated"); - }).pipe( - Effect.provide( - mockSpawnerLayer((args) => { - const joined = args.join(" "); - if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; - if (joined === "login status") return { stdout: "Logged in\n", stderr: "", code: 0 }; - throw new Error(`Unexpected args: ${joined}`); +/** + * Create a temporary CODEX_HOME scoped to the current Effect test. + * Cleanup is registered in the test scope rather than via Vitest hooks. + */ +function withTempCodexHome(configContent?: string) { + return Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const tmpDir = yield* fileSystem.makeTempDirectoryScoped({ prefix: "t3-test-codex-" }); + + yield* Effect.acquireRelease( + Effect.sync(() => { + const originalCodexHome = process.env.CODEX_HOME; + process.env.CODEX_HOME = tmpDir; + return originalCodexHome; }), - ), - ), -); - -it.effect("returns unavailable when codex is missing", () => - Effect.gen(function* () { - 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.message, "Codex CLI (`codex`) is not installed or not on PATH."); - }).pipe(Effect.provide(failingSpawnerLayer("spawn codex ENOENT"))), -); - -it.effect("returns unavailable when codex is below the minimum supported version", () => - Effect.gen(function* () { - 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.message, - "Codex CLI v0.36.0 is too old for T3 Code. Upgrade to v0.37.0 or newer and restart T3 Code.", - ); - }).pipe( - Effect.provide( - mockSpawnerLayer((args) => { - const joined = args.join(" "); - if (joined === "--version") return { stdout: "codex 0.36.0\n", stderr: "", code: 0 }; - throw new Error(`Unexpected args: ${joined}`); + (originalCodexHome) => + Effect.sync(() => { + if (originalCodexHome !== undefined) { + process.env.CODEX_HOME = originalCodexHome; + } else { + delete process.env.CODEX_HOME; + } + }), + ); + + if (configContent !== undefined) { + yield* fileSystem.writeFileString(path.join(tmpDir, "config.toml"), configContent); + } + + return { tmpDir } as const; + }); +} + +it.layer(NodeServices.layer)("ProviderHealth", (it) => { + // ── checkCodexProviderStatus tests ──────────────────────────────── + // + // These tests control CODEX_HOME to ensure the custom-provider detection + // in hasCustomModelProvider() does not interfere with the auth-probe + // path being tested. + + describe("checkCodexProviderStatus", () => { + it.effect("returns ready when codex is installed and authenticated", () => + Effect.gen(function* () { + // Point CODEX_HOME at an empty tmp dir (no config.toml) so the + // default code path (OpenAI provider, auth probe runs) is exercised. + yield* withTempCodexHome(); + const status = yield* checkCodexProviderStatus; + assert.strictEqual(status.provider, "codex"); + assert.strictEqual(status.status, "ready"); + assert.strictEqual(status.available, true); + assert.strictEqual(status.authStatus, "authenticated"); + }).pipe( + Effect.provide( + mockSpawnerLayer((args) => { + const joined = args.join(" "); + if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; + if (joined === "login status") return { stdout: "Logged in\n", stderr: "", code: 0 }; + throw new Error(`Unexpected args: ${joined}`); + }), + ), + ), + ); + + it.effect("returns unavailable when codex is missing", () => + 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.message, "Codex CLI (`codex`) is not installed or not on PATH."); + }).pipe(Effect.provide(failingSpawnerLayer("spawn codex ENOENT"))), + ); + + it.effect("returns unavailable 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.message, + "Codex CLI v0.36.0 is too old for T3 Code. Upgrade to v0.37.0 or newer and restart T3 Code.", + ); + }).pipe( + Effect.provide( + mockSpawnerLayer((args) => { + const joined = args.join(" "); + if (joined === "--version") return { stdout: "codex 0.36.0\n", stderr: "", code: 0 }; + throw new Error(`Unexpected args: ${joined}`); + }), + ), + ), + ); + + it.effect("returns unauthenticated when auth probe reports login required", () => + Effect.gen(function* () { + yield* withTempCodexHome(); + const status = yield* checkCodexProviderStatus; + assert.strictEqual(status.provider, "codex"); + assert.strictEqual(status.status, "error"); + assert.strictEqual(status.available, true); + assert.strictEqual(status.authStatus, "unauthenticated"); + assert.strictEqual( + status.message, + "Codex CLI is not authenticated. Run `codex login` and try again.", + ); + }).pipe( + Effect.provide( + mockSpawnerLayer((args) => { + const joined = args.join(" "); + if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; + if (joined === "login status") { + return { stdout: "", stderr: "Not logged in. Run codex login.", code: 1 }; + } + throw new Error(`Unexpected args: ${joined}`); + }), + ), + ), + ); + + it.effect("returns unauthenticated when login status output includes 'not logged in'", () => + Effect.gen(function* () { + yield* withTempCodexHome(); + const status = yield* checkCodexProviderStatus; + assert.strictEqual(status.provider, "codex"); + assert.strictEqual(status.status, "error"); + assert.strictEqual(status.available, true); + assert.strictEqual(status.authStatus, "unauthenticated"); + assert.strictEqual( + status.message, + "Codex CLI is not authenticated. Run `codex login` and try again.", + ); + }).pipe( + Effect.provide( + mockSpawnerLayer((args) => { + const joined = args.join(" "); + if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; + if (joined === "login status") + return { stdout: "Not logged in\n", stderr: "", code: 1 }; + throw new Error(`Unexpected args: ${joined}`); + }), + ), + ), + ); + + it.effect("returns warning when login status command is unsupported", () => + Effect.gen(function* () { + yield* withTempCodexHome(); + const status = yield* checkCodexProviderStatus; + assert.strictEqual(status.provider, "codex"); + assert.strictEqual(status.status, "warning"); + assert.strictEqual(status.available, true); + assert.strictEqual(status.authStatus, "unknown"); + assert.strictEqual( + status.message, + "Codex CLI authentication status command is unavailable in this Codex version.", + ); + }).pipe( + Effect.provide( + mockSpawnerLayer((args) => { + const joined = args.join(" "); + if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; + if (joined === "login status") { + return { stdout: "", stderr: "error: unknown command 'login'", code: 2 }; + } + throw new Error(`Unexpected args: ${joined}`); + }), + ), + ), + ); + }); + + // ── Custom model provider: checkCodexProviderStatus integration ─── + + describe("checkCodexProviderStatus with custom model provider", () => { + it.effect("skips auth probe and returns ready when a custom model provider is configured", () => + Effect.gen(function* () { + yield* withTempCodexHome( + [ + 'model_provider = "portkey"', + "", + "[model_providers.portkey]", + 'base_url = "https://api.portkey.ai/v1"', + 'env_key = "PORTKEY_API_KEY"', + ].join("\n"), + ); + const status = yield* checkCodexProviderStatus; + assert.strictEqual(status.provider, "codex"); + assert.strictEqual(status.status, "ready"); + assert.strictEqual(status.available, true); + assert.strictEqual(status.authStatus, "unknown"); + assert.strictEqual( + status.message, + "Using a custom Codex model provider; OpenAI login check skipped.", + ); + }).pipe( + Effect.provide( + // The spawner only handles --version; if the test attempts + // "login status" the throw proves the auth probe was NOT skipped. + mockSpawnerLayer((args) => { + const joined = args.join(" "); + if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; + throw new Error(`Auth probe should have been skipped but got args: ${joined}`); + }), + ), + ), + ); + + it.effect("still reports error when codex CLI is missing even with custom provider", () => + Effect.gen(function* () { + yield* withTempCodexHome( + [ + 'model_provider = "portkey"', + "", + "[model_providers.portkey]", + 'base_url = "https://api.portkey.ai/v1"', + 'env_key = "PORTKEY_API_KEY"', + ].join("\n"), + ); + const status = yield* checkCodexProviderStatus; + assert.strictEqual(status.status, "error"); + assert.strictEqual(status.available, false); + }).pipe(Effect.provide(failingSpawnerLayer("spawn codex ENOENT"))), + ); + }); + + describe("checkCodexProviderStatus with openai model provider", () => { + it.effect("still runs auth probe when model_provider is openai", () => + Effect.gen(function* () { + yield* withTempCodexHome('model_provider = "openai"\n'); + const status = yield* checkCodexProviderStatus; + // The auth probe runs and sees "not logged in" → error + assert.strictEqual(status.status, "error"); + assert.strictEqual(status.authStatus, "unauthenticated"); + }).pipe( + Effect.provide( + mockSpawnerLayer((args) => { + const joined = args.join(" "); + if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; + if (joined === "login status") + return { stdout: "Not logged in\n", stderr: "", code: 1 }; + throw new Error(`Unexpected args: ${joined}`); + }), + ), + ), + ); + }); + + // ── parseAuthStatusFromOutput pure tests ────────────────────────── + + describe("parseAuthStatusFromOutput", () => { + it("exit code 0 with no auth markers is ready", () => { + const parsed = parseAuthStatusFromOutput({ stdout: "OK\n", stderr: "", code: 0 }); + assert.strictEqual(parsed.status, "ready"); + assert.strictEqual(parsed.authStatus, "authenticated"); + }); + + it("JSON with authenticated=false is unauthenticated", () => { + const parsed = parseAuthStatusFromOutput({ + stdout: '[{"authenticated":false}]\n', + stderr: "", + code: 0, + }); + assert.strictEqual(parsed.status, "error"); + assert.strictEqual(parsed.authStatus, "unauthenticated"); + }); + + it("JSON without auth marker is warning", () => { + const parsed = parseAuthStatusFromOutput({ + stdout: '[{"ok":true}]\n', + stderr: "", + code: 0, + }); + assert.strictEqual(parsed.status, "warning"); + assert.strictEqual(parsed.authStatus, "unknown"); + }); + }); + + // ── readCodexConfigModelProvider tests ───────────────────────────── + + describe("readCodexConfigModelProvider", () => { + it.effect("returns undefined when config file does not exist", () => + Effect.gen(function* () { + yield* withTempCodexHome(); + assert.strictEqual(yield* readCodexConfigModelProvider, undefined); }), - ), - ), -); - -it.effect("returns unauthenticated when auth probe reports login required", () => - Effect.gen(function* () { - const status = yield* checkCodexProviderStatus; - assert.strictEqual(status.provider, "codex"); - assert.strictEqual(status.status, "error"); - assert.strictEqual(status.available, true); - assert.strictEqual(status.authStatus, "unauthenticated"); - assert.strictEqual( - status.message, - "Codex CLI is not authenticated. Run `codex login` and try again.", - ); - }).pipe( - Effect.provide( - mockSpawnerLayer((args) => { - const joined = args.join(" "); - if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; - if (joined === "login status") { - return { stdout: "", stderr: "Not logged in. Run codex login.", code: 1 }; - } - throw new Error(`Unexpected args: ${joined}`); + ); + + it.effect("returns undefined when config has no model_provider key", () => + Effect.gen(function* () { + yield* withTempCodexHome('model = "gpt-5-codex"\n'); + assert.strictEqual(yield* readCodexConfigModelProvider, undefined); }), - ), - ), -); - -it.effect("returns unauthenticated when login status output includes 'not logged in'", () => - Effect.gen(function* () { - const status = yield* checkCodexProviderStatus; - assert.strictEqual(status.provider, "codex"); - assert.strictEqual(status.status, "error"); - assert.strictEqual(status.available, true); - assert.strictEqual(status.authStatus, "unauthenticated"); - assert.strictEqual( - status.message, - "Codex CLI is not authenticated. Run `codex login` and try again.", - ); - }).pipe( - Effect.provide( - mockSpawnerLayer((args) => { - const joined = args.join(" "); - if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; - if (joined === "login status") return { stdout: "Not logged in\n", stderr: "", code: 1 }; - throw new Error(`Unexpected args: ${joined}`); + ); + + it.effect("returns the provider when model_provider is set at top level", () => + Effect.gen(function* () { + yield* withTempCodexHome('model = "gpt-5-codex"\nmodel_provider = "portkey"\n'); + assert.strictEqual(yield* readCodexConfigModelProvider, "portkey"); }), - ), - ), -); - -it.effect("returns warning when login status command is unsupported", () => - Effect.gen(function* () { - const status = yield* checkCodexProviderStatus; - assert.strictEqual(status.provider, "codex"); - assert.strictEqual(status.status, "warning"); - assert.strictEqual(status.available, true); - assert.strictEqual(status.authStatus, "unknown"); - assert.strictEqual( - status.message, - "Codex CLI authentication status command is unavailable in this Codex version.", - ); - }).pipe( - Effect.provide( - mockSpawnerLayer((args) => { - const joined = args.join(" "); - if (joined === "--version") return { stdout: "codex 1.0.0\n", stderr: "", code: 0 }; - if (joined === "login status") { - return { stdout: "", stderr: "error: unknown command 'login'", code: 2 }; - } - throw new Error(`Unexpected args: ${joined}`); + ); + + it.effect("returns openai when model_provider is openai", () => + Effect.gen(function* () { + yield* withTempCodexHome('model_provider = "openai"\n'); + assert.strictEqual(yield* readCodexConfigModelProvider, "openai"); }), - ), - ), -); + ); -// ── Pure function tests ───────────────────────────────────────────── + it.effect("ignores model_provider inside section headers", () => + Effect.gen(function* () { + yield* withTempCodexHome( + [ + 'model = "gpt-5-codex"', + "", + "[model_providers.portkey]", + 'base_url = "https://api.portkey.ai/v1"', + 'model_provider = "should-be-ignored"', + "", + ].join("\n"), + ); + assert.strictEqual(yield* readCodexConfigModelProvider, undefined); + }), + ); -it("parseAuthStatusFromOutput: exit code 0 with no auth markers is ready", () => { - const parsed = parseAuthStatusFromOutput({ stdout: "OK\n", stderr: "", code: 0 }); - assert.strictEqual(parsed.status, "ready"); - assert.strictEqual(parsed.authStatus, "authenticated"); -}); + it.effect("handles comments and whitespace", () => + Effect.gen(function* () { + yield* withTempCodexHome( + [ + "# This is a comment", + "", + ' model_provider = "azure" ', + "", + "[profiles.deep-review]", + 'model = "gpt-5-pro"', + ].join("\n"), + ); + assert.strictEqual(yield* readCodexConfigModelProvider, "azure"); + }), + ); -it("parseAuthStatusFromOutput: JSON with authenticated=false is unauthenticated", () => { - const parsed = parseAuthStatusFromOutput({ - stdout: '[{"authenticated":false}]\n', - stderr: "", - code: 0, + it.effect("handles single-quoted values in TOML", () => + Effect.gen(function* () { + yield* withTempCodexHome("model_provider = 'mistral'\n"); + assert.strictEqual(yield* readCodexConfigModelProvider, "mistral"); + }), + ); }); - assert.strictEqual(parsed.status, "error"); - assert.strictEqual(parsed.authStatus, "unauthenticated"); -}); -it("parseAuthStatusFromOutput: JSON without auth marker is warning", () => { - const parsed = parseAuthStatusFromOutput({ - stdout: '[{"ok":true}]\n', - stderr: "", - code: 0, + // ── hasCustomModelProvider tests ─────────────────────────────────── + + describe("hasCustomModelProvider", () => { + it.effect("returns false when no config file exists", () => + Effect.gen(function* () { + yield* withTempCodexHome(); + assert.strictEqual(yield* hasCustomModelProvider, false); + }), + ); + + it.effect("returns false when model_provider is not set", () => + Effect.gen(function* () { + yield* withTempCodexHome('model = "gpt-5-codex"\n'); + assert.strictEqual(yield* hasCustomModelProvider, false); + }), + ); + + it.effect("returns false when model_provider is openai", () => + Effect.gen(function* () { + yield* withTempCodexHome('model_provider = "openai"\n'); + assert.strictEqual(yield* hasCustomModelProvider, false); + }), + ); + + it.effect("returns true when model_provider is portkey", () => + Effect.gen(function* () { + yield* withTempCodexHome('model_provider = "portkey"\n'); + assert.strictEqual(yield* hasCustomModelProvider, true); + }), + ); + + it.effect("returns true when model_provider is azure", () => + Effect.gen(function* () { + yield* withTempCodexHome('model_provider = "azure"\n'); + assert.strictEqual(yield* hasCustomModelProvider, true); + }), + ); + + it.effect("returns true when model_provider is ollama", () => + Effect.gen(function* () { + yield* withTempCodexHome('model_provider = "ollama"\n'); + assert.strictEqual(yield* hasCustomModelProvider, true); + }), + ); + + it.effect("returns true when model_provider is a custom proxy", () => + Effect.gen(function* () { + yield* withTempCodexHome('model_provider = "my-company-proxy"\n'); + assert.strictEqual(yield* hasCustomModelProvider, true); + }), + ); }); - assert.strictEqual(parsed.status, "warning"); - assert.strictEqual(parsed.authStatus, "unknown"); }); diff --git a/apps/server/src/provider/Layers/ProviderHealth.ts b/apps/server/src/provider/Layers/ProviderHealth.ts index 4eb6a288cb..1fed0597a2 100644 --- a/apps/server/src/provider/Layers/ProviderHealth.ts +++ b/apps/server/src/provider/Layers/ProviderHealth.ts @@ -8,12 +8,13 @@ * * @module ProviderHealthLive */ +import * as OS from "node:os"; import type { ServerProviderAuthStatus, ServerProviderStatus, ServerProviderStatusState, } from "@t3tools/contracts"; -import { Array, Effect, Fiber, Layer, Option, Result, Stream } from "effect"; +import { Array, Effect, Fiber, FileSystem, Layer, Option, Path, Result, Stream } from "effect"; import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import { @@ -167,6 +168,72 @@ export function parseAuthStatusFromOutput(result: CommandResult): { }; } +// ── Codex CLI config detection ────────────────────────────────────── + +/** + * Providers that use OpenAI-native authentication via `codex login`. + * When the configured `model_provider` is one of these, the `codex login + * status` probe still runs. For any other provider value the auth probe + * is skipped because authentication is handled externally (e.g. via + * environment variables like `PORTKEY_API_KEY` or `AZURE_API_KEY`). + */ +const OPENAI_AUTH_PROVIDERS = new Set(["openai"]); + +/** + * Read the `model_provider` value from the Codex CLI config file. + * + * Looks for the file at `$CODEX_HOME/config.toml` (falls back to + * `~/.codex/config.toml`). Uses a simple line-by-line scan rather than + * a full TOML parser to avoid adding a dependency for a single key. + * + * Returns `undefined` when the file does not exist or does not set + * `model_provider`. + */ +export const readCodexConfigModelProvider = Effect.gen(function* () { + const fileSystem = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const codexHome = process.env.CODEX_HOME || path.join(OS.homedir(), ".codex"); + const configPath = path.join(codexHome, "config.toml"); + + const content = yield* fileSystem + .readFileString(configPath) + .pipe(Effect.orElseSucceed(() => undefined)); + if (content === undefined) { + return undefined; + } + + // We need to find `model_provider = "..."` at the top level of the + // TOML file (i.e. before any `[section]` header). Lines inside + // `[profiles.*]`, `[model_providers.*]`, etc. are ignored. + let inTopLevel = true; + for (const line of content.split("\n")) { + const trimmed = line.trim(); + // Skip comments and empty lines. + if (!trimmed || trimmed.startsWith("#")) continue; + // Detect section headers — once we leave the top level, stop. + if (trimmed.startsWith("[")) { + inTopLevel = false; + continue; + } + if (!inTopLevel) continue; + + const match = trimmed.match(/^model_provider\s*=\s*["']([^"']+)["']/); + if (match) return match[1]; + } + return undefined; +}); + +/** + * Returns `true` when the Codex CLI is configured with a custom + * (non-OpenAI) model provider, meaning `codex login` auth is not + * required because authentication is handled through provider-specific + * environment variables. + */ +export const hasCustomModelProvider = Effect.map( + readCodexConfigModelProvider, + (provider) => provider !== undefined && !OPENAI_AUTH_PROVIDERS.has(provider), +); + // ── Effect-native command execution ───────────────────────────────── const collectStreamAsString = (stream: Stream.Stream): Effect.Effect => @@ -202,7 +269,7 @@ const runCodexCommand = (args: ReadonlyArray) => export const checkCodexProviderStatus: Effect.Effect< ServerProviderStatus, never, - ChildProcessSpawner.ChildProcessSpawner + ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path > = Effect.gen(function* () { const checkedAt = new Date().toISOString(); @@ -265,6 +332,22 @@ export const checkCodexProviderStatus: Effect.Effect< } // Probe 2: `codex login status` — is the user authenticated? + // + // Custom model providers (e.g. Portkey, Azure OpenAI proxy) handle + // authentication through their own environment variables, so `codex + // login status` will report "not logged in" even when the CLI works + // fine. Skip the auth probe entirely for non-OpenAI providers. + if (yield* hasCustomModelProvider) { + return { + provider: CODEX_PROVIDER, + status: "ready" as const, + available: true, + authStatus: "unknown" as const, + checkedAt, + message: "Using a custom Codex model provider; OpenAI login check skipped.", + } satisfies ServerProviderStatus; + } + const authProbe = yield* runCodexCommand(["login", "status"]).pipe( Effect.timeoutOption(DEFAULT_TIMEOUT_MS), Effect.result,