mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:00:44 +00:00
fix: keep legacy usage auth plugin fallback
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const resolveProviderUsageAuthWithPluginMock = vi.fn(
|
||||
@@ -28,6 +31,15 @@ vi.mock("../plugins/provider-runtime.js", async () => {
|
||||
|
||||
let resolveProviderAuths: typeof import("./provider-usage.auth.js").resolveProviderAuths;
|
||||
|
||||
async function withTempHome<T>(fn: (homeDir: string) => Promise<T>): Promise<T> {
|
||||
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-provider-usage-"));
|
||||
try {
|
||||
return await fn(homeDir);
|
||||
} finally {
|
||||
fs.rmSync(homeDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
describe("resolveProviderAuths plugin boundary", () => {
|
||||
beforeAll(async () => {
|
||||
({ resolveProviderAuths } = await import("./provider-usage.auth.js"));
|
||||
@@ -58,33 +70,70 @@ describe("resolveProviderAuths plugin boundary", () => {
|
||||
});
|
||||
|
||||
it("skips plugin usage auth when requested and no direct credential source exists", async () => {
|
||||
await expect(
|
||||
resolveProviderAuths({
|
||||
providers: ["zai"],
|
||||
skipPluginAuthWithoutCredentialSource: true,
|
||||
env: {},
|
||||
}),
|
||||
).resolves.toEqual([]);
|
||||
await withTempHome(async (homeDir) => {
|
||||
await expect(
|
||||
resolveProviderAuths({
|
||||
providers: ["zai"],
|
||||
skipPluginAuthWithoutCredentialSource: true,
|
||||
env: { HOME: homeDir },
|
||||
}),
|
||||
).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
expect(resolveProviderUsageAuthWithPluginMock).not.toHaveBeenCalled();
|
||||
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips plugin usage auth per provider when only another provider has direct credentials", async () => {
|
||||
await expect(
|
||||
resolveProviderAuths({
|
||||
providers: ["anthropic", "zai"],
|
||||
skipPluginAuthWithoutCredentialSource: true,
|
||||
env: {
|
||||
ANTHROPIC_API_KEY: "sk-ant",
|
||||
it("keeps plugin usage auth when a shared legacy plugin credential source exists", async () => {
|
||||
await withTempHome(async (homeDir) => {
|
||||
fs.mkdirSync(path.join(homeDir, ".pi", "agent"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(homeDir, ".pi", "agent", "auth.json"),
|
||||
`${JSON.stringify({ "z-ai": { access: "legacy-zai-token" } })}\n`,
|
||||
);
|
||||
resolveProviderUsageAuthWithPluginMock.mockResolvedValueOnce({
|
||||
token: "legacy-zai-token",
|
||||
});
|
||||
await expect(
|
||||
resolveProviderAuths({
|
||||
providers: ["zai"],
|
||||
skipPluginAuthWithoutCredentialSource: true,
|
||||
env: { HOME: homeDir },
|
||||
}),
|
||||
).resolves.toEqual([
|
||||
{
|
||||
provider: "zai",
|
||||
token: "legacy-zai-token",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
provider: "zai",
|
||||
}),
|
||||
).resolves.toEqual([
|
||||
{
|
||||
provider: "anthropic",
|
||||
token: "sk-ant",
|
||||
},
|
||||
]);
|
||||
);
|
||||
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("skips plugin usage auth per provider when only another provider has direct credentials", async () => {
|
||||
await withTempHome(async (homeDir) => {
|
||||
await expect(
|
||||
resolveProviderAuths({
|
||||
providers: ["anthropic", "zai"],
|
||||
skipPluginAuthWithoutCredentialSource: true,
|
||||
env: {
|
||||
HOME: homeDir,
|
||||
ANTHROPIC_API_KEY: "sk-ant",
|
||||
},
|
||||
}),
|
||||
).resolves.toEqual([
|
||||
{
|
||||
provider: "anthropic",
|
||||
token: "sk-ant",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledTimes(1);
|
||||
expect(resolveProviderUsageAuthWithPluginMock).toHaveBeenCalledWith(
|
||||
|
||||
@@ -13,6 +13,7 @@ import { normalizeProviderId } from "../agents/model-selection.js";
|
||||
import { loadConfig, type OpenClawConfig } from "../config/config.js";
|
||||
import { resolveProviderUsageAuthWithPlugin } from "../plugins/provider-runtime.js";
|
||||
import { normalizeSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import { hasLegacyPiAgentAuthSource } from "./provider-usage.shared.js";
|
||||
import type { UsageProviderId } from "./provider-usage.types.js";
|
||||
|
||||
export type ProviderAuth = {
|
||||
@@ -243,6 +244,7 @@ export async function resolveProviderAuths(params: {
|
||||
agentDir: params.agentDir,
|
||||
};
|
||||
const hasAuthProfileStoreSource = hasAnyAuthProfileStoreSource(params.agentDir);
|
||||
const hasSharedPluginCredentialSource = hasLegacyPiAgentAuthSource(stateBase.env);
|
||||
const auths: ProviderAuth[] = [];
|
||||
|
||||
for (const provider of params.providers) {
|
||||
@@ -260,8 +262,10 @@ export async function resolveProviderAuths(params: {
|
||||
...stateBase,
|
||||
allowAuthProfileStore,
|
||||
};
|
||||
const hasPluginCredentialSource =
|
||||
hasDirectCredentialSource || hasAuthProfileStoreSource || hasSharedPluginCredentialSource;
|
||||
|
||||
if (!params.skipPluginAuthWithoutCredentialSource || allowAuthProfileStore) {
|
||||
if (!params.skipPluginAuthWithoutCredentialSource || hasPluginCredentialSource) {
|
||||
const pluginAuth = await resolveProviderUsageAuthViaPlugin({
|
||||
state,
|
||||
provider,
|
||||
|
||||
@@ -71,17 +71,24 @@ export const withTimeout = async <T>(work: Promise<T>, ms: number, fallback: T):
|
||||
}
|
||||
};
|
||||
|
||||
function resolveLegacyPiAgentAuthPath(env: NodeJS.ProcessEnv): string {
|
||||
return path.join(resolveRequiredHomeDir(env, os.homedir), ".pi", "agent", "auth.json");
|
||||
}
|
||||
|
||||
export function hasLegacyPiAgentAuthSource(env: NodeJS.ProcessEnv): boolean {
|
||||
try {
|
||||
return fs.existsSync(resolveLegacyPiAgentAuthPath(env));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveLegacyPiAgentAccessToken(
|
||||
env: NodeJS.ProcessEnv,
|
||||
providerIds: string[],
|
||||
): string | undefined {
|
||||
try {
|
||||
const authPath = path.join(
|
||||
resolveRequiredHomeDir(env, os.homedir),
|
||||
".pi",
|
||||
"agent",
|
||||
"auth.json",
|
||||
);
|
||||
const authPath = resolveLegacyPiAgentAuthPath(env);
|
||||
if (!fs.existsSync(authPath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user