mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-17 21:10:54 +00:00
refactor(usage): share legacy pi auth token lookup
This commit is contained in:
@@ -1,6 +1,3 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
emptyPluginConfigSchema,
|
||||
type OpenClawPluginApi,
|
||||
@@ -10,7 +7,6 @@ import {
|
||||
type ProviderResolveDynamicModelContext,
|
||||
type ProviderRuntimeModel,
|
||||
} from "openclaw/plugin-sdk/core";
|
||||
import { resolveRequiredHomeDir } from "openclaw/plugin-sdk/infra-runtime";
|
||||
import {
|
||||
applyAuthProfileConfig,
|
||||
buildApiKeyCredential,
|
||||
@@ -23,7 +19,7 @@ import {
|
||||
} from "openclaw/plugin-sdk/provider-auth";
|
||||
import { DEFAULT_CONTEXT_TOKENS, normalizeModelCompat } from "openclaw/plugin-sdk/provider-models";
|
||||
import { createZaiToolStreamWrapper } from "openclaw/plugin-sdk/provider-stream";
|
||||
import { fetchZaiUsage } from "openclaw/plugin-sdk/provider-usage";
|
||||
import { fetchZaiUsage, resolveLegacyPiAgentAccessToken } from "openclaw/plugin-sdk/provider-usage";
|
||||
import { detectZaiEndpoint, type ZaiEndpointId } from "./detect.js";
|
||||
import { zaiMediaUnderstandingProvider } from "./media-understanding-provider.js";
|
||||
import { applyZaiConfig, applyZaiProviderConfig, ZAI_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
@@ -68,27 +64,6 @@ function resolveGlm5ForwardCompatModel(
|
||||
} as ProviderRuntimeModel);
|
||||
}
|
||||
|
||||
function resolveLegacyZaiUsageToken(env: NodeJS.ProcessEnv): string | undefined {
|
||||
try {
|
||||
const authPath = path.join(
|
||||
resolveRequiredHomeDir(env, os.homedir),
|
||||
".pi",
|
||||
"agent",
|
||||
"auth.json",
|
||||
);
|
||||
if (!fs.existsSync(authPath)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = JSON.parse(fs.readFileSync(authPath, "utf8")) as Record<
|
||||
string,
|
||||
{ access?: string }
|
||||
>;
|
||||
return parsed["z-ai"]?.access || parsed.zai?.access;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveZaiDefaultModel(modelIdOverride?: string): string {
|
||||
return modelIdOverride ? `zai/${modelIdOverride}` : ZAI_DEFAULT_MODEL_REF;
|
||||
}
|
||||
@@ -328,7 +303,7 @@ const zaiPlugin = {
|
||||
if (apiKey) {
|
||||
return { token: apiKey };
|
||||
}
|
||||
const legacyToken = resolveLegacyZaiUsageToken(ctx.env);
|
||||
const legacyToken = resolveLegacyPiAgentAccessToken(ctx.env, ["z-ai", "zai"]);
|
||||
return legacyToken ? { token: legacyToken } : null;
|
||||
},
|
||||
fetchUsageSnapshot: async (ctx) => await fetchZaiUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import {
|
||||
dedupeProfileIds,
|
||||
ensureAuthProfileStore,
|
||||
@@ -12,9 +9,9 @@ import { isNonSecretApiKeyMarker } from "../agents/model-auth-markers.js";
|
||||
import { resolveUsableCustomProviderApiKey } from "../agents/model-auth.js";
|
||||
import { normalizeProviderId } from "../agents/model-selection.js";
|
||||
import { loadConfig, type OpenClawConfig } from "../config/config.js";
|
||||
import { resolveRequiredHomeDir } from "../infra/home-dir.js";
|
||||
import { resolveProviderUsageAuthWithPlugin } from "../plugins/provider-runtime.js";
|
||||
import { normalizeSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import { resolveLegacyPiAgentAccessToken } from "./provider-usage.shared.js";
|
||||
import type { UsageProviderId } from "./provider-usage.types.js";
|
||||
|
||||
export type ProviderAuth = {
|
||||
@@ -44,27 +41,6 @@ function parseGoogleUsageToken(apiKey: string): string {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
function resolveLegacyZaiUsageToken(env: NodeJS.ProcessEnv): string | undefined {
|
||||
try {
|
||||
const authPath = path.join(
|
||||
resolveRequiredHomeDir(env, os.homedir),
|
||||
".pi",
|
||||
"agent",
|
||||
"auth.json",
|
||||
);
|
||||
if (!fs.existsSync(authPath)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = JSON.parse(fs.readFileSync(authPath, "utf8")) as Record<
|
||||
string,
|
||||
{ access?: string }
|
||||
>;
|
||||
return parsed["z-ai"]?.access || parsed.zai?.access;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function resolveProviderApiKeyFromConfigAndStore(params: {
|
||||
state: UsageAuthState;
|
||||
providerIds: string[];
|
||||
@@ -225,7 +201,7 @@ async function resolveProviderUsageAuthFallback(params: {
|
||||
if (apiKey) {
|
||||
return { provider: "zai", token: apiKey };
|
||||
}
|
||||
const legacyToken = resolveLegacyZaiUsageToken(params.state.env);
|
||||
const legacyToken = resolveLegacyPiAgentAccessToken(params.state.env, ["z-ai", "zai"]);
|
||||
return legacyToken ? { provider: "zai", token: legacyToken } : null;
|
||||
}
|
||||
case "minimax": {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { clampPercent, resolveUsageProviderId, withTimeout } from "./provider-usage.shared.js";
|
||||
import {
|
||||
clampPercent,
|
||||
resolveLegacyPiAgentAccessToken,
|
||||
resolveUsageProviderId,
|
||||
withTimeout,
|
||||
} from "./provider-usage.shared.js";
|
||||
|
||||
describe("provider-usage.shared", () => {
|
||||
afterEach(() => {
|
||||
@@ -52,4 +60,34 @@ describe("provider-usage.shared", () => {
|
||||
|
||||
expect(clearTimeoutSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("reads legacy pi auth tokens for known provider aliases", async () => {
|
||||
const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-provider-usage-"));
|
||||
await fs.mkdir(path.join(home, ".pi", "agent"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(home, ".pi", "agent", "auth.json"),
|
||||
`${JSON.stringify({ "z-ai": { access: "legacy-zai-key" } }, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
try {
|
||||
expect(resolveLegacyPiAgentAccessToken({ HOME: home }, ["z-ai", "zai"])).toBe(
|
||||
"legacy-zai-key",
|
||||
);
|
||||
} finally {
|
||||
await fs.rm(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("returns undefined for invalid legacy pi auth files", async () => {
|
||||
const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-provider-usage-"));
|
||||
await fs.mkdir(path.join(home, ".pi", "agent"), { recursive: true });
|
||||
await fs.writeFile(path.join(home, ".pi", "agent", "auth.json"), "{not-json", "utf8");
|
||||
|
||||
try {
|
||||
expect(resolveLegacyPiAgentAccessToken({ HOME: home }, ["z-ai", "zai"])).toBeUndefined();
|
||||
} finally {
|
||||
await fs.rm(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { normalizeProviderId } from "../agents/model-selection.js";
|
||||
import { resolveRequiredHomeDir } from "./home-dir.js";
|
||||
import type { UsageProviderId } from "./provider-usage.types.js";
|
||||
|
||||
export const DEFAULT_TIMEOUT_MS = 5000;
|
||||
@@ -59,3 +63,33 @@ export const withTimeout = async <T>(work: Promise<T>, ms: number, fallback: T):
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function resolveLegacyPiAgentAccessToken(
|
||||
env: NodeJS.ProcessEnv,
|
||||
providerIds: string[],
|
||||
): string | undefined {
|
||||
try {
|
||||
const authPath = path.join(
|
||||
resolveRequiredHomeDir(env, os.homedir),
|
||||
".pi",
|
||||
"agent",
|
||||
"auth.json",
|
||||
);
|
||||
if (!fs.existsSync(authPath)) {
|
||||
return undefined;
|
||||
}
|
||||
const parsed = JSON.parse(fs.readFileSync(authPath, "utf8")) as Record<
|
||||
string,
|
||||
{ access?: string }
|
||||
>;
|
||||
for (const providerId of providerIds) {
|
||||
const token = parsed[providerId]?.access;
|
||||
if (typeof token === "string" && token.trim()) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,11 @@ export {
|
||||
fetchMinimaxUsage,
|
||||
fetchZaiUsage,
|
||||
} from "../infra/provider-usage.fetch.js";
|
||||
export { clampPercent, PROVIDER_LABELS } from "../infra/provider-usage.shared.js";
|
||||
export {
|
||||
clampPercent,
|
||||
PROVIDER_LABELS,
|
||||
resolveLegacyPiAgentAccessToken,
|
||||
} from "../infra/provider-usage.shared.js";
|
||||
export {
|
||||
buildUsageErrorSnapshot,
|
||||
buildUsageHttpErrorSnapshot,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createProviderUsageFetch, makeResponse } from "../../test-utils/provider-usage-fetch.js";
|
||||
import type { ProviderRuntimeModel } from "../types.js";
|
||||
@@ -514,6 +517,33 @@ describe("provider runtime contract", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to legacy pi auth tokens for usage auth", async () => {
|
||||
const provider = requireProvider("zai");
|
||||
const home = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-zai-contract-"));
|
||||
await fs.mkdir(path.join(home, ".pi", "agent"), { recursive: true });
|
||||
await fs.writeFile(
|
||||
path.join(home, ".pi", "agent", "auth.json"),
|
||||
`${JSON.stringify({ "z-ai": { access: "legacy-zai-token" } }, null, 2)}\n`,
|
||||
"utf8",
|
||||
);
|
||||
|
||||
try {
|
||||
await expect(
|
||||
provider.resolveUsageAuth?.({
|
||||
config: {} as never,
|
||||
env: { HOME: home } as NodeJS.ProcessEnv,
|
||||
provider: "zai",
|
||||
resolveApiKeyFromConfigAndStore: () => undefined,
|
||||
resolveOAuthToken: async () => null,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
token: "legacy-zai-token",
|
||||
});
|
||||
} finally {
|
||||
await fs.rm(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("owns usage snapshot fetching", async () => {
|
||||
const provider = requireProviderContractProvider("zai");
|
||||
const mockFetch = createProviderUsageFetch(async (url) => {
|
||||
|
||||
Reference in New Issue
Block a user