Skip to content

Commit f363904

Browse files
dbpolitonexxeln
andauthored
feat(opencode): Adding options to auth login to skip questions (#14470)
Co-authored-by: Shoubhit Dash <[email protected]>
1 parent 85ff056 commit f363904

1 file changed

Lines changed: 81 additions & 45 deletions

File tree

  • packages/opencode/src/cli/cmd

packages/opencode/src/cli/cmd/auth.ts

Lines changed: 81 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,19 @@ type PluginAuth = NonNullable<Hooks["auth"]>
2020
* Handle plugin-based authentication flow.
2121
* Returns true if auth was handled, false if it should fall through to default handling.
2222
*/
23-
async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string): Promise<boolean> {
23+
async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string, methodName?: string): Promise<boolean> {
2424
let index = 0
25-
if (plugin.auth.methods.length > 1) {
26-
const method = await prompts.select({
25+
if (methodName) {
26+
const match = plugin.auth.methods.findIndex((x) => x.label.toLowerCase() === methodName.toLowerCase())
27+
if (match === -1) {
28+
prompts.log.error(
29+
`Unknown method "${methodName}" for ${provider}. Available: ${plugin.auth.methods.map((x) => x.label).join(", ")}`,
30+
)
31+
process.exit(1)
32+
}
33+
index = match
34+
} else if (plugin.auth.methods.length > 1) {
35+
const selected = await prompts.select({
2736
message: "Login method",
2837
options: [
2938
...plugin.auth.methods.map((x, index) => ({
@@ -32,8 +41,8 @@ async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string):
3241
})),
3342
],
3443
})
35-
if (prompts.isCancel(method)) throw new UI.CancelledError()
36-
index = parseInt(method)
44+
if (prompts.isCancel(selected)) throw new UI.CancelledError()
45+
index = parseInt(selected)
3746
}
3847
const method = plugin.auth.methods[index]
3948

@@ -252,10 +261,21 @@ export const AuthLoginCommand = cmd({
252261
command: "login [url]",
253262
describe: "log in to a provider",
254263
builder: (yargs) =>
255-
yargs.positional("url", {
256-
describe: "opencode auth provider",
257-
type: "string",
258-
}),
264+
yargs
265+
.positional("url", {
266+
describe: "opencode auth provider",
267+
type: "string",
268+
})
269+
.option("provider", {
270+
alias: ["p"],
271+
describe: "provider id or name to log in to (skips provider selection)",
272+
type: "string",
273+
})
274+
.option("method", {
275+
alias: ["m"],
276+
describe: "login method label (skips method selection)",
277+
type: "string",
278+
}),
259279
async handler(args) {
260280
await Instance.provide({
261281
directory: process.cwd(),
@@ -322,60 +342,76 @@ export const AuthLoginCommand = cmd({
322342
enabled,
323343
providerNames: Object.fromEntries(Object.entries(config.provider ?? {}).map(([id, p]) => [id, p.name])),
324344
})
325-
let provider = await prompts.autocomplete({
326-
message: "Select provider",
327-
maxItems: 8,
328-
options: [
329-
...pipe(
330-
providers,
331-
values(),
332-
sortBy(
333-
(x) => priority[x.id] ?? 99,
334-
(x) => x.name ?? x.id,
335-
),
336-
map((x) => ({
337-
label: x.name,
338-
value: x.id,
339-
hint: {
340-
opencode: "recommended",
341-
anthropic: "Claude Max or API key",
342-
openai: "ChatGPT Plus/Pro or API key",
343-
}[x.id],
344-
})),
345+
const options = [
346+
...pipe(
347+
providers,
348+
values(),
349+
sortBy(
350+
(x) => priority[x.id] ?? 99,
351+
(x) => x.name ?? x.id,
345352
),
346-
...pluginProviders.map((x) => ({
353+
map((x) => ({
347354
label: x.name,
348355
value: x.id,
349-
hint: "plugin",
356+
hint: {
357+
opencode: "recommended",
358+
anthropic: "Claude Max or API key",
359+
openai: "ChatGPT Plus/Pro or API key",
360+
}[x.id],
350361
})),
351-
{
352-
value: "other",
353-
label: "Other",
354-
},
355-
],
356-
})
357-
358-
if (prompts.isCancel(provider)) throw new UI.CancelledError()
362+
),
363+
...pluginProviders.map((x) => ({
364+
label: x.name,
365+
value: x.id,
366+
hint: "plugin",
367+
})),
368+
]
369+
370+
let provider: string
371+
if (args.provider) {
372+
const input = args.provider
373+
const byID = options.find((x) => x.value === input)
374+
const byName = options.find((x) => x.label.toLowerCase() === input.toLowerCase())
375+
const match = byID ?? byName
376+
if (!match) {
377+
prompts.log.error(`Unknown provider "${input}"`)
378+
process.exit(1)
379+
}
380+
provider = match.value
381+
} else {
382+
const selected = await prompts.autocomplete({
383+
message: "Select provider",
384+
maxItems: 8,
385+
options: [
386+
...options,
387+
{
388+
value: "other",
389+
label: "Other",
390+
},
391+
],
392+
})
393+
if (prompts.isCancel(selected)) throw new UI.CancelledError()
394+
provider = selected as string
395+
}
359396

360397
const plugin = await Plugin.list().then((x) => x.findLast((x) => x.auth?.provider === provider))
361398
if (plugin && plugin.auth) {
362-
const handled = await handlePluginAuth({ auth: plugin.auth }, provider)
399+
const handled = await handlePluginAuth({ auth: plugin.auth }, provider, args.method)
363400
if (handled) return
364401
}
365402

366403
if (provider === "other") {
367-
provider = await prompts.text({
404+
const custom = await prompts.text({
368405
message: "Enter provider id",
369406
validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"),
370407
})
371-
if (prompts.isCancel(provider)) throw new UI.CancelledError()
372-
provider = provider.replace(/^@ai-sdk\//, "")
373-
if (prompts.isCancel(provider)) throw new UI.CancelledError()
408+
if (prompts.isCancel(custom)) throw new UI.CancelledError()
409+
provider = custom.replace(/^@ai-sdk\//, "")
374410

375411
// Check if a plugin provides auth for this custom provider
376412
const customPlugin = await Plugin.list().then((x) => x.findLast((x) => x.auth?.provider === provider))
377413
if (customPlugin && customPlugin.auth) {
378-
const handled = await handlePluginAuth({ auth: customPlugin.auth }, provider)
414+
const handled = await handlePluginAuth({ auth: customPlugin.auth }, provider, args.method)
379415
if (handled) return
380416
}
381417

0 commit comments

Comments
 (0)