fix(models): complete full catalog auth refresh

This commit is contained in:
joshavant
2026-08-12 15:20:04 -05:00
parent 3864270f60
commit 7cf4981ef7
7 changed files with 40 additions and 27 deletions
@@ -132,6 +132,11 @@ function listExternalCliProviderIds(providerConfig: ExternalCliSyncProvider): st
return [providerConfig.provider, ...(providerConfig.aliases ?? [])];
}
/** Provider ids whose external CLI credentials can be refreshed by this owner. */
export function listExternalCliSyncProviderIds(): string[] {
return [...new Set(EXTERNAL_CLI_SYNC_PROVIDERS.flatMap(listExternalCliProviderIds))];
}
function normalizeExternalCliCredentialProvider(
credential: OAuthCredential | null,
provider: string,
@@ -22,7 +22,6 @@ import {
getPreparedModelFullCatalogAuth,
runPreparedModelCatalogWorker,
} from "./prepared-model-catalog-worker.js";
import { copyPreparedModelRuntimeAuthState } from "./prepared-model-runtime-auth.js";
import { startSerializedSnapshotBuild } from "./prepared-model-runtime.build.js";
import type { PreparedModelRuntimeAgentFacts } from "./prepared-model-runtime.facts.js";
import { AuthStorage } from "./sessions/auth-storage.js";
@@ -281,12 +280,21 @@ describe("prepared model catalog worker boundary", () => {
config,
modelCatalog: { entries: [route], routeVariants: [route] },
});
copyPreparedModelRuntimeAuthState(fixture.snapshot, owner);
const project = async () =>
await loadGatewayModelCatalogSnapshot({
const project = async () => {
const fullCatalog = await fixture.snapshot.loadFullModelCatalog?.();
const fullAuth = fullCatalog && getPreparedModelFullCatalogAuth(fullCatalog);
if (!fullAuth) {
throw new Error("full catalog omitted prepared auth");
}
return await loadGatewayModelCatalogSnapshot({
getConfig: () => config,
loadPublishedPreparedModelCatalogOwnerSnapshot: async () => owner,
loadPublishedPreparedModelCatalogOwnerSnapshot: async () => ({
...owner,
authModes: fullAuth.authModes,
authStore: fullAuth.authStore,
}),
});
};
const projectModels = async () => {
const projected = await project();
const context = {
@@ -388,11 +396,19 @@ describe("prepared model catalog worker boundary", () => {
config,
modelCatalog: { entries: [route], routeVariants: [route] },
});
copyPreparedModelRuntimeAuthState(fixture.snapshot, owner);
const listModels = async () => {
const fullCatalog = await fixture.snapshot.loadFullModelCatalog?.();
const fullAuth = fullCatalog && getPreparedModelFullCatalogAuth(fullCatalog);
if (!fullAuth) {
throw new Error("full catalog omitted prepared auth");
}
const projected = await loadGatewayModelCatalogSnapshot({
getConfig: () => config,
loadPublishedPreparedModelCatalogOwnerSnapshot: async () => owner,
loadPublishedPreparedModelCatalogOwnerSnapshot: async () => ({
...owner,
authModes: fullAuth.authModes,
authStore: fullAuth.authStore,
}),
});
const context = {
getRuntimeConfig: () => config,
+1 -1
View File
@@ -56,7 +56,7 @@ const authByFullCatalog = new WeakMap<
Readonly<{ authStore: AuthProfileStore; authModes: PreparedAgentCredentialModes }>
>();
export function setPreparedModelFullCatalogAuth(
function setPreparedModelFullCatalogAuth(
modelCatalog: object,
auth: Readonly<{ authStore: AuthProfileStore; authModes: PreparedAgentCredentialModes }>,
): void {
+9 -2
View File
@@ -10,6 +10,9 @@ const mocks = vi.hoisted(() => ({
loadSnapshot: vi.fn(),
prepareSnapshot: vi.fn(),
prepareScopedCatalog: vi.fn(),
fullCatalogAuth: undefined as
| undefined
| { authStore: { version: number; profiles: object }; authModes: object },
isFullCatalog: vi.fn(),
releaseSnapshot: vi.fn(),
}));
@@ -53,11 +56,14 @@ vi.mock("./prepared-model-runtime.facts.js", () => ({
isPreparedModelCatalogFull: (...args: unknown[]) => mocks.isFullCatalog(...args),
}));
vi.mock("./prepared-model-catalog-worker.js", () => ({
getPreparedModelFullCatalogAuth: () => mocks.fullCatalogAuth,
}));
vi.mock("./prepared-model-runtime.scoped-catalog.js", () => ({
prepareScopedReadOnlyModelCatalog: (...args: unknown[]) => mocks.prepareScopedCatalog(...args),
}));
import { setPreparedModelFullCatalogAuth } from "./prepared-model-catalog-worker.js";
import { PreparedModelCatalogConfigReplacedError } from "./prepared-model-catalog.errors.js";
import {
getPublishedPreparedModelCatalogOwnerSnapshot,
@@ -99,6 +105,7 @@ describe("prepared model catalog access", () => {
mocks.loadSnapshot.mockReset();
mocks.prepareSnapshot.mockReset();
mocks.prepareScopedCatalog.mockReset();
mocks.fullCatalogAuth = undefined;
mocks.isFullCatalog.mockReset();
mocks.releaseSnapshot.mockReset();
});
@@ -200,7 +207,7 @@ describe("prepared model catalog access", () => {
routeVariants: [],
};
const { authStore, ...snapshotFacts } = fullSnapshot;
setPreparedModelFullCatalogAuth(discoveredCatalog, { authStore, authModes: {} });
mocks.fullCatalogAuth = { authStore, authModes: {} };
const loadFullModelCatalog = vi.fn(async () => discoveredCatalog);
const snapshot = {
...snapshotFacts,
@@ -7,6 +7,7 @@ import {
} from "./agent-auth-credentials.js";
import { resolveAmbientAgentCredentialsForDiscovery } from "./agent-auth-discovery.js";
import { overlayExternalAuthProfiles } from "./auth-profiles/external-auth.js";
import { listExternalCliSyncProviderIds } from "./auth-profiles/external-cli-sync.js";
import { replaceRuntimeAuthProfileStoreSnapshots } from "./auth-profiles/runtime-snapshots.js";
import {
loadAuthProfileStoreWithoutExternalProfiles,
@@ -98,6 +99,7 @@ export async function runPreparedModelCatalogWorkerInput(
authStore: value.authStore,
config: value.input.config,
env: value.input.env ?? process.env,
providerIds: listExternalCliSyncProviderIds(),
});
replaceRuntimeAuthProfileStoreSnapshots([{ agentDir: value.input.agentDir, store: authStore }]);
const ambientCredentials = withPluginRuntimeRegistryScope(
-12
View File
@@ -48,15 +48,3 @@ export function getPreparedModelRuntimeAuthMaterializations(
): readonly RuntimeAuthMaterialization[] {
return materializationsBySnapshot.get(snapshot) ?? [];
}
export function copyPreparedModelRuntimeAuthState(source: object, target: object): void {
const authStore = authStoreBySnapshot.get(source);
if (authStore) {
authStoreBySnapshot.set(target, authStore);
}
const authStoreLoader = authStoreLoaderBySnapshot.get(source);
if (authStoreLoader) {
authStoreLoaderBySnapshot.set(target, authStoreLoader);
}
materializationsBySnapshot.set(target, getPreparedModelRuntimeAuthMaterializations(source));
}
@@ -7,7 +7,6 @@ import {
} from "../../agents/agent-auth-credentials.js";
import type { AuthProfileStore } from "../../agents/auth-profiles.js";
import type { ModelCatalogEntry } from "../../agents/model-catalog.types.js";
import { setPreparedModelFullCatalogAuth } from "../../agents/prepared-model-catalog-worker.js";
import {
getPreparedModelRuntimeAuthStore,
setPreparedModelRuntimeAuthStore,
@@ -498,10 +497,6 @@ describe("gateway chat metadata runtime", () => {
...owner.modelCatalog,
providerOutcomes: [{ provider: "openai", status: "auth-rejected" as const }],
};
setPreparedModelFullCatalogAuth(fullCatalog, {
authStore: getPreparedModelRuntimeAuthStore(owner)!,
authModes: owner.authModes,
});
const loadFullModelCatalog = vi.fn(async () => fullCatalog);
harness.setOwner({
...owner,