summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorKit Langton <[email protected]>2026-04-16 20:01:12 -0400
committerGitHub <[email protected]>2026-04-17 00:01:12 +0000
commitfdd5b77bfd9c525a2ae6656a011c1419748761e3 (patch)
tree1dccbfd61e7c035c2f24dda701db45c896605ef4
parentcde105e7a8832b9c6d9d0a43d5699b4768156533 (diff)
downloadopencode-fdd5b77bfd9c525a2ae6656a011c1419748761e3.tar.gz
opencode-fdd5b77bfd9c525a2ae6656a011c1419748761e3.zip
refactor: unwrap McpAuth namespace + self-reexport (#22942)
-rw-r--r--packages/opencode/src/mcp/auth.ts264
1 files changed, 132 insertions, 132 deletions
diff --git a/packages/opencode/src/mcp/auth.ts b/packages/opencode/src/mcp/auth.ts
index 85f9e1d8c..efb046d7a 100644
--- a/packages/opencode/src/mcp/auth.ts
+++ b/packages/opencode/src/mcp/auth.ts
@@ -4,141 +4,141 @@ import { Global } from "../global"
import { Effect, Layer, Context } from "effect"
import { AppFileSystem } from "@opencode-ai/shared/filesystem"
-export namespace McpAuth {
- export const Tokens = z.object({
- accessToken: z.string(),
- refreshToken: z.string().optional(),
- expiresAt: z.number().optional(),
- scope: z.string().optional(),
- })
- export type Tokens = z.infer<typeof Tokens>
-
- export const ClientInfo = z.object({
- clientId: z.string(),
- clientSecret: z.string().optional(),
- clientIdIssuedAt: z.number().optional(),
- clientSecretExpiresAt: z.number().optional(),
- })
- export type ClientInfo = z.infer<typeof ClientInfo>
-
- export const Entry = z.object({
- tokens: Tokens.optional(),
- clientInfo: ClientInfo.optional(),
- codeVerifier: z.string().optional(),
- oauthState: z.string().optional(),
- serverUrl: z.string().optional(),
- })
- export type Entry = z.infer<typeof Entry>
-
- const filepath = path.join(Global.Path.data, "mcp-auth.json")
-
- export interface Interface {
- readonly all: () => Effect.Effect<Record<string, Entry>>
- readonly get: (mcpName: string) => Effect.Effect<Entry | undefined>
- readonly getForUrl: (mcpName: string, serverUrl: string) => Effect.Effect<Entry | undefined>
- readonly set: (mcpName: string, entry: Entry, serverUrl?: string) => Effect.Effect<void>
- readonly remove: (mcpName: string) => Effect.Effect<void>
- readonly updateTokens: (mcpName: string, tokens: Tokens, serverUrl?: string) => Effect.Effect<void>
- readonly updateClientInfo: (mcpName: string, clientInfo: ClientInfo, serverUrl?: string) => Effect.Effect<void>
- readonly updateCodeVerifier: (mcpName: string, codeVerifier: string) => Effect.Effect<void>
- readonly clearCodeVerifier: (mcpName: string) => Effect.Effect<void>
- readonly updateOAuthState: (mcpName: string, oauthState: string) => Effect.Effect<void>
- readonly getOAuthState: (mcpName: string) => Effect.Effect<string | undefined>
- readonly clearOAuthState: (mcpName: string) => Effect.Effect<void>
- readonly isTokenExpired: (mcpName: string) => Effect.Effect<boolean | null>
- }
-
- export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
-
- export const layer = Layer.effect(
- Service,
- Effect.gen(function* () {
- const fs = yield* AppFileSystem.Service
-
- const all = Effect.fn("McpAuth.all")(function* () {
- return yield* fs.readJson(filepath).pipe(
- Effect.map((data) => data as Record<string, Entry>),
- Effect.catch(() => Effect.succeed({} as Record<string, Entry>)),
- )
- })
-
- const get = Effect.fn("McpAuth.get")(function* (mcpName: string) {
- const data = yield* all()
- return data[mcpName]
- })
-
- const getForUrl = Effect.fn("McpAuth.getForUrl")(function* (mcpName: string, serverUrl: string) {
- const entry = yield* get(mcpName)
- if (!entry) return undefined
- if (!entry.serverUrl) return undefined
- if (entry.serverUrl !== serverUrl) return undefined
- return entry
- })
-
- const set = Effect.fn("McpAuth.set")(function* (mcpName: string, entry: Entry, serverUrl?: string) {
- const data = yield* all()
- if (serverUrl) entry.serverUrl = serverUrl
- yield* fs.writeJson(filepath, { ...data, [mcpName]: entry }, 0o600).pipe(Effect.orDie)
- })
-
- const remove = Effect.fn("McpAuth.remove")(function* (mcpName: string) {
- const data = yield* all()
- delete data[mcpName]
- yield* fs.writeJson(filepath, data, 0o600).pipe(Effect.orDie)
- })
+export const Tokens = z.object({
+ accessToken: z.string(),
+ refreshToken: z.string().optional(),
+ expiresAt: z.number().optional(),
+ scope: z.string().optional(),
+})
+export type Tokens = z.infer<typeof Tokens>
+
+export const ClientInfo = z.object({
+ clientId: z.string(),
+ clientSecret: z.string().optional(),
+ clientIdIssuedAt: z.number().optional(),
+ clientSecretExpiresAt: z.number().optional(),
+})
+export type ClientInfo = z.infer<typeof ClientInfo>
+
+export const Entry = z.object({
+ tokens: Tokens.optional(),
+ clientInfo: ClientInfo.optional(),
+ codeVerifier: z.string().optional(),
+ oauthState: z.string().optional(),
+ serverUrl: z.string().optional(),
+})
+export type Entry = z.infer<typeof Entry>
+
+const filepath = path.join(Global.Path.data, "mcp-auth.json")
+
+export interface Interface {
+ readonly all: () => Effect.Effect<Record<string, Entry>>
+ readonly get: (mcpName: string) => Effect.Effect<Entry | undefined>
+ readonly getForUrl: (mcpName: string, serverUrl: string) => Effect.Effect<Entry | undefined>
+ readonly set: (mcpName: string, entry: Entry, serverUrl?: string) => Effect.Effect<void>
+ readonly remove: (mcpName: string) => Effect.Effect<void>
+ readonly updateTokens: (mcpName: string, tokens: Tokens, serverUrl?: string) => Effect.Effect<void>
+ readonly updateClientInfo: (mcpName: string, clientInfo: ClientInfo, serverUrl?: string) => Effect.Effect<void>
+ readonly updateCodeVerifier: (mcpName: string, codeVerifier: string) => Effect.Effect<void>
+ readonly clearCodeVerifier: (mcpName: string) => Effect.Effect<void>
+ readonly updateOAuthState: (mcpName: string, oauthState: string) => Effect.Effect<void>
+ readonly getOAuthState: (mcpName: string) => Effect.Effect<string | undefined>
+ readonly clearOAuthState: (mcpName: string) => Effect.Effect<void>
+ readonly isTokenExpired: (mcpName: string) => Effect.Effect<boolean | null>
+}
- const updateField = <K extends keyof Entry>(field: K, spanName: string) =>
- Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string, value: NonNullable<Entry[K]>, serverUrl?: string) {
- const entry = (yield* get(mcpName)) ?? {}
- entry[field] = value
- yield* set(mcpName, entry, serverUrl)
- })
-
- const clearField = <K extends keyof Entry>(field: K, spanName: string) =>
- Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string) {
- const entry = yield* get(mcpName)
- if (entry) {
- delete entry[field]
- yield* set(mcpName, entry)
- }
- })
-
- const updateTokens = updateField("tokens", "updateTokens")
- const updateClientInfo = updateField("clientInfo", "updateClientInfo")
- const updateCodeVerifier = updateField("codeVerifier", "updateCodeVerifier")
- const updateOAuthState = updateField("oauthState", "updateOAuthState")
- const clearCodeVerifier = clearField("codeVerifier", "clearCodeVerifier")
- const clearOAuthState = clearField("oauthState", "clearOAuthState")
-
- const getOAuthState = Effect.fn("McpAuth.getOAuthState")(function* (mcpName: string) {
- const entry = yield* get(mcpName)
- return entry?.oauthState
+export class Service extends Context.Service<Service, Interface>()("@opencode/McpAuth") {}
+
+export const layer = Layer.effect(
+ Service,
+ Effect.gen(function* () {
+ const fs = yield* AppFileSystem.Service
+
+ const all = Effect.fn("McpAuth.all")(function* () {
+ return yield* fs.readJson(filepath).pipe(
+ Effect.map((data) => data as Record<string, Entry>),
+ Effect.catch(() => Effect.succeed({} as Record<string, Entry>)),
+ )
+ })
+
+ const get = Effect.fn("McpAuth.get")(function* (mcpName: string) {
+ const data = yield* all()
+ return data[mcpName]
+ })
+
+ const getForUrl = Effect.fn("McpAuth.getForUrl")(function* (mcpName: string, serverUrl: string) {
+ const entry = yield* get(mcpName)
+ if (!entry) return undefined
+ if (!entry.serverUrl) return undefined
+ if (entry.serverUrl !== serverUrl) return undefined
+ return entry
+ })
+
+ const set = Effect.fn("McpAuth.set")(function* (mcpName: string, entry: Entry, serverUrl?: string) {
+ const data = yield* all()
+ if (serverUrl) entry.serverUrl = serverUrl
+ yield* fs.writeJson(filepath, { ...data, [mcpName]: entry }, 0o600).pipe(Effect.orDie)
+ })
+
+ const remove = Effect.fn("McpAuth.remove")(function* (mcpName: string) {
+ const data = yield* all()
+ delete data[mcpName]
+ yield* fs.writeJson(filepath, data, 0o600).pipe(Effect.orDie)
+ })
+
+ const updateField = <K extends keyof Entry>(field: K, spanName: string) =>
+ Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string, value: NonNullable<Entry[K]>, serverUrl?: string) {
+ const entry = (yield* get(mcpName)) ?? {}
+ entry[field] = value
+ yield* set(mcpName, entry, serverUrl)
})
- const isTokenExpired = Effect.fn("McpAuth.isTokenExpired")(function* (mcpName: string) {
+ const clearField = <K extends keyof Entry>(field: K, spanName: string) =>
+ Effect.fn(`McpAuth.${spanName}`)(function* (mcpName: string) {
const entry = yield* get(mcpName)
- if (!entry?.tokens) return null
- if (!entry.tokens.expiresAt) return false
- return entry.tokens.expiresAt < Date.now() / 1000
+ if (entry) {
+ delete entry[field]
+ yield* set(mcpName, entry)
+ }
})
- return Service.of({
- all,
- get,
- getForUrl,
- set,
- remove,
- updateTokens,
- updateClientInfo,
- updateCodeVerifier,
- clearCodeVerifier,
- updateOAuthState,
- getOAuthState,
- clearOAuthState,
- isTokenExpired,
- })
- }),
- )
-
- export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
-}
+ const updateTokens = updateField("tokens", "updateTokens")
+ const updateClientInfo = updateField("clientInfo", "updateClientInfo")
+ const updateCodeVerifier = updateField("codeVerifier", "updateCodeVerifier")
+ const updateOAuthState = updateField("oauthState", "updateOAuthState")
+ const clearCodeVerifier = clearField("codeVerifier", "clearCodeVerifier")
+ const clearOAuthState = clearField("oauthState", "clearOAuthState")
+
+ const getOAuthState = Effect.fn("McpAuth.getOAuthState")(function* (mcpName: string) {
+ const entry = yield* get(mcpName)
+ return entry?.oauthState
+ })
+
+ const isTokenExpired = Effect.fn("McpAuth.isTokenExpired")(function* (mcpName: string) {
+ const entry = yield* get(mcpName)
+ if (!entry?.tokens) return null
+ if (!entry.tokens.expiresAt) return false
+ return entry.tokens.expiresAt < Date.now() / 1000
+ })
+
+ return Service.of({
+ all,
+ get,
+ getForUrl,
+ set,
+ remove,
+ updateTokens,
+ updateClientInfo,
+ updateCodeVerifier,
+ clearCodeVerifier,
+ updateOAuthState,
+ getOAuthState,
+ clearOAuthState,
+ isTokenExpired,
+ })
+ }),
+)
+
+export const defaultLayer = layer.pipe(Layer.provide(AppFileSystem.defaultLayer))
+
+export * as McpAuth from "./auth"