diff --git a/packages/opencode/src/auth/service.ts b/packages/opencode/src/auth/service.ts index 0898b39df7..66d907dfb2 100644 --- a/packages/opencode/src/auth/service.ts +++ b/packages/opencode/src/auth/service.ts @@ -1,5 +1,5 @@ import path from "path" -import { Effect, Layer, Option, Schema, ServiceMap } from "effect" +import { Effect, Layer, Record, Result, Schema, ServiceMap } from "effect" import { Global } from "../global" import { Filesystem } from "../util/filesystem" @@ -39,9 +39,36 @@ const fail = (message: string) => (cause: unknown) => new AuthServiceError({ mes export namespace AuthService { export interface Service { + /** + * Loads the auth entry stored under the given key. + * + * Keys are usually provider IDs, but some callers store URL-shaped keys. + */ readonly get: (providerID: string) => Effect.Effect + + /** + * Loads all persisted auth entries. + * + * Invalid entries are ignored instead of failing the whole file so older or + * partially-corrupt auth records do not break unrelated providers. + */ readonly all: () => Effect.Effect, AuthServiceError> + + /** + * Stores an auth entry under a normalized key. + * + * URL-shaped keys are normalized by trimming trailing slashes. Before + * writing, we delete both the original key and the normalized-with-slash + * variant so historical duplicates collapse to one canonical entry. + */ readonly set: (key: string, info: Info) => Effect.Effect + + /** + * Removes the auth entry stored under the provided key. + * + * The raw key and its normalized form are both deleted so callers can pass + * either version during cleanup. + */ readonly remove: (key: string) => Effect.Effect } } @@ -56,15 +83,7 @@ export class AuthService extends ServiceMap.Service { const data = await Filesystem.readJson>(file).catch(() => ({})) - return Object.entries(data).reduce( - (acc, [key, value]) => { - const parsed = decode(value) - if (Option.isNone(parsed)) return acc - acc[key] = parsed.value - return acc - }, - {} as Record, - ) + return Record.filterMap(data, (value) => Result.fromOption(decode(value), () => undefined)) }, catch: fail("Failed to read auth data"), }),