diff --git a/src/config/loader.ts b/src/config/loader.ts index af9a7a7a..05768cc5 100644 --- a/src/config/loader.ts +++ b/src/config/loader.ts @@ -85,7 +85,7 @@ export function loadConfig(flags: GlobalFlags): Config { const baseUrl = flags.baseUrl || process.env.MINIMAX_BASE_URL || file.base_url - || file.oauth?.resource_url + || (!apiKey ? file.oauth?.resource_url : undefined) || REGIONS[region] || REGIONS.global; diff --git a/test/config/loader.test.ts b/test/config/loader.test.ts index 4095aa22..97d4a44f 100644 --- a/test/config/loader.test.ts +++ b/test/config/loader.test.ts @@ -4,6 +4,7 @@ import { join } from 'path'; import { tmpdir } from 'os'; import { loadConfig, renameWithCrossDeviceFallback, writeConfigFile } from '../../src/config/loader'; import { CLIError } from '../../src/errors/base'; +import { resolveCredential } from '../../src/auth/resolver'; import type { GlobalFlags } from '../../src/types/flags'; const baseFlags: GlobalFlags = { @@ -58,6 +59,54 @@ describe('loadConfig', () => { }); }); +describe('OAuth resource URL credential scope', () => { + const testDir = join(tmpdir(), `mmx-oauth-host-test-${Date.now()}`); + const envKeys = ['MMX_CONFIG_DIR', 'MINIMAX_REGION', 'MINIMAX_BASE_URL'] as const; + let savedEnv: Array; + + beforeEach(async () => { + savedEnv = envKeys.map(key => process.env[key]); + for (const key of envKeys) delete process.env[key]; + process.env.MMX_CONFIG_DIR = testDir; + await writeConfigFile({ + region: 'cn', + api_key: 'file-key', + oauth: { + access_token: 'oauth-token', + refresh_token: 'refresh-token', + expires_at: '2099-01-01T00:00:00Z', + region: 'cn', + resource_url: 'https://oauth-resource.example', + }, + }); + }); + + afterEach(() => { + envKeys.forEach((key, index) => { + if (savedEnv[index] === undefined) delete process.env[key]; + else process.env[key] = savedEnv[index]; + }); + rmSync(testDir, { recursive: true, force: true }); + }); + + it.each(['global', 'cn'])('uses the %s API host when an explicit key overrides OAuth', async region => { + const config = loadConfig({ ...baseFlags, apiKey: 'flag-key', region }); + expect(await resolveCredential(config)).toMatchObject({ token: 'flag-key', method: 'api-key' }); + expect(config.baseUrl).toBe(region === 'cn' ? 'https://api.minimax.cn' : 'https://api.minimax.io'); + }); + + it('keeps the resource URL when OAuth wins over a stored API key', async () => { + const config = loadConfig(baseFlags); + expect(await resolveCredential(config)).toMatchObject({ token: 'oauth-token', method: 'oauth' }); + expect(config.baseUrl).toBe('https://oauth-resource.example'); + }); + + it('preserves an explicit base URL with an explicit key', () => { + const config = loadConfig({ ...baseFlags, apiKey: 'flag-key', baseUrl: 'https://custom.example' }); + expect(config.baseUrl).toBe('https://custom.example'); + }); +}); + describe('writeConfigFile', () => { const testDir = join(tmpdir(), `mmx-config-write-test-${Date.now()}`); const originalConfigDir = process.env.MMX_CONFIG_DIR;