diff --git a/packages/opencode/src/cli/cmd/providers.ts b/packages/opencode/src/cli/cmd/providers.ts index 61fe4b2da2..278522555f 100644 --- a/packages/opencode/src/cli/cmd/providers.ts +++ b/packages/opencode/src/cli/cmd/providers.ts @@ -156,28 +156,38 @@ async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string, } if (method.type === "api") { - if (method.authorize) { - const key = await prompts.password({ - message: "Enter your API key", - validate: (x) => (x && x.length > 0 ? undefined : "Required"), - }) - if (prompts.isCancel(key)) throw new UI.CancelledError() + const key = await prompts.password({ + message: "Enter your API key", + validate: (x) => (x && x.length > 0 ? undefined : "Required"), + }) + if (prompts.isCancel(key)) throw new UI.CancelledError() - const result = await method.authorize(inputs) - if (result.type === "failed") { - prompts.log.error("Failed to authorize") - } - if (result.type === "success") { - const saveProvider = result.provider ?? provider - await put(saveProvider, { - type: "api", - key: result.key ?? key, - }) - prompts.log.success("Login successful") - } + const metadata = Object.keys(inputs).length ? { metadata: inputs } : {} + if (!method.authorize) { + await put(provider, { + type: "api", + key, + ...metadata, + }) prompts.outro("Done") return true } + + const result = await method.authorize(inputs) + if (result.type === "failed") { + prompts.log.error("Failed to authorize") + } + if (result.type === "success") { + const saveProvider = result.provider ?? provider + await put(saveProvider, { + type: "api", + key: result.key ?? key, + ...metadata, + }) + prompts.log.success("Login successful") + } + prompts.outro("Done") + return true } return false diff --git a/packages/opencode/src/plugin/azure.ts b/packages/opencode/src/plugin/azure.ts new file mode 100644 index 0000000000..62792b3bd2 --- /dev/null +++ b/packages/opencode/src/plugin/azure.ts @@ -0,0 +1,26 @@ +import type { Hooks, PluginInput } from "@opencode-ai/plugin" + +export async function AzureAuthPlugin(_input: PluginInput): Promise { + const prompts = [] + if (!process.env.AZURE_RESOURCE_NAME) { + prompts.push({ + type: "text" as const, + key: "resourceName", + message: "Enter Azure Resource Name", + placeholder: "e.g. my-models", + }) + } + + return { + auth: { + provider: "azure", + methods: [ + { + type: "api", + label: "API key", + prompts, + }, + ], + }, + } +} diff --git a/packages/opencode/src/plugin/index.ts b/packages/opencode/src/plugin/index.ts index 0313022c36..ac37823c34 100644 --- a/packages/opencode/src/plugin/index.ts +++ b/packages/opencode/src/plugin/index.ts @@ -17,6 +17,7 @@ import { CopilotAuthPlugin } from "./github-copilot/copilot" import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth" import { PoeAuthPlugin } from "opencode-poe-auth" import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cloudflare" +import { AzureAuthPlugin } from "./azure" import { Effect, Layer, Context, Stream } from "effect" import { EffectBridge } from "@/effect/bridge" import { InstanceState } from "@/effect/instance-state" @@ -61,6 +62,7 @@ const INTERNAL_PLUGINS: PluginInstance[] = [ PoeAuthPlugin, CloudflareWorkersAuthPlugin, CloudflareAIGatewayAuthPlugin, + AzureAuthPlugin, ] function isServerPlugin(value: unknown): value is PluginInstance { diff --git a/packages/opencode/src/provider/provider.ts b/packages/opencode/src/provider/provider.ts index 702435d7da..fc835cf5ee 100644 --- a/packages/opencode/src/provider/provider.ts +++ b/packages/opencode/src/provider/provider.ts @@ -199,12 +199,26 @@ function custom(dep: CustomDep): Record { }), azure: Effect.fnUntraced(function* (provider: Info) { const env = yield* dep.env() + const auth = yield* dep.auth(provider.id) const resource = iife(() => { - const name = provider.options?.resourceName - if (typeof name === "string" && name.trim() !== "") return name - return env["AZURE_RESOURCE_NAME"] + return [ + provider.options?.resourceName, + auth?.type === "api" ? auth.metadata?.resourceName : undefined, + env["AZURE_RESOURCE_NAME"], + ].find((name) => typeof name === "string" && name.trim() !== "") }) + if (!resource && !provider.options?.baseURL) { + return { + autoload: false, + async getModel() { + throw new Error( + "AZURE_RESOURCE_NAME is missing, set it using env var or reconnecting the azure provider and setting it", + ) + }, + } + } + return { autoload: false, async getModel(sdk: any, modelID: string, options?: Record) { @@ -215,11 +229,16 @@ function custom(dep: CustomDep): Record { return sdk.responses(modelID) } }, - options: {}, - vars(_options) { - return { - ...(resource && { AZURE_RESOURCE_NAME: resource }), + options: { + resourceName: resource, + }, + vars(_options): Record { + if (resource) { + return { + AZURE_RESOURCE_NAME: resource, + } } + return {} }, } }),