mirror of
https://github.com/sst/opencode.git
synced 2025-12-23 10:11:41 +00:00
tui onboarding
This commit is contained in:
parent
13ffccec1f
commit
913cf0dc5e
7 changed files with 116 additions and 18 deletions
|
|
@ -2,8 +2,7 @@ import { createMemo } from "solid-js"
|
|||
import { useSync } from "@tui/context/sync"
|
||||
import { map, pipe, sortBy } from "remeda"
|
||||
import { DialogSelect } from "@tui/ui/dialog-select"
|
||||
import { useDialog } from "@tui/ui/dialog"
|
||||
import { DialogPrompt } from "../ui/dialog-prompt"
|
||||
import { Dialog, useDialog } from "@tui/ui/dialog"
|
||||
import { useSDK } from "../context/sdk"
|
||||
|
||||
const PROVIDER_PRIORITY: Record<string, number> = {
|
||||
|
|
@ -33,20 +32,28 @@ export function createDialogProviderOptions() {
|
|||
anthropic: "Claude Max or API key",
|
||||
}[provider.id],
|
||||
async onSelect() {
|
||||
const key = await DialogPrompt.show(dialog, "Enter API key")
|
||||
if (!key) return
|
||||
await sdk.client.auth.set({
|
||||
path: {
|
||||
id: provider.id,
|
||||
},
|
||||
body: {
|
||||
type: "api",
|
||||
key,
|
||||
},
|
||||
})
|
||||
await sdk.client.instance.dispose()
|
||||
await sync.bootstrap()
|
||||
dialog.clear()
|
||||
const methods = sync.data.provider_auth[provider.id]
|
||||
let method = methods[0]?.type ?? "api"
|
||||
if (methods.length > 1) {
|
||||
const index = await new Promise<number | null>((resolve) => {
|
||||
dialog.replace(
|
||||
() => (
|
||||
<DialogSelect
|
||||
title="Select auth method"
|
||||
options={methods.map((x, index) => ({
|
||||
title: x.label,
|
||||
value: index,
|
||||
category: "Method",
|
||||
}))}
|
||||
onSelect={(option) => resolve(option.value)}
|
||||
/>
|
||||
),
|
||||
() => resolve(null),
|
||||
)
|
||||
})
|
||||
if (!index) return
|
||||
method = methods[index].type
|
||||
}
|
||||
},
|
||||
})),
|
||||
sortBy((x) => PROVIDER_PRIORITY[x.value] ?? 99),
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||
provider: Provider[]
|
||||
provider_default: Record<string, string>
|
||||
provider_next: ProviderListResponse
|
||||
provider_auth: Record<string, { type: string; label: string }[]>
|
||||
agent: Agent[]
|
||||
command: Command[]
|
||||
permission: {
|
||||
|
|
@ -63,6 +64,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||
default: {},
|
||||
connected: [],
|
||||
},
|
||||
provider_auth: {},
|
||||
config: {},
|
||||
status: "loading",
|
||||
agent: [],
|
||||
|
|
@ -271,6 +273,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({
|
|||
sdk.client.mcp.status().then((x) => setStore("mcp", x.data!)),
|
||||
sdk.client.formatter.status().then((x) => setStore("formatter", x.data!)),
|
||||
sdk.client.session.status().then((x) => setStore("session_status", x.data!)),
|
||||
sdk.client.provider.auth().then((x) => setStore("provider_auth", x.data ?? {})),
|
||||
]).then(() => {
|
||||
setStore("status", "complete")
|
||||
})
|
||||
|
|
|
|||
34
packages/opencode/src/provider/auth.ts
Normal file
34
packages/opencode/src/provider/auth.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { Instance } from "@/project/instance"
|
||||
import { Plugin } from "../plugin"
|
||||
import { map, filter, pipe, fromEntries, mapValues } from "remeda"
|
||||
import z from "zod"
|
||||
|
||||
export namespace ProviderAuth {
|
||||
const state = Instance.state(async () => {
|
||||
const result = pipe(
|
||||
await Plugin.list(),
|
||||
filter((x) => x.auth?.provider !== undefined),
|
||||
map((x) => [x.auth!.provider, x.auth!] as const),
|
||||
fromEntries(),
|
||||
)
|
||||
return result
|
||||
})
|
||||
|
||||
export const Method = z.object({
|
||||
type: z.union([z.literal("oauth"), z.literal("api")]),
|
||||
label: z.string(),
|
||||
})
|
||||
export type Method = z.infer<typeof Method>
|
||||
|
||||
export async function methods() {
|
||||
const s = await state()
|
||||
return mapValues(s, (x) =>
|
||||
x.methods.map(
|
||||
(y): Method => ({
|
||||
type: y.type,
|
||||
label: y.label,
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import z from "zod"
|
||||
import path from "path"
|
||||
import { Config } from "../config/config"
|
||||
import { mergeDeep, sortBy } from "remeda"
|
||||
import { NoSuchModelError, type LanguageModel, type Provider as SDK } from "ai"
|
||||
|
|
@ -10,7 +9,6 @@ import { ModelsDev } from "./models"
|
|||
import { NamedError } from "../util/error"
|
||||
import { Auth } from "../auth"
|
||||
import { Instance } from "../project/instance"
|
||||
import { Global } from "../global"
|
||||
import { Flag } from "../flag/flag"
|
||||
import { iife } from "@/util/iife"
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import { Instance } from "../project/instance"
|
|||
import { Agent } from "../agent/agent"
|
||||
import { Auth } from "../auth"
|
||||
import { Command } from "../command"
|
||||
import { ProviderAuth } from "../provider/auth"
|
||||
import { Global } from "../global"
|
||||
import { ProjectRoute } from "./project"
|
||||
import { ToolRegistry } from "../tool/registry"
|
||||
|
|
@ -1216,6 +1217,26 @@ export namespace Server {
|
|||
})
|
||||
},
|
||||
)
|
||||
.get(
|
||||
"/provider/auth",
|
||||
describeRoute({
|
||||
description: "Get provider authentication methods",
|
||||
operationId: "provider.auth",
|
||||
responses: {
|
||||
200: {
|
||||
description: "Provider auth methods",
|
||||
content: {
|
||||
"application/json": {
|
||||
schema: resolver(z.record(z.string(), z.array(ProviderAuth.Method))),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
async (c) => {
|
||||
return c.json(await ProviderAuth.methods())
|
||||
},
|
||||
)
|
||||
.get(
|
||||
"/find",
|
||||
describeRoute({
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ import type {
|
|||
ConfigProvidersResponses,
|
||||
ProviderListData,
|
||||
ProviderListResponses,
|
||||
ProviderAuthData,
|
||||
ProviderAuthResponses,
|
||||
FindTextData,
|
||||
FindTextResponses,
|
||||
FindFilesData,
|
||||
|
|
@ -580,6 +582,16 @@ class Provider extends _HeyApiClient {
|
|||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get provider authentication methods
|
||||
*/
|
||||
public auth<ThrowOnError extends boolean = false>(options?: Options<ProviderAuthData, ThrowOnError>) {
|
||||
return (options?.client ?? this._client).get<ProviderAuthResponses, unknown, ThrowOnError>({
|
||||
url: "/provider/auth",
|
||||
...options,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Find extends _HeyApiClient {
|
||||
|
|
|
|||
|
|
@ -2524,6 +2524,29 @@ export type ProviderListResponses = {
|
|||
|
||||
export type ProviderListResponse = ProviderListResponses[keyof ProviderListResponses]
|
||||
|
||||
export type ProviderAuthData = {
|
||||
body?: never
|
||||
path?: never
|
||||
query?: {
|
||||
directory?: string
|
||||
}
|
||||
url: "/provider/auth"
|
||||
}
|
||||
|
||||
export type ProviderAuthResponses = {
|
||||
/**
|
||||
* Provider auth methods
|
||||
*/
|
||||
200: {
|
||||
[key: string]: Array<{
|
||||
type: "oauth" | "api"
|
||||
label: string
|
||||
}>
|
||||
}
|
||||
}
|
||||
|
||||
export type ProviderAuthResponse = ProviderAuthResponses[keyof ProviderAuthResponses]
|
||||
|
||||
export type FindTextData = {
|
||||
body?: never
|
||||
path?: never
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue