diff --git a/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts b/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts index 0fa37ca4b..72e98689e 100644 --- a/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts @@ -82,6 +82,7 @@ import { LoginDialogComponent } from "./components/login-dialog.js"; import { ModelSelectorComponent, providerDisplayName } from "./components/model-selector.js"; import { OAuthSelectorComponent } from "./components/oauth-selector.js"; import { ProviderManagerComponent } from "./components/provider-manager.js"; +import { getProviderSetupAction } from "./provider-auth-setup.js"; import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js"; import { SessionSelectorComponent } from "./components/session-selector.js"; import { SettingsSelectorComponent } from "./components/settings-selector.js"; @@ -3412,9 +3413,21 @@ export class InteractiveMode { this.ui.requestRender(); }, async (provider: string) => { - // Enter key → auth setup for selected provider (#3579) done(); - await this.showLoginDialog(provider); + + const action = getProviderSetupAction({ + provider, + authMode: this.session.modelRegistry.getProviderAuthMode(provider), + hasAuth: this.session.modelRegistry.authStorage.hasAuth(provider), + }); + + if (action.kind === "oauth-login") { + await this.showLoginDialog(provider); + return; + } + + this.showStatus(action.message); + this.ui.requestRender(); }, ); return { component, focus: component }; diff --git a/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts b/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts new file mode 100644 index 000000000..6f22384a5 --- /dev/null +++ b/packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts @@ -0,0 +1,40 @@ +import type { ProviderAuthMode } from "../../core/model-registry.js"; + +export type ProviderSetupAction = + | { kind: "oauth-login" } + | { kind: "status"; message: string }; + +export function getProviderSetupAction(options: { + provider: string; + authMode: ProviderAuthMode; + hasAuth: boolean; +}): ProviderSetupAction { + const { provider, authMode, hasAuth } = options; + + if (authMode === "oauth") { + return { kind: "oauth-login" }; + } + + if (authMode === "none") { + return { + kind: "status", + message: `${provider} does not need auth setup. Use /model to select it.`, + }; + } + + if (authMode === "externalCli") { + return { + kind: "status", + message: hasAuth + ? `${provider} is already authenticated. Use /model to select it.` + : `${provider} uses external CLI auth. Sign in with the provider CLI, then use /model.`, + }; + } + + return { + kind: "status", + message: hasAuth + ? `${provider} already has credentials configured. Use /model to select it.` + : `${provider} uses API-key auth, not OAuth. Configure its credentials, then use /model.`, + }; +} diff --git a/src/tests/provider-auth-setup.test.ts b/src/tests/provider-auth-setup.test.ts new file mode 100644 index 000000000..26b9ff4c1 --- /dev/null +++ b/src/tests/provider-auth-setup.test.ts @@ -0,0 +1,43 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +const { getProviderSetupAction } = await import( + "../../packages/pi-coding-agent/src/modes/interactive/provider-auth-setup.ts" +); + +test("routes OAuth providers to the login dialog", () => { + const action = getProviderSetupAction({ + provider: "github-copilot", + authMode: "oauth", + hasAuth: false, + }); + + assert.deepEqual(action, { kind: "oauth-login" }); +}); + +test("keeps API-key providers out of the OAuth login flow", () => { + for (const provider of ["alibaba-coding-plan", "zai", "xai"]) { + const action = getProviderSetupAction({ + provider, + authMode: "apiKey", + hasAuth: false, + }); + + assert.equal(action.kind, "status"); + assert.match(action.message, /API-key auth, not OAuth/); + assert.match(action.message, new RegExp(provider)); + } +}); + +test("tells already-configured API-key providers to use model selection", () => { + const action = getProviderSetupAction({ + provider: "xai", + authMode: "apiKey", + hasAuth: true, + }); + + assert.deepEqual(action, { + kind: "status", + message: "xai already has credentials configured. Use /model to select it.", + }); +});