Просмотр исходного кода

experimental well-known auth support

Dax Raad 6 месяцев назад
Родитель
Сommit
9bedd62da4

+ 7 - 1
packages/opencode/src/auth/index.ts

@@ -16,7 +16,13 @@ export namespace Auth {
     key: z.string(),
   })
 
-  export const Info = z.discriminatedUnion("type", [Oauth, Api])
+  export const WellKnown = z.object({
+    type: z.literal("wellknown"),
+    key: z.string(),
+    token: z.string(),
+  })
+
+  export const Info = z.discriminatedUnion("type", [Oauth, Api, WellKnown])
   export type Info = z.infer<typeof Info>
 
   const filepath = path.join(Global.Path.data, "auth.json")

+ 33 - 8
packages/opencode/src/cli/cmd/auth.ts

@@ -16,7 +16,7 @@ export const AuthCommand = cmd({
   describe: "manage credentials",
   builder: (yargs) =>
     yargs.command(AuthLoginCommand).command(AuthLogoutCommand).command(AuthListCommand).demandCommand(),
-  async handler() { },
+  async handler() {},
 })
 
 export const AuthListCommand = cmd({
@@ -61,20 +61,45 @@ export const AuthListCommand = cmd({
         prompts.log.info(`${provider} ${UI.Style.TEXT_DIM}${envVar}`)
       }
 
-      prompts.outro(
-        `${activeEnvVars.length} environment variable`
-        + (activeEnvVars.length === 1 ? "" : "s")
-      )
+      prompts.outro(`${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s"))
     }
   },
 })
 
 export const AuthLoginCommand = cmd({
-  command: "login",
+  command: "login [url]",
   describe: "log in to a provider",
-  async handler() {
-    UI.empty()
+  builder: (yargs) =>
+    yargs.positional("url", {
+      describe: "opencode auth provider",
+      type: "string",
+    }),
+  async handler(args) {
     prompts.intro("Add credential")
+    if (args.url) {
+      const wellknown = await fetch(`${args.url}/.well-known/opencode`).then((x) => x.json())
+      prompts.log.info(`Running \`${wellknown.auth.command.join(" ")}\``)
+      const proc = Bun.spawn({
+        cmd: wellknown.auth.command,
+        stdout: "pipe",
+      })
+      const exit = await proc.exited
+      if (exit !== 0) {
+        prompts.log.error("Failed")
+        prompts.outro("Done")
+        return
+      }
+      const token = await new Response(proc.stdout).text()
+      await Auth.set(args.url, {
+        type: "wellknown",
+        key: wellknown.auth.env,
+        token: token.trim(),
+      })
+      prompts.log.success("Logged into " + args.url)
+      prompts.outro("Done")
+      return
+    }
+    UI.empty()
     const providers = await ModelsDev.get()
     const priority: Record<string, number> = {
       anthropic: 0,

+ 13 - 0
packages/opencode/src/config/config.ts

@@ -11,11 +11,13 @@ import { lazy } from "../util/lazy"
 import { NamedError } from "../util/error"
 import matter from "gray-matter"
 import { Flag } from "../flag/flag"
+import { Auth } from "../auth"
 
 export namespace Config {
   const log = Log.create({ service: "config" })
 
   export const state = App.state("config", async (app) => {
+    const auth = await Auth.all()
     let result = await global()
     for (const file of ["opencode.jsonc", "opencode.json"]) {
       const found = await Filesystem.findUp(file, app.path.cwd, app.path.root)
@@ -30,6 +32,14 @@ export namespace Config {
       log.debug("loaded custom config", { path: Flag.OPENCODE_CONFIG })
     }
 
+    for (const [key, value] of Object.entries(auth)) {
+      if (value.type === "wellknown") {
+        process.env[value.key] = value.token
+        const wellknown = await fetch(`${key}/.well-known/opencode`).then((x) => x.json())
+        result = mergeDeep(result, await loadRaw(JSON.stringify(wellknown.config ?? {}), process.cwd()))
+      }
+    }
+
     result.agent = result.agent || {}
     const markdownAgents = [
       ...(await Filesystem.globUp("agent/*.md", Global.Path.config, Global.Path.config)),
@@ -307,7 +317,10 @@ export namespace Config {
         throw new JsonError({ path: configPath }, { cause: err })
       })
     if (!text) return {}
+    return loadRaw(text, configPath)
+  }
 
+  async function loadRaw(text: string, configPath: string) {
     text = text.replace(/\{env:([^}]+)\}/g, (_, varName) => {
       return process.env[varName] || ""
     })