Migrate authProviderOptions to ModelRuntime API

Rederive login/logout provider options from runtime.getProviders() +
listCredentials() + getProviderAuthStatus() instead of the removed
authStorage.getOAuthProviders()/list()/get() + getAll()/
getProviderDisplayName() surface (Pi 0.80.8+).

- Replace the AuthProviderModelRegistry structural interface with a
  runtime-shaped AuthProviderRuntime (getProviders/listCredentials/
  getProviderAuthStatus); a real ModelRuntime satisfies it.
- Make getLoginProviderOptions/getLogoutProviderOptions async to match
  the await call sites already in authService.ts.
- OAuth-capable providers = auth.oauth present; api-key providers =
  auth.apiKey present, preserving OAUTH_ONLY_PROVIDERS /
  isApiKeyLoginProvider logic. Display names from Provider.name.
- Update the test double to the new runtime shape.

Slice 2 of the authStorage migration relay. tsc: 31 -> 28 errors
(remaining are cross-slice: slices 3/4/5).
This commit is contained in:
Federico Jaramillo Martinez
2026-07-17 20:58:46 +02:00
parent 842e651658
commit d09d7cc1ff
2 changed files with 65 additions and 56 deletions
+49 -31
View File
@@ -2,51 +2,69 @@ import type { AuthProviderOption, AuthProviderStatus, AuthType } from "../../sha
const OAUTH_ONLY_PROVIDERS = new Set(["github-copilot", "openai-codex"]);
export interface AuthProviderModelRegistry {
authStorage: {
getOAuthProviders(): { id: string; name: string }[];
list(): string[];
get(provider: string): { type: AuthType } | undefined;
};
getAll(): { provider: string }[];
getProviderDisplayName(provider: string): string;
getProviderAuthStatus(provider: string): AuthProviderStatus;
/** Minimal provider shape needed to enumerate login/logout options. */
interface AuthProviderInfo {
id: string;
name: string;
auth: { apiKey?: unknown; oauth?: unknown };
}
export function getLoginProviderOptions(modelRegistry: AuthProviderModelRegistry, authType?: AuthType): AuthProviderOption[] {
const oauthProviders = modelRegistry.authStorage.getOAuthProviders();
const oauthProviderIds = new Set(oauthProviders.map((provider) => provider.id));
const options: AuthProviderOption[] = oauthProviders.map((provider) => ({
id: provider.id,
name: provider.name,
authType: "oauth",
status: modelRegistry.getProviderAuthStatus(provider.id),
}));
/** Non-secret stored-credential metadata, keyed by provider id. */
interface AuthProviderCredentialInfo {
providerId: string;
type: AuthType;
}
const modelProviders = new Set(modelRegistry.getAll().map((model) => model.provider));
for (const providerId of modelProviders) {
if (!isApiKeyLoginProvider(providerId, oauthProviderIds)) continue;
/**
* Structural slice of the SDK `ModelRuntime` used to derive auth provider
* options. Kept structural (rather than `Pick<ModelRuntime, ...>`) so tests can
* supply a lightweight double without constructing a full runtime; a real
* `ModelRuntime` satisfies it.
*/
export interface AuthProviderRuntime {
getProviders(): readonly AuthProviderInfo[];
listCredentials(): Promise<readonly AuthProviderCredentialInfo[]>;
getProviderAuthStatus(providerId: string): AuthProviderStatus;
}
export async function getLoginProviderOptions(runtime: AuthProviderRuntime, authType?: AuthType): Promise<AuthProviderOption[]> {
const providers = runtime.getProviders();
const oauthProviderIds = new Set(providers.filter((provider) => provider.auth.oauth !== undefined).map((provider) => provider.id));
const options: AuthProviderOption[] = [];
for (const provider of providers) {
if (provider.auth.oauth === undefined) continue;
options.push({
id: providerId,
name: modelRegistry.getProviderDisplayName(providerId),
id: provider.id,
name: provider.name,
authType: "oauth",
status: runtime.getProviderAuthStatus(provider.id),
});
}
for (const provider of providers) {
if (provider.auth.apiKey === undefined) continue;
if (!isApiKeyLoginProvider(provider.id, oauthProviderIds)) continue;
options.push({
id: provider.id,
name: provider.name,
authType: "api_key",
status: modelRegistry.getProviderAuthStatus(providerId),
status: runtime.getProviderAuthStatus(provider.id),
});
}
return filterAndSort(options, authType);
}
export function getLogoutProviderOptions(modelRegistry: AuthProviderModelRegistry): AuthProviderOption[] {
export async function getLogoutProviderOptions(runtime: AuthProviderRuntime): Promise<AuthProviderOption[]> {
const providerNames = new Map(runtime.getProviders().map((provider) => [provider.id, provider.name]));
const options: AuthProviderOption[] = [];
for (const providerId of modelRegistry.authStorage.list()) {
const credential = modelRegistry.authStorage.get(providerId);
if (credential === undefined) continue;
for (const credential of await runtime.listCredentials()) {
options.push({
id: providerId,
name: modelRegistry.getProviderDisplayName(providerId),
id: credential.providerId,
name: providerNames.get(credential.providerId) ?? credential.providerId,
authType: credential.type,
status: modelRegistry.getProviderAuthStatus(providerId),
status: runtime.getProviderAuthStatus(credential.providerId),
});
}
return filterAndSort(options);