1- import { Instance } from "@/project/instance"
2- import { Plugin } from "../plugin"
3- import { map , filter , pipe , fromEntries , mapValues } from "remeda"
1+ import { Effect , ManagedRuntime } from "effect"
42import z from "zod"
3+
54import { fn } from "@/util/fn"
6- import type { AuthOuathResult , Hooks } from "@opencode-ai/plugin"
7- import { NamedError } from "@opencode-ai/util/error"
8- import { Auth } from "@/auth"
5+ import * as S from "./auth-service"
96import { ProviderID } from "./schema"
107
11- export namespace ProviderAuth {
12- const state = Instance . state ( async ( ) => {
13- const methods = pipe (
14- await Plugin . list ( ) ,
15- filter ( ( x ) => x . auth ?. provider !== undefined ) ,
16- map ( ( x ) => [ x . auth ! . provider , x . auth ! ] as const ) ,
17- fromEntries ( ) ,
18- )
19- return { methods, pending : { } as Record < string , AuthOuathResult > }
20- } )
8+ // Separate runtime: ProviderAuthService can't join the shared runtime because
9+ // runtime.ts → auth-service.ts → provider/auth.ts creates a circular import.
10+ // AuthService is stateless file I/O so the duplicate instance is harmless.
11+ const rt = ManagedRuntime . make ( S . ProviderAuthService . defaultLayer )
2112
22- export const Method = z
23- . object ( {
24- type : z . union ( [ z . literal ( "oauth" ) , z . literal ( "api" ) ] ) ,
25- label : z . string ( ) ,
26- } )
27- . meta ( {
28- ref : "ProviderAuthMethod" ,
29- } )
30- export type Method = z . infer < typeof Method >
13+ function runPromise < A > ( f : ( service : S . ProviderAuthService . Service ) => Effect . Effect < A , S . ProviderAuthError > ) {
14+ return rt . runPromise ( S . ProviderAuthService . use ( f ) )
15+ }
16+
17+ export namespace ProviderAuth {
18+ export const Method = S . Method
19+ export type Method = S . Method
3120
3221 export async function methods ( ) {
33- const s = await state ( ) . then ( ( x ) => x . methods )
34- return mapValues ( s , ( x ) =>
35- x . methods . map (
36- ( y ) : Method => ( {
37- type : y . type ,
38- label : y . label ,
39- } ) ,
40- ) ,
41- )
22+ return runPromise ( ( service ) => service . methods ( ) )
4223 }
4324
44- export const Authorization = z
45- . object ( {
46- url : z . string ( ) ,
47- method : z . union ( [ z . literal ( "auto" ) , z . literal ( "code" ) ] ) ,
48- instructions : z . string ( ) ,
49- } )
50- . meta ( {
51- ref : "ProviderAuthAuthorization" ,
52- } )
53- export type Authorization = z . infer < typeof Authorization >
25+ export const Authorization = S . Authorization
26+ export type Authorization = S . Authorization
5427
5528 export const authorize = fn (
5629 z . object ( {
5730 providerID : ProviderID . zod ,
5831 method : z . number ( ) ,
5932 } ) ,
60- async ( input ) : Promise < Authorization | undefined > => {
61- const auth = await state ( ) . then ( ( s ) => s . methods [ input . providerID ] )
62- const method = auth . methods [ input . method ]
63- if ( method . type === "oauth" ) {
64- const result = await method . authorize ( )
65- await state ( ) . then ( ( s ) => ( s . pending [ input . providerID ] = result ) )
66- return {
67- url : result . url ,
68- method : result . method ,
69- instructions : result . instructions ,
70- }
71- }
72- } ,
33+ async ( input ) : Promise < Authorization | undefined > => runPromise ( ( service ) => service . authorize ( input ) ) ,
7334 )
7435
7536 export const callback = fn (
@@ -78,71 +39,18 @@ export namespace ProviderAuth {
7839 method : z . number ( ) ,
7940 code : z . string ( ) . optional ( ) ,
8041 } ) ,
81- async ( input ) => {
82- const match = await state ( ) . then ( ( s ) => s . pending [ input . providerID ] )
83- if ( ! match ) throw new OauthMissing ( { providerID : input . providerID } )
84- let result
85-
86- if ( match . method === "code" ) {
87- if ( ! input . code ) throw new OauthCodeMissing ( { providerID : input . providerID } )
88- result = await match . callback ( input . code )
89- }
90-
91- if ( match . method === "auto" ) {
92- result = await match . callback ( )
93- }
94-
95- if ( result ?. type === "success" ) {
96- if ( "key" in result ) {
97- await Auth . set ( input . providerID , {
98- type : "api" ,
99- key : result . key ,
100- } )
101- }
102- if ( "refresh" in result ) {
103- const info : Auth . Info = {
104- type : "oauth" ,
105- access : result . access ,
106- refresh : result . refresh ,
107- expires : result . expires ,
108- }
109- if ( result . accountId ) {
110- info . accountId = result . accountId
111- }
112- await Auth . set ( input . providerID , info )
113- }
114- return
115- }
116-
117- throw new OauthCallbackFailed ( { } )
118- } ,
42+ async ( input ) => runPromise ( ( service ) => service . callback ( input ) ) ,
11943 )
12044
12145 export const api = fn (
12246 z . object ( {
12347 providerID : ProviderID . zod ,
12448 key : z . string ( ) ,
12549 } ) ,
126- async ( input ) => {
127- await Auth . set ( input . providerID , {
128- type : "api" ,
129- key : input . key ,
130- } )
131- } ,
132- )
133-
134- export const OauthMissing = NamedError . create (
135- "ProviderAuthOauthMissing" ,
136- z . object ( {
137- providerID : ProviderID . zod ,
138- } ) ,
139- )
140- export const OauthCodeMissing = NamedError . create (
141- "ProviderAuthOauthCodeMissing" ,
142- z . object ( {
143- providerID : ProviderID . zod ,
144- } ) ,
50+ async ( input ) => runPromise ( ( service ) => service . api ( input ) ) ,
14551 )
14652
147- export const OauthCallbackFailed = NamedError . create ( "ProviderAuthOauthCallbackFailed" , z . object ( { } ) )
53+ export import OauthMissing = S . OauthMissing
54+ export import OauthCodeMissing = S . OauthCodeMissing
55+ export import OauthCallbackFailed = S . OauthCallbackFailed
14856}
0 commit comments