| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- import { Instance } from "@/project/instance"
- import { Plugin } from "../plugin"
- import { map, filter, pipe, fromEntries, mapValues } from "remeda"
- import z from "zod"
- import { fn } from "@/util/fn"
- import type { AuthOuathResult, Hooks } from "@opencode-ai/plugin"
- import { NamedError } from "@opencode-ai/util/error"
- import { Auth } from "@/auth"
- export namespace ProviderAuth {
- const state = Instance.state(async () => {
- const methods = pipe(
- await Plugin.list(),
- filter((x) => x.auth?.provider !== undefined),
- map((x) => [x.auth!.provider, x.auth!] as const),
- fromEntries(),
- )
- return { methods, pending: {} as Record<string, AuthOuathResult> }
- })
- export const Method = z
- .object({
- type: z.union([z.literal("oauth"), z.literal("api")]),
- label: z.string(),
- })
- .meta({
- ref: "ProviderAuthMethod",
- })
- export type Method = z.infer<typeof Method>
- export async function methods() {
- const s = await state().then((x) => x.methods)
- return mapValues(s, (x) =>
- x.methods.map(
- (y): Method => ({
- type: y.type,
- label: y.label,
- }),
- ),
- )
- }
- export const Authorization = z
- .object({
- url: z.string(),
- method: z.union([z.literal("auto"), z.literal("code")]),
- instructions: z.string(),
- })
- .meta({
- ref: "ProviderAuthAuthorization",
- })
- export type Authorization = z.infer<typeof Authorization>
- export const authorize = fn(
- z.object({
- providerID: z.string(),
- method: z.number(),
- }),
- async (input): Promise<Authorization | undefined> => {
- const auth = await state().then((s) => s.methods[input.providerID])
- const method = auth.methods[input.method]
- if (method.type === "oauth") {
- const result = await method.authorize()
- await state().then((s) => (s.pending[input.providerID] = result))
- return {
- url: result.url,
- method: result.method,
- instructions: result.instructions,
- }
- }
- },
- )
- export const callback = fn(
- z.object({
- providerID: z.string(),
- method: z.number(),
- code: z.string().optional(),
- }),
- async (input) => {
- const match = await state().then((s) => s.pending[input.providerID])
- if (!match) throw new OauthMissing({ providerID: input.providerID })
- let result
- if (match.method === "code") {
- if (!input.code) throw new OauthCodeMissing({ providerID: input.providerID })
- result = await match.callback(input.code)
- }
- if (match.method === "auto") {
- result = await match.callback()
- }
- if (result?.type === "success") {
- if ("key" in result) {
- await Auth.set(input.providerID, {
- type: "api",
- key: result.key,
- })
- }
- if ("refresh" in result) {
- const info: Auth.Info = {
- type: "oauth",
- access: result.access,
- refresh: result.refresh,
- expires: result.expires,
- }
- if (result.accountId) {
- info.accountId = result.accountId
- }
- await Auth.set(input.providerID, info)
- }
- return
- }
- throw new OauthCallbackFailed({})
- },
- )
- export const api = fn(
- z.object({
- providerID: z.string(),
- key: z.string(),
- }),
- async (input) => {
- await Auth.set(input.providerID, {
- type: "api",
- key: input.key,
- })
- },
- )
- export const OauthMissing = NamedError.create(
- "ProviderAuthOauthMissing",
- z.object({
- providerID: z.string(),
- }),
- )
- export const OauthCodeMissing = NamedError.create(
- "ProviderAuthOauthCodeMissing",
- z.object({
- providerID: z.string(),
- }),
- )
- export const OauthCallbackFailed = NamedError.create("ProviderAuthOauthCallbackFailed", z.object({}))
- }
|