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, }), ), ); } });}
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,
}),
),
);
}
});
}