444 lines · 13.6 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 {  isValidGrokReasoningEffortToken,  makeGrokAcpRuntime,  resolveGrokAcpBaseModelId,} from "../acp/GrokAcpSupport.ts";import { discoverGrokSkills } from "../Drivers/GrokSkills.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 nonEmptyString(value: unknown): string | undefined {  return typeof value === "string" ? value.trim() || undefined : undefined;} function isRecord(value: unknown): value is Record<string, unknown> {  return typeof value === "object" && value !== null && !Array.isArray(value);} function grokReasoningOptionsFromModel(model: EffectAcpSchema.ModelInfo): {  readonly options: ReadonlyArray<{    value: string;    label: string;    description?: string;    isDefault?: boolean;  }>;  readonly currentValue: string | undefined;} {  const meta = model._meta;  if (!meta || meta.supportsReasoningEffort === false) {    return { options: [], currentValue: undefined };  }   const currentEffort = nonEmptyString(meta.reasoningEffort);  const advertisedOptions = Array.isArray(meta.reasoningEfforts) ? meta.reasoningEfforts : [];  const seen = new Set<string>();  const options: Array<{    value: string;    label: string;    description?: string;    advertisedDefault: boolean;  }> = [];   for (const entry of advertisedOptions) {    if (!isRecord(entry)) {      continue;    }    const rawValue = nonEmptyString(entry.value);    const rawId = nonEmptyString(entry.id);    const value =      rawValue && isValidGrokReasoningEffortToken(rawValue)        ? rawValue        : rawId && isValidGrokReasoningEffortToken(rawId)          ? rawId          : undefined;    if (value === undefined || seen.has(value)) {      continue;    }    seen.add(value);    const description = nonEmptyString(entry.description);    options.push({      value,      label: nonEmptyString(entry.label) ?? value,      ...(description ? { description } : {}),      advertisedDefault: entry.default === true || entry.isDefault === true,    });  }   const currentValue =    currentEffort && options.some((option) => option.value === currentEffort)      ? currentEffort      : undefined;  const advertisedDefaults = options.filter((option) => option.advertisedDefault);  const selectedDefault =    advertisedDefaults.find((option) => option.value === currentValue)?.value ??    advertisedDefaults[0]?.value;  return {    options: options.map(({ value, label, description }) => ({      value,      label,      ...(description ? { description } : {}),      ...(value === selectedDefault ? { isDefault: true } : {}),    })),    currentValue: currentValue ?? selectedDefault,  };} export function buildGrokModelCapabilities(model: EffectAcpSchema.ModelInfo): ModelCapabilities {  const reasoning = grokReasoningOptionsFromModel(model);  return reasoning.options.length > 0    ? createModelCapabilities({        optionDescriptors: [          {            id: "reasoningEffort",            label: "Reasoning",            type: "select",            options: reasoning.options.map((option) => ({              id: option.value,              label: option.label,              ...(option.description ? { description: option.description } : {}),              ...(option.isDefault ? { isDefault: true } : {}),            })),            ...(reasoning.currentValue ? { currentValue: reasoning.currentValue } : {}),          },        ],      })    : 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: buildGrokModelCapabilities(model),      };    })    .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,  cwd?: string,): 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 skills = yield* discoverGrokSkills(grokSettings, environment, cwd);   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,      skills,      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,      skills,      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,    skills,    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,  );};