refactor(core): replace legacy logger with Effect logging (#31310)
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
import type { Model } from "@opencode-ai/sdk/v2"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { createServer } from "http"
|
||||
import open from "open"
|
||||
|
||||
const log = Log.create({ service: "plugin.digitalocean" })
|
||||
|
||||
const DO_OAUTH_CLIENT_ID = "b1a6c5158156caac821fd1b30253ca8acb52454a48fa744420e41889cb589f82"
|
||||
const DO_AUTHORIZE_URL = "https://cloud.digitalocean.com/v1/oauth/authorize"
|
||||
@@ -188,7 +186,6 @@ async function startOAuthServer(): Promise<void> {
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
oauthServer!.listen(OAUTH_PORT, () => {
|
||||
log.info("digitalocean oauth server started", { port: OAUTH_PORT })
|
||||
resolve()
|
||||
})
|
||||
oauthServer!.on("error", reject)
|
||||
@@ -197,7 +194,7 @@ async function startOAuthServer(): Promise<void> {
|
||||
|
||||
function stopOAuthServer() {
|
||||
if (!oauthServer) return
|
||||
oauthServer.close(() => log.info("digitalocean oauth server stopped"))
|
||||
oauthServer.close()
|
||||
oauthServer = undefined
|
||||
}
|
||||
|
||||
@@ -315,11 +312,9 @@ export async function DigitalOceanAuthPlugin(input: PluginInput): Promise<Hooks>
|
||||
path: { id: "digitalocean" },
|
||||
body: { type: "api", key: ctx.auth.key, metadata: updated },
|
||||
})
|
||||
.catch((err) => log.warn("failed to persist refreshed routers", { error: err }))
|
||||
.catch(() => {})
|
||||
} else if (result.status === 401 || result.status === 403) {
|
||||
log.warn("digitalocean oauth bearer rejected; using cached routers", { status: result.status })
|
||||
} else if (result.status !== 0) {
|
||||
log.warn("digitalocean router refresh failed", { status: result.status })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,7 +350,6 @@ export async function DigitalOceanAuthPlugin(input: PluginInput): Promise<Hooks>
|
||||
const routerResult = await listRouters(tokens.access_token)
|
||||
const routers = routerResult.ok ? routerResult.routers : []
|
||||
if (!routerResult.ok) {
|
||||
log.warn("digitalocean initial router fetch failed", { status: routerResult.status })
|
||||
}
|
||||
return {
|
||||
type: "success" as const,
|
||||
@@ -372,7 +366,6 @@ export async function DigitalOceanAuthPlugin(input: PluginInput): Promise<Hooks>
|
||||
},
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("digitalocean oauth callback failed", { error: err })
|
||||
return { type: "failed" as const }
|
||||
} finally {
|
||||
stopOAuthServer()
|
||||
|
||||
@@ -2,12 +2,10 @@ import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
import type { Model } from "@opencode-ai/sdk/v2"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { iife } from "@/util/iife"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { setTimeout as sleep } from "node:timers/promises"
|
||||
import { CopilotModels } from "./models"
|
||||
import { MessageV2 } from "@/session/message-v2"
|
||||
|
||||
const log = Log.create({ service: "plugin.copilot" })
|
||||
|
||||
const CLIENT_ID = "Ov23li8tweQw6odWQebz"
|
||||
const API_VERSION = "2026-06-01"
|
||||
@@ -87,7 +85,6 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> {
|
||||
})
|
||||
.catch((error) => {
|
||||
models = {}
|
||||
log.error("failed to fetch copilot models", { error })
|
||||
return Object.fromEntries(
|
||||
Object.entries(provider.models).map(([id, model]) => [id, fix(model, base(auth.enterpriseUrl))]),
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ import type {
|
||||
WorkspaceAdapter as PluginWorkspaceAdapter,
|
||||
} from "@opencode-ai/plugin"
|
||||
import { Config } from "@/config/config"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { createOpencodeClient } from "@opencode-ai/sdk"
|
||||
import { ServerAuth } from "@/server/auth"
|
||||
import { CodexAuthPlugin } from "./openai/codex"
|
||||
@@ -31,7 +30,6 @@ import { RuntimeFlags } from "@/effect/runtime-flags"
|
||||
import { EventV2Bridge } from "@/event-v2-bridge"
|
||||
import { InstallationChannel } from "@opencode-ai/core/installation/version"
|
||||
|
||||
const log = Log.create({ service: "plugin" })
|
||||
|
||||
type State = {
|
||||
hooks: Hooks[]
|
||||
@@ -163,11 +161,9 @@ export const layer = Layer.effect(
|
||||
}
|
||||
|
||||
for (const plugin of flags.disableDefaultPlugins ? [] : internalPlugins(flags)) {
|
||||
log.info("loading internal plugin", { name: plugin.name })
|
||||
const init = yield* Effect.tryPromise({
|
||||
try: () => plugin(input),
|
||||
catch: (err) => {
|
||||
log.error("failed to load internal plugin", { name: plugin.name, error: err })
|
||||
},
|
||||
}).pipe(Effect.option)
|
||||
if (init._tag === "Some") hooks.push(init.value)
|
||||
@@ -175,7 +171,6 @@ export const layer = Layer.effect(
|
||||
|
||||
const plugins = flags.pure ? [] : (cfg.plugin_origins ?? [])
|
||||
if (flags.pure && cfg.plugin_origins?.length) {
|
||||
log.info("skipping external plugins in pure mode", { count: cfg.plugin_origins.length })
|
||||
}
|
||||
if (plugins.length) yield* config.waitForDependencies()
|
||||
|
||||
@@ -185,10 +180,8 @@ export const layer = Layer.effect(
|
||||
kind: "server",
|
||||
report: {
|
||||
start(candidate) {
|
||||
log.info("loading plugin", { path: candidate.plan.spec })
|
||||
},
|
||||
missing(candidate, _retry, message) {
|
||||
log.warn("plugin has no server entrypoint", { path: candidate.plan.spec, message })
|
||||
},
|
||||
error(candidate, _retry, stage, error, resolved) {
|
||||
const spec = candidate.plan.spec
|
||||
@@ -197,24 +190,20 @@ export const layer = Layer.effect(
|
||||
|
||||
if (stage === "install") {
|
||||
const parsed = parsePluginSpecifier(spec)
|
||||
log.error("failed to install plugin", { pkg: parsed.pkg, version: parsed.version, error: message })
|
||||
publishPluginError(`Failed to install plugin ${parsed.pkg}@${parsed.version}: ${message}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (stage === "compatibility") {
|
||||
log.warn("plugin incompatible", { path: spec, error: message })
|
||||
publishPluginError(`Plugin ${spec} skipped: ${message}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (stage === "entry") {
|
||||
log.error("failed to resolve plugin server entry", { path: spec, error: message })
|
||||
publishPluginError(`Failed to load plugin ${spec}: ${message}`)
|
||||
return
|
||||
}
|
||||
|
||||
log.error("failed to load plugin", { path: spec, target: resolved?.entry, error: message })
|
||||
publishPluginError(`Failed to load plugin ${spec}: ${message}`)
|
||||
},
|
||||
},
|
||||
@@ -229,7 +218,6 @@ export const layer = Layer.effect(
|
||||
try: () => applyPlugin(load, input, hooks),
|
||||
catch: (err) => {
|
||||
const message = errorMessage(err)
|
||||
log.error("failed to load plugin", { path: load.spec, error: message })
|
||||
return message
|
||||
},
|
||||
}).pipe(
|
||||
@@ -249,10 +237,11 @@ export const layer = Layer.effect(
|
||||
for (const hook of hooks) {
|
||||
yield* Effect.tryPromise({
|
||||
try: () => Promise.resolve((hook as any).config?.(cfg)),
|
||||
catch: (err) => {
|
||||
log.error("plugin config hook failed", { error: err })
|
||||
},
|
||||
}).pipe(Effect.ignore)
|
||||
catch: errorMessage,
|
||||
}).pipe(
|
||||
Effect.tapError((error) => Effect.logError("plugin config hook failed", { error })),
|
||||
Effect.ignore,
|
||||
)
|
||||
}
|
||||
|
||||
const unsubscribe = yield* events.listen((event) => {
|
||||
@@ -272,7 +261,6 @@ export const layer = Layer.effect(
|
||||
Effect.tryPromise({
|
||||
try: () => Promise.resolve(hook.dispose?.()),
|
||||
catch: (error) => {
|
||||
log.error("plugin dispose hook failed", { error })
|
||||
},
|
||||
}).pipe(Effect.ignore),
|
||||
{ discard: true },
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import { OAUTH_DUMMY_KEY } from "../../auth"
|
||||
import os from "os"
|
||||
@@ -7,7 +6,6 @@ import { setTimeout as sleep } from "node:timers/promises"
|
||||
import { createServer } from "http"
|
||||
import { OpenAIWebSocketPool } from "./ws-pool"
|
||||
|
||||
const log = Log.create({ service: "plugin.codex" })
|
||||
|
||||
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
|
||||
const ISSUER = "https://auth.openai.com"
|
||||
@@ -306,7 +304,6 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
oauthServer!.listen(OAUTH_PORT, () => {
|
||||
log.info("codex oauth server started", { port: OAUTH_PORT })
|
||||
resolve()
|
||||
})
|
||||
oauthServer!.on("error", reject)
|
||||
@@ -318,7 +315,6 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
function stopOAuthServer() {
|
||||
if (oauthServer) {
|
||||
oauthServer.close(() => {
|
||||
log.info("codex oauth server stopped")
|
||||
})
|
||||
oauthServer = undefined
|
||||
}
|
||||
@@ -442,7 +438,6 @@ export async function CodexAuthPlugin(input: PluginInput, options: CodexAuthPlug
|
||||
|
||||
if (!currentAuth.access || currentAuth.expires < Date.now()) {
|
||||
if (!refreshPromise) {
|
||||
log.info("refreshing codex access token")
|
||||
refreshPromise = refreshAccessToken(currentAuth.refresh, issuer)
|
||||
.then(async (tokens) => {
|
||||
const accountId = extractAccountId(tokens) || authWithAccount.accountId
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import WebSocket from "ws"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { ProviderError } from "@/provider/error"
|
||||
import { isRecord } from "@/util/record"
|
||||
import { OpenAIWebSocket } from "./ws"
|
||||
|
||||
export const TITLE_HEADER = "x-opencode-title"
|
||||
|
||||
const log = Log.create({ service: "plugin.openai.ws" })
|
||||
|
||||
export interface CreateWebSocketFetchOptions {
|
||||
httpFetch?: typeof globalThis.fetch
|
||||
@@ -63,13 +61,11 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
})()
|
||||
if (!body?.stream) return httpFetch(input, httpInit)
|
||||
if (internalHeaders[TITLE_HEADER] === "true") {
|
||||
log.debug("http fallback", { reason: "title" })
|
||||
return httpFetch(input, httpInit)
|
||||
}
|
||||
|
||||
const sessionID = internalHeaders["x-session-affinity"] ?? internalHeaders["session-id"]
|
||||
if (!sessionID) {
|
||||
log.debug("http fallback", { reason: "missing_session" })
|
||||
return httpFetch(input, httpInit)
|
||||
}
|
||||
const key = `${sessionID}:conversation`
|
||||
@@ -78,11 +74,9 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
pool.set(key, entry)
|
||||
|
||||
if (entry.fallback) {
|
||||
log.debug("http fallback", { key, reason: "fallback_active" })
|
||||
return httpFetch(input, httpInit)
|
||||
}
|
||||
if (entry.busy) {
|
||||
log.debug("http fallback", { key, reason: "busy" })
|
||||
return httpFetch(input, httpInit)
|
||||
}
|
||||
|
||||
@@ -114,12 +108,10 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
entry.lastUsedAt = Date.now()
|
||||
entry.streamFailures = 0
|
||||
if (event.type !== "response.completed" && event.type !== "response.done") {
|
||||
log.warn("websocket terminal failure", { key, type: event.type })
|
||||
invalidate(entry)
|
||||
}
|
||||
},
|
||||
onConnectionInvalid: (error) => {
|
||||
log.warn("websocket invalidated", { key, error: error.message })
|
||||
entry.busy = false
|
||||
entry.lastUsedAt = Date.now()
|
||||
if (!entry.fallback) recordStreamFailure(entry)
|
||||
@@ -127,7 +119,6 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
resolveFirstEvent(false)
|
||||
},
|
||||
onAbort: (error) => {
|
||||
log.debug("websocket aborted", { key })
|
||||
entry.busy = false
|
||||
entry.lastUsedAt = Date.now()
|
||||
entry.streamFailures = 0
|
||||
@@ -137,7 +128,6 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
onRetryableTerminal: async (event) => {
|
||||
const error = connectionLimitError(event)
|
||||
if (!error) return undefined
|
||||
log.warn("websocket connection limit reached", { key })
|
||||
throw error
|
||||
},
|
||||
})
|
||||
@@ -150,7 +140,6 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
})
|
||||
}
|
||||
if (!entry.fallback) return response
|
||||
log.debug("http fallback", { key, reason: "websocket_retries_exhausted" })
|
||||
return httpFetch(input, httpInit)
|
||||
} catch (error) {
|
||||
entry.busy = false
|
||||
@@ -162,11 +151,6 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
}
|
||||
|
||||
recordStreamFailure(entry)
|
||||
log.warn("websocket setup failed", {
|
||||
key,
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
fallback: entry.fallback ? "http" : undefined,
|
||||
})
|
||||
invalidate(entry)
|
||||
if (entry.fallback) return httpFetch(input, httpInit)
|
||||
return failedResponse(
|
||||
@@ -189,14 +173,12 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
if (entry.busy) continue
|
||||
if (entry.fallback) continue
|
||||
if (now - entry.lastUsedAt < idleTimeout) continue
|
||||
log.debug("websocket idle prune", { key })
|
||||
invalidate(entry)
|
||||
pool.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
log.debug("websocket pool close", { count: pool.size })
|
||||
clearInterval(pruneTimer)
|
||||
for (const entry of pool.values()) invalidate(entry)
|
||||
pool.clear()
|
||||
@@ -206,7 +188,6 @@ export function createWebSocketFetch(options?: CreateWebSocketFetchOptions) {
|
||||
const key = `${sessionID}:conversation`
|
||||
const entry = pool.get(key)
|
||||
if (!entry) return
|
||||
log.debug("websocket pool remove", { key })
|
||||
invalidate(entry)
|
||||
pool.delete(key)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
import path from "path"
|
||||
import { fileURLToPath } from "url"
|
||||
import { TuiConfig } from "@/config/tui"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { errorData, errorMessage } from "@opencode-ai/tui/util/error"
|
||||
import { isRecord } from "@opencode-ai/tui/util/record"
|
||||
import { resolveHostAttentionSoundPaths } from "@/config/tui-host-attention"
|
||||
@@ -119,7 +118,6 @@ type RuntimeState = {
|
||||
dispose_timeout_ms: number
|
||||
}
|
||||
|
||||
const log = Log.create({ service: "tui.plugin" })
|
||||
const DISPOSE_TIMEOUT_MS = 5000
|
||||
const KV_KEY = "plugin_enabled"
|
||||
const EMPTY_TUI: TuiPluginModule = {
|
||||
@@ -128,19 +126,16 @@ const EMPTY_TUI: TuiPluginModule = {
|
||||
|
||||
function fail(message: string, data: Record<string, unknown>) {
|
||||
if (!("error" in data)) {
|
||||
log.error(message, data)
|
||||
console.error(`[tui.plugin] ${message}`, data)
|
||||
return
|
||||
}
|
||||
|
||||
const text = `${message}: ${errorMessage(data.error)}`
|
||||
const next = { ...data, error: errorData(data.error) }
|
||||
log.error(text, next)
|
||||
console.error(`[tui.plugin] ${text}`, next)
|
||||
}
|
||||
|
||||
function warn(message: string, data: Record<string, unknown>) {
|
||||
log.warn(message, data)
|
||||
console.warn(`[tui.plugin] ${message}`, data)
|
||||
}
|
||||
|
||||
@@ -275,15 +270,7 @@ function createThemeInstaller(
|
||||
await Flock.withLock(`tui-theme:${dest}`, async () => {
|
||||
const save = async () => {
|
||||
plugin.themes[name] = info
|
||||
await PluginMeta.setTheme(plugin.id, name, info).catch((error) => {
|
||||
log.warn("failed to track tui plugin theme", {
|
||||
path: spec,
|
||||
id: plugin.id,
|
||||
theme: src,
|
||||
dest,
|
||||
error,
|
||||
})
|
||||
})
|
||||
await PluginMeta.setTheme(plugin.id, name, info).catch(() => {})
|
||||
}
|
||||
|
||||
const exists = hasTheme(name)
|
||||
@@ -298,37 +285,26 @@ function createThemeInstaller(
|
||||
if (prev?.dest === dest && prev.mtime === mtime && prev.size === size) return
|
||||
}
|
||||
|
||||
const text = await Filesystem.readText(src).catch((error) => {
|
||||
log.warn("failed to read tui plugin theme", { path: spec, theme: src, error })
|
||||
return
|
||||
})
|
||||
const text = await Filesystem.readText(src).catch(() => undefined)
|
||||
if (text === undefined) return
|
||||
|
||||
const fail = Symbol()
|
||||
const data = await Promise.resolve(text)
|
||||
.then((x) => JSON.parse(x))
|
||||
.catch((error) => {
|
||||
log.warn("failed to parse tui plugin theme", { path: spec, theme: src, error })
|
||||
return fail
|
||||
})
|
||||
.catch(() => fail)
|
||||
if (data === fail) return
|
||||
|
||||
if (!isTheme(data)) {
|
||||
log.warn("invalid tui plugin theme", { path: spec, theme: src })
|
||||
return
|
||||
}
|
||||
|
||||
if (exists || !(await Filesystem.exists(dest))) {
|
||||
await Filesystem.write(dest, text).catch((error) => {
|
||||
log.warn("failed to persist tui plugin theme", { path: spec, theme: src, dest, error })
|
||||
})
|
||||
await Filesystem.write(dest, text).catch(() => {})
|
||||
}
|
||||
|
||||
upsertTheme(name, data)
|
||||
await save()
|
||||
}).catch((error) => {
|
||||
log.warn("failed to lock tui plugin theme install", { path: spec, theme: src, dest, error })
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,9 +677,7 @@ async function resolveExternalPlugins(list: ConfigPlugin.Origin[], wait: () => P
|
||||
items: list,
|
||||
kind: "tui",
|
||||
wait: async () => {
|
||||
await wait().catch((error) => {
|
||||
log.warn("failed waiting for tui plugin dependencies", { error })
|
||||
})
|
||||
await wait().catch(() => {})
|
||||
},
|
||||
finish: async (loaded, origin, retry) => {
|
||||
const mod = await Promise.resolve()
|
||||
@@ -774,9 +748,7 @@ async function resolveExternalPlugins(list: ConfigPlugin.Origin[], wait: () => P
|
||||
}
|
||||
},
|
||||
report: {
|
||||
start(candidate, retry) {
|
||||
log.info("loading tui plugin", { path: candidate.plan.spec, retry })
|
||||
},
|
||||
start() {},
|
||||
missing(candidate, retry, message) {
|
||||
warn("tui plugin has no entrypoint", { path: candidate.plan.spec, retry, message })
|
||||
},
|
||||
@@ -809,10 +781,7 @@ async function addExternalPluginEntries(state: RuntimeState, ready: PluginLoad[]
|
||||
target: item.target,
|
||||
id: item.id,
|
||||
})),
|
||||
).catch((error) => {
|
||||
log.warn("failed to track tui plugins", { error })
|
||||
return undefined
|
||||
})
|
||||
).catch(() => undefined)
|
||||
|
||||
const plugins: PluginEntry[] = []
|
||||
let ok = true
|
||||
@@ -820,17 +789,6 @@ async function addExternalPluginEntries(state: RuntimeState, ready: PluginLoad[]
|
||||
const entry = ready[i]
|
||||
if (!entry) continue
|
||||
const hit = meta?.[i]
|
||||
if (hit && hit.state !== "same") {
|
||||
log.info("tui plugin metadata updated", {
|
||||
path: entry.spec,
|
||||
retry: entry.retry,
|
||||
state: hit.state,
|
||||
source: hit.entry.source,
|
||||
version: hit.entry.version,
|
||||
modified: hit.entry.modified,
|
||||
})
|
||||
}
|
||||
|
||||
const info = createMeta(entry.source, entry.spec, entry.target, hit, entry.id)
|
||||
const themes = hit?.entry.themes ? { ...hit.entry.themes } : {}
|
||||
const plugin: PluginEntry = {
|
||||
@@ -1129,11 +1087,9 @@ async function load(input: {
|
||||
const pluginOrigins = config.plugin_origins ?? (await TuiConfig.pluginOrigins())
|
||||
const records = Flag.OPENCODE_PURE ? [] : pluginOrigins
|
||||
if (Flag.OPENCODE_PURE && pluginOrigins.length) {
|
||||
log.info("skipping external tui plugins in pure mode", { count: pluginOrigins.length })
|
||||
}
|
||||
|
||||
for (const item of internalTuiPlugins(flags)) {
|
||||
log.info("loading internal tui plugin", { id: item.id })
|
||||
const entry = loadInternalPlugin(item)
|
||||
const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id)
|
||||
addPluginEntry(next, {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import type { Hooks, PluginInput } from "@opencode-ai/plugin"
|
||||
import * as Log from "@opencode-ai/core/util/log"
|
||||
import { OAUTH_DUMMY_KEY } from "../auth"
|
||||
import { createServer } from "http"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
|
||||
const log = Log.create({ service: "plugin.xai" })
|
||||
|
||||
// Public Grok-CLI OAuth client. xAI's auth server rejects loopback OAuth from
|
||||
// non-allowlisted clients, so we reuse the Grok-CLI client_id that xAI ships
|
||||
@@ -510,8 +508,6 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
// behavior and crash the entire opencode process. Matches the silent-
|
||||
// swallow behavior the Codex plugin gets from its permanent
|
||||
// `oauthServer!.on("error", reject)`.
|
||||
server.on("error", (err) => log.warn("xai oauth server error", { error: err }))
|
||||
log.info("xai oauth server started", { host: OAUTH_HOST, port: OAUTH_PORT })
|
||||
resolve()
|
||||
})
|
||||
oauthServer = server
|
||||
@@ -522,7 +518,7 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
|
||||
|
||||
function stopOAuthServer() {
|
||||
if (oauthServer) {
|
||||
oauthServer.close(() => log.info("xai oauth server stopped"))
|
||||
oauthServer.close()
|
||||
oauthServer = undefined
|
||||
}
|
||||
}
|
||||
@@ -607,7 +603,6 @@ export async function XaiAuthPlugin(input: PluginInput, options: XaiAuthPluginOp
|
||||
if (expiresSoon) {
|
||||
if (!refreshPromise) {
|
||||
const refreshToken = currentAuth.refresh
|
||||
log.info("refreshing xai access token")
|
||||
refreshPromise = refreshAccessToken(refreshToken, options)
|
||||
.then(async (tokens) => {
|
||||
const refreshedExpires = Date.now() + (tokens.expires_in ?? 3600) * 1000
|
||||
@@ -627,7 +622,7 @@ export async function XaiAuthPlugin(input: PluginInput, options: XaiAuthPluginOp
|
||||
expires: refreshedExpires,
|
||||
},
|
||||
})
|
||||
.catch((err) => log.warn("failed to persist refreshed xai tokens", { error: err }))
|
||||
.catch(() => {})
|
||||
return { access: tokens.access_token, refresh: refreshedRefresh, expires: refreshedExpires }
|
||||
})
|
||||
.finally(() => {
|
||||
@@ -688,7 +683,6 @@ export async function XaiAuthPlugin(input: PluginInput, options: XaiAuthPluginOp
|
||||
expires: Date.now() + (tokens.expires_in ?? 3600) * 1000,
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("xai oauth callback failed", { error: err })
|
||||
return { type: "failed" as const }
|
||||
} finally {
|
||||
stopOAuthServer()
|
||||
@@ -725,7 +719,6 @@ export async function XaiAuthPlugin(input: PluginInput, options: XaiAuthPluginOp
|
||||
expires: Date.now() + (tokens.expires_in ?? 3600) * 1000,
|
||||
}
|
||||
} catch (err) {
|
||||
log.error("xai device code callback failed", { error: err })
|
||||
return { type: "failed" as const }
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user