summaryrefslogtreecommitdiffhomepage
path: root/packages/opencode/src/cli/cmd/provider.ts
blob: 49b10e85dd9eb7dbdca32b3277e5f71fafba2900 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import { App } from "../../app/app"
import { AuthAnthropic } from "../../auth/anthropic"
import { AuthKeys } from "../../auth/keys"
import { cmd } from "./cmd"
import * as prompts from "@clack/prompts"
import open from "open"
import { VERSION } from "../version"
import { Provider } from "../../provider/provider"

export const ProviderCommand = cmd({
  command: "provider",
  builder: (yargs) =>
    yargs
      .command(ProviderAddCommand)
      .command(ProviderListCommand)
      .demandCommand(),
  describe: "initialize opencode",
  async handler() {},
})

export const ProviderListCommand = cmd({
  command: "list",
  aliases: ["ls"],
  describe: "list providers",
  async handler() {
    await App.provide({ cwd: process.cwd(), version: VERSION }, async () => {
      prompts.intro("Providers")
      const providers = await Provider.list().then((x) => Object.values(x))
      for (const value of providers) {
        prompts.log.success(value.info.name + " (" + value.source + ")")
      }
      prompts.outro(`${providers.length} configured`)
    })
  },
})

export const ProviderAddCommand = cmd({
  command: "add",
  describe: "add credentials for various providers",
  async handler() {
    await App.provide({ cwd: process.cwd(), version: VERSION }, async () => {
      const providers = await Provider.list()
      prompts.intro("Add provider")
      const provider = await prompts.select({
        message: "Select",
        maxItems: 2,
        options: [
          {
            label: "Anthropic",
            value: "anthropic",
            hint: providers["anthropic"] ? "configured" : "",
          },
          {
            label: "OpenAI",
            value: "openai",
            hint: providers["openai"] ? "configured" : "",
          },
          {
            label: "Google",
            value: "google",
            hint: providers["google"] ? "configured" : "",
          },
        ],
      })
      if (prompts.isCancel(provider)) return

      if (provider === "anthropic") {
        const method = await prompts.select({
          message: "Login method",
          options: [
            {
              label: "Claude Pro/Max",
              value: "oauth",
            },
            {
              label: "API Key",
              value: "api",
            },
          ],
        })
        if (prompts.isCancel(method)) return

        if (method === "oauth") {
          // some weird bug where program exits without this
          await new Promise((resolve) => setTimeout(resolve, 10))
          const { url, verifier } = await AuthAnthropic.authorize()
          prompts.note("Opening browser...")
          await open(url)
          prompts.log.info(url)

          const code = await prompts.text({
            message: "Paste the authorization code here: ",
            validate: (x) => (x.length > 0 ? undefined : "Required"),
          })
          if (prompts.isCancel(code)) return
          await AuthAnthropic.exchange(code, verifier)
            .then(() => {
              prompts.log.success("Login successful")
            })
            .catch(() => {
              prompts.log.error("Invalid code")
            })
          prompts.outro("Done")
          return
        }
      }

      const key = await prompts.password({
        message: "Enter your API key",
        validate: (x) => (x.length > 0 ? undefined : "Required"),
      })
      if (prompts.isCancel(key)) return
      await AuthKeys.set(provider, key)

      prompts.outro("Done")
    })
  },
})