127 lines · 5.1 KB · TypeScript
RawDownload
import {  ProviderSetupError,  type ProviderInstanceId,  type ProviderSessionId,} from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Stream from "effect/Stream"; import { ProviderSessionManagerV2 } from "../../orchestration-v2/ProviderSessionManager.ts";import { ProjectionStoreV2 } from "../../orchestration-v2/ProjectionStore.ts";import { ProviderAuthService } from "../Services/ProviderAuthService.ts";import { ProviderInstanceRegistry } from "../Services/ProviderInstanceRegistry.ts"; export const makeProviderAuthService = Effect.gen(function* () {  const registry = yield* ProviderInstanceRegistry;  const projections = yield* ProjectionStoreV2;  const providerSessions = yield* ProviderSessionManagerV2;   const getController = Effect.fn("ProviderAuthService.getController")(function* (    instanceId: ProviderInstanceId,    operation: string,  ) {    const instance = yield* registry.getInstance(instanceId);    if (!instance?.auth) {      return yield* new ProviderSetupError({        instanceId,        operation,        detail: instance          ? "This provider does not support sign-in in T3 Code."          : "This provider instance is no longer available.",      });    }    return instance.auth;  });   // Native sessions may still belong to the previous provider after the  // selected model changes. Read session bindings, not the selected model,  // when invalidating credentials for sign-in or sign-out.  const stopSessions = Effect.fn("ProviderAuthService.stopSessions")(function* (    instanceId: ProviderInstanceId,  ) {    const failure = (detail: string) =>      new ProviderSetupError({ instanceId, operation: "stopSessions", detail });    const threadIds = yield* projections      .getRecoveryThreadIds("runtime")      .pipe(        Effect.mapError(() => failure("Could not read the provider's active sessions. Try again.")),      );    const released = new Set<ProviderSessionId>();    yield* Effect.forEach(      threadIds,      (threadId) =>        projections.getThreadProjection(threadId).pipe(          Effect.flatMap((projection) =>            Effect.forEach(              projection.providerSessions.filter(                (session) =>                  session.providerInstanceId === instanceId &&                  session.status !== "stopped" &&                  session.status !== "error" &&                  !released.has(session.id),              ),              (session) =>                providerSessions                  .release({                    providerSessionId: session.id,                    reason: "manual_shutdown",                    detail: "Provider sign-in changed.",                  })                  .pipe(Effect.tap(() => Effect.sync(() => released.add(session.id)))),              { discard: true },            ),          ),          Effect.mapError(() =>            failure("Could not stop all sessions for this provider. Try again."),          ),        ),      { discard: true },    );  });   return ProviderAuthService.of({    start: Effect.fn("ProviderAuthService.start")(function* (input, ownerSessionId) {      const auth = yield* getController(input.instanceId, "start");      return yield* auth.start(ownerSessionId, stopSessions(input.instanceId));    }),    complete: Effect.fn("ProviderAuthService.complete")(function* (input, ownerSessionId) {      const auth = yield* getController(input.instanceId, "complete");      return yield* auth.complete(ownerSessionId, input);    }),    cancel: Effect.fn("ProviderAuthService.cancel")(function* (input, ownerSessionId) {      const auth = yield* getController(input.instanceId, "cancel");      return yield* auth.cancel(ownerSessionId, input.flowId);    }),    logout: Effect.fn("ProviderAuthService.logout")(function* (input) {      const auth = yield* getController(input.instanceId, "logout");      return yield* auth.logout(stopSessions(input.instanceId));    }),    subscribe: (input, ownerSessionId) =>      Effect.gen(function* () {        const changes = yield* registry.subscribeChanges;        const initial = yield* getController(input.instanceId, "subscribe");        return Stream.concat(          Stream.succeed(initial),          Stream.fromSubscription(changes).pipe(            Stream.mapEffect(() => getController(input.instanceId, "subscribe")),          ),        ).pipe(          Stream.changesWith((previous, next) => previous === next),          Stream.switchMap((auth) => auth.subscribe(ownerSessionId)),        );      }).pipe(Stream.unwrap),    tryHandlePromptCommand: Effect.fn("ProviderAuthService.tryHandlePromptCommand")(      function* (input) {        const instance = yield* registry.getInstance(input.instanceId);        if (!instance?.auth?.isLogoutPrompt?.(input.text, input.hasAttachments)) {          return false;        }        yield* instance.auth.logout(stopSessions(input.instanceId));        return true;      },    ),  });}); export const ProviderAuthServiceLive = Layer.effect(ProviderAuthService, makeProviderAuthService);