diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts index bbaecfd8c71..b901c8343ad 100644 --- a/packages/opencode/src/cli/cmd/auth.ts +++ b/packages/opencode/src/cli/cmd/auth.ts @@ -18,13 +18,16 @@ type PluginAuth = NonNullable * Handle plugin-based authentication flow. * Returns true if auth was handled, false if it should fall through to default handling. */ -async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string): Promise { +async function handlePluginAuth(plugins: { auth: PluginAuth }[], provider: string): Promise { + const methods = plugins.flatMap((p) => p.auth.methods) + if (methods.length === 0) return false + let index = 0 - if (plugin.auth.methods.length > 1) { + if (methods.length > 1) { const method = await prompts.select({ message: "Login method", options: [ - ...plugin.auth.methods.map((x, index) => ({ + ...methods.map((x, index) => ({ label: x.label, value: index.toString(), })), @@ -33,7 +36,7 @@ async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string): if (prompts.isCancel(method)) throw new UI.CancelledError() index = parseInt(method) } - const method = plugin.auth.methods[index] + const method = methods[index] // Handle prompts for all auth types await Bun.sleep(10) @@ -307,9 +310,11 @@ export const AuthLoginCommand = cmd({ if (prompts.isCancel(provider)) throw new UI.CancelledError() - const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider)) - if (plugin && plugin.auth) { - const handled = await handlePluginAuth({ auth: plugin.auth }, provider) + const plugins = await Plugin.list().then((x) => + x.filter((x): x is typeof x & { auth: PluginAuth } => x.auth?.provider === provider), + ) + if (plugins.length > 0) { + const handled = await handlePluginAuth(plugins, provider) if (handled) return } @@ -323,9 +328,11 @@ export const AuthLoginCommand = cmd({ if (prompts.isCancel(provider)) throw new UI.CancelledError() // Check if a plugin provides auth for this custom provider - const customPlugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider)) - if (customPlugin && customPlugin.auth) { - const handled = await handlePluginAuth({ auth: customPlugin.auth }, provider) + const customPlugins = await Plugin.list().then((x) => + x.filter((x): x is typeof x & { auth: PluginAuth } => x.auth?.provider === provider), + ) + if (customPlugins.length > 0) { + const handled = await handlePluginAuth(customPlugins, provider) if (handled) return } diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index 76d34b845ae..d2aa643d8cc 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -81,11 +81,16 @@ export namespace Plugin { // as both a named export and default export (e.g., `export const X` and `export default X`). // Object.entries(mod) would return both entries pointing to the same function reference. const seen = new Set() - for (const [_name, fn] of Object.entries(mod)) { + for (const [name, fn] of Object.entries(mod)) { + if (typeof fn !== "function") continue if (seen.has(fn)) continue seen.add(fn) - const init = await fn(input) - hooks.push(init) + try { + const init = await fn(input) + hooks.push(init) + } catch (error) { + log.error("failed to initialize plugin", { path: plugin, name, error }) + } } }