mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 23:38:27 +00:00
refactor: dedupe provider usage auth/fetch logic and expand coverage
This commit is contained in:
@@ -74,6 +74,39 @@ describe("resolveProviderAuths key normalization", () => {
|
||||
);
|
||||
}
|
||||
|
||||
async function writeConfig(home: string, config: Record<string, unknown>) {
|
||||
const stateDir = path.join(home, ".openclaw");
|
||||
await fs.mkdir(stateDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(stateDir, "openclaw.json"),
|
||||
`${JSON.stringify(config, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
}
|
||||
|
||||
async function writeProfileOrder(home: string, provider: string, profileIds: string[]) {
|
||||
const agentDir = path.join(home, ".openclaw", "agents", "main", "agent");
|
||||
const parsed = JSON.parse(
|
||||
await fs.readFile(path.join(agentDir, "auth-profiles.json"), "utf8"),
|
||||
) as Record<string, unknown>;
|
||||
const order = (parsed.order && typeof parsed.order === "object" ? parsed.order : {}) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
order[provider] = profileIds;
|
||||
parsed.order = order;
|
||||
await fs.writeFile(
|
||||
path.join(agentDir, "auth-profiles.json"),
|
||||
`${JSON.stringify(parsed, null, 2)}\n`,
|
||||
);
|
||||
}
|
||||
|
||||
async function writeLegacyPiAuth(home: string, raw: string) {
|
||||
const legacyDir = path.join(home, ".pi", "agent");
|
||||
await fs.mkdir(legacyDir, { recursive: true });
|
||||
await fs.writeFile(path.join(legacyDir, "auth.json"), raw, "utf8");
|
||||
}
|
||||
|
||||
it("strips embedded CR/LF from env keys", async () => {
|
||||
await withSuiteHome(
|
||||
async () => {
|
||||
@@ -144,12 +177,9 @@ describe("resolveProviderAuths key normalization", () => {
|
||||
it("falls back to legacy .pi auth file for zai keys", async () => {
|
||||
await withSuiteHome(
|
||||
async (home) => {
|
||||
const legacyDir = path.join(home, ".pi", "agent");
|
||||
await fs.mkdir(legacyDir, { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(legacyDir, "auth.json"),
|
||||
await writeLegacyPiAuth(
|
||||
home,
|
||||
`${JSON.stringify({ "z-ai": { access: "legacy-zai-key" } }, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const auths = await resolveProviderAuths({
|
||||
@@ -180,4 +210,195 @@ describe("resolveProviderAuths key normalization", () => {
|
||||
expect(auths).toEqual([{ provider: "google-gemini-cli", token: "google-oauth-token" }]);
|
||||
}, {});
|
||||
});
|
||||
|
||||
it("keeps raw google token when token payload is not JSON", async () => {
|
||||
await withSuiteHome(async (home) => {
|
||||
await writeAuthProfiles(home, {
|
||||
"google-antigravity:default": {
|
||||
type: "token",
|
||||
provider: "google-antigravity",
|
||||
token: "plain-google-token",
|
||||
},
|
||||
});
|
||||
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["google-antigravity"],
|
||||
});
|
||||
expect(auths).toEqual([{ provider: "google-antigravity", token: "plain-google-token" }]);
|
||||
}, {});
|
||||
});
|
||||
|
||||
it("uses config api keys when env and profiles are missing", async () => {
|
||||
await withSuiteHome(
|
||||
async (home) => {
|
||||
const modelDef = {
|
||||
id: "test-model",
|
||||
name: "Test Model",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
contextWindow: 1024,
|
||||
maxTokens: 256,
|
||||
};
|
||||
await writeConfig(home, {
|
||||
models: {
|
||||
providers: {
|
||||
zai: {
|
||||
baseUrl: "https://api.z.ai",
|
||||
models: [modelDef],
|
||||
apiKey: "cfg-zai-key",
|
||||
},
|
||||
minimax: {
|
||||
baseUrl: "https://api.minimaxi.com",
|
||||
models: [modelDef],
|
||||
apiKey: "cfg-minimax-key",
|
||||
},
|
||||
xiaomi: {
|
||||
baseUrl: "https://api.xiaomi.example",
|
||||
models: [modelDef],
|
||||
apiKey: "cfg-xiaomi-key",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["zai", "minimax", "xiaomi"],
|
||||
});
|
||||
expect(auths).toEqual([
|
||||
{ provider: "zai", token: "cfg-zai-key" },
|
||||
{ provider: "minimax", token: "cfg-minimax-key" },
|
||||
{ provider: "xiaomi", token: "cfg-xiaomi-key" },
|
||||
]);
|
||||
},
|
||||
{
|
||||
ZAI_API_KEY: undefined,
|
||||
Z_AI_API_KEY: undefined,
|
||||
MINIMAX_API_KEY: undefined,
|
||||
MINIMAX_CODE_PLAN_KEY: undefined,
|
||||
XIAOMI_API_KEY: undefined,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("returns no auth when providers have no configured credentials", async () => {
|
||||
await withSuiteHome(
|
||||
async () => {
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["zai", "minimax", "xiaomi"],
|
||||
});
|
||||
expect(auths).toEqual([]);
|
||||
},
|
||||
{
|
||||
ZAI_API_KEY: undefined,
|
||||
Z_AI_API_KEY: undefined,
|
||||
MINIMAX_API_KEY: undefined,
|
||||
MINIMAX_CODE_PLAN_KEY: undefined,
|
||||
XIAOMI_API_KEY: undefined,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("uses zai api_key auth profiles when env and config are missing", async () => {
|
||||
await withSuiteHome(
|
||||
async (home) => {
|
||||
await writeAuthProfiles(home, {
|
||||
"zai:default": { type: "api_key", provider: "zai", key: "profile-zai-key" },
|
||||
});
|
||||
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["zai"],
|
||||
});
|
||||
expect(auths).toEqual([{ provider: "zai", token: "profile-zai-key" }]);
|
||||
},
|
||||
{
|
||||
ZAI_API_KEY: undefined,
|
||||
Z_AI_API_KEY: undefined,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("ignores invalid legacy z-ai auth files", async () => {
|
||||
await withSuiteHome(
|
||||
async (home) => {
|
||||
await writeLegacyPiAuth(home, "{not-json");
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["zai"],
|
||||
});
|
||||
expect(auths).toEqual([]);
|
||||
},
|
||||
{
|
||||
ZAI_API_KEY: undefined,
|
||||
Z_AI_API_KEY: undefined,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("discovers oauth provider from config but skips mismatched profile providers", async () => {
|
||||
await withSuiteHome(async (home) => {
|
||||
await writeConfig(home, {
|
||||
auth: {
|
||||
profiles: {
|
||||
"anthropic:default": { provider: "anthropic", mode: "token" },
|
||||
},
|
||||
},
|
||||
});
|
||||
await writeAuthProfiles(home, {
|
||||
"anthropic:default": {
|
||||
type: "token",
|
||||
provider: "zai",
|
||||
token: "mismatched-provider-token",
|
||||
},
|
||||
});
|
||||
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["anthropic"],
|
||||
});
|
||||
expect(auths).toEqual([]);
|
||||
}, {});
|
||||
});
|
||||
|
||||
it("skips providers without oauth-compatible profiles", async () => {
|
||||
await withSuiteHome(async () => {
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["anthropic"],
|
||||
});
|
||||
expect(auths).toEqual([]);
|
||||
}, {});
|
||||
});
|
||||
|
||||
it("skips oauth profiles that resolve without an api key and uses later profiles", async () => {
|
||||
await withSuiteHome(async (home) => {
|
||||
await writeAuthProfiles(home, {
|
||||
"anthropic:empty": {
|
||||
type: "token",
|
||||
provider: "anthropic",
|
||||
token: "expired-token",
|
||||
expires: Date.now() - 60_000,
|
||||
},
|
||||
"anthropic:valid": { type: "token", provider: "anthropic", token: "anthropic-token" },
|
||||
});
|
||||
await writeProfileOrder(home, "anthropic", ["anthropic:empty", "anthropic:valid"]);
|
||||
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["anthropic"],
|
||||
});
|
||||
expect(auths).toEqual([{ provider: "anthropic", token: "anthropic-token" }]);
|
||||
}, {});
|
||||
});
|
||||
|
||||
it("skips api_key entries in oauth token resolution order", async () => {
|
||||
await withSuiteHome(async (home) => {
|
||||
await writeAuthProfiles(home, {
|
||||
"anthropic:api": { type: "api_key", provider: "anthropic", key: "api-key-1" },
|
||||
"anthropic:token": { type: "token", provider: "anthropic", token: "token-1" },
|
||||
});
|
||||
await writeProfileOrder(home, "anthropic", ["anthropic:api", "anthropic:token"]);
|
||||
|
||||
const auths = await resolveProviderAuths({
|
||||
providers: ["anthropic"],
|
||||
});
|
||||
expect(auths).toEqual([{ provider: "anthropic", token: "token-1" }]);
|
||||
}, {});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user