diff --git a/src/agents/auth-profiles/external-auth.ts b/src/agents/auth-profiles/external-auth.ts index 6fcc9690f6f..21a1f153a1e 100644 --- a/src/agents/auth-profiles/external-auth.ts +++ b/src/agents/auth-profiles/external-auth.ts @@ -3,6 +3,18 @@ import type { ProviderExternalAuthProfile } from "../../plugins/types.js"; import type { AuthProfileStore, OAuthCredential } from "./types.js"; type ExternalAuthProfileMap = Map; +type ResolveExternalAuthProfiles = typeof resolveExternalAuthProfilesWithPlugins; + +let resolveExternalAuthProfilesForRuntime: ResolveExternalAuthProfiles | undefined; + +export const __testing = { + resetResolveExternalAuthProfilesForTest(): void { + resolveExternalAuthProfilesForRuntime = undefined; + }, + setResolveExternalAuthProfilesForTest(resolver: ResolveExternalAuthProfiles): void { + resolveExternalAuthProfilesForRuntime = resolver; + }, +}; function normalizeExternalAuthProfile( profile: ProviderExternalAuthProfile, @@ -22,7 +34,9 @@ function resolveExternalAuthProfileMap(params: { env?: NodeJS.ProcessEnv; }): ExternalAuthProfileMap { const env = params.env ?? process.env; - const profiles = resolveExternalAuthProfilesWithPlugins({ + const resolveProfiles = + resolveExternalAuthProfilesForRuntime ?? resolveExternalAuthProfilesWithPlugins; + const profiles = resolveProfiles({ env, context: { config: undefined, diff --git a/src/agents/auth-profiles/external-oauth.test.ts b/src/agents/auth-profiles/external-oauth.test.ts index 3561d9649f2..161732aea48 100644 --- a/src/agents/auth-profiles/external-oauth.test.ts +++ b/src/agents/auth-profiles/external-oauth.test.ts @@ -1,18 +1,16 @@ -import { beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { ProviderExternalAuthProfile } from "../../plugins/types.js"; +import { + __testing, + overlayExternalOAuthProfiles, + shouldPersistExternalOAuthProfile, +} from "./external-auth.js"; import type { AuthProfileStore, OAuthCredential } from "./types.js"; const resolveExternalAuthProfilesWithPluginsMock = vi.fn< (params: unknown) => ProviderExternalAuthProfile[] >(() => []); -vi.mock("../../plugins/provider-runtime.js", () => ({ - resolveExternalAuthProfilesWithPlugins: (params: unknown) => - resolveExternalAuthProfilesWithPluginsMock(params), - resolveExternalOAuthProfilesWithPlugins: (params: unknown) => - resolveExternalAuthProfilesWithPluginsMock(params), -})); - function createStore(profiles: AuthProfileStore["profiles"] = {}): AuthProfileStore { return { version: 1, profiles }; } @@ -31,9 +29,15 @@ function createCredential(overrides: Partial = {}): OAuthCreden describe("auth external oauth helpers", () => { beforeEach(() => { resolveExternalAuthProfilesWithPluginsMock.mockReset(); + resolveExternalAuthProfilesWithPluginsMock.mockReturnValue([]); + __testing.setResolveExternalAuthProfilesForTest(resolveExternalAuthProfilesWithPluginsMock); }); - it("overlays provider-managed runtime oauth profiles onto the store", async () => { + afterEach(() => { + __testing.resetResolveExternalAuthProfilesForTest(); + }); + + it("overlays provider-managed runtime oauth profiles onto the store", () => { resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([ { profileId: "openai-codex:default", @@ -41,7 +45,6 @@ describe("auth external oauth helpers", () => { }, ]); - const { overlayExternalOAuthProfiles } = await import("./external-auth.js"); const store = overlayExternalOAuthProfiles(createStore()); expect(store.profiles["openai-codex:default"]).toMatchObject({ @@ -51,7 +54,7 @@ describe("auth external oauth helpers", () => { }); }); - it("omits exact runtime-only overlays from persisted store writes", async () => { + it("omits exact runtime-only overlays from persisted store writes", () => { const credential = createCredential(); resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([ { @@ -60,7 +63,6 @@ describe("auth external oauth helpers", () => { }, ]); - const { shouldPersistExternalOAuthProfile } = await import("./external-auth.js"); const shouldPersist = shouldPersistExternalOAuthProfile({ store: createStore({ "openai-codex:default": credential }), profileId: "openai-codex:default", @@ -70,7 +72,7 @@ describe("auth external oauth helpers", () => { expect(shouldPersist).toBe(false); }); - it("keeps persisted copies when the external overlay is marked persisted", async () => { + it("keeps persisted copies when the external overlay is marked persisted", () => { const credential = createCredential(); resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([ { @@ -80,7 +82,6 @@ describe("auth external oauth helpers", () => { }, ]); - const { shouldPersistExternalOAuthProfile } = await import("./external-auth.js"); const shouldPersist = shouldPersistExternalOAuthProfile({ store: createStore({ "openai-codex:default": credential }), profileId: "openai-codex:default", @@ -90,7 +91,7 @@ describe("auth external oauth helpers", () => { expect(shouldPersist).toBe(true); }); - it("keeps stale local copies when runtime overlay no longer matches", async () => { + it("keeps stale local copies when runtime overlay no longer matches", () => { const credential = createCredential(); resolveExternalAuthProfilesWithPluginsMock.mockReturnValueOnce([ { @@ -99,7 +100,6 @@ describe("auth external oauth helpers", () => { }, ]); - const { shouldPersistExternalOAuthProfile } = await import("./external-auth.js"); const shouldPersist = shouldPersistExternalOAuthProfile({ store: createStore({ "openai-codex:default": credential }), profileId: "openai-codex:default", diff --git a/src/agents/model-selection.test.ts b/src/agents/model-selection.test.ts index aa7c0458d70..fca4f965e3a 100644 --- a/src/agents/model-selection.test.ts +++ b/src/agents/model-selection.test.ts @@ -1,7 +1,8 @@ -import { describe, it, expect, vi } from "vitest"; +import { afterEach, beforeEach, describe, it, expect, vi } from "vitest"; import type { OpenClawConfig } from "../config/types.js"; import { resetLogger, setLoggerOverride } from "../logging/logger.js"; import { createWarnLogCapture } from "../logging/test-helpers/warn-log-capture.js"; +import { __testing as setupRegistryRuntimeTesting } from "../plugins/setup-registry.runtime.js"; import { buildAllowedModelSet, inferUniqueProviderFromConfiguredModels, @@ -135,6 +136,23 @@ describe("model-selection", () => { }); describe("isCliProvider", () => { + beforeEach(() => { + setupRegistryRuntimeTesting.resetRuntimeState(); + setupRegistryRuntimeTesting.setRuntimeModuleForTest({ + resolvePluginSetupCliBackend: ({ backend }) => + backend === "claude-cli" + ? { + pluginId: "anthropic", + backend: { id: "claude-cli", config: { command: "claude" } }, + } + : undefined, + }); + }); + + afterEach(() => { + setupRegistryRuntimeTesting.resetRuntimeState(); + }); + it("returns true for setup-registered cli backends", () => { expect(isCliProvider("claude-cli", {} as OpenClawConfig)).toBe(true); }); diff --git a/src/secrets/provider-env-vars.ts b/src/secrets/provider-env-vars.ts index 6eb47aa440b..63c3e74c60b 100644 --- a/src/secrets/provider-env-vars.ts +++ b/src/secrets/provider-env-vars.ts @@ -3,6 +3,8 @@ import type { OpenClawConfig } from "../config/config.js"; import { loadPluginManifestRegistry } from "../plugins/manifest-registry.js"; const CORE_PROVIDER_AUTH_ENV_VAR_CANDIDATES = { + anthropic: ["ANTHROPIC_OAUTH_TOKEN", "ANTHROPIC_API_KEY"], + openai: ["OPENAI_API_KEY"], voyage: ["VOYAGE_API_KEY"], cerebras: ["CEREBRAS_API_KEY"], "anthropic-openai": ["ANTHROPIC_API_KEY"],