From 39953ab72a1b24242b07f82fcd7a4d4d1259bc2d Mon Sep 17 00:00:00 2001 From: hclsys Date: Sat, 9 May 2026 16:55:41 +0800 Subject: [PATCH] fix(minimax): resolve portal OAuth token via resolveProviderAuth with oauthMarker (#79731) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: resolvePortalCatalog manually called ensureAuthProfileStore + listProfilesForProvider to detect OAuth profiles, then returned the raw MINIMAX_OAUTH_MARKER string as the catalog apiKey. The request layer has no handler for the marker string — it reached the provider as a literal API key value, causing "No API key found" even with valid OAuth tokens. Fix: use ctx.resolveProviderAuth(PORTAL_PROVIDER_ID, { oauthMarker }) matching the pattern used by the chutes extension. The resolver returns the marker when an OAuth profile exists, which the request layer correctly intercepts and replaces with the live access token. Co-Authored-By: Claude Sonnet 4.6 --- extensions/minimax/index.test.ts | 28 +++++++++++++++++++++ extensions/minimax/provider-registration.ts | 18 ++++++------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/extensions/minimax/index.test.ts b/extensions/minimax/index.test.ts index 6f2caef2685..e9213007188 100644 --- a/extensions/minimax/index.test.ts +++ b/extensions/minimax/index.test.ts @@ -4,6 +4,7 @@ import { registerProviderPlugin, requireRegisteredProvider, } from "openclaw/plugin-sdk/plugin-test-runtime"; +import { MINIMAX_OAUTH_MARKER } from "openclaw/plugin-sdk/provider-auth"; import { describe, expect, it, vi } from "vitest"; import { registerMinimaxProviders } from "./provider-registration.js"; import { createMiniMaxWebSearchProvider } from "./src/minimax-web-search-provider.js"; @@ -325,6 +326,33 @@ describe("minimax provider hooks", () => { expect(result?.windows).toEqual([{ label: "5h", usedPercent: 2, resetAt: undefined }]); }); + it("portal catalog resolves OAuth token via resolveProviderAuth with oauthMarker", async () => { + const { providers } = await registerProviderPlugin({ + plugin: minimaxProviderPlugin, + id: "minimax", + name: "MiniMax Provider", + }); + const portalProvider = requireRegisteredProvider(providers, "minimax-portal"); + const resolveProviderAuth = vi.fn(() => ({ + apiKey: MINIMAX_OAUTH_MARKER, + mode: "oauth" as const, + source: "profile" as const, + })); + const result = await portalProvider.catalog?.run({ + config: {}, + resolveProviderAuth, + resolveProviderApiKey: () => ({ apiKey: undefined }), + } as never); + + expect(resolveProviderAuth).toHaveBeenCalledWith("minimax-portal", { + oauthMarker: MINIMAX_OAUTH_MARKER, + }); + expect(result).not.toBeNull(); + if (result && "provider" in result) { + expect(result.provider.apiKey).toBe(MINIMAX_OAUTH_MARKER); + } + }); + it("writes api and authHeader into the MiniMax portal OAuth config patch", async () => { const { providers } = await registerProviderPlugin({ plugin: minimaxProviderPlugin, diff --git a/extensions/minimax/provider-registration.ts b/extensions/minimax/provider-registration.ts index b62ef3a1cf5..91a0d04c15d 100644 --- a/extensions/minimax/provider-registration.ts +++ b/extensions/minimax/provider-registration.ts @@ -6,11 +6,7 @@ import type { ProviderAuthResult, ProviderCatalogContext, } from "openclaw/plugin-sdk/plugin-entry"; -import { - MINIMAX_OAUTH_MARKER, - ensureAuthProfileStore, - listProfilesForProvider, -} from "openclaw/plugin-sdk/provider-auth"; +import { MINIMAX_OAUTH_MARKER } from "openclaw/plugin-sdk/provider-auth"; import { buildOauthProviderAuthResult } from "openclaw/plugin-sdk/provider-auth"; import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key"; import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared"; @@ -100,13 +96,13 @@ function resolveApiCatalog(ctx: ProviderCatalogContext) { function resolvePortalCatalog(ctx: ProviderCatalogContext) { const explicitProvider = ctx.config.models?.providers?.[PORTAL_PROVIDER_ID]; - const envApiKey = ctx.resolveProviderApiKey(PORTAL_PROVIDER_ID).apiKey; - const authStore = ensureAuthProfileStore(ctx.agentDir, { - allowKeychainPrompt: false, - }); - const hasProfiles = listProfilesForProvider(authStore, PORTAL_PROVIDER_ID).length > 0; const explicitApiKey = normalizeOptionalString(explicitProvider?.apiKey); - const apiKey = envApiKey ?? explicitApiKey ?? (hasProfiles ? MINIMAX_OAUTH_MARKER : undefined); + // resolveProviderAuth handles OAuth profiles via oauthMarker, returning the + // sentinel so the request layer can swap in the live access token (#79731). + const { apiKey: profileApiKey } = ctx.resolveProviderAuth(PORTAL_PROVIDER_ID, { + oauthMarker: MINIMAX_OAUTH_MARKER, + }); + const apiKey = explicitApiKey ?? profileApiKey; if (!apiKey) { return null; }