Skip to content

Commit 0cd70ad

Browse files
committed
refactor: unwrap McpAuth namespace + self-reexport
1 parent f9aa3d7 commit 0cd70ad

1 file changed

Lines changed: 132 additions & 132 deletions

File tree

packages/opencode/src/mcp/auth.ts

Lines changed: 132 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -4,141 +4,141 @@ import { Global } from "../global"
44
import { Effect, Layer, Context } from "effect"
55
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
66

7-
export namespace McpAuth {
8-
export const Tokens = z.object({
9-
accessToken: z.string(),
10-
refreshToken: z.string().optional(),
11-
expiresAt: z.number().optional(),
12-
scope: z.string().optional(),
13-
})
14-
export type Tokens = z.infer<typeof Tokens>
15-
16-
export const ClientInfo = z.object({
17-
clientId: z.string(),
18-
clientSecret: z.string().optional(),
19-
clientIdIssuedAt: z.number().optional(),
20-
clientSecretExpiresAt: z.number().optional(),
21-
})
22-
export type ClientInfo = z.infer<typeof ClientInfo>
23-
24-
export const Entry = z.object({
25-
tokens: Tokens.optional(),
26-
clientInfo: ClientInfo.optional(),
27-
codeVerifier: z.string().optional(),
28-
oauthState: z.string().optional(),
29-
serverUrl: z.string().optional(),
30-
})
31-
export type Entry = z.infer<typeof Entry>
32-
33-
const filepath = path.join(Global.Path.data, "mcp-auth.json")
34-
35-
export interface Interface {
36-
readonly all: () => Effect.Effect<Record<string, Entry>>
37-
readonly get: (mcpName: string) => Effect.Effect<Entry | undefined>
38-
readonly getForUrl: (mcpName: string, serverUrl: string) => Effect.Effect<Entry | undefined>
39-
readonly set: (mcpName: string, entry: Entry, serverUrl?: string) => Effect.Effect<void>
40-
readonly remove: (mcpName: string) => Effect.Effect<void>
41-
readonly updateTokens: (mcpName: string, tokens: Tokens, serverUrl?: string) => Effect.Effect<void>
42-
readonly updateClientInfo: (mcpName: string, clientInfo: ClientInfo, serverUrl?: string) => Effect.Effect<void>
43-
readonly updateCodeVerifier: (mcpName: string, codeVerifier: string) => Effect.Effect<void>
44-
readonly clearCodeVerifier: (mcpName: string) => Effect.Effect<void>
45-
readonly updateOAuthState: (mcpName: string, oauthState: string) => Effect.Effect<void>
46-
readonly getOAuthState: (mcpName: string) => Effect.Effect<string | undefined>
47-
readonly clearOAuthState: (mcpName: string) => Effect.Effect<void>
48-
readonly isTokenExpired: (mcpName: string) => Effect.Effect<boolean | null>
49-
}
50-
51-
export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
52-
53-
export const layer = Layer.effect(
54-
Service,
55-
Effect.gen(function* () {
56-
const fs = yield* AppFileSystem.Service
57-
58-
const all = Effect.fn("McpAuth.all")(function* () {
59-
return yield* fs.readJson(filepath).pipe(
60-
Effect.map((data) => data as Record<string, Entry>),
61-
Effect.catch(() => Effect.succeed({} as Record<string, Entry>)),
62-
)
63-
})
64-
65-
const get = Effect.fn("McpAuth.get")(function* (mcpName: string) {
66-
const data = yield* all()
67-
return data[mcpName]
68-
})
69-
70-
const getForUrl = Effect.fn("McpAuth.getForUrl")(function* (mcpName: string, serverUrl: string) {
71-
const entry = yield* get(mcpName)
72-
if (!entry) return undefined
73-
if (!entry.serverUrl) return undefined
74-
if (entry.serverUrl !== serverUrl) return undefined
75-
return entry
76-
})
77-
78-
const set = Effect.fn("McpAuth.set")(function* (mcpName: string, entry: Entry, serverUrl?: string) {
79-
const data = yield* all()
80-
if (serverUrl) entry.serverUrl = serverUrl
81-
yield* fs.writeJson(filepath, { ...data, [mcpName]: entry }, 0o600).pipe(Effect.orDie)
82-
})
83-
84-
const remove = Effect.fn("McpAuth.remove")(function* (mcpName: string) {
85-
const data = yield* all()
86-
delete data[mcpName]
87-
yield* fs.writeJson(filepath, data, 0o600).pipe(Effect.orDie)
88-
})
7+
export const Tokens = z.object({
8+
accessToken: z.string(),
9+
refreshToken: z.string().optional(),
10+
expiresAt: z.number().optional(),
11+
scope: z.string().optional(),
12+
})
13+
export type Tokens = z.infer<typeof Tokens>
14+
15+
export const ClientInfo = z.object({
16+
clientId: z.string(),
17+
clientSecret: z.string().optional(),
18+
clientIdIssuedAt: z.number().optional(),
19+
clientSecretExpiresAt: z.number().optional(),
20+
})
21+
export type ClientInfo = z.infer<typeof ClientInfo>
22+
23+
export const Entry = z.object({
24+
tokens: Tokens.optional(),
25+
clientInfo: ClientInfo.optional(),
26+
codeVerifier: z.string().optional(),
27+
oauthState: z.string().optional(),
28+
serverUrl: z.string().optional(),
29+
})
30+
export type Entry = z.infer<typeof Entry>
31+
32+
const filepath = path.join(Global.Path.data, "mcp-auth.json")
33+
34+
export interface Interface {
35+
readonly all: () => Effect.Effect<Record<string, Entry>>
36+
readonly get: (mcpName: string) => Effect.Effect<Entry | undefined>
37+
readonly getForUrl: (mcpName: string, serverUrl: string) => Effect.Effect<Entry | undefined>
38+
readonly set: (mcpName: string, entry: Entry, serverUrl?: string) => Effect.Effect<void>
39+
readonly remove: (mcpName: string) => Effect.Effect<void>
40+
readonly updateTokens: (mcpName: string, tokens: Tokens, serverUrl?: string) => Effect.Effect<void>
41+
readonly updateClientInfo: (mcpName: string, clientInfo: ClientInfo, serverUrl?: string) => Effect.Effect<void>
42+
readonly updateCodeVerifier: (mcpName: string, codeVerifier: string) => Effect.Effect<void>
43+
readonly clearCodeVerifier: (mcpName: string) => Effect.Effect<void>
44+
readonly updateOAuthState: (mcpName: string, oauthState: string) => Effect.Effect<void>
45+
readonly getOAuthState: (mcpName: string) => Effect.Effect<string | undefined>
46+
readonly clearOAuthState: (mcpName: string) => Effect.Effect<void>
47+
readonly isTokenExpired: (mcpName: string) => Effect.Effect<boolean | null>
48+
}
8949

90-
const updateField = <K extends keyof Entry>(field: K, spanName: string) =>
91-
Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string, value: NonNullable<Entry[K]>, serverUrl?: string) {
92-
const entry = (yield* get(mcpName)) ?? {}
93-
entry[field] = value
94-
yield* set(mcpName, entry, serverUrl)
95-
})
96-
97-
const clearField = <K extends keyof Entry>(field: K, spanName: string) =>
98-
Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string) {
99-
const entry = yield* get(mcpName)
100-
if (entry) {
101-
delete entry[field]
102-
yield* set(mcpName, entry)
103-
}
104-
})
105-
106-
const updateTokens = updateField("tokens", "updateTokens")
107-
const updateClientInfo = updateField("clientInfo", "updateClientInfo")
108-
const updateCodeVerifier = updateField("codeVerifier", "updateCodeVerifier")
109-
const updateOAuthState = updateField("oauthState", "updateOAuthState")
110-
const clearCodeVerifier = clearField("codeVerifier", "clearCodeVerifier")
111-
const clearOAuthState = clearField("oauthState", "clearOAuthState")
112-
113-
const getOAuthState = Effect.fn("McpAuth.getOAuthState")(function* (mcpName: string) {
114-
const entry = yield* get(mcpName)
115-
return entry?.oauthState
50+
export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
51+
52+
export const layer = Layer.effect(
53+
Service,
54+
Effect.gen(function* () {
55+
const fs = yield* AppFileSystem.Service
56+
57+
const all = Effect.fn("McpAuth.all")(function* () {
58+
return yield* fs.readJson(filepath).pipe(
59+
Effect.map((data) => data as Record<string, Entry>),
60+
Effect.catch(() => Effect.succeed({} as Record<string, Entry>)),
61+
)
62+
})
63+
64+
const get = Effect.fn("McpAuth.get")(function* (mcpName: string) {
65+
const data = yield* all()
66+
return data[mcpName]
67+
})
68+
69+
const getForUrl = Effect.fn("McpAuth.getForUrl")(function* (mcpName: string, serverUrl: string) {
70+
const entry = yield* get(mcpName)
71+
if (!entry) return undefined
72+
if (!entry.serverUrl) return undefined
73+
if (entry.serverUrl !== serverUrl) return undefined
74+
return entry
75+
})
76+
77+
const set = Effect.fn("McpAuth.set")(function* (mcpName: string, entry: Entry, serverUrl?: string) {
78+
const data = yield* all()
79+
if (serverUrl) entry.serverUrl = serverUrl
80+
yield* fs.writeJson(filepath, { ...data, [mcpName]: entry }, 0o600).pipe(Effect.orDie)
81+
})
82+
83+
const remove = Effect.fn("McpAuth.remove")(function* (mcpName: string) {
84+
const data = yield* all()
85+
delete data[mcpName]
86+
yield* fs.writeJson(filepath, data, 0o600).pipe(Effect.orDie)
87+
})
88+
89+
const updateField = <K extends keyof Entry>(field: K, spanName: string) =>
90+
Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string, value: NonNullable<Entry[K]>, serverUrl?: string) {
91+
const entry = (yield* get(mcpName)) ?? {}
92+
entry[field] = value
93+
yield* set(mcpName, entry, serverUrl)
11694
})
11795

118-
const isTokenExpired = Effect.fn("McpAuth.isTokenExpired")(function* (mcpName: string) {
96+
const clearField = <K extends keyof Entry>(field: K, spanName: string) =>
97+
Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string) {
11998
const entry = yield* get(mcpName)
120-
if (!entry?.tokens) return null
121-
if (!entry.tokens.expiresAt) return false
122-
return entry.tokens.expiresAt < Date.now() / 1000
99+
if (entry) {
100+
delete entry[field]
101+
yield* set(mcpName, entry)
102+
}
123103
})
124104

125-
return Service.of({
126-
all,
127-
get,
128-
getForUrl,
129-
set,
130-
remove,
131-
updateTokens,
132-
updateClientInfo,
133-
updateCodeVerifier,
134-
clearCodeVerifier,
135-
updateOAuthState,
136-
getOAuthState,
137-
clearOAuthState,
138-
isTokenExpired,
139-
})
140-
}),
141-
)
142-
143-
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
144-
}
105+
const updateTokens = updateField("tokens", "updateTokens")
106+
const updateClientInfo = updateField("clientInfo", "updateClientInfo")
107+
const updateCodeVerifier = updateField("codeVerifier", "updateCodeVerifier")
108+
const updateOAuthState = updateField("oauthState", "updateOAuthState")
109+
const clearCodeVerifier = clearField("codeVerifier", "clearCodeVerifier")
110+
const clearOAuthState = clearField("oauthState", "clearOAuthState")
111+
112+
const getOAuthState = Effect.fn("McpAuth.getOAuthState")(function* (mcpName: string) {
113+
const entry = yield* get(mcpName)
114+
return entry?.oauthState
115+
})
116+
117+
const isTokenExpired = Effect.fn("McpAuth.isTokenExpired")(function* (mcpName: string) {
118+
const entry = yield* get(mcpName)
119+
if (!entry?.tokens) return null
120+
if (!entry.tokens.expiresAt) return false
121+
return entry.tokens.expiresAt < Date.now() / 1000
122+
})
123+
124+
return Service.of({
125+
all,
126+
get,
127+
getForUrl,
128+
set,
129+
remove,
130+
updateTokens,
131+
updateClientInfo,
132+
updateCodeVerifier,
133+
clearCodeVerifier,
134+
updateOAuthState,
135+
getOAuthState,
136+
clearOAuthState,
137+
isTokenExpired,
138+
})
139+
}),
140+
)
141+
142+
export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
143+
144+
export * as McpAuth from "./auth"

0 commit comments

Comments
 (0)