mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(gateway): refresh model availability from persisted auth (#102289)
This commit is contained in:
@@ -8,7 +8,7 @@ import {
|
||||
resolveDefaultAgentId,
|
||||
} from "../../agents/agent-scope.js";
|
||||
import {
|
||||
ensureAuthProfileStoreWithoutExternalProfiles,
|
||||
loadAuthProfileStoreWithoutExternalProfiles,
|
||||
resolveAuthProfileOrder,
|
||||
type AuthProfileCredential,
|
||||
type AuthProfileStore,
|
||||
@@ -170,10 +170,10 @@ function createModelsListProviderAuthChecker(params: {
|
||||
workspaceDir: string;
|
||||
}): ModelsListProviderAuthChecker {
|
||||
const agentDir = resolveAgentDir(params.cfg, params.agentId);
|
||||
const store = ensureAuthProfileStoreWithoutExternalProfiles(agentDir, {
|
||||
// Auth refreshes can be persisted by another CLI process while the gateway
|
||||
// keeps an older execution snapshot, so browse availability reads SQLite.
|
||||
const store = loadAuthProfileStoreWithoutExternalProfiles(agentDir, {
|
||||
allowKeychainPrompt: false,
|
||||
readOnly: true,
|
||||
syncExternalCli: false,
|
||||
});
|
||||
return createInFlightProviderAuthChecker(
|
||||
(provider, modelApi) =>
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// validation errors, and protocol response shapes.
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { ErrorCodes } from "../../../packages/gateway-protocol/src/index.js";
|
||||
import {
|
||||
clearRuntimeAuthProfileStoreSnapshots,
|
||||
replaceRuntimeAuthProfileStoreSnapshots,
|
||||
} from "../../agents/auth-profiles.js";
|
||||
import type { OpenClawConfig } from "../../config/types.openclaw.js";
|
||||
import { createDeferred } from "../../test-utils/deferred.js";
|
||||
import { withEnvAsync } from "../../test-utils/env.js";
|
||||
@@ -21,6 +25,21 @@ const withoutOpenAIEnvAuth = async <T>(run: () => Promise<T>): Promise<T> =>
|
||||
run,
|
||||
);
|
||||
|
||||
function createDemoOAuthStore(params: { access: string; expires: number }) {
|
||||
return {
|
||||
version: 1 as const,
|
||||
profiles: {
|
||||
"demo-provider:oauth": {
|
||||
type: "oauth" as const,
|
||||
provider: "demo-provider",
|
||||
access: params.access,
|
||||
refresh: "refresh-token",
|
||||
expires: params.expires,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function requestModelsList(params: {
|
||||
view: "configured" | "all";
|
||||
respond?: ReturnType<typeof vi.fn>;
|
||||
@@ -488,18 +507,12 @@ describe("models.list", () => {
|
||||
agentEnv: "main",
|
||||
},
|
||||
async (state) => {
|
||||
await state.writeAuthProfiles({
|
||||
version: 1,
|
||||
profiles: {
|
||||
"demo-provider:expired": {
|
||||
type: "oauth",
|
||||
provider: "demo-provider",
|
||||
access: "expired-access",
|
||||
refresh: "refresh-token",
|
||||
expires: Date.now() - 60_000,
|
||||
},
|
||||
},
|
||||
});
|
||||
await state.writeAuthProfiles(
|
||||
createDemoOAuthStore({
|
||||
access: "expired-access",
|
||||
expires: Date.now() - 60_000,
|
||||
}),
|
||||
);
|
||||
|
||||
const { request, respond } = requestModelsList({
|
||||
view: "all",
|
||||
@@ -528,6 +541,64 @@ describe("models.list", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("uses refreshed persisted OAuth when the runtime auth snapshot is stale", async () => {
|
||||
await withOpenClawTestState(
|
||||
{
|
||||
layout: "state-only",
|
||||
prefix: "openclaw-models-list-stale-runtime-profile-",
|
||||
agentEnv: "main",
|
||||
},
|
||||
async (state) => {
|
||||
const agentDir = state.agentDir();
|
||||
await state.writeAuthProfiles(
|
||||
createDemoOAuthStore({
|
||||
access: "refreshed-access",
|
||||
expires: Date.now() + 60 * 60_000,
|
||||
}),
|
||||
);
|
||||
replaceRuntimeAuthProfileStoreSnapshots([
|
||||
{
|
||||
agentDir,
|
||||
store: createDemoOAuthStore({
|
||||
access: "expired-access",
|
||||
expires: Date.now() - 60_000,
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
try {
|
||||
const { request, respond } = requestModelsList({
|
||||
view: "all",
|
||||
loadGatewayModelCatalog: vi.fn(() =>
|
||||
Promise.resolve([
|
||||
{ id: "demo-model", name: "Demo Model", provider: "demo-provider" },
|
||||
]),
|
||||
),
|
||||
reqId: "req-models-list-stale-runtime-profile",
|
||||
});
|
||||
await request;
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
true,
|
||||
{
|
||||
models: [
|
||||
{
|
||||
id: "demo-model",
|
||||
name: "Demo Model",
|
||||
provider: "demo-provider",
|
||||
available: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
} finally {
|
||||
clearRuntimeAuthProfileStoreSnapshots();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it("marks env SecretRef-backed auth profiles available", async () => {
|
||||
await withOpenClawTestState(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user