export * as ServerAuth from "./auth" import { Config as EffectConfig, Context, Effect, Layer, Option, Redacted } from "effect" export type Credentials = { password?: string username?: string } export type DecodedCredentials = { readonly username: string readonly password: Redacted.Redacted } export type Info = { readonly password: Option.Option readonly username: string } export class Config extends Context.Service()("@opencode/ServerAuthConfig") { static layer(input: Info) { return Layer.succeed(this, this.of(input)) } static get defaultLayer() { return Layer.effect( this, Effect.gen(function* () { return Config.of( yield* EffectConfig.all({ password: EffectConfig.string("OPENCODE_SERVER_PASSWORD").pipe(EffectConfig.option), username: EffectConfig.string("OPENCODE_SERVER_USERNAME").pipe(EffectConfig.withDefault("opencode")), }), ) }), ) } } export function required(config: Info) { return Option.isSome(config.password) && config.password.value !== "" } export function authorized(credentials: DecodedCredentials, config: Info) { return ( Option.isSome(config.password) && credentials.username === config.username && Redacted.value(credentials.password) === config.password.value ) } export function header(credentials?: Credentials) { const password = credentials?.password ?? process.env.OPENCODE_SERVER_PASSWORD if (!password) return undefined return `Basic ${Buffer.from(`${credentials?.username ?? process.env.OPENCODE_SERVER_USERNAME ?? "opencode"}:${password}`).toString("base64")}` } export function headers(credentials?: Credentials) { const authorization = header(credentials) if (!authorization) return undefined return { Authorization: authorization } }