fix(coding-agent,ai): show env auth source in /login
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
import type { AuthCredential, AuthStatus } from "../../../core/auth-storage.js";
|
||||
|
||||
export type SelectorAuthType = "oauth" | "api_key";
|
||||
|
||||
export type AuthSelectorIndicator =
|
||||
| { kind: "configured"; label: string }
|
||||
| { kind: "configured-other"; label: string }
|
||||
| { kind: "runtime" }
|
||||
| { kind: "environment"; label: string }
|
||||
| { kind: "fallback" }
|
||||
| { kind: "unconfigured" };
|
||||
|
||||
function getConfiguredLabel(authType: SelectorAuthType): string {
|
||||
return authType === "oauth" ? "subscription configured" : "api key configured";
|
||||
}
|
||||
|
||||
export function getAuthSelectorIndicator(
|
||||
authType: SelectorAuthType,
|
||||
credential: AuthCredential | undefined,
|
||||
authStatus?: AuthStatus,
|
||||
): AuthSelectorIndicator {
|
||||
if (credential) {
|
||||
const label = getConfiguredLabel(credential.type);
|
||||
return credential.type === authType ? { kind: "configured", label } : { kind: "configured-other", label };
|
||||
}
|
||||
|
||||
if (authType === "oauth") {
|
||||
return { kind: "unconfigured" };
|
||||
}
|
||||
|
||||
if (authStatus?.source === "runtime") {
|
||||
return { kind: "runtime" };
|
||||
}
|
||||
|
||||
if (authStatus?.source === "environment") {
|
||||
return { kind: "environment", label: authStatus.label ?? "API key" };
|
||||
}
|
||||
|
||||
if (authStatus?.source === "fallback") {
|
||||
return { kind: "fallback" };
|
||||
}
|
||||
|
||||
return { kind: "unconfigured" };
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "@mariozechner/pi-tui";
|
||||
import type { AuthStorage } from "../../../core/auth-storage.js";
|
||||
import { theme } from "../theme/theme.js";
|
||||
import { getAuthSelectorIndicator } from "./auth-selector-status.js";
|
||||
import { DynamicBorder } from "./dynamic-border.js";
|
||||
|
||||
export type AuthSelectorProvider = {
|
||||
@@ -114,11 +115,7 @@ export class OAuthSelectorComponent extends Container implements Focusable {
|
||||
|
||||
const isSelected = i === this.selectedIndex;
|
||||
|
||||
// Check if user is configured for this provider
|
||||
const credentials = this.authStorage.get(provider.id);
|
||||
const statusIndicator = credentials
|
||||
? theme.fg("success", " ✓ configured")
|
||||
: theme.fg("muted", " • unconfigured");
|
||||
const statusIndicator = this.formatStatusIndicator(provider);
|
||||
let line = "";
|
||||
if (isSelected) {
|
||||
const prefix = theme.fg("accent", "→ ");
|
||||
@@ -149,6 +146,32 @@ export class OAuthSelectorComponent extends Container implements Focusable {
|
||||
}
|
||||
}
|
||||
|
||||
private formatStatusIndicator(provider: AuthSelectorProvider): string {
|
||||
const status = provider.authType === "api_key" ? this.authStorage.getAuthStatus(provider.id) : undefined;
|
||||
const indicator = getAuthSelectorIndicator(provider.authType, this.authStorage.get(provider.id), status);
|
||||
|
||||
if (indicator.kind === "configured") {
|
||||
return theme.fg("success", ` ✓ ${indicator.label}`);
|
||||
}
|
||||
|
||||
if (indicator.kind === "configured-other") {
|
||||
return theme.fg("muted", " • ") + theme.fg("warning", indicator.label);
|
||||
}
|
||||
|
||||
const base = theme.fg("muted", " • unconfigured");
|
||||
if (indicator.kind === "environment") {
|
||||
return base + theme.fg("success", ` · env: ${indicator.label}`);
|
||||
}
|
||||
if (indicator.kind === "runtime") {
|
||||
return base + theme.fg("success", " · runtime API key");
|
||||
}
|
||||
if (indicator.kind === "fallback") {
|
||||
return base + theme.fg("success", " · custom API key");
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
handleInput(keyData: string): void {
|
||||
const kb = getKeybindings();
|
||||
// Up arrow
|
||||
|
||||
@@ -8,7 +8,14 @@ import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||
import type { AssistantMessage, ImageContent, Message, Model, OAuthProviderId } from "@mariozechner/pi-ai";
|
||||
import {
|
||||
type AssistantMessage,
|
||||
getProviders,
|
||||
type ImageContent,
|
||||
type Message,
|
||||
type Model,
|
||||
type OAuthProviderId,
|
||||
} from "@mariozechner/pi-ai";
|
||||
import type {
|
||||
AutocompleteItem,
|
||||
AutocompleteProvider,
|
||||
@@ -168,7 +175,8 @@ function hasDefaultModelProvider(providerId: string): providerId is keyof typeof
|
||||
return providerId in defaultModelPerProvider;
|
||||
}
|
||||
|
||||
const API_KEY_PROVIDER_NAMES: Record<string, string> = {
|
||||
const API_KEY_LOGIN_PROVIDERS: Record<string, string> = {
|
||||
anthropic: "Anthropic",
|
||||
"azure-openai-responses": "Azure OpenAI Responses",
|
||||
cerebras: "Cerebras",
|
||||
fireworks: "Fireworks",
|
||||
@@ -189,10 +197,25 @@ const API_KEY_PROVIDER_NAMES: Record<string, string> = {
|
||||
zai: "ZAI",
|
||||
};
|
||||
|
||||
const API_KEY_LOGIN_PROVIDER_BLOCKLIST = new Set(["amazon-bedrock", "llama.cpp", "lmstudio", "ollama"]);
|
||||
const BUILT_IN_API_KEY_LOGIN_PROVIDERS = new Set(Object.keys(API_KEY_LOGIN_PROVIDERS));
|
||||
const BUILT_IN_MODEL_PROVIDERS = new Set<string>(getProviders());
|
||||
|
||||
function getApiKeyProviderDisplayName(providerId: string): string {
|
||||
return API_KEY_PROVIDER_NAMES[providerId] ?? providerId;
|
||||
export function isApiKeyLoginProvider(
|
||||
providerId: string,
|
||||
oauthProviderIds: ReadonlySet<string>,
|
||||
builtInProviderIds: ReadonlySet<string> = BUILT_IN_MODEL_PROVIDERS,
|
||||
): boolean {
|
||||
if (BUILT_IN_API_KEY_LOGIN_PROVIDERS.has(providerId)) {
|
||||
return true;
|
||||
}
|
||||
if (builtInProviderIds.has(providerId)) {
|
||||
return false;
|
||||
}
|
||||
return !oauthProviderIds.has(providerId);
|
||||
}
|
||||
|
||||
export function getApiKeyProviderDisplayName(providerId: string): string {
|
||||
return API_KEY_LOGIN_PROVIDERS[providerId] ?? providerId;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -4325,7 +4348,7 @@ export class InteractiveMode {
|
||||
|
||||
const modelProviders = new Set(this.session.modelRegistry.getAll().map((model) => model.provider));
|
||||
for (const providerId of modelProviders) {
|
||||
if (oauthProviderIds.has(providerId) || API_KEY_LOGIN_PROVIDER_BLOCKLIST.has(providerId)) {
|
||||
if (!isApiKeyLoginProvider(providerId, oauthProviderIds)) {
|
||||
continue;
|
||||
}
|
||||
options.push({
|
||||
|
||||
Reference in New Issue
Block a user