diff --git a/AGENTS.md b/AGENTS.md index 758714d10a..074a9305f3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -111,3 +111,7 @@ const table = sqliteTable("session", { - Avoid mocks as much as possible - Test actual implementation, do not duplicate logic into tests - Tests cannot run from repo root (guard: `do-not-run-tests-from-root`); run from package dirs like `packages/opencode`. + +## Type Checking + +- Always run `bun typecheck` from package directories (e.g., `packages/opencode`), never `tsc` directly. diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/providers.ts similarity index 93% rename from packages/opencode/src/cli/cmd/auth.ts rename to packages/opencode/src/cli/cmd/providers.ts index 9563591641..499a6f0e36 100644 --- a/packages/opencode/src/cli/cmd/auth.ts +++ b/packages/opencode/src/cli/cmd/providers.ts @@ -16,10 +16,6 @@ import { text } from "node:stream/consumers" type PluginAuth = NonNullable -/** - * Handle plugin-based authentication flow. - * Returns true if auth was handled, false if it should fall through to default handling. - */ async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string): Promise { let index = 0 if (plugin.auth.methods.length > 1) { @@ -37,7 +33,6 @@ async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string): } const method = plugin.auth.methods[index] - // Handle prompts for all auth types await Bun.sleep(10) const inputs: Record = {} if (method.prompts) { @@ -161,11 +156,6 @@ async function handlePluginAuth(plugin: { auth: PluginAuth }, provider: string): return false } -/** - * Build a deduplicated list of plugin-registered auth providers that are not - * already present in models.dev, respecting enabled/disabled provider lists. - * Pure function with no side effects; safe to test without mocking. - */ export function resolvePluginProviders(input: { hooks: Hooks[] existingProviders: Record @@ -193,19 +183,20 @@ export function resolvePluginProviders(input: { return result } -export const AuthCommand = cmd({ - command: "auth", - describe: "manage credentials", +export const ProvidersCommand = cmd({ + command: "providers", + aliases: ["auth"], + describe: "manage AI providers and credentials", builder: (yargs) => - yargs.command(AuthLoginCommand).command(AuthLogoutCommand).command(AuthListCommand).demandCommand(), + yargs.command(ProvidersListCommand).command(ProvidersLoginCommand).command(ProvidersLogoutCommand).demandCommand(), async handler() {}, }) -export const AuthListCommand = cmd({ +export const ProvidersListCommand = cmd({ command: "list", aliases: ["ls"], - describe: "list providers", - async handler() { + describe: "list providers and credentials", + async handler(_args) { UI.empty() const authPath = path.join(Global.Path.data, "auth.json") const homedir = os.homedir() @@ -221,7 +212,6 @@ export const AuthListCommand = cmd({ prompts.outro(`${results.length} credentials`) - // Environment variables section const activeEnvVars: Array<{ provider: string; envVar: string }> = [] for (const [providerID, provider] of Object.entries(database)) { @@ -248,7 +238,7 @@ export const AuthListCommand = cmd({ }, }) -export const AuthLoginCommand = cmd({ +export const ProvidersLoginCommand = cmd({ command: "login [url]", describe: "log in to a provider", builder: (yargs) => @@ -371,7 +361,6 @@ export const AuthLoginCommand = cmd({ provider = provider.replace(/^@ai-sdk\//, "") if (prompts.isCancel(provider)) throw new UI.CancelledError() - // Check if a plugin provides auth for this custom provider const customPlugin = await Plugin.list().then((x) => x.findLast((x) => x.auth?.provider === provider)) if (customPlugin && customPlugin.auth) { const handled = await handlePluginAuth({ auth: customPlugin.auth }, provider) @@ -423,10 +412,10 @@ export const AuthLoginCommand = cmd({ }, }) -export const AuthLogoutCommand = cmd({ +export const ProvidersLogoutCommand = cmd({ command: "logout", describe: "log out from a configured provider", - async handler() { + async handler(_args) { UI.empty() const credentials = await Auth.all().then((x) => Object.entries(x)) prompts.intro("Remove credential") diff --git a/packages/opencode/src/index.ts b/packages/opencode/src/index.ts index 35b42dce77..66f5e4c832 100644 --- a/packages/opencode/src/index.ts +++ b/packages/opencode/src/index.ts @@ -3,7 +3,7 @@ import { hideBin } from "yargs/helpers" import { RunCommand } from "./cli/cmd/run" import { GenerateCommand } from "./cli/cmd/generate" import { Log } from "./util/log" -import { AuthCommand } from "./cli/cmd/auth" +import { ProvidersCommand } from "./cli/cmd/providers" import { AgentCommand } from "./cli/cmd/agent" import { UpgradeCommand } from "./cli/cmd/upgrade" import { UninstallCommand } from "./cli/cmd/uninstall" @@ -129,7 +129,7 @@ let cli = yargs(hideBin(process.argv)) .command(RunCommand) .command(GenerateCommand) .command(DebugCommand) - .command(AuthCommand) + .command(ProvidersCommand) .command(AgentCommand) .command(UpgradeCommand) .command(UninstallCommand) diff --git a/packages/opencode/test/cli/plugin-auth-picker.test.ts b/packages/opencode/test/cli/plugin-auth-picker.test.ts index 3ce9094e92..5a1cf059d5 100644 --- a/packages/opencode/test/cli/plugin-auth-picker.test.ts +++ b/packages/opencode/test/cli/plugin-auth-picker.test.ts @@ -1,5 +1,5 @@ import { test, expect, describe } from "bun:test" -import { resolvePluginProviders } from "../../src/cli/cmd/auth" +import { resolvePluginProviders } from "../../src/cli/cmd/providers" import type { Hooks } from "@opencode-ai/plugin" function hookWithAuth(provider: string): Hooks { diff --git a/turbo.json b/turbo.json index ba3d01d360..dba35379d8 100644 --- a/turbo.json +++ b/turbo.json @@ -4,7 +4,7 @@ "globalPassThroughEnv": ["CI", "OPENCODE_DISABLE_SHARE"], "tasks": { "typecheck": { - "dependsOn": ["^build"] + "dependsOn": [] }, "build": { "dependsOn": ["^build"],