335 lines · 10.2 KB · TypeScript
RawDownload
import {  type GrokSettings,  type ModelCapabilities,  type ServerProvider,  type ServerProviderModel,} from "@t3tools/contracts";import type * as EffectAcpSchema from "effect-acp/schema";import { causeErrorTag } from "@t3tools/shared/observability";import * as Crypto from "effect/Crypto";import * as DateTime from "effect/DateTime";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Option from "effect/Option";import * as Result from "effect/Result";import { HttpClient } from "effect/unstable/http";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";import { createModelCapabilities } from "@t3tools/shared/model";import { resolveSpawnCommand } from "@t3tools/shared/shell"; import {  buildServerProvider,  isCommandMissingCause,  parseGenericCliVersion,  providerModelsFromSettings,  spawnAndCollect,  type ServerProviderDraft,} from "../providerSnapshot.ts";import {  enrichProviderSnapshotWithVersionAdvisory,  type ProviderMaintenanceCapabilities,} from "../providerMaintenance.ts";import { makeGrokAcpRuntime, resolveGrokAcpBaseModelId } from "../acp/GrokAcpSupport.ts"; const GROK_PRESENTATION = {  displayName: "Grok",  badgeLabel: "Early Access",  showInteractionModeToggle: false,  requiresNewThreadForModelChange: true,} as const;const EMPTY_CAPABILITIES: ModelCapabilities = createModelCapabilities({  optionDescriptors: [],}); const VERSION_PROBE_TIMEOUT_MS = 4_000;const GROK_ACP_MODEL_DISCOVERY_TIMEOUT_MS = 15_000; const GROK_BUILT_IN_MODELS: ReadonlyArray<ServerProviderModel> = [  {    slug: "grok-build",    name: "Grok Build",    isCustom: false,    capabilities: EMPTY_CAPABILITIES,  },]; export function buildInitialGrokProviderSnapshot(  grokSettings: GrokSettings,): Effect.Effect<ServerProviderDraft> {  return Effect.gen(function* () {    const checkedAt = yield* Effect.map(DateTime.now, DateTime.formatIso);    const models = grokModelsFromSettings(grokSettings.customModels);     if (!grokSettings.enabled) {      return buildServerProvider({        presentation: GROK_PRESENTATION,        enabled: false,        checkedAt,        models,        probe: {          installed: false,          version: null,          status: "warning",          auth: { status: "unknown" },          message: "Grok is disabled in T3 Code settings.",        },      });    }     return buildServerProvider({      presentation: GROK_PRESENTATION,      enabled: true,      checkedAt,      models,      probe: {        installed: true,        version: null,        status: "warning",        auth: { status: "unknown" },        message: "Checking Grok CLI availability...",      },    });  });} function grokModelsFromSettings(  customModels: ReadonlyArray<string> | undefined,  builtInModels: ReadonlyArray<ServerProviderModel> = GROK_BUILT_IN_MODELS,): ReadonlyArray<ServerProviderModel> {  return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES);} function buildGrokDiscoveredModelsFromSessionModelState(  modelState: EffectAcpSchema.SessionModelState | null | undefined,): ReadonlyArray<ServerProviderModel> {  if (!modelState || modelState.availableModels.length === 0) {    return [];  }  const seen = new Set<string>();  return modelState.availableModels    .map((model): ServerProviderModel | undefined => {      const slug = resolveGrokAcpBaseModelId(model.modelId);      if (!slug || seen.has(slug)) {        return undefined;      }      seen.add(slug);      return {        slug,        name: model.name.trim() || slug,        isCustom: false,        capabilities: EMPTY_CAPABILITIES,      };    })    .filter((model): model is ServerProviderModel => model !== undefined);} const discoverGrokModelsViaAcp = (  grokSettings: GrokSettings,  environment: NodeJS.ProcessEnv = process.env,) =>  Effect.gen(function* () {    const childProcessSpawner = yield* ChildProcessSpawner.ChildProcessSpawner;    const acp = yield* makeGrokAcpRuntime({      grokSettings,      environment,      childProcessSpawner,      cwd: process.cwd(),      clientInfo: { name: "t3-code-provider-probe", version: "0.0.0" },    });    const started = yield* acp.start();    return buildGrokDiscoveredModelsFromSessionModelState(started.sessionSetupResult.models);  }).pipe(Effect.scoped); const runGrokVersionCommand = (  grokSettings: GrokSettings,  environment: NodeJS.ProcessEnv = process.env,) =>  Effect.gen(function* () {    const command = grokSettings.binaryPath || "grok";    const spawnCommand = yield* resolveSpawnCommand(command, ["--version"], {      env: environment,    });    return yield* spawnAndCollect(      command,      ChildProcess.make(spawnCommand.command, spawnCommand.args, {        env: environment,        shell: spawnCommand.shell,      }),    );  }); export const checkGrokProviderStatus = Effect.fn("checkGrokProviderStatus")(function* (  grokSettings: GrokSettings,  environment: NodeJS.ProcessEnv = process.env,): Effect.fn.Return<  ServerProviderDraft,  never,  ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto> {  const checkedAt = DateTime.formatIso(yield* DateTime.now);  const fallbackModels = grokModelsFromSettings(grokSettings.customModels);   if (!grokSettings.enabled) {    return buildServerProvider({      presentation: GROK_PRESENTATION,      enabled: false,      checkedAt,      models: fallbackModels,      probe: {        installed: false,        version: null,        status: "warning",        auth: { status: "unknown" },        message: "Grok is disabled in T3 Code settings.",      },    });  }   const versionResult = yield* runGrokVersionCommand(grokSettings, environment).pipe(    Effect.timeoutOption(VERSION_PROBE_TIMEOUT_MS),    Effect.result,  );   if (Result.isFailure(versionResult)) {    const error = versionResult.failure;    yield* Effect.logWarning("Grok CLI health check failed.", {      errorTag: error._tag,    });    return buildServerProvider({      presentation: GROK_PRESENTATION,      enabled: grokSettings.enabled,      checkedAt,      models: fallbackModels,      probe: {        installed: !isCommandMissingCause(error),        version: null,        status: "error",        auth: { status: "unknown" },        message: isCommandMissingCause(error)          ? "Grok CLI (`grok`) is not installed or not on PATH."          : "Failed to execute Grok CLI health check.",      },    });  }   if (Option.isNone(versionResult.success)) {    return buildServerProvider({      presentation: GROK_PRESENTATION,      enabled: grokSettings.enabled,      checkedAt,      models: fallbackModels,      probe: {        installed: true,        version: null,        status: "error",        auth: { status: "unknown" },        message: "Grok CLI is installed but timed out while running `grok --version`.",      },    });  }   const versionOutput = versionResult.success.value;  const version = parseGenericCliVersion(`${versionOutput.stdout}\n${versionOutput.stderr}`);  if (versionOutput.code !== 0) {    yield* Effect.logWarning("Grok CLI version probe exited with a non-zero status.", {      exitCode: versionOutput.code,      stdoutLength: versionOutput.stdout.length,      stderrLength: versionOutput.stderr.length,    });    return buildServerProvider({      presentation: GROK_PRESENTATION,      enabled: grokSettings.enabled,      checkedAt,      models: fallbackModels,      probe: {        installed: true,        version,        status: "error",        auth: { status: "unknown" },        message: "Grok CLI is installed but failed to run.",      },    });  }   const discoveryExit = yield* discoverGrokModelsViaAcp(grokSettings, environment).pipe(    Effect.timeoutOption(GROK_ACP_MODEL_DISCOVERY_TIMEOUT_MS),    Effect.exit,  );  if (Exit.isFailure(discoveryExit)) {    yield* Effect.logWarning("Grok ACP model discovery failed", {      errorTag: causeErrorTag(discoveryExit.cause),    });    return buildServerProvider({      presentation: GROK_PRESENTATION,      enabled: grokSettings.enabled,      checkedAt,      models: fallbackModels,      probe: {        installed: true,        version,        status: "error",        auth: { status: "unknown" },        message: "Grok CLI is installed but ACP startup failed. Check server logs for details.",      },    });  }  if (Option.isNone(discoveryExit.value)) {    yield* Effect.logWarning(      `Grok ACP model discovery timed out after ${GROK_ACP_MODEL_DISCOVERY_TIMEOUT_MS}ms.`,    );    return buildServerProvider({      presentation: GROK_PRESENTATION,      enabled: grokSettings.enabled,      checkedAt,      models: fallbackModels,      probe: {        installed: true,        version,        status: "error",        auth: { status: "unknown" },        message: `Grok CLI is installed but ACP startup timed out after ${GROK_ACP_MODEL_DISCOVERY_TIMEOUT_MS}ms.`,      },    });  }  const discoveredModels = discoveryExit.value.value;  const models =    discoveredModels.length > 0      ? grokModelsFromSettings(grokSettings.customModels, discoveredModels)      : fallbackModels;   return buildServerProvider({    presentation: GROK_PRESENTATION,    enabled: grokSettings.enabled,    checkedAt,    models,    probe: {      installed: true,      version,      status: "ready",      auth: { status: "unknown" },    },  });}); export const enrichGrokSnapshot = (input: {  readonly snapshot: ServerProvider;  readonly maintenanceCapabilities: ProviderMaintenanceCapabilities;  readonly enableProviderUpdateChecks?: boolean;  readonly publishSnapshot: (snapshot: ServerProvider) => Effect.Effect<void>;  readonly httpClient: HttpClient.HttpClient;}): Effect.Effect<void> => {  const { snapshot, publishSnapshot } = input;   return enrichProviderSnapshotWithVersionAdvisory(snapshot, input.maintenanceCapabilities, {    enableProviderUpdateChecks: input.enableProviderUpdateChecks,  }).pipe(    Effect.provideService(HttpClient.HttpClient, input.httpClient),    Effect.flatMap((enrichedSnapshot) => publishSnapshot(enrichedSnapshot)),    Effect.catchCause((cause) =>      Effect.logWarning("Grok version advisory enrichment failed", {        errorTag: causeErrorTag(cause),      }),    ),    Effect.asVoid,  );};