From 9fa685e3b3e48da193be74e13737b4eb746de887 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Tue, 5 May 2026 02:00:39 -0700 Subject: [PATCH] test(live): scope provider auth discovery --- src/agents/models.profiles.live.test.ts | 9 ++- .../pi-auth-discovery.external-cli.test.ts | 79 +++++++++++++++++++ src/agents/pi-auth-discovery.ts | 14 +++- 3 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 src/agents/pi-auth-discovery.external-cli.test.ts diff --git a/src/agents/models.profiles.live.test.ts b/src/agents/models.profiles.live.test.ts index eb805c9e8dd..1e1e2fcfb26 100644 --- a/src/agents/models.profiles.live.test.ts +++ b/src/agents/models.profiles.live.test.ts @@ -5,6 +5,7 @@ import { getRuntimeConfig } from "../config/config.js"; import { parseLiveCsvFilter } from "../media-generation/live-test-helpers.js"; import { runTasksWithConcurrency } from "../utils/run-with-concurrency.js"; import { resolveOpenClawAgentDir } from "./agent-paths.js"; +import { externalCliDiscoveryForProviders } from "./auth-profiles/external-cli-discovery.js"; import { collectAnthropicApiKeys, isAnthropicBillingError, @@ -730,8 +731,13 @@ describeLive("live models (profile keys)", () => { logProgress(`[live-models] anthropic keys loaded: ${anthropicKeys.length}`); } + const providers = parseProviderFilter(process.env.OPENCLAW_LIVE_PROVIDERS); const agentDir = resolveOpenClawAgentDir(); - const authStorage = discoverAuthStorage(agentDir); + const authStorage = discoverAuthStorage(agentDir, { + config: cfg, + env: process.env, + ...(providers ? { externalCli: externalCliDiscoveryForProviders({ cfg, providers }) } : {}), + }); logProgress("[live-models] loading model registry"); const models = await withLiveStageTimeout( Promise.resolve().then(() => discoverModels(authStorage, agentDir).getAll()), @@ -743,7 +749,6 @@ describeLive("live models (profile keys)", () => { const useExplicit = Boolean(rawModels) && !useModern; const filter = useExplicit ? parseModelFilter(rawModels) : null; const allowNotFoundSkip = useModern; - const providers = parseProviderFilter(process.env.OPENCLAW_LIVE_PROVIDERS); const perModelTimeoutMs = toInt(process.env.OPENCLAW_LIVE_MODEL_TIMEOUT_MS, 30_000); const maxModels = resolveHighSignalLiveModelLimit({ rawMaxModels: process.env.OPENCLAW_LIVE_MAX_MODELS, diff --git a/src/agents/pi-auth-discovery.external-cli.test.ts b/src/agents/pi-auth-discovery.external-cli.test.ts new file mode 100644 index 00000000000..06cdea27c8c --- /dev/null +++ b/src/agents/pi-auth-discovery.external-cli.test.ts @@ -0,0 +1,79 @@ +import { describe, expect, it, vi } from "vitest"; +import type { OpenClawConfig } from "../config/types.openclaw.js"; + +const storeMocks = vi.hoisted(() => ({ + ensureAuthProfileStore: vi.fn(() => ({ version: 1, profiles: {} })), + loadAuthProfileStoreForRuntime: vi.fn(() => ({ version: 1, profiles: {} })), + loadAuthProfileStoreForSecretsRuntime: vi.fn(() => ({ version: 1, profiles: {} })), +})); + +const credentialMocks = vi.hoisted(() => ({ + resolvePiCredentialMapFromStore: vi.fn(() => ({})), +})); + +const discoveryCoreMocks = vi.hoisted(() => ({ + addEnvBackedPiCredentials: vi.fn((credentials: unknown) => credentials), + scrubLegacyStaticAuthJsonEntriesForDiscovery: vi.fn(), +})); + +vi.mock("./auth-profiles/store.js", () => storeMocks); + +vi.mock("./pi-auth-credentials.js", () => credentialMocks); + +vi.mock("./pi-auth-discovery-core.js", () => discoveryCoreMocks); + +vi.mock("./synthetic-auth.runtime.js", () => ({ + resolveRuntimeSyntheticAuthProviderRefs: () => [], +})); + +vi.mock("../plugins/provider-runtime.js", () => ({ + resolveProviderSyntheticAuthWithPlugin: vi.fn(), +})); + +import { externalCliDiscoveryForProviders } from "./auth-profiles/external-cli-discovery.js"; +import { resolvePiCredentialsForDiscovery } from "./pi-auth-discovery.js"; + +describe("resolvePiCredentialsForDiscovery external CLI scoping", () => { + it("threads scoped external CLI discovery into writable auth store loading", () => { + const cfg = {} as OpenClawConfig; + const externalCli = externalCliDiscoveryForProviders({ + cfg, + providers: ["fireworks"], + }); + + resolvePiCredentialsForDiscovery("/tmp/openclaw-agent", { + config: cfg, + env: {}, + externalCli, + }); + + expect(storeMocks.ensureAuthProfileStore).toHaveBeenCalledWith("/tmp/openclaw-agent", { + allowKeychainPrompt: false, + config: cfg, + externalCli, + }); + expect(storeMocks.loadAuthProfileStoreForRuntime).not.toHaveBeenCalled(); + }); + + it("preserves scoped external CLI discovery for read-only auth store loading", () => { + const cfg = {} as OpenClawConfig; + const externalCli = externalCliDiscoveryForProviders({ + cfg, + providers: ["fireworks"], + }); + + resolvePiCredentialsForDiscovery("/tmp/openclaw-agent", { + config: cfg, + env: {}, + externalCli, + readOnly: true, + }); + + expect(storeMocks.loadAuthProfileStoreForRuntime).toHaveBeenCalledWith("/tmp/openclaw-agent", { + allowKeychainPrompt: false, + config: cfg, + externalCli, + readOnly: true, + }); + }); +}); diff --git a/src/agents/pi-auth-discovery.ts b/src/agents/pi-auth-discovery.ts index a5993123db7..1b09d6b28d0 100644 --- a/src/agents/pi-auth-discovery.ts +++ b/src/agents/pi-auth-discovery.ts @@ -1,7 +1,9 @@ import { resolveProviderSyntheticAuthWithPlugin } from "../plugins/provider-runtime.js"; import { resolveRuntimeSyntheticAuthProviderRefs } from "../plugins/synthetic-auth.runtime.js"; +import type { ExternalCliAuthDiscovery } from "./auth-profiles/external-cli-discovery.js"; import { ensureAuthProfileStore, + loadAuthProfileStoreForRuntime, loadAuthProfileStoreForSecretsRuntime, } from "./auth-profiles/store.js"; import { resolvePiCredentialMapFromStore, type PiCredentialMap } from "./pi-auth-credentials.js"; @@ -11,6 +13,7 @@ import { } from "./pi-auth-discovery-core.js"; export type DiscoverAuthStorageOptions = { + externalCli?: ExternalCliAuthDiscovery; readOnly?: boolean; skipCredentials?: boolean; } & PiDiscoveryAuthLookupOptions; @@ -19,10 +22,17 @@ export function resolvePiCredentialsForDiscovery( agentDir: string, options?: DiscoverAuthStorageOptions, ): PiCredentialMap { + const storeOptions = { + allowKeychainPrompt: false, + ...(options?.config ? { config: options.config } : {}), + ...(options?.externalCli ? { externalCli: options.externalCli } : {}), + }; const store = options?.readOnly === true - ? loadAuthProfileStoreForSecretsRuntime(agentDir) - : ensureAuthProfileStore(agentDir, { allowKeychainPrompt: false }); + ? options.externalCli || options.config + ? loadAuthProfileStoreForRuntime(agentDir, { readOnly: true, ...storeOptions }) + : loadAuthProfileStoreForSecretsRuntime(agentDir) + : ensureAuthProfileStore(agentDir, storeOptions); const credentials = addEnvBackedPiCredentials(resolvePiCredentialMapFromStore(store), { config: options?.config, workspaceDir: options?.workspaceDir,