mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:30:42 +00:00
fix: preserve manifest-backed model list auth
This commit is contained in:
@@ -71,6 +71,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"setup": {
|
||||
"providers": [
|
||||
{
|
||||
"id": "google-vertex",
|
||||
"authMethods": ["api-key"],
|
||||
"envVars": ["GOOGLE_CLOUD_API_KEY"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"cliBackends": ["google-gemini-cli"],
|
||||
"providerAuthEnvVars": {
|
||||
"google": ["GEMINI_API_KEY", "GOOGLE_API_KEY"]
|
||||
|
||||
@@ -2,10 +2,23 @@ import { describe, expect, it, vi } from "vitest";
|
||||
import type { AuthProfileStore } from "../../agents/auth-profiles/types.js";
|
||||
import { createModelListAuthIndex } from "./list.auth-index.js";
|
||||
|
||||
vi.mock("../../plugins/installed-plugin-index-store.js", () => ({
|
||||
readPersistedInstalledPluginIndexSync: vi.fn(() => null),
|
||||
const pluginRegistryMocks = vi.hoisted(() => ({
|
||||
loadPluginRegistrySnapshotWithMetadata: vi.fn(() => ({
|
||||
source: "persisted",
|
||||
snapshot: { plugins: [] },
|
||||
diagnostics: [],
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock("../../plugins/plugin-registry.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../plugins/plugin-registry.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadPluginRegistrySnapshotWithMetadata:
|
||||
pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata,
|
||||
};
|
||||
});
|
||||
|
||||
const emptyStore: AuthProfileStore = {
|
||||
version: 1,
|
||||
profiles: {},
|
||||
@@ -57,6 +70,18 @@ describe("createModelListAuthIndex", () => {
|
||||
expect(index.hasProviderAuth("openai")).toBe(false);
|
||||
});
|
||||
|
||||
it("uses manifest env metadata for google vertex auth", () => {
|
||||
const index = createModelListAuthIndex({
|
||||
cfg: {},
|
||||
authStore: emptyStore,
|
||||
env: {
|
||||
GOOGLE_CLOUD_API_KEY: "gcp-test",
|
||||
},
|
||||
});
|
||||
|
||||
expect(index.hasProviderAuth("google-vertex")).toBe(true);
|
||||
});
|
||||
|
||||
it("records configured provider API keys", () => {
|
||||
const index = createModelListAuthIndex({
|
||||
cfg: {
|
||||
@@ -108,4 +133,38 @@ describe("createModelListAuthIndex", () => {
|
||||
|
||||
expect(index.hasProviderAuth("codex")).toBe(true);
|
||||
});
|
||||
|
||||
it("ignores derived synthetic auth snapshots", () => {
|
||||
pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata.mockReturnValueOnce({
|
||||
source: "derived",
|
||||
snapshot: {
|
||||
plugins: [{ enabled: true, syntheticAuthRefs: ["codex"] }],
|
||||
},
|
||||
diagnostics: [],
|
||||
});
|
||||
const index = createModelListAuthIndex({
|
||||
cfg: {},
|
||||
authStore: emptyStore,
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(index.hasProviderAuth("codex")).toBe(false);
|
||||
});
|
||||
|
||||
it("ignores disabled synthetic auth snapshot entries", () => {
|
||||
pluginRegistryMocks.loadPluginRegistrySnapshotWithMetadata.mockReturnValueOnce({
|
||||
source: "persisted",
|
||||
snapshot: {
|
||||
plugins: [{ enabled: false, syntheticAuthRefs: ["codex"] }],
|
||||
},
|
||||
diagnostics: [],
|
||||
});
|
||||
const index = createModelListAuthIndex({
|
||||
cfg: {},
|
||||
authStore: emptyStore,
|
||||
env: {},
|
||||
});
|
||||
|
||||
expect(index.hasProviderAuth("codex")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { resolveProviderAuthAliasMap } from "../../agents/provider-auth-aliases.js";
|
||||
import { normalizeProviderIdForAuth } from "../../agents/provider-id.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { readPersistedInstalledPluginIndexSync } from "../../plugins/installed-plugin-index-store.js";
|
||||
import { loadPluginRegistrySnapshotWithMetadata } from "../../plugins/plugin-registry.js";
|
||||
|
||||
export type ModelListAuthIndex = {
|
||||
hasProviderAuth(provider: string): boolean;
|
||||
@@ -34,9 +34,20 @@ function normalizeAuthProvider(
|
||||
return aliasMap[normalized] ?? normalized;
|
||||
}
|
||||
|
||||
function listPersistedSyntheticAuthProviderRefs(): readonly string[] {
|
||||
const index = readPersistedInstalledPluginIndexSync();
|
||||
return index?.plugins.flatMap((plugin) => plugin.syntheticAuthRefs ?? []) ?? [];
|
||||
function listValidatedSyntheticAuthProviderRefs(params: {
|
||||
cfg: OpenClawConfig;
|
||||
env: NodeJS.ProcessEnv;
|
||||
}): readonly string[] {
|
||||
const result = loadPluginRegistrySnapshotWithMetadata({
|
||||
config: params.cfg,
|
||||
env: params.env,
|
||||
});
|
||||
if (result.source !== "persisted" && result.source !== "provided") {
|
||||
return [];
|
||||
}
|
||||
return result.snapshot.plugins
|
||||
.filter((plugin) => plugin.enabled)
|
||||
.flatMap((plugin) => plugin.syntheticAuthRefs ?? []);
|
||||
}
|
||||
|
||||
export function createModelListAuthIndex(
|
||||
@@ -62,6 +73,11 @@ export function createModelListAuthIndex(
|
||||
addProvider(provider);
|
||||
}
|
||||
}
|
||||
// Google Vertex ADC is still represented by resolveEnvApiKey's compatibility
|
||||
// path. Move this into manifest auth signals once that contract exists.
|
||||
if (resolveEnvApiKey("google-vertex", env, { aliasMap, candidateMap: envCandidateMap })) {
|
||||
addProvider("google-vertex");
|
||||
}
|
||||
|
||||
if (resolveAwsSdkEnvVarName(env)) {
|
||||
addProvider("amazon-bedrock");
|
||||
@@ -77,7 +93,7 @@ export function createModelListAuthIndex(
|
||||
}
|
||||
|
||||
for (const provider of params.syntheticAuthProviderRefs ??
|
||||
listPersistedSyntheticAuthProviderRefs()) {
|
||||
listValidatedSyntheticAuthProviderRefs({ cfg: params.cfg, env })) {
|
||||
addProvider(provider);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ const mocks = vi.hoisted(() => {
|
||||
printModelTable: vi.fn(),
|
||||
resolveModelWithRegistry: vi.fn(),
|
||||
readPersistedInstalledPluginIndexSync: vi.fn(),
|
||||
loadPluginRegistrySnapshotWithMetadata: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -95,6 +96,11 @@ function resetMocks() {
|
||||
mocks.printModelTable.mockReset();
|
||||
mocks.resolveModelWithRegistry.mockReturnValue({ ...OPENAI_CODEX_MODEL });
|
||||
mocks.readPersistedInstalledPluginIndexSync.mockReturnValue(null);
|
||||
mocks.loadPluginRegistrySnapshotWithMetadata.mockReturnValue({
|
||||
source: "persisted",
|
||||
snapshot: { plugins: [] },
|
||||
diagnostics: [],
|
||||
});
|
||||
}
|
||||
|
||||
function createRuntime() {
|
||||
@@ -215,6 +221,14 @@ function installModelsListCommandForwardCompatMocks() {
|
||||
vi.doMock("../../plugins/installed-plugin-index-store.js", () => ({
|
||||
readPersistedInstalledPluginIndexSync: mocks.readPersistedInstalledPluginIndexSync,
|
||||
}));
|
||||
|
||||
vi.doMock("../../plugins/plugin-registry.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../plugins/plugin-registry.js")>();
|
||||
return {
|
||||
...actual,
|
||||
loadPluginRegistrySnapshotWithMetadata: mocks.loadPluginRegistrySnapshotWithMetadata,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
@@ -507,8 +521,12 @@ describe("modelsListCommand forward-compat", () => {
|
||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
||||
},
|
||||
]);
|
||||
mocks.readPersistedInstalledPluginIndexSync.mockReturnValue({
|
||||
plugins: [{ syntheticAuthRefs: ["codex"] }],
|
||||
mocks.loadPluginRegistrySnapshotWithMetadata.mockReturnValueOnce({
|
||||
source: "persisted",
|
||||
snapshot: {
|
||||
plugins: [{ enabled: true, syntheticAuthRefs: ["codex"] }],
|
||||
},
|
||||
diagnostics: [],
|
||||
});
|
||||
const runtime = createRuntime();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user