Fix Zai and Minimax auth for usage
This commit is contained in:
@@ -291,6 +291,12 @@ describe("runDashboard — AuthStorage & ModelRegistry wiring", () => {
|
||||
expect(serverOpts.authStorage.setApiKey).toBeTypeOf("function");
|
||||
expect(serverOpts.authStorage.clearApiKey).toBeTypeOf("function");
|
||||
expect(serverOpts.authStorage.hasApiKey).toBeTypeOf("function");
|
||||
expect(serverOpts.authStorage.getApiKeyProviders()).toEqual([
|
||||
{ id: "kimi-coding", name: "Kimi" },
|
||||
{ id: "minimax", name: "Minimax" },
|
||||
{ id: "openrouter", name: "OpenRouter" },
|
||||
{ id: "zai", name: "Zai" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("creates AuthStorage via AuthStorage.create()", async () => {
|
||||
@@ -642,4 +648,3 @@ describe("runDashboard — per-project engine manager (multi-project)", () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -247,9 +247,18 @@ const mocks = vi.hoisted(() => {
|
||||
|
||||
const authStorage = {
|
||||
getApiKey: vi.fn().mockResolvedValue(undefined),
|
||||
reload: vi.fn(),
|
||||
getOAuthProviders: vi.fn().mockReturnValue([]),
|
||||
hasAuth: vi.fn().mockReturnValue(false),
|
||||
login: vi.fn(),
|
||||
logout: vi.fn(),
|
||||
set: vi.fn(),
|
||||
remove: vi.fn(),
|
||||
get: vi.fn(),
|
||||
};
|
||||
|
||||
const modelRegistry = {
|
||||
getAll: vi.fn().mockReturnValue([]),
|
||||
registerProvider: vi.fn(),
|
||||
refresh: vi.fn(),
|
||||
};
|
||||
|
||||
@@ -10,12 +10,11 @@ import {
|
||||
} from "./task-lifecycle.js";
|
||||
import { promptForPort } from "./port-prompt.js";
|
||||
import { createReadOnlyProviderSettingsView } from "./provider-settings.js";
|
||||
import { wrapAuthStorageWithApiKeyProviders } from "./provider-auth.js";
|
||||
|
||||
// Re-export for backward compatibility with tests
|
||||
export { promptForPort };
|
||||
|
||||
type LoginCallbacks = Parameters<AuthStorage["login"]>[1];
|
||||
|
||||
let processDiagnosticsRegistered = false;
|
||||
let diagnosticIntervalHandle: ReturnType<typeof setInterval> | null = null;
|
||||
const DIAGNOSTIC_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
|
||||
@@ -184,80 +183,6 @@ function setDiagnosticStoreListenerCheck(check: () => Record<string, number>): v
|
||||
diagnosticStoreListenerCheck = check;
|
||||
}
|
||||
|
||||
interface DashboardAuthStorage {
|
||||
reload(): void;
|
||||
getOAuthProviders(): Array<{ id: string; name: string }>;
|
||||
hasAuth(provider: string): boolean;
|
||||
login(providerId: string, callbacks: LoginCallbacks): Promise<void>;
|
||||
logout(provider: string): void;
|
||||
getApiKeyProviders(): Array<{ id: string; name: string }>;
|
||||
setApiKey(providerId: string, apiKey: string): void;
|
||||
clearApiKey(providerId: string): void;
|
||||
hasApiKey(providerId: string): boolean;
|
||||
}
|
||||
|
||||
function getProviderDisplayName(providerId: string): string {
|
||||
const knownProviderNames: Record<string, string> = {
|
||||
openrouter: "OpenRouter",
|
||||
"kimi-coding": "Kimi",
|
||||
};
|
||||
|
||||
if (knownProviderNames[providerId]) {
|
||||
return knownProviderNames[providerId];
|
||||
}
|
||||
|
||||
return providerId
|
||||
.split(/[-_]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function wrapAuthStorageWithApiKeyProviders(
|
||||
authStorage: AuthStorage,
|
||||
modelRegistry: ModelRegistry,
|
||||
): DashboardAuthStorage {
|
||||
return {
|
||||
reload: () => authStorage.reload(),
|
||||
getOAuthProviders: () =>
|
||||
authStorage
|
||||
.getOAuthProviders()
|
||||
.map((provider) => ({ id: provider.id, name: provider.name })),
|
||||
hasAuth: (provider) => authStorage.hasAuth(provider),
|
||||
login: (providerId, callbacks) =>
|
||||
authStorage.login(providerId as Parameters<AuthStorage["login"]>[0], callbacks),
|
||||
logout: (provider) => authStorage.logout(provider),
|
||||
getApiKeyProviders: () => {
|
||||
const oauthProviderIds = new Set(
|
||||
authStorage.getOAuthProviders().map((provider) => provider.id),
|
||||
);
|
||||
const providers = new Map<string, string>();
|
||||
|
||||
for (const model of modelRegistry.getAll()) {
|
||||
const providerId = model.provider;
|
||||
if (!providerId || oauthProviderIds.has(providerId) || providers.has(providerId)) {
|
||||
continue;
|
||||
}
|
||||
providers.set(providerId, getProviderDisplayName(providerId));
|
||||
}
|
||||
|
||||
return Array.from(providers, ([id, name]) => ({ id, name })).sort((a, b) =>
|
||||
a.name.localeCompare(b.name),
|
||||
);
|
||||
},
|
||||
setApiKey: (providerId, apiKey) => {
|
||||
authStorage.set(providerId, { type: "api_key", key: apiKey });
|
||||
},
|
||||
clearApiKey: (providerId) => {
|
||||
authStorage.remove(providerId);
|
||||
},
|
||||
hasApiKey: (providerId) => {
|
||||
const credential = authStorage.get(providerId);
|
||||
return credential?.type === "api_key" || authStorage.hasAuth(providerId);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function runDashboard(port: number, opts: { paused?: boolean; dev?: boolean; interactive?: boolean; open?: boolean } = {}) {
|
||||
ensureProcessDiagnostics();
|
||||
|
||||
|
||||
95
packages/cli/src/commands/provider-auth.ts
Normal file
95
packages/cli/src/commands/provider-auth.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type {
|
||||
AuthStorage,
|
||||
ModelRegistry,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
|
||||
export type LoginCallbacks = Parameters<AuthStorage["login"]>[1];
|
||||
|
||||
export interface DashboardAuthStorage {
|
||||
reload(): void;
|
||||
getOAuthProviders(): Array<{ id: string; name: string }>;
|
||||
hasAuth(provider: string): boolean;
|
||||
login(providerId: string, callbacks: LoginCallbacks): Promise<void>;
|
||||
logout(provider: string): void;
|
||||
getApiKeyProviders(): Array<{ id: string; name: string }>;
|
||||
setApiKey(providerId: string, apiKey: string): void;
|
||||
clearApiKey(providerId: string): void;
|
||||
hasApiKey(providerId: string): boolean;
|
||||
getApiKey(providerId: string): Promise<string | undefined>;
|
||||
get(providerId: string): { type?: string; key?: string } | undefined;
|
||||
}
|
||||
|
||||
const BUILT_IN_API_KEY_PROVIDERS: Array<{ id: string; name: string }> = [
|
||||
{ id: "kimi-coding", name: "Kimi" },
|
||||
{ id: "minimax", name: "Minimax" },
|
||||
{ id: "openrouter", name: "OpenRouter" },
|
||||
{ id: "zai", name: "Zai" },
|
||||
];
|
||||
|
||||
function getProviderDisplayName(providerId: string): string {
|
||||
const knownProviderNames = new Map(
|
||||
BUILT_IN_API_KEY_PROVIDERS.map((provider) => [provider.id, provider.name]),
|
||||
);
|
||||
|
||||
const knownName = knownProviderNames.get(providerId);
|
||||
if (knownName) return knownName;
|
||||
|
||||
return providerId
|
||||
.split(/[-_]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
export function wrapAuthStorageWithApiKeyProviders(
|
||||
authStorage: AuthStorage,
|
||||
modelRegistry: ModelRegistry,
|
||||
): DashboardAuthStorage {
|
||||
return {
|
||||
reload: () => authStorage.reload(),
|
||||
getOAuthProviders: () =>
|
||||
authStorage
|
||||
.getOAuthProviders()
|
||||
.map((provider) => ({ id: provider.id, name: provider.name })),
|
||||
hasAuth: (provider) => authStorage.hasAuth(provider),
|
||||
login: (providerId, callbacks) =>
|
||||
authStorage.login(providerId as Parameters<AuthStorage["login"]>[0], callbacks),
|
||||
logout: (provider) => authStorage.logout(provider),
|
||||
getApiKeyProviders: () => {
|
||||
const oauthProviderIds = new Set(
|
||||
authStorage.getOAuthProviders().map((provider) => provider.id),
|
||||
);
|
||||
const providers = new Map<string, string>();
|
||||
|
||||
for (const provider of BUILT_IN_API_KEY_PROVIDERS) {
|
||||
if (!oauthProviderIds.has(provider.id)) {
|
||||
providers.set(provider.id, provider.name);
|
||||
}
|
||||
}
|
||||
|
||||
for (const model of modelRegistry.getAll()) {
|
||||
const providerId = model.provider;
|
||||
if (!providerId || oauthProviderIds.has(providerId) || providers.has(providerId)) {
|
||||
continue;
|
||||
}
|
||||
providers.set(providerId, getProviderDisplayName(providerId));
|
||||
}
|
||||
|
||||
return Array.from(providers, ([id, name]) => ({ id, name })).sort((a, b) =>
|
||||
a.name.localeCompare(b.name),
|
||||
);
|
||||
},
|
||||
setApiKey: (providerId, apiKey) => {
|
||||
authStorage.set(providerId, { type: "api_key", key: apiKey });
|
||||
},
|
||||
clearApiKey: (providerId) => {
|
||||
authStorage.remove(providerId);
|
||||
},
|
||||
hasApiKey: (providerId) => {
|
||||
const credential = authStorage.get(providerId);
|
||||
return credential?.type === "api_key" || authStorage.hasAuth(providerId);
|
||||
},
|
||||
getApiKey: (providerId) => authStorage.getApiKey(providerId),
|
||||
get: (providerId) => authStorage.get(providerId),
|
||||
};
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
} from "./task-lifecycle.js";
|
||||
import { promptForPort } from "./port-prompt.js";
|
||||
import { createReadOnlyProviderSettingsView } from "./provider-settings.js";
|
||||
import { wrapAuthStorageWithApiKeyProviders } from "./provider-auth.js";
|
||||
|
||||
const DIAGNOSTIC_INTERVAL_MS = 30 * 60 * 1000; // 30 minutes
|
||||
let diagnosticIntervalHandle: ReturnType<typeof setInterval> | null = null;
|
||||
@@ -493,9 +494,11 @@ export async function runServe(
|
||||
modelRegistry.refresh();
|
||||
}
|
||||
|
||||
const dashboardAuthStorage = wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry);
|
||||
|
||||
const app = createServer(store, {
|
||||
onMerge: (taskId) => engine.onMerge(taskId),
|
||||
authStorage,
|
||||
authStorage: dashboardAuthStorage,
|
||||
modelRegistry,
|
||||
automationStore,
|
||||
missionAutopilot,
|
||||
|
||||
Reference in New Issue
Block a user