refactor(auth): drop legacy external cli oauth sync path

This commit is contained in:
Vincent Koc
2026-04-17 13:51:57 -07:00
parent 0e7a992d3f
commit ff55cd5c16
10 changed files with 147 additions and 312 deletions

View File

@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
- OpenAI Codex/OAuth: treat the OpenAI TLS prerequisites probe as advisory instead of a hard blocker, so Codex sign-in can still proceed when the speculative Node/OpenSSL precheck fails but the real OAuth flow still works. Thanks @vincentkoc.
- Models status/OAuth health: align OAuth health reporting with the same effective credential view runtime uses, so expired refreshable sessions stop showing healthy by default and fresher imported Codex CLI credentials surface correctly in `models status`, doctor, and gateway auth status. Thanks @vincentkoc.
- OpenAI Codex/OAuth: keep external CLI OAuth imports runtime-only by overlaying fresher Codex CLI credentials without mutating `auth-profiles.json`, so `.codex` stays a bootstrap/runtime input instead of becoming durable OpenClaw state. Thanks @vincentkoc.
- OpenAI Codex/OAuth: drop legacy CLI-manager routing from the remaining bootstrap path so Codex and MiniMax CLI imports are matched by their canonical OpenClaw profile ids instead of stale `managedBy` metadata. Thanks @vincentkoc.
- Twitch/setup: load Twitch through the bundled setup-entry discovery path and keep setup/status account detection aligned with runtime config. (#68008) Thanks @gumadeiras.
- Feishu/card actions: resolve card-action chat type from the Feishu chat API when stored context is missing, preferring `chat_mode` over `chat_type`, so DM-originated card actions no longer bypass `dmPolicy` by falling through to the group handling path. (#68201)
- Cron/isolated-agent: preserve `trusted: false` on isolated cron awareness events mirrored into the main session, and forward the optional `trusted` flag through the gateway cron wrapper so explicit trust downgrades survive session-key scoping. (#68210)

View File

@@ -6,9 +6,9 @@ const mocks = vi.hoisted(() => ({
readMiniMaxCliCredentialsCached: vi.fn<() => OAuthCredential | null>(() => null),
}));
let syncExternalCliCredentials: typeof import("./auth-profiles/external-cli-sync.js").syncExternalCliCredentials;
let readManagedExternalCliCredential: typeof import("./auth-profiles/external-cli-sync.js").readManagedExternalCliCredential;
let resolveExternalCliAuthProfiles: typeof import("./auth-profiles/external-cli-sync.js").resolveExternalCliAuthProfiles;
let shouldReplaceStoredOAuthCredential: typeof import("./auth-profiles/external-cli-sync.js").shouldReplaceStoredOAuthCredential;
let CODEX_CLI_PROFILE_ID: typeof import("./auth-profiles/constants.js").CODEX_CLI_PROFILE_ID;
let OPENAI_CODEX_DEFAULT_PROFILE_ID: typeof import("./auth-profiles/constants.js").OPENAI_CODEX_DEFAULT_PROFILE_ID;
let MINIMAX_CLI_PROFILE_ID: typeof import("./auth-profiles/constants.js").MINIMAX_CLI_PROFILE_ID;
@@ -35,37 +35,21 @@ function makeStore(profileId?: string, credential?: OAuthCredential): AuthProfil
};
}
function getProviderCases() {
return [
{
label: "Codex",
profileId: OPENAI_CODEX_DEFAULT_PROFILE_ID,
provider: "openai-codex" as const,
readMock: mocks.readCodexCliCredentialsCached,
legacyProfileId: CODEX_CLI_PROFILE_ID,
},
{
label: "MiniMax",
profileId: MINIMAX_CLI_PROFILE_ID,
provider: "minimax-portal" as const,
readMock: mocks.readMiniMaxCliCredentialsCached,
},
];
}
describe("syncExternalCliCredentials", () => {
describe("external cli oauth resolution", () => {
beforeEach(async () => {
vi.resetModules();
vi.doUnmock("./auth-profiles/external-cli-sync.js");
mocks.readCodexCliCredentialsCached.mockReset().mockReturnValue(null);
mocks.readMiniMaxCliCredentialsCached.mockReset().mockReturnValue(null);
vi.doMock("./cli-credentials.js", () => ({
readCodexCliCredentialsCached: mocks.readCodexCliCredentialsCached,
readMiniMaxCliCredentialsCached: mocks.readMiniMaxCliCredentialsCached,
}));
({ syncExternalCliCredentials, shouldReplaceStoredOAuthCredential } =
await import("./auth-profiles/external-cli-sync.js"));
({ CODEX_CLI_PROFILE_ID, OPENAI_CODEX_DEFAULT_PROFILE_ID, MINIMAX_CLI_PROFILE_ID } =
mocks.readCodexCliCredentialsCached.mockReset().mockReturnValue(null);
mocks.readMiniMaxCliCredentialsCached.mockReset().mockReturnValue(null);
({
readManagedExternalCliCredential,
resolveExternalCliAuthProfiles,
shouldReplaceStoredOAuthCredential,
} = await import("./auth-profiles/external-cli-sync.js"));
({ OPENAI_CODEX_DEFAULT_PROFILE_ID, MINIMAX_CLI_PROFILE_ID } =
await import("./auth-profiles/constants.js"));
});
@@ -120,150 +104,110 @@ describe("syncExternalCliCredentials", () => {
});
});
it.each([{ providerLabel: "Codex" }, { providerLabel: "MiniMax" }])(
"syncs $providerLabel CLI credentials into the target auth profile",
({ providerLabel }) => {
const providerCase = getProviderCases().find((entry) => entry.label === providerLabel);
expect(providerCase).toBeDefined();
const current = providerCase!;
const expires = Date.now() + 60_000;
current.readMock.mockReturnValue(
makeOAuthCredential({
provider: current.provider,
access: `${current.provider}-access-token`,
refresh: `${current.provider}-refresh-token`,
expires,
accountId: "acct_123",
}),
);
const store = makeStore();
const mutated = syncExternalCliCredentials(store);
expect(mutated).toBe(true);
expect(current.readMock).toHaveBeenCalledWith(
expect.objectContaining({ ttlMs: expect.any(Number) }),
);
expect(store.profiles[current.profileId]).toMatchObject({
type: "oauth",
provider: current.provider,
access: `${current.provider}-access-token`,
refresh: `${current.provider}-refresh-token`,
expires,
accountId: "acct_123",
managedBy: current.provider === "openai-codex" ? "codex-cli" : ("minimax-cli" as const),
});
if (current.legacyProfileId) {
expect(store.profiles[current.legacyProfileId]).toBeUndefined();
}
},
);
it("refreshes stored Codex expiry from external CLI even when the cached profile looks fresh", () => {
const staleExpiry = Date.now() + 30 * 60_000;
const freshExpiry = Date.now() + 5 * 24 * 60 * 60_000;
it("reads codex external cli credentials by profile id", () => {
mocks.readCodexCliCredentialsCached.mockReturnValue(
makeOAuthCredential({
provider: "openai-codex",
access: "new-access-token",
refresh: "new-refresh-token",
expires: freshExpiry,
accountId: "acct_456",
access: "codex-access-token",
refresh: "codex-refresh-token",
}),
);
const store = makeStore(
OPENAI_CODEX_DEFAULT_PROFILE_ID,
makeOAuthCredential({
provider: "openai-codex",
access: "old-access-token",
refresh: "old-refresh-token",
expires: staleExpiry,
accountId: "acct_456",
}),
);
const credential = readManagedExternalCliCredential({
profileId: OPENAI_CODEX_DEFAULT_PROFILE_ID,
credential: makeOAuthCredential({ provider: "openai-codex" }),
});
const mutated = syncExternalCliCredentials(store);
expect(mutated).toBe(true);
expect(store.profiles[OPENAI_CODEX_DEFAULT_PROFILE_ID]).toMatchObject({
access: "new-access-token",
refresh: "new-refresh-token",
expires: freshExpiry,
managedBy: "codex-cli",
expect(credential).toMatchObject({
access: "codex-access-token",
refresh: "codex-refresh-token",
});
});
it.each([{ providerLabel: "Codex" }, { providerLabel: "MiniMax" }])(
"does not overwrite newer stored $providerLabel credentials",
({ providerLabel }) => {
const providerCase = getProviderCases().find((entry) => entry.label === providerLabel);
expect(providerCase).toBeDefined();
const current = providerCase!;
const staleExpiry = Date.now() + 30 * 60_000;
const freshExpiry = Date.now() + 5 * 24 * 60 * 60_000;
current.readMock.mockReturnValue(
makeOAuthCredential({
provider: current.provider,
access: `stale-${current.provider}-access-token`,
refresh: `stale-${current.provider}-refresh-token`,
expires: staleExpiry,
accountId: "acct_789",
}),
);
it("returns null when the profile id/provider do not map to the same external source", () => {
mocks.readCodexCliCredentialsCached.mockReturnValue(
makeOAuthCredential({ provider: "openai-codex" }),
);
const store = makeStore(
current.profileId,
makeOAuthCredential({
provider: current.provider,
access: `fresh-${current.provider}-access-token`,
refresh: `fresh-${current.provider}-refresh-token`,
expires: freshExpiry,
accountId: "acct_789",
}),
);
const credential = readManagedExternalCliCredential({
profileId: OPENAI_CODEX_DEFAULT_PROFILE_ID,
credential: makeOAuthCredential({ provider: "anthropic" }),
});
const mutated = syncExternalCliCredentials(store);
expect(credential).toBeNull();
});
expect(mutated).toBe(false);
expect(store.profiles[current.profileId]).toMatchObject({
access: `fresh-${current.provider}-access-token`,
refresh: `fresh-${current.provider}-refresh-token`,
expires: freshExpiry,
});
},
);
it("upgrades matching Codex CLI credentials with external ownership metadata", () => {
const expires = Date.now() + 60_000;
it("resolves fresher codex and minimax external oauth profiles as runtime overlays", () => {
mocks.readCodexCliCredentialsCached.mockReturnValue(
makeOAuthCredential({
provider: "openai-codex",
access: "same-access-token",
refresh: "same-refresh-token",
expires,
access: "codex-fresh-access",
refresh: "codex-fresh-refresh",
expires: Date.now() + 5 * 24 * 60 * 60_000,
}),
);
mocks.readMiniMaxCliCredentialsCached.mockReturnValue(
makeOAuthCredential({
provider: "minimax-portal",
access: "minimax-fresh-access",
refresh: "minimax-fresh-refresh",
expires: Date.now() + 5 * 24 * 60 * 60_000,
}),
);
const store = makeStore(
OPENAI_CODEX_DEFAULT_PROFILE_ID,
const profiles = resolveExternalCliAuthProfiles({
version: 1,
profiles: {
[OPENAI_CODEX_DEFAULT_PROFILE_ID]: makeOAuthCredential({
provider: "openai-codex",
access: "codex-stale-access",
refresh: "codex-stale-refresh",
expires: Date.now() - 5_000,
}),
[MINIMAX_CLI_PROFILE_ID]: makeOAuthCredential({
provider: "minimax-portal",
access: "minimax-stale-access",
refresh: "minimax-stale-refresh",
expires: Date.now() - 5_000,
}),
},
});
const profilesById = new Map(
profiles.map((profile) => [profile.profileId, profile.credential]),
);
expect(profilesById.get(OPENAI_CODEX_DEFAULT_PROFILE_ID)).toMatchObject({
access: "codex-fresh-access",
refresh: "codex-fresh-refresh",
});
expect(profilesById.get(MINIMAX_CLI_PROFILE_ID)).toMatchObject({
access: "minimax-fresh-access",
refresh: "minimax-fresh-refresh",
});
});
it("does not emit runtime overlays when the stored credential is newer", () => {
mocks.readCodexCliCredentialsCached.mockReturnValue(
makeOAuthCredential({
provider: "openai-codex",
access: "same-access-token",
refresh: "same-refresh-token",
expires,
access: "stale-external-access",
refresh: "stale-external-refresh",
expires: Date.now() - 5_000,
}),
);
const mutated = syncExternalCliCredentials(store);
const profiles = resolveExternalCliAuthProfiles(
makeStore(
OPENAI_CODEX_DEFAULT_PROFILE_ID,
makeOAuthCredential({
provider: "openai-codex",
access: "fresh-store-access",
refresh: "fresh-store-refresh",
expires: Date.now() + 5 * 24 * 60 * 60_000,
}),
),
);
expect(mutated).toBe(true);
expect(store.profiles[OPENAI_CODEX_DEFAULT_PROFILE_ID]).toMatchObject({
access: "same-access-token",
refresh: "same-refresh-token",
expires,
managedBy: "codex-cli",
});
expect(profiles).toEqual([]);
});
});

View File

@@ -3,20 +3,11 @@ import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { AUTH_STORE_VERSION } from "./auth-profiles/constants.js";
import type { AuthProfileStore } from "./auth-profiles/types.js";
const AUTH_STORE_CACHE_TTL_MS = 15 * 60 * 1000;
const mocks = vi.hoisted(() => ({
syncExternalCliCredentials: vi.fn((_: AuthProfileStore) => false),
}));
vi.mock("./auth-profiles/external-cli-sync.js", () => ({
syncExternalCliCredentials: mocks.syncExternalCliCredentials,
}));
const resolveExternalAuthProfilesWithPluginsMock = vi.fn(() => []);
vi.mock("../plugins/provider-runtime.js", () => ({
resolveExternalAuthProfilesWithPlugins: () => [],
resolveExternalAuthProfilesWithPlugins: resolveExternalAuthProfilesWithPluginsMock,
}));
let clearRuntimeAuthProfileStoreSnapshots: typeof import("./auth-profiles.js").clearRuntimeAuthProfileStoreSnapshots;
@@ -82,23 +73,29 @@ describe("auth profile store cache", () => {
afterEach(() => {
vi.useRealTimers();
clearRuntimeAuthProfileStoreSnapshots();
resolveExternalAuthProfilesWithPluginsMock.mockReset();
resolveExternalAuthProfilesWithPluginsMock.mockReturnValue([]);
vi.clearAllMocks();
});
it("reuses the synced auth store while auth-profiles.json is unchanged", async () => {
it("reuses the cached auth store while auth-profiles.json is unchanged", async () => {
await withAgentDirEnv("openclaw-auth-store-cache-", (agentDir) => {
writeAuthStore(agentDir, "sk-test");
const authPath = writeAuthStore(agentDir, "sk-test");
const readFileSyncSpy = vi.spyOn(fs, "readFileSync");
ensureAuthProfileStore(agentDir);
ensureAuthProfileStore(agentDir);
expect(mocks.syncExternalCliCredentials).toHaveBeenCalledTimes(1);
expect(
readFileSyncSpy.mock.calls.filter(([target]) => String(target) === authPath),
).toHaveLength(1);
});
});
it("refreshes the cached auth store after auth-profiles.json changes", async () => {
await withAgentDirEnv("openclaw-auth-store-refresh-", async (agentDir) => {
const authPath = writeAuthStore(agentDir, "sk-test-1");
const readFileSyncSpy = vi.spyOn(fs, "readFileSync");
ensureAuthProfileStore(agentDir);
@@ -108,30 +105,35 @@ describe("auth profile store cache", () => {
const reloaded = ensureAuthProfileStore(agentDir);
expect(mocks.syncExternalCliCredentials).toHaveBeenCalledTimes(2);
expect(
readFileSyncSpy.mock.calls.filter(([target]) => String(target) === authPath),
).toHaveLength(2);
expect(reloaded.profiles["openai:default"]).toMatchObject({
key: "sk-test-2",
});
});
});
it("re-syncs external CLI credentials after the cache ttl when auth-profiles.json is absent", () => {
it("reapplies runtime-only external auth overlays over a cached missing auth store", () => {
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-auth-store-missing-"));
const previousAgentDir = process.env.OPENCLAW_AGENT_DIR;
const previousPiAgentDir = process.env.PI_CODING_AGENT_DIR;
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-03-21T15:00:00.000Z"));
let syncCount = 0;
mocks.syncExternalCliCredentials.mockImplementation((store) => {
syncCount += 1;
store.profiles["openai-codex:default"] = {
type: "oauth",
provider: "openai-codex",
access: `access-${syncCount}`,
refresh: `refresh-${syncCount}`,
expires: Date.now() + 60_000,
};
return true;
let overlayCount = 0;
resolveExternalAuthProfilesWithPluginsMock.mockImplementation(() => {
overlayCount += 1;
return [
{
profileId: "openai-codex:default",
credential: {
type: "oauth" as const,
provider: "openai-codex",
access: `access-${overlayCount}`,
refresh: `refresh-${overlayCount}`,
expires: Date.now() + 60_000,
},
persistence: "runtime-only" as const,
},
];
});
try {
process.env.OPENCLAW_AGENT_DIR = agentDir;
@@ -141,15 +143,8 @@ describe("auth profile store cache", () => {
const second = ensureAuthProfileStore(agentDir);
expect(first.profiles["openai-codex:default"]).toMatchObject({ access: "access-1" });
expect(second.profiles["openai-codex:default"]).toMatchObject({ access: "access-1" });
expect(mocks.syncExternalCliCredentials).toHaveBeenCalledTimes(1);
vi.advanceTimersByTime(AUTH_STORE_CACHE_TTL_MS + 1);
const third = ensureAuthProfileStore(agentDir);
expect(mocks.syncExternalCliCredentials).toHaveBeenCalledTimes(2);
expect(third.profiles["openai-codex:default"]).toMatchObject({ access: "access-2" });
expect(second.profiles["openai-codex:default"]).toMatchObject({ access: "access-2" });
expect(resolveExternalAuthProfilesWithPluginsMock).toHaveBeenCalledTimes(2);
} finally {
if (previousAgentDir === undefined) {
delete process.env.OPENCLAW_AGENT_DIR;

View File

@@ -4,15 +4,10 @@ import {
} from "../cli-credentials.js";
import {
EXTERNAL_CLI_SYNC_TTL_MS,
OPENAI_CODEX_DEFAULT_PROFILE_ID,
MINIMAX_CLI_PROFILE_ID,
log,
OPENAI_CODEX_DEFAULT_PROFILE_ID,
} from "./constants.js";
import type { AuthProfileStore, ExternalOAuthManager, OAuthCredential } from "./types.js";
type ExternalCliSyncOptions = {
log?: boolean;
};
import type { AuthProfileStore, OAuthCredential } from "./types.js";
export type ExternalCliResolvedProfile = {
profileId: string;
@@ -22,7 +17,6 @@ export type ExternalCliResolvedProfile = {
type ExternalCliSyncProvider = {
profileId: string;
provider: string;
managedBy: ExternalOAuthManager;
readCredentials: () => OAuthCredential | null;
};
@@ -44,8 +38,7 @@ export function areOAuthCredentialsEquivalent(
a.email === b.email &&
a.enterpriseUrl === b.enterpriseUrl &&
a.projectId === b.projectId &&
a.accountId === b.accountId &&
a.managedBy === b.managedBy
a.accountId === b.accountId
);
}
@@ -78,69 +71,40 @@ const EXTERNAL_CLI_SYNC_PROVIDERS: ExternalCliSyncProvider[] = [
{
profileId: MINIMAX_CLI_PROFILE_ID,
provider: "minimax-portal",
managedBy: "minimax-cli",
readCredentials: () => readMiniMaxCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS }),
},
{
profileId: OPENAI_CODEX_DEFAULT_PROFILE_ID,
provider: "openai-codex",
managedBy: "codex-cli",
readCredentials: () => readCodexCliCredentialsCached({ ttlMs: EXTERNAL_CLI_SYNC_TTL_MS }),
},
];
function withExternalCliManager(
creds: OAuthCredential,
managedBy: ExternalOAuthManager,
): OAuthCredential {
return {
...creds,
managedBy,
};
}
function stripExternalCliManager(creds: OAuthCredential): OAuthCredential {
const { managedBy: _managedBy, ...runtimeCredential } = creds;
return runtimeCredential;
}
function resolveExternalCliSyncProvider(params: {
profileId?: string;
profileId: string;
credential?: OAuthCredential;
}): ExternalCliSyncProvider | null {
const byProfileId =
typeof params.profileId === "string"
? EXTERNAL_CLI_SYNC_PROVIDERS.find((entry) => entry.profileId === params.profileId)
: undefined;
if (byProfileId) {
return byProfileId;
}
const managedBy = params.credential?.managedBy;
if (!managedBy) {
const provider = EXTERNAL_CLI_SYNC_PROVIDERS.find(
(entry) => entry.profileId === params.profileId,
);
if (!provider) {
return null;
}
return (
EXTERNAL_CLI_SYNC_PROVIDERS.find(
(entry) =>
entry.managedBy === managedBy &&
(!params.credential || entry.provider === params.credential.provider),
) ?? null
);
if (params.credential && provider.provider !== params.credential.provider) {
return null;
}
return provider;
}
export function readManagedExternalCliCredential(params: {
profileId?: string;
profileId: string;
credential: OAuthCredential;
}): OAuthCredential | null {
const provider = resolveExternalCliSyncProvider(params);
if (!provider) {
return null;
}
const creds = provider.readCredentials();
if (!creds) {
return null;
}
return withExternalCliManager(creds, provider.managedBy);
return provider.readCredentials();
}
export function resolveExternalCliAuthProfiles(
@@ -152,83 +116,18 @@ export function resolveExternalCliAuthProfiles(
if (!creds) {
continue;
}
const runtimeCredential = stripExternalCliManager(
withExternalCliManager(creds, providerConfig.managedBy),
);
const existing = store.profiles[providerConfig.profileId];
const existingOAuth = existing?.type === "oauth" ? existing : undefined;
if (
!shouldReplaceStoredOAuthCredential(existingOAuth, runtimeCredential) &&
!areOAuthCredentialsEquivalent(existingOAuth, runtimeCredential)
!shouldReplaceStoredOAuthCredential(existingOAuth, creds) &&
!areOAuthCredentialsEquivalent(existingOAuth, creds)
) {
continue;
}
profiles.push({
profileId: providerConfig.profileId,
credential: runtimeCredential,
credential: creds,
});
}
return profiles;
}
/** Sync external CLI credentials into the store for a given provider. */
function syncExternalCliCredentialsForProvider(
store: AuthProfileStore,
providerConfig: ExternalCliSyncProvider,
options: ExternalCliSyncOptions,
): boolean {
const { profileId, provider, managedBy, readCredentials } = providerConfig;
const existing = store.profiles[profileId];
const creds = readCredentials();
if (!creds) {
return false;
}
const managedCreds = withExternalCliManager(creds, managedBy);
const existingOAuth = existing?.type === "oauth" ? existing : undefined;
if (!shouldReplaceStoredOAuthCredential(existingOAuth, managedCreds)) {
if (options.log !== false) {
if (!areOAuthCredentialsEquivalent(existingOAuth, managedCreds) && existingOAuth) {
log.debug(`kept newer stored ${provider} credentials over external cli sync`, {
profileId,
storedExpires: new Date(existingOAuth.expires).toISOString(),
externalExpires: Number.isFinite(managedCreds.expires)
? new Date(managedCreds.expires).toISOString()
: null,
});
}
}
return false;
}
store.profiles[profileId] = managedCreds;
if (options.log !== false) {
log.info(`synced ${provider} credentials from external cli`, {
profileId,
expires: new Date(managedCreds.expires).toISOString(),
managedBy,
});
}
return true;
}
/**
* Sync OAuth credentials from external CLI tools (MiniMax CLI, Codex CLI)
* into the store.
*
* Returns true if any credentials were updated.
*/
export function syncExternalCliCredentials(
store: AuthProfileStore,
options: ExternalCliSyncOptions = {},
): boolean {
let mutated = false;
for (const provider of EXTERNAL_CLI_SYNC_PROVIDERS) {
if (syncExternalCliCredentialsForProvider(store, provider, options)) {
mutated = true;
}
}
return mutated;
}

View File

@@ -72,8 +72,8 @@ vi.mock("./external-auth.js", () => ({
}));
vi.mock("./external-cli-sync.js", () => ({
syncExternalCliCredentials: () => false,
readManagedExternalCliCredential: () => null,
resolveExternalCliAuthProfiles: () => [],
areOAuthCredentialsEquivalent: (a: unknown, b: unknown) => a === b,
}));

View File

@@ -79,8 +79,8 @@ vi.mock("./doctor.js", () => ({
}));
vi.mock("./external-cli-sync.js", () => ({
syncExternalCliCredentials: () => false,
readManagedExternalCliCredential: () => null,
resolveExternalCliAuthProfiles: () => [],
areOAuthCredentialsEquivalent: (a: unknown, b: unknown) => a === b,
}));

View File

@@ -65,8 +65,8 @@ vi.mock("./doctor.js", () => ({
// credential files; it is slow and can pollute test state. Stub it to a no-op
// so the suite only exercises in-repo auth-profile logic.
vi.mock("./external-cli-sync.js", () => ({
syncExternalCliCredentials: () => false,
readManagedExternalCliCredential: () => null,
resolveExternalCliAuthProfiles: () => [],
areOAuthCredentialsEquivalent: (a: unknown, b: unknown) => a === b,
}));

View File

@@ -76,8 +76,8 @@ vi.mock("./doctor.js", () => ({
}));
vi.mock("./external-cli-sync.js", () => ({
syncExternalCliCredentials: () => false,
readManagedExternalCliCredential: () => null,
resolveExternalCliAuthProfiles: () => [],
areOAuthCredentialsEquivalent: (a: unknown, b: unknown) => a === b,
}));

View File

@@ -12,10 +12,6 @@ import {
} from "./models-config.e2e-harness.js";
import type { ProviderConfig as ModelsProviderConfig } from "./models-config.providers.secrets.js";
vi.mock("./auth-profiles/external-cli-sync.js", () => ({
syncExternalCliCredentials: () => false,
}));
vi.mock("./models-config.providers.js", async () => {
function createImplicitProvider(baseUrl: string): ModelsProviderConfig {
return {

View File

@@ -11,7 +11,7 @@ vi.mock("../plugins/provider-runtime.js", () => ({
vi.mock("./auth-profiles/external-cli-sync.js", () => ({
readManagedExternalCliCredential: () => null,
syncExternalCliCredentials: () => false,
resolveExternalCliAuthProfiles: () => [],
}));
type AuthProfileStore = Parameters<typeof saveAuthProfileStore>[0];