114 lines · 3.8 KB · TypeScript
RawDownload
import { type CursorSettings, type ProviderOptionSelection } from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Scope from "effect/Scope";import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";import type * as EffectAcpErrors from "effect-acp/errors"; import {  CURSOR_PARAMETERIZED_MODEL_PICKER_CAPABILITIES,  resolveCursorAcpBaseModelId,  resolveCursorAcpConfigUpdates,} from "../Layers/CursorProvider.ts";import * as AcpSessionRuntime from "./AcpSessionRuntime.ts"; type CursorAcpRuntimeCursorSettings = Pick<CursorSettings, "apiEndpoint" | "binaryPath">; export interface CursorAcpRuntimeInput extends Omit<  AcpSessionRuntime.AcpSessionRuntimeOptions,  "authMethodId" | "clientCapabilities" | "spawn"> {  readonly childProcessSpawner: ChildProcessSpawner.ChildProcessSpawner["Service"];  readonly cursorSettings: CursorAcpRuntimeCursorSettings | null | undefined;  readonly environment?: NodeJS.ProcessEnv;} export interface CursorAcpModelSelectionErrorContext {  readonly cause: EffectAcpErrors.AcpError;  readonly step: "set-config-option" | "set-model";  readonly configId?: string;} export function buildCursorAcpSpawnInput(  cursorSettings: CursorAcpRuntimeCursorSettings | null | undefined,  cwd: string,  environment?: NodeJS.ProcessEnv,): AcpSessionRuntime.AcpSpawnInput {  return {    command: cursorSettings?.binaryPath || "agent",    args: [      ...(cursorSettings?.apiEndpoint ? (["-e", cursorSettings.apiEndpoint] as const) : []),      "acp",    ],    cwd,    ...(environment ? { env: environment } : {}),  };} export const makeCursorAcpRuntime = (  input: CursorAcpRuntimeInput,): Effect.Effect<  AcpSessionRuntime.AcpSessionRuntime["Service"],  EffectAcpErrors.AcpError,  Scope.Scope> =>  Effect.gen(function* () {    const acpContext = yield* Layer.build(      AcpSessionRuntime.layer({        ...input,        spawn: buildCursorAcpSpawnInput(input.cursorSettings, input.cwd, input.environment),        authMethodId: "cursor_login",        clientCapabilities: CURSOR_PARAMETERIZED_MODEL_PICKER_CAPABILITIES,      }).pipe(        Layer.provide(          Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, input.childProcessSpawner),        ),      ),    );    return yield* Effect.service(AcpSessionRuntime.AcpSessionRuntime).pipe(      Effect.provide(acpContext),    );  }); interface CursorAcpModelSelectionRuntime {  readonly getConfigOptions: AcpSessionRuntime.AcpSessionRuntime["Service"]["getConfigOptions"];  readonly setConfigOption: (    configId: string,    value: string | boolean,  ) => Effect.Effect<unknown, EffectAcpErrors.AcpError>;  readonly setModel: (model: string) => Effect.Effect<unknown, EffectAcpErrors.AcpError>;} export function applyCursorAcpModelSelection<E>(input: {  readonly runtime: CursorAcpModelSelectionRuntime;  readonly model: string | null | undefined;  readonly selections: ReadonlyArray<ProviderOptionSelection> | null | undefined;  readonly mapError: (context: CursorAcpModelSelectionErrorContext) => E;}): Effect.Effect<void, E> {  return Effect.gen(function* () {    yield* input.runtime.setModel(resolveCursorAcpBaseModelId(input.model)).pipe(      Effect.mapError((cause) =>        input.mapError({          cause,          step: "set-model",        }),      ),    );     const configUpdates = resolveCursorAcpConfigUpdates(      yield* input.runtime.getConfigOptions,      input.selections,    );    for (const update of configUpdates) {      yield* input.runtime.setConfigOption(update.configId, update.value).pipe(        Effect.mapError((cause) =>          input.mapError({            cause,            step: "set-config-option",            configId: update.configId,          }),        ),      );    }  });}