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 = [ { slug: "grok-build", name: "Grok Build", isCustom: false, capabilities: EMPTY_CAPABILITIES, }, ]; export function buildInitialGrokProviderSnapshot( grokSettings: GrokSettings, ): Effect.Effect { 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 | undefined, builtInModels: ReadonlyArray = GROK_BUILT_IN_MODELS, ): ReadonlyArray { return providerModelsFromSettings(builtInModels, customModels ?? [], EMPTY_CAPABILITIES); } function buildGrokDiscoveredModelsFromSessionModelState( modelState: EffectAcpSchema.SessionModelState | null | undefined, ): ReadonlyArray { if (!modelState || modelState.availableModels.length === 0) { return []; } const seen = new Set(); 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; readonly httpClient: HttpClient.HttpClient; }): Effect.Effect => { 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, ); };