import { describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => { const store = { version: 1, profiles: { "anthropic:default": { type: "oauth", provider: "anthropic", access: "sk-ant-oat01-ACCESS-TOKEN-1234567890", refresh: "sk-ant-ort01-REFRESH-TOKEN-1234567890", expires: Date.now() + 60_000, email: "peter@example.com", }, "anthropic:work": { type: "api_key", provider: "anthropic", key: "sk-ant-api-0123456789abcdefghijklmnopqrstuvwxyz", }, "openai-codex:default": { type: "oauth", provider: "openai-codex", access: "eyJhbGciOi-ACCESS", refresh: "oai-refresh-1234567890", expires: Date.now() + 60_000, }, }, }; return { store, resolveOpenClawAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"), resolveAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"), resolveAgentModelPrimary: vi.fn().mockReturnValue(undefined), resolveAgentModelFallbacksOverride: vi.fn().mockReturnValue(undefined), listAgentIds: vi.fn().mockReturnValue(["main", "jeremiah"]), ensureAuthProfileStore: vi.fn().mockReturnValue(store), listProfilesForProvider: vi.fn((s: typeof store, provider: string) => { return Object.entries(s.profiles) .filter(([, cred]) => cred.provider === provider) .map(([id]) => id); }), resolveAuthProfileDisplayLabel: vi.fn(({ profileId }: { profileId: string }) => profileId), resolveAuthStorePathForDisplay: vi .fn() .mockReturnValue("/tmp/openclaw-agent/auth-profiles.json"), resolveEnvApiKey: vi.fn((provider: string) => { if (provider === "openai") { return { apiKey: "sk-openai-0123456789abcdefghijklmnopqrstuvwxyz", source: "shell env: OPENAI_API_KEY", }; } if (provider === "anthropic") { return { apiKey: "sk-ant-oat01-ACCESS-TOKEN-1234567890", source: "env: ANTHROPIC_OAUTH_TOKEN", }; } return null; }), getCustomProviderApiKey: vi.fn().mockReturnValue(undefined), getShellEnvAppliedKeys: vi.fn().mockReturnValue(["OPENAI_API_KEY", "ANTHROPIC_OAUTH_TOKEN"]), shouldEnableShellEnvFallback: vi.fn().mockReturnValue(true), loadConfig: vi.fn().mockReturnValue({ agents: { defaults: { model: { primary: "anthropic/claude-opus-4-5", fallbacks: [] }, models: { "anthropic/claude-opus-4-5": { alias: "Opus" } }, }, }, models: { providers: {} }, env: { shellEnv: { enabled: true } }, }), }; }); vi.mock("../../agents/agent-paths.js", () => ({ resolveOpenClawAgentDir: mocks.resolveOpenClawAgentDir, })); vi.mock("../../agents/agent-scope.js", () => ({ resolveAgentDir: mocks.resolveAgentDir, resolveAgentModelPrimary: mocks.resolveAgentModelPrimary, resolveAgentModelFallbacksOverride: mocks.resolveAgentModelFallbacksOverride, listAgentIds: mocks.listAgentIds, })); vi.mock("../../agents/auth-profiles.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, ensureAuthProfileStore: mocks.ensureAuthProfileStore, listProfilesForProvider: mocks.listProfilesForProvider, resolveAuthProfileDisplayLabel: mocks.resolveAuthProfileDisplayLabel, resolveAuthStorePathForDisplay: mocks.resolveAuthStorePathForDisplay, }; }); vi.mock("../../agents/model-auth.js", () => ({ resolveEnvApiKey: mocks.resolveEnvApiKey, getCustomProviderApiKey: mocks.getCustomProviderApiKey, })); vi.mock("../../infra/shell-env.js", () => ({ getShellEnvAppliedKeys: mocks.getShellEnvAppliedKeys, shouldEnableShellEnvFallback: mocks.shouldEnableShellEnvFallback, })); vi.mock("../../config/config.js", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, loadConfig: mocks.loadConfig, }; }); import { modelsStatusCommand } from "./list.js"; const runtime = { log: vi.fn(), error: vi.fn(), exit: vi.fn(), }; describe("modelsStatusCommand auth overview", () => { it("includes masked auth sources in JSON output", async () => { await modelsStatusCommand({ json: true }, runtime as never); const payload = JSON.parse(String((runtime.log as vi.Mock).mock.calls[0][0])); expect(mocks.resolveOpenClawAgentDir).toHaveBeenCalled(); expect(payload.defaultModel).toBe("anthropic/claude-opus-4-5"); expect(payload.auth.storePath).toBe("/tmp/openclaw-agent/auth-profiles.json"); expect(payload.auth.shellEnvFallback.enabled).toBe(true); expect(payload.auth.shellEnvFallback.appliedKeys).toContain("OPENAI_API_KEY"); expect(payload.auth.missingProvidersInUse).toEqual([]); expect(payload.auth.oauth.warnAfterMs).toBeGreaterThan(0); expect(payload.auth.oauth.profiles.length).toBeGreaterThan(0); const providers = payload.auth.providers as Array<{ provider: string; profiles: { labels: string[] }; env?: { value: string; source: string }; }>; const anthropic = providers.find((p) => p.provider === "anthropic"); expect(anthropic).toBeTruthy(); expect(anthropic?.profiles.labels.join(" ")).toContain("OAuth"); expect(anthropic?.profiles.labels.join(" ")).toContain("..."); const openai = providers.find((p) => p.provider === "openai"); expect(openai?.env?.source).toContain("OPENAI_API_KEY"); expect(openai?.env?.value).toContain("..."); expect( (payload.auth.providersWithOAuth as string[]).some((e) => e.startsWith("anthropic")), ).toBe(true); expect( (payload.auth.providersWithOAuth as string[]).some((e) => e.startsWith("openai-codex")), ).toBe(true); }); it("uses agent overrides and reports sources", async () => { const localRuntime = { log: vi.fn(), error: vi.fn(), exit: vi.fn(), }; const originalPrimary = mocks.resolveAgentModelPrimary.getMockImplementation(); const originalFallbacks = mocks.resolveAgentModelFallbacksOverride.getMockImplementation(); const originalAgentDir = mocks.resolveAgentDir.getMockImplementation(); mocks.resolveAgentModelPrimary.mockReturnValue("openai/gpt-4"); mocks.resolveAgentModelFallbacksOverride.mockReturnValue(["openai/gpt-3.5"]); mocks.resolveAgentDir.mockReturnValue("/tmp/openclaw-agent-custom"); try { await modelsStatusCommand({ json: true, agent: "Jeremiah" }, localRuntime as never); expect(mocks.resolveAgentDir).toHaveBeenCalledWith(expect.anything(), "jeremiah"); const payload = JSON.parse(String((localRuntime.log as vi.Mock).mock.calls[0][0])); expect(payload.agentId).toBe("jeremiah"); expect(payload.agentDir).toBe("/tmp/openclaw-agent-custom"); expect(payload.defaultModel).toBe("openai/gpt-4"); expect(payload.fallbacks).toEqual(["openai/gpt-3.5"]); expect(payload.modelConfig).toEqual({ defaultSource: "agent", fallbacksSource: "agent", }); } finally { mocks.resolveAgentModelPrimary.mockImplementation(originalPrimary); mocks.resolveAgentModelFallbacksOverride.mockImplementation(originalFallbacks); mocks.resolveAgentDir.mockImplementation(originalAgentDir); } }); it("labels defaults when --agent has no overrides", async () => { const localRuntime = { log: vi.fn(), error: vi.fn(), exit: vi.fn(), }; const originalPrimary = mocks.resolveAgentModelPrimary.getMockImplementation(); const originalFallbacks = mocks.resolveAgentModelFallbacksOverride.getMockImplementation(); mocks.resolveAgentModelPrimary.mockReturnValue(undefined); mocks.resolveAgentModelFallbacksOverride.mockReturnValue(undefined); try { await modelsStatusCommand({ agent: "main" }, localRuntime as never); const output = (localRuntime.log as vi.Mock).mock.calls .map((call) => String(call[0])) .join("\n"); expect(output).toContain("Default (defaults)"); expect(output).toContain("Fallbacks (0) (defaults)"); } finally { mocks.resolveAgentModelPrimary.mockImplementation(originalPrimary); mocks.resolveAgentModelFallbacksOverride.mockImplementation(originalFallbacks); } }); it("throws when agent id is unknown", async () => { const localRuntime = { log: vi.fn(), error: vi.fn(), exit: vi.fn(), }; await expect(modelsStatusCommand({ agent: "unknown" }, localRuntime as never)).rejects.toThrow( 'Unknown agent id "unknown".', ); }); it("exits non-zero when auth is missing", async () => { const originalProfiles = { ...mocks.store.profiles }; mocks.store.profiles = {}; const localRuntime = { log: vi.fn(), error: vi.fn(), exit: vi.fn(), }; const originalEnvImpl = mocks.resolveEnvApiKey.getMockImplementation(); mocks.resolveEnvApiKey.mockImplementation(() => null); try { await modelsStatusCommand({ check: true, plain: true }, localRuntime as never); expect(localRuntime.exit).toHaveBeenCalledWith(1); } finally { mocks.store.profiles = originalProfiles; mocks.resolveEnvApiKey.mockImplementation(originalEnvImpl); } }); });