fix: keep usage auth gate runtime-free

This commit is contained in:
Gustavo Madeira Santana
2026-04-20 22:04:30 -04:00
parent 9422a2125e
commit c857e2dc12
4 changed files with 47 additions and 8 deletions

View File

@@ -27,6 +27,7 @@ export {
export {
clearRuntimeAuthProfileStoreSnapshots,
ensureAuthProfileStore,
ensureAuthProfileStoreWithoutExternalProfiles,
hasAnyAuthProfileStoreSource,
loadAuthProfileStoreForSecretsRuntime,
loadAuthProfileStoreWithoutExternalProfiles,

View File

@@ -298,23 +298,30 @@ export function loadAuthProfileStoreWithoutExternalProfiles(agentDir?: string):
export function ensureAuthProfileStore(
agentDir?: string,
options?: { allowKeychainPrompt?: boolean },
): AuthProfileStore {
return overlayExternalAuthProfiles(
ensureAuthProfileStoreWithoutExternalProfiles(agentDir, options),
{ agentDir },
);
}
export function ensureAuthProfileStoreWithoutExternalProfiles(
agentDir?: string,
options?: { allowKeychainPrompt?: boolean },
): AuthProfileStore {
const runtimeStore = resolveRuntimeAuthProfileStore(agentDir);
if (runtimeStore) {
return overlayExternalAuthProfiles(runtimeStore, { agentDir });
return runtimeStore;
}
const store = loadAuthProfileStoreForAgent(agentDir, options);
const authPath = resolveAuthStorePath(agentDir);
const mainAuthPath = resolveAuthStorePath();
if (!agentDir || authPath === mainAuthPath) {
return overlayExternalAuthProfiles(store, { agentDir });
return store;
}
const mainStore = loadAuthProfileStoreForAgent(undefined, options);
const merged = mergeAuthProfileStores(mainStore, store);
return overlayExternalAuthProfiles(merged, { agentDir });
return mergeAuthProfileStores(mainStore, store);
}
export function findPersistedAuthProfileCredential(params: {

View File

@@ -10,11 +10,16 @@ const hasAnyAuthProfileStoreSourceMock = vi.fn(() => false);
const ensureAuthProfileStoreMock = vi.fn(() => ({
profiles: {},
}));
const ensureAuthProfileStoreWithoutExternalProfilesMock = vi.fn(() => ({
profiles: {},
}));
const resolveAuthProfileOrderMock = vi.fn((_params: unknown): string[] => []);
vi.mock("../agents/auth-profiles.js", () => ({
dedupeProfileIds: (profileIds: string[]) => [...new Set(profileIds)],
ensureAuthProfileStore: () => ensureAuthProfileStoreMock(),
ensureAuthProfileStoreWithoutExternalProfiles: () =>
ensureAuthProfileStoreWithoutExternalProfilesMock(),
hasAnyAuthProfileStoreSource: () => hasAnyAuthProfileStoreSourceMock(),
listProfilesForProvider: () => [],
resolveApiKeyForProfile: async () => null,
@@ -54,6 +59,10 @@ describe("resolveProviderAuths plugin boundary", () => {
ensureAuthProfileStoreMock.mockReturnValue({
profiles: {},
});
ensureAuthProfileStoreWithoutExternalProfilesMock.mockClear();
ensureAuthProfileStoreWithoutExternalProfilesMock.mockReturnValue({
profiles: {},
});
resolveAuthProfileOrderMock.mockReset();
resolveAuthProfileOrderMock.mockReturnValue([]);
resolveProviderUsageAuthWithPluginMock.mockReset();
@@ -160,7 +169,7 @@ describe("resolveProviderAuths plugin boundary", () => {
it("keeps auth-profile credential sources provider-specific", async () => {
hasAnyAuthProfileStoreSourceMock.mockReturnValue(true);
ensureAuthProfileStoreMock.mockReturnValue({
ensureAuthProfileStoreWithoutExternalProfilesMock.mockReturnValue({
profiles: {
"anthropic:default": {
type: "api_key",
@@ -201,6 +210,25 @@ describe("resolveProviderAuths plugin boundary", () => {
provider: "anthropic",
}),
);
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
});
it("does not overlay external auth profiles while checking the skip gate", async () => {
hasAnyAuthProfileStoreSourceMock.mockReturnValue(true);
await withTempHome(async (homeDir) => {
await expect(
resolveProviderAuths({
providers: ["anthropic"],
skipPluginAuthWithoutCredentialSource: true,
env: { HOME: homeDir },
}),
).resolves.toEqual([]);
});
expect(ensureAuthProfileStoreWithoutExternalProfilesMock).toHaveBeenCalledTimes(1);
expect(ensureAuthProfileStoreMock).not.toHaveBeenCalled();
expect(resolveProviderUsageAuthWithPluginMock).not.toHaveBeenCalled();
});
it("skips plugin usage auth per provider when only another provider has direct credentials", async () => {

View File

@@ -1,6 +1,7 @@
import {
dedupeProfileIds,
ensureAuthProfileStore,
ensureAuthProfileStoreWithoutExternalProfiles,
hasAnyAuthProfileStoreSource,
listProfilesForProvider,
resolveApiKeyForProfile,
@@ -230,7 +231,9 @@ function hasAuthProfileCredentialSource(params: {
state: UsageAuthState;
provider: UsageProviderId;
}): boolean {
const store = resolveUsageAuthStore(params.state);
const store = ensureAuthProfileStoreWithoutExternalProfiles(params.state.agentDir, {
allowKeychainPrompt: false,
});
const order = resolveAuthProfileOrder({
cfg: params.state.cfg,
store,