fix minimax oauth failed and upgrade openclaw to 2.26 (#206)
This commit is contained in:
committed by
GitHub
Unverified
parent
f70d5b0c28
commit
0fb1a1a78d
@@ -1167,8 +1167,8 @@ function registerProviderHandlers(gatewayManager: GatewayManager): void {
|
|||||||
: 'openai-completions';
|
: 'openai-completions';
|
||||||
|
|
||||||
let baseUrl = provider.baseUrl || defaultBaseUrl;
|
let baseUrl = provider.baseUrl || defaultBaseUrl;
|
||||||
if ((provider.type === 'minimax-portal' || provider.type === 'minimax-portal-cn') && baseUrl && !baseUrl.endsWith('/anthropic')) {
|
if ((provider.type === 'minimax-portal' || provider.type === 'minimax-portal-cn') && baseUrl) {
|
||||||
baseUrl = baseUrl.replace(/\/$/, '') + '/anthropic';
|
baseUrl = baseUrl.replace(/\/v1$/, '').replace(/\/anthropic$/, '').replace(/\/$/, '') + '/anthropic';
|
||||||
}
|
}
|
||||||
|
|
||||||
// To ensure the OpenClaw Gateway's internal token refresher works,
|
// To ensure the OpenClaw Gateway's internal token refresher works,
|
||||||
@@ -1180,10 +1180,27 @@ function registerProviderHandlers(gatewayManager: GatewayManager): void {
|
|||||||
setOpenClawDefaultModelWithOverride(targetProviderKey, undefined, {
|
setOpenClawDefaultModelWithOverride(targetProviderKey, undefined, {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
api,
|
api,
|
||||||
|
authHeader: targetProviderKey === 'minimax-portal' ? true : undefined,
|
||||||
|
// Relies on OpenClaw Gateway native auth-profiles syncing
|
||||||
apiKeyEnv: targetProviderKey === 'minimax-portal' ? 'minimax-oauth' : 'qwen-oauth',
|
apiKeyEnv: targetProviderKey === 'minimax-portal' ? 'minimax-oauth' : 'qwen-oauth',
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info(`Configured openclaw.json for OAuth provider "${provider.type}"`);
|
logger.info(`Configured openclaw.json for OAuth provider "${provider.type}"`);
|
||||||
|
|
||||||
|
// Also write models.json directly so pi-ai picks up the correct baseUrl and
|
||||||
|
// authHeader immediately, without waiting for Gateway to sync openclaw.json.
|
||||||
|
try {
|
||||||
|
const defaultModelId = provider.model?.split('/').pop();
|
||||||
|
updateAgentModelProvider(targetProviderKey, {
|
||||||
|
baseUrl,
|
||||||
|
api,
|
||||||
|
authHeader: targetProviderKey === 'minimax-portal' ? true : undefined,
|
||||||
|
apiKey: targetProviderKey === 'minimax-portal' ? 'minimax-oauth' : 'qwen-oauth',
|
||||||
|
models: defaultModelId ? [{ id: defaultModelId, name: defaultModelId }] : [],
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`Failed to update models.json for OAuth provider "${targetProviderKey}":`, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For custom/ollama providers, also update the per-agent models.json
|
// For custom/ollama providers, also update the per-agent models.json
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class DeviceOAuthManager extends EventEmitter {
|
|||||||
expires: token.expires,
|
expires: token.expires,
|
||||||
// MiniMax returns a per-account resourceUrl as the API base URL
|
// MiniMax returns a per-account resourceUrl as the API base URL
|
||||||
resourceUrl: token.resourceUrl,
|
resourceUrl: token.resourceUrl,
|
||||||
// MiniMax uses Anthropic Messages API format
|
// Revert back to anthropic-messages
|
||||||
api: 'anthropic-messages',
|
api: 'anthropic-messages',
|
||||||
region,
|
region,
|
||||||
});
|
});
|
||||||
@@ -217,17 +217,15 @@ class DeviceOAuthManager extends EventEmitter {
|
|||||||
// This mirrors what the OpenClaw plugin's configPatch does after CLI login.
|
// This mirrors what the OpenClaw plugin's configPatch does after CLI login.
|
||||||
// The baseUrl comes from token.resourceUrl (per-account URL from the OAuth server)
|
// The baseUrl comes from token.resourceUrl (per-account URL from the OAuth server)
|
||||||
// or falls back to the provider's default public endpoint.
|
// or falls back to the provider's default public endpoint.
|
||||||
// Note: MiniMax Anthropic-compatible API requires the /anthropic suffix.
|
|
||||||
const defaultBaseUrl = providerType === 'minimax-portal'
|
const defaultBaseUrl = providerType === 'minimax-portal'
|
||||||
? 'https://api.minimax.io/anthropic'
|
? 'https://api.minimax.io/anthropic'
|
||||||
: (providerType === 'minimax-portal-cn' ? 'https://api.minimaxi.com/anthropic' : 'https://portal.qwen.ai/v1');
|
: (providerType === 'minimax-portal-cn' ? 'https://api.minimaxi.com/anthropic' : 'https://portal.qwen.ai/v1');
|
||||||
|
|
||||||
let baseUrl = token.resourceUrl || defaultBaseUrl;
|
let baseUrl = token.resourceUrl || defaultBaseUrl;
|
||||||
|
|
||||||
// If MiniMax returned a resourceUrl (e.g. https://api.minimax.io) but no /anthropic suffix,
|
// Ensure the base URL ends with /anthropic
|
||||||
// we must append it because we use the 'anthropic-messages' API mode
|
if (providerType.startsWith('minimax-portal') && baseUrl) {
|
||||||
if (providerType.startsWith('minimax-portal') && baseUrl && !baseUrl.endsWith('/anthropic')) {
|
baseUrl = baseUrl.replace(/\/v1$/, '').replace(/\/anthropic$/, '').replace(/\/$/, '') + '/anthropic';
|
||||||
baseUrl = baseUrl.replace(/\/$/, '') + '/anthropic';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -235,11 +233,10 @@ class DeviceOAuthManager extends EventEmitter {
|
|||||||
setOpenClawDefaultModelWithOverride(tokenProviderId, undefined, {
|
setOpenClawDefaultModelWithOverride(tokenProviderId, undefined, {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
api: token.api,
|
api: token.api,
|
||||||
|
// Tells OpenClaw's anthropic adapter to use `Authorization: Bearer` instead of `x-api-key`
|
||||||
|
authHeader: providerType.startsWith('minimax-portal') ? true : undefined,
|
||||||
// OAuth placeholder — tells Gateway to resolve credentials
|
// OAuth placeholder — tells Gateway to resolve credentials
|
||||||
// from auth-profiles.json (type: 'oauth') instead of a static API key.
|
// from auth-profiles.json (type: 'oauth') instead of a static API key.
|
||||||
// This matches what the OpenClaw plugin's configPatch writes:
|
|
||||||
// minimax-portal → apiKey: 'minimax-oauth'
|
|
||||||
// qwen-portal → apiKey: 'qwen-oauth'
|
|
||||||
apiKeyEnv: tokenProviderId === 'minimax-portal' ? 'minimax-oauth' : 'qwen-oauth',
|
apiKeyEnv: tokenProviderId === 'minimax-portal' ? 'minimax-oauth' : 'qwen-oauth',
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -153,6 +153,32 @@ export function saveOAuthTokenToOpenClaw(
|
|||||||
console.log(`Saved OAuth token for provider "${provider}" to OpenClaw auth-profiles (agents: ${agentIds.join(', ')})`);
|
console.log(`Saved OAuth token for provider "${provider}" to OpenClaw auth-profiles (agents: ${agentIds.join(', ')})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve an OAuth token from OpenClaw's auth-profiles.json.
|
||||||
|
* Useful when the Gateway does not natively inject the Authorization header.
|
||||||
|
*
|
||||||
|
* @param provider - Provider type (e.g., 'minimax-portal')
|
||||||
|
* @param agentId - Optional single agent ID to read from, defaults to 'main'
|
||||||
|
* @returns The OAuth token access string or null if not found
|
||||||
|
*/
|
||||||
|
export function getOAuthTokenFromOpenClaw(
|
||||||
|
provider: string,
|
||||||
|
agentId = 'main'
|
||||||
|
): string | null {
|
||||||
|
try {
|
||||||
|
const store = readAuthProfiles(agentId);
|
||||||
|
const profileId = `${provider}:default`;
|
||||||
|
const profile = store.profiles[profileId];
|
||||||
|
|
||||||
|
if (profile && profile.type === 'oauth' && 'access' in profile) {
|
||||||
|
return (profile as OAuthProfileEntry).access;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`[getOAuthToken] Failed to read token for ${provider}:`, err);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a provider API key to OpenClaw's auth-profiles.json
|
* Save a provider API key to OpenClaw's auth-profiles.json
|
||||||
* This writes the key in the format OpenClaw expects so the gateway
|
* This writes the key in the format OpenClaw expects so the gateway
|
||||||
@@ -278,7 +304,25 @@ export function removeProviderFromOpenClaw(provider: string): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Remove from openclaw.json
|
// 2. Remove from models.json (per-agent model registry used by pi-ai directly)
|
||||||
|
for (const agentId of agentIds) {
|
||||||
|
const modelsPath = join(homedir(), '.openclaw', 'agents', agentId, 'agent', 'models.json');
|
||||||
|
try {
|
||||||
|
if (existsSync(modelsPath)) {
|
||||||
|
const data = JSON.parse(readFileSync(modelsPath, 'utf-8')) as Record<string, unknown>;
|
||||||
|
const providers = data.providers as Record<string, unknown> | undefined;
|
||||||
|
if (providers && providers[provider]) {
|
||||||
|
delete providers[provider];
|
||||||
|
writeFileSync(modelsPath, JSON.stringify(data, null, 2), 'utf-8');
|
||||||
|
console.log(`Removed models.json entry for provider "${provider}" (agent "${agentId}")`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`Failed to remove provider ${provider} from models.json (agent "${agentId}"):`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Remove from openclaw.json
|
||||||
const configPath = join(homedir(), '.openclaw', 'openclaw.json');
|
const configPath = join(homedir(), '.openclaw', 'openclaw.json');
|
||||||
try {
|
try {
|
||||||
if (existsSync(configPath)) {
|
if (existsSync(configPath)) {
|
||||||
@@ -447,6 +491,7 @@ interface RuntimeProviderConfigOverride {
|
|||||||
api?: string;
|
api?: string;
|
||||||
apiKeyEnv?: string;
|
apiKeyEnv?: string;
|
||||||
headers?: Record<string, string>;
|
headers?: Record<string, string>;
|
||||||
|
authHeader?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -573,6 +618,9 @@ export function setOpenClawDefaultModelWithOverride(
|
|||||||
if (override.headers && Object.keys(override.headers).length > 0) {
|
if (override.headers && Object.keys(override.headers).length > 0) {
|
||||||
nextProvider.headers = override.headers;
|
nextProvider.headers = override.headers;
|
||||||
}
|
}
|
||||||
|
if (override.authHeader !== undefined) {
|
||||||
|
nextProvider.authHeader = override.authHeader;
|
||||||
|
}
|
||||||
|
|
||||||
providers[provider] = nextProvider;
|
providers[provider] = nextProvider;
|
||||||
models.providers = providers;
|
models.providers = providers;
|
||||||
@@ -766,6 +814,8 @@ export function updateAgentModelProvider(
|
|||||||
api?: string;
|
api?: string;
|
||||||
models?: Array<{ id: string; name: string }>;
|
models?: Array<{ id: string; name: string }>;
|
||||||
apiKey?: string;
|
apiKey?: string;
|
||||||
|
/** When true, pi-ai sends Authorization: Bearer instead of x-api-key */
|
||||||
|
authHeader?: boolean;
|
||||||
}
|
}
|
||||||
): void {
|
): void {
|
||||||
const agentIds = discoverAgentIds();
|
const agentIds = discoverAgentIds();
|
||||||
@@ -804,6 +854,7 @@ export function updateAgentModelProvider(
|
|||||||
if (entry.api !== undefined) existing.api = entry.api;
|
if (entry.api !== undefined) existing.api = entry.api;
|
||||||
if (mergedModels.length > 0) existing.models = mergedModels;
|
if (mergedModels.length > 0) existing.models = mergedModels;
|
||||||
if (entry.apiKey !== undefined) existing.apiKey = entry.apiKey;
|
if (entry.apiKey !== undefined) existing.apiKey = entry.apiKey;
|
||||||
|
if (entry.authHeader !== undefined) existing.authHeader = entry.authHeader;
|
||||||
|
|
||||||
providers[providerType] = existing;
|
providers[providerType] = existing;
|
||||||
data.providers = providers;
|
data.providers = providers;
|
||||||
|
|||||||
@@ -84,7 +84,7 @@
|
|||||||
"i18next": "^25.8.11",
|
"i18next": "^25.8.11",
|
||||||
"jsdom": "^28.1.0",
|
"jsdom": "^28.1.0",
|
||||||
"lucide-react": "^0.563.0",
|
"lucide-react": "^0.563.0",
|
||||||
"openclaw": "2026.2.24",
|
"openclaw": "2026.2.26",
|
||||||
"png2icons": "^2.0.1",
|
"png2icons": "^2.0.1",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
|
|||||||
1299
pnpm-lock.yaml
generated
1299
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user