feat: add new provider for minimax and qwen portals (#203)
Co-authored-by: Haze <709547807@qq.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
5d548da2e6
commit
f70d5b0c28
@@ -33,7 +33,7 @@ import {
|
||||
} from '../../node_modules/openclaw/extensions/qwen-portal-auth/oauth';
|
||||
import { saveOAuthTokenToOpenClaw, setOpenClawDefaultModelWithOverride } from './openclaw-auth';
|
||||
|
||||
export type OAuthProviderType = 'minimax-portal' | 'qwen-portal';
|
||||
export type OAuthProviderType = 'minimax-portal' | 'minimax-portal-cn' | 'qwen-portal';
|
||||
export type { MiniMaxRegion };
|
||||
|
||||
// ─────────────────────────────────────────────────────────────
|
||||
@@ -55,15 +55,17 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
}
|
||||
|
||||
this.active = true;
|
||||
this.emit('oauth:start', { provider: provider });
|
||||
this.activeProvider = provider;
|
||||
|
||||
try {
|
||||
if (provider === 'minimax-portal') {
|
||||
await this.runMiniMaxFlow(region);
|
||||
if (provider === 'minimax-portal' || provider === 'minimax-portal-cn') {
|
||||
const actualRegion = provider === 'minimax-portal-cn' ? 'cn' : (region || 'global');
|
||||
await this.runMiniMaxFlow(actualRegion, provider);
|
||||
} else if (provider === 'qwen-portal') {
|
||||
await this.runQwenFlow();
|
||||
} else {
|
||||
throw new Error(`Unsupported OAuth provider: ${provider}`);
|
||||
throw new Error(`Unsupported OAuth provider type: ${provider}`);
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -89,7 +91,7 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
// MiniMax flow
|
||||
// ─────────────────────────────────────────────────────────
|
||||
|
||||
private async runMiniMaxFlow(region: MiniMaxRegion): Promise<void> {
|
||||
private async runMiniMaxFlow(region?: MiniMaxRegion, providerType: OAuthProviderType = 'minimax-portal'): Promise<void> {
|
||||
if (!isOpenClawPresent()) {
|
||||
throw new Error('OpenClaw package not found');
|
||||
}
|
||||
@@ -123,7 +125,7 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
|
||||
if (!this.active) return;
|
||||
|
||||
await this.onSuccess('minimax-portal', {
|
||||
await this.onSuccess(providerType, {
|
||||
access: token.access,
|
||||
refresh: token.refresh,
|
||||
expires: token.expires,
|
||||
@@ -131,6 +133,7 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
resourceUrl: token.resourceUrl,
|
||||
// MiniMax uses Anthropic Messages API format
|
||||
api: 'anthropic-messages',
|
||||
region,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -189,15 +192,19 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
expires: number;
|
||||
resourceUrl?: string;
|
||||
api: 'anthropic-messages' | 'openai-completions';
|
||||
region?: MiniMaxRegion;
|
||||
}) {
|
||||
this.active = false;
|
||||
this.activeProvider = null;
|
||||
logger.info(`[DeviceOAuth] Successfully completed OAuth for ${providerType}`);
|
||||
|
||||
// 1. Write OAuth token to OpenClaw's auth-profiles.json in native OAuth format
|
||||
// (matches what `openclaw models auth login` → upsertAuthProfile writes)
|
||||
// 1. Write OAuth token to OpenClaw's auth-profiles.json in native OAuth format.
|
||||
// (matches what `openclaw models auth login` → upsertAuthProfile writes).
|
||||
// We save both MiniMax providers to the generic "minimax-portal" profile
|
||||
// so OpenClaw's gateway auto-refresher knows how to find it.
|
||||
try {
|
||||
saveOAuthTokenToOpenClaw(providerType, {
|
||||
const tokenProviderId = providerType.startsWith('minimax-portal') ? 'minimax-portal' : providerType;
|
||||
saveOAuthTokenToOpenClaw(tokenProviderId, {
|
||||
access: token.access,
|
||||
refresh: token.refresh,
|
||||
expires: token.expires,
|
||||
@@ -213,18 +220,19 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
// Note: MiniMax Anthropic-compatible API requires the /anthropic suffix.
|
||||
const defaultBaseUrl = providerType === 'minimax-portal'
|
||||
? 'https://api.minimax.io/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;
|
||||
|
||||
// If MiniMax returned a resourceUrl (e.g. https://api.minimax.io) but no /anthropic suffix,
|
||||
// we must append it because we use the 'anthropic-messages' API mode
|
||||
if (providerType === 'minimax-portal' && baseUrl && !baseUrl.endsWith('/anthropic')) {
|
||||
if (providerType.startsWith('minimax-portal') && baseUrl && !baseUrl.endsWith('/anthropic')) {
|
||||
baseUrl = baseUrl.replace(/\/$/, '') + '/anthropic';
|
||||
}
|
||||
|
||||
try {
|
||||
setOpenClawDefaultModelWithOverride(providerType, undefined, {
|
||||
const tokenProviderId = providerType.startsWith('minimax-portal') ? 'minimax-portal' : providerType;
|
||||
setOpenClawDefaultModelWithOverride(tokenProviderId, undefined, {
|
||||
baseUrl,
|
||||
api: token.api,
|
||||
// OAuth placeholder — tells Gateway to resolve credentials
|
||||
@@ -232,7 +240,7 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
// This matches what the OpenClaw plugin's configPatch writes:
|
||||
// minimax-portal → apiKey: 'minimax-oauth'
|
||||
// qwen-portal → apiKey: 'qwen-oauth'
|
||||
apiKeyEnv: providerType === 'minimax-portal' ? 'minimax-oauth' : 'qwen-oauth',
|
||||
apiKeyEnv: tokenProviderId === 'minimax-portal' ? 'minimax-oauth' : 'qwen-oauth',
|
||||
});
|
||||
} catch (err) {
|
||||
logger.warn(`[DeviceOAuth] Failed to configure openclaw models:`, err);
|
||||
@@ -240,19 +248,28 @@ class DeviceOAuthManager extends EventEmitter {
|
||||
|
||||
// 3. Save provider record in ClawX's own store so UI shows it as configured
|
||||
const existing = await getProvider(providerType);
|
||||
const nameMap: Record<OAuthProviderType, string> = {
|
||||
'minimax-portal': 'MiniMax (Global)',
|
||||
'minimax-portal-cn': 'MiniMax (CN)',
|
||||
'qwen-portal': 'Qwen',
|
||||
};
|
||||
const providerConfig: ProviderConfig = {
|
||||
id: providerType,
|
||||
name: providerType === 'minimax-portal' ? 'MiniMax' : 'Qwen',
|
||||
name: nameMap[providerType as OAuthProviderType] || providerType,
|
||||
type: providerType,
|
||||
enabled: existing?.enabled ?? true,
|
||||
baseUrl: existing?.baseUrl,
|
||||
baseUrl, // Save the dynamically resolved URL (Global vs CN)
|
||||
|
||||
model: existing?.model || getProviderDefaultModel(providerType),
|
||||
createdAt: existing?.createdAt || new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
await saveProvider(providerConfig);
|
||||
|
||||
// 4. Emit success to frontend
|
||||
// 4. Emit success internally so the main process can restart the Gateway
|
||||
this.emit('oauth:success', providerType);
|
||||
|
||||
// 5. Emit success to frontend
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||
this.mainWindow.webContents.send('oauth:success', { provider: providerType, success: true });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user