Julius Marmingealign branding guide lines33791cd8 months agoHistory
603 lines · 19.3 KB · TypeScript
RawDownload
/** * ProviderHealthLive - Startup-time provider health checks. * * Performs one-time provider readiness probes when the server starts and * keeps the resulting snapshot in memory for `server.getConfig`. * * Uses effect's ChildProcessSpawner to run CLI probes natively. * * @module ProviderHealthLive */import * as OS from "node:os";import type {  ServerProviderAuthStatus,  ServerProviderStatus,  ServerProviderStatusState,} from "@t3tools/contracts";import { Array, Effect, Fiber, FileSystem, Layer, Option, Path, Result, Stream } from "effect";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import {  formatCodexCliUpgradeMessage,  isCodexCliVersionSupported,  parseCodexCliVersion,} from "../codexCliVersion";import { ProviderHealth, type ProviderHealthShape } from "../Services/ProviderHealth"; const DEFAULT_TIMEOUT_MS = 4_000;const CODEX_PROVIDER = "codex" as const;const CLAUDE_AGENT_PROVIDER = "claudeAgent" as const; // ── Pure helpers ──────────────────────────────────────────────────── export interface CommandResult {  readonly stdout: string;  readonly stderr: string;  readonly code: number;} function nonEmptyTrimmed(value: string | undefined): string | undefined {  if (!value) return undefined;  const trimmed = value.trim();  return trimmed.length > 0 ? trimmed : undefined;} function isCommandMissingCause(error: unknown): boolean {  if (!(error instanceof Error)) return false;  const lower = error.message.toLowerCase();  return lower.includes("enoent") || lower.includes("notfound");} function detailFromResult(  result: CommandResult & { readonly timedOut?: boolean },): string | undefined {  if (result.timedOut) return "Timed out while running command.";  const stderr = nonEmptyTrimmed(result.stderr);  if (stderr) return stderr;  const stdout = nonEmptyTrimmed(result.stdout);  if (stdout) return stdout;  if (result.code !== 0) {    return `Command exited with code ${result.code}.`;  }  return undefined;} function extractAuthBoolean(value: unknown): boolean | undefined {  if (Array.isArray(value)) {    for (const entry of value) {      const nested = extractAuthBoolean(entry);      if (nested !== undefined) return nested;    }    return undefined;  }   if (!value || typeof value !== "object") return undefined;   const record = value as Record<string, unknown>;  for (const key of ["authenticated", "isAuthenticated", "loggedIn", "isLoggedIn"] as const) {    if (typeof record[key] === "boolean") return record[key];  }  for (const key of ["auth", "status", "session", "account"] as const) {    const nested = extractAuthBoolean(record[key]);    if (nested !== undefined) return nested;  }  return undefined;} export function parseAuthStatusFromOutput(result: CommandResult): {  readonly status: ServerProviderStatusState;  readonly authStatus: ServerProviderAuthStatus;  readonly message?: string;} {  const lowerOutput = `${result.stdout}\n${result.stderr}`.toLowerCase();   if (    lowerOutput.includes("unknown command") ||    lowerOutput.includes("unrecognized command") ||    lowerOutput.includes("unexpected argument")  ) {    return {      status: "warning",      authStatus: "unknown",      message: "Codex CLI authentication status command is unavailable in this Codex version.",    };  }   if (    lowerOutput.includes("not logged in") ||    lowerOutput.includes("login required") ||    lowerOutput.includes("authentication required") ||    lowerOutput.includes("run `codex login`") ||    lowerOutput.includes("run codex login")  ) {    return {      status: "error",      authStatus: "unauthenticated",      message: "Codex CLI is not authenticated. Run `codex login` and try again.",    };  }   const parsedAuth = (() => {    const trimmed = result.stdout.trim();    if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) {      return { attemptedJsonParse: false as const, auth: undefined as boolean | undefined };    }    try {      return {        attemptedJsonParse: true as const,        auth: extractAuthBoolean(JSON.parse(trimmed)),      };    } catch {      return { attemptedJsonParse: false as const, auth: undefined as boolean | undefined };    }  })();   if (parsedAuth.auth === true) {    return { status: "ready", authStatus: "authenticated" };  }  if (parsedAuth.auth === false) {    return {      status: "error",      authStatus: "unauthenticated",      message: "Codex CLI is not authenticated. Run `codex login` and try again.",    };  }  if (parsedAuth.attemptedJsonParse) {    return {      status: "warning",      authStatus: "unknown",      message:        "Could not verify Codex authentication status from JSON output (missing auth marker).",    };  }  if (result.code === 0) {    return { status: "ready", authStatus: "authenticated" };  }   const detail = detailFromResult(result);  return {    status: "warning",    authStatus: "unknown",    message: detail      ? `Could not verify Codex authentication status. ${detail}`      : "Could not verify Codex authentication status.",  };} // ── 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 = <E>(stream: Stream.Stream<Uint8Array, E>): Effect.Effect<string, E> =>  Stream.runFold(    stream,    () => "",    (acc, chunk) => acc + new TextDecoder().decode(chunk),  ); const runCodexCommand = (args: ReadonlyArray<string>) =>  Effect.gen(function* () {    const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;    const command = ChildProcess.make("codex", [...args], {      shell: process.platform === "win32",    });     const child = yield* spawner.spawn(command);     const [stdout, stderr, exitCode] = yield* Effect.all(      [        collectStreamAsString(child.stdout),        collectStreamAsString(child.stderr),        child.exitCode.pipe(Effect.map(Number)),      ],      { concurrency: "unbounded" },    );     return { stdout, stderr, code: exitCode } satisfies CommandResult;  }).pipe(Effect.scoped); const runClaudeCommand = (args: ReadonlyArray<string>) =>  Effect.gen(function* () {    const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;    const command = ChildProcess.make("claude", [...args], {      shell: process.platform === "win32",    });     const child = yield* spawner.spawn(command);     const [stdout, stderr, exitCode] = yield* Effect.all(      [        collectStreamAsString(child.stdout),        collectStreamAsString(child.stderr),        child.exitCode.pipe(Effect.map(Number)),      ],      { concurrency: "unbounded" },    );     return { stdout, stderr, code: exitCode } satisfies CommandResult;  }).pipe(Effect.scoped); // ── Health check ──────────────────────────────────────────────────── export const checkCodexProviderStatus: Effect.Effect<  ServerProviderStatus,  never,  ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path> = Effect.gen(function* () {  const checkedAt = new Date().toISOString();   // Probe 1: `codex --version` — is the CLI reachable?  const versionProbe = yield* runCodexCommand(["--version"]).pipe(    Effect.timeoutOption(DEFAULT_TIMEOUT_MS),    Effect.result,  );   if (Result.isFailure(versionProbe)) {    const error = versionProbe.failure;    return {      provider: CODEX_PROVIDER,      status: "error" as const,      available: false,      authStatus: "unknown" as const,      checkedAt,      message: isCommandMissingCause(error)        ? "Codex CLI (`codex`) is not installed or not on PATH."        : `Failed to execute Codex CLI health check: ${error instanceof Error ? error.message : String(error)}.`,    };  }   if (Option.isNone(versionProbe.success)) {    return {      provider: CODEX_PROVIDER,      status: "error" as const,      available: false,      authStatus: "unknown" as const,      checkedAt,      message: "Codex CLI is installed but failed to run. Timed out while running command.",    };  }   const version = versionProbe.success.value;  if (version.code !== 0) {    const detail = detailFromResult(version);    return {      provider: CODEX_PROVIDER,      status: "error" as const,      available: false,      authStatus: "unknown" as const,      checkedAt,      message: detail        ? `Codex CLI is installed but failed to run. ${detail}`        : "Codex CLI is installed but failed to run.",    };  }   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),    };  }   // 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,  );   if (Result.isFailure(authProbe)) {    const error = authProbe.failure;    return {      provider: CODEX_PROVIDER,      status: "warning" as const,      available: true,      authStatus: "unknown" as const,      checkedAt,      message:        error instanceof Error          ? `Could not verify Codex authentication status: ${error.message}.`          : "Could not verify Codex authentication status.",    };  }   if (Option.isNone(authProbe.success)) {    return {      provider: CODEX_PROVIDER,      status: "warning" as const,      available: true,      authStatus: "unknown" as const,      checkedAt,      message: "Could not verify Codex authentication status. Timed out while running command.",    };  }   const parsed = parseAuthStatusFromOutput(authProbe.success.value);  return {    provider: CODEX_PROVIDER,    status: parsed.status,    available: true,    authStatus: parsed.authStatus,    checkedAt,    ...(parsed.message ? { message: parsed.message } : {}),  } satisfies ServerProviderStatus;}); // ── Claude Agent health check ─────────────────────────────────────── export function parseClaudeAuthStatusFromOutput(result: CommandResult): {  readonly status: ServerProviderStatusState;  readonly authStatus: ServerProviderAuthStatus;  readonly message?: string;} {  const lowerOutput = `${result.stdout}\n${result.stderr}`.toLowerCase();   if (    lowerOutput.includes("unknown command") ||    lowerOutput.includes("unrecognized command") ||    lowerOutput.includes("unexpected argument")  ) {    return {      status: "warning",      authStatus: "unknown",      message:        "Claude Agent authentication status command is unavailable in this version of Claude.",    };  }   if (    lowerOutput.includes("not logged in") ||    lowerOutput.includes("login required") ||    lowerOutput.includes("authentication required") ||    lowerOutput.includes("run `claude login`") ||    lowerOutput.includes("run claude login")  ) {    return {      status: "error",      authStatus: "unauthenticated",      message: "Claude is not authenticated. Run `claude auth login` and try again.",    };  }   // `claude auth status` returns JSON with a `loggedIn` boolean.  const parsedAuth = (() => {    const trimmed = result.stdout.trim();    if (!trimmed || (!trimmed.startsWith("{") && !trimmed.startsWith("["))) {      return { attemptedJsonParse: false as const, auth: undefined as boolean | undefined };    }    try {      return {        attemptedJsonParse: true as const,        auth: extractAuthBoolean(JSON.parse(trimmed)),      };    } catch {      return { attemptedJsonParse: false as const, auth: undefined as boolean | undefined };    }  })();   if (parsedAuth.auth === true) {    return { status: "ready", authStatus: "authenticated" };  }  if (parsedAuth.auth === false) {    return {      status: "error",      authStatus: "unauthenticated",      message: "Claude is not authenticated. Run `claude auth login` and try again.",    };  }  if (parsedAuth.attemptedJsonParse) {    return {      status: "warning",      authStatus: "unknown",      message:        "Could not verify Claude authentication status from JSON output (missing auth marker).",    };  }  if (result.code === 0) {    return { status: "ready", authStatus: "authenticated" };  }   const detail = detailFromResult(result);  return {    status: "warning",    authStatus: "unknown",    message: detail      ? `Could not verify Claude authentication status. ${detail}`      : "Could not verify Claude authentication status.",  };} export const checkClaudeProviderStatus: Effect.Effect<  ServerProviderStatus,  never,  ChildProcessSpawner.ChildProcessSpawner> = Effect.gen(function* () {  const checkedAt = new Date().toISOString(); 

Lines 1–500 of 603.