Adapt MiniMax auth plugin compatibility (#913)
Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Haze <hazeone@users.noreply.github.com>
This commit is contained in:
@@ -30,6 +30,16 @@ vi.mock('electron', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@electron/utils/paths', async () => {
|
||||
const actual = await vi.importActual<typeof import('@electron/utils/paths')>('@electron/utils/paths');
|
||||
const resolvedDir = join(testHome, '.openclaw-test-openclaw');
|
||||
return {
|
||||
...actual,
|
||||
getOpenClawResolvedDir: () => resolvedDir,
|
||||
getOpenClawDir: () => resolvedDir,
|
||||
};
|
||||
});
|
||||
|
||||
async function writeOpenClawJson(config: unknown): Promise<void> {
|
||||
const openclawDir = join(testHome, '.openclaw');
|
||||
await mkdir(openclawDir, { recursive: true });
|
||||
@@ -494,6 +504,58 @@ describe('sanitizeOpenClawConfig', () => {
|
||||
expect(dingtalk.clientId).toBe('dt-client-id');
|
||||
expect(dingtalk.clientSecret).toBe('dt-secret');
|
||||
});
|
||||
|
||||
it('removes stale minimax-portal-auth plugin entries when merged minimax plugin is installed', async () => {
|
||||
await writeOpenClawJson({
|
||||
plugins: {
|
||||
allow: ['minimax-portal-auth', 'custom-plugin'],
|
||||
entries: {
|
||||
'minimax-portal-auth': { enabled: true },
|
||||
'custom-plugin': { enabled: true },
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
'minimax-portal': {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const openclawDir = join(testHome, '.openclaw-package-sanitize');
|
||||
await mkdir(join(openclawDir, 'dist', 'extensions', 'minimax'), { recursive: true });
|
||||
await writeFile(
|
||||
join(openclawDir, 'dist', 'extensions', 'minimax', 'openclaw.plugin.json'),
|
||||
JSON.stringify({
|
||||
id: 'minimax',
|
||||
providers: ['minimax', 'minimax-portal'],
|
||||
legacyPluginIds: ['minimax-portal-auth'],
|
||||
}, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
vi.doMock('@electron/utils/paths', async () => {
|
||||
const actual = await vi.importActual<typeof import('@electron/utils/paths')>('@electron/utils/paths');
|
||||
return {
|
||||
...actual,
|
||||
getOpenClawResolvedDir: () => openclawDir,
|
||||
};
|
||||
});
|
||||
|
||||
const { sanitizeOpenClawConfig } = await import('@electron/utils/openclaw-auth');
|
||||
await sanitizeOpenClawConfig();
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const plugins = result.plugins as Record<string, unknown>;
|
||||
const allow = plugins.allow as string[];
|
||||
const entries = plugins.entries as Record<string, Record<string, unknown>>;
|
||||
|
||||
expect(allow).toEqual(['custom-plugin']);
|
||||
expect(entries['minimax-portal-auth']).toBeUndefined();
|
||||
expect(entries['custom-plugin']).toEqual({ enabled: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('syncProviderConfigToOpenClaw', () => {
|
||||
@@ -504,6 +566,100 @@ describe('syncProviderConfigToOpenClaw', () => {
|
||||
await rm(testUserData, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
it('uses legacy minimax-portal-auth plugin registration when only the legacy plugin exists', async () => {
|
||||
await writeOpenClawJson({
|
||||
models: { providers: {} },
|
||||
});
|
||||
|
||||
const openclawDir = join(testHome, '.openclaw-package-old');
|
||||
await mkdir(join(openclawDir, 'extensions', 'minimax-portal-auth'), { recursive: true });
|
||||
await writeFile(
|
||||
join(openclawDir, 'extensions', 'minimax-portal-auth', 'openclaw.plugin.json'),
|
||||
JSON.stringify({
|
||||
id: 'minimax-portal-auth',
|
||||
providers: ['minimax-portal'],
|
||||
}, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
vi.doMock('@electron/utils/paths', async () => {
|
||||
const actual = await vi.importActual<typeof import('@electron/utils/paths')>('@electron/utils/paths');
|
||||
return {
|
||||
...actual,
|
||||
getOpenClawResolvedDir: () => openclawDir,
|
||||
};
|
||||
});
|
||||
|
||||
const { syncProviderConfigToOpenClaw } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await syncProviderConfigToOpenClaw('minimax-portal', 'MiniMax-M2.7', {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
apiKeyEnv: 'minimax-oauth',
|
||||
});
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const plugins = result.plugins as Record<string, unknown>;
|
||||
const allow = plugins.allow as string[];
|
||||
const entries = plugins.entries as Record<string, Record<string, unknown>>;
|
||||
|
||||
expect(allow).toContain('minimax-portal-auth');
|
||||
expect(entries['minimax-portal-auth']).toEqual({ enabled: true });
|
||||
expect(entries.minimax).toBeUndefined();
|
||||
});
|
||||
|
||||
it('uses merged minimax plugin registration and removes stale legacy ids when minimax plugin is installed', async () => {
|
||||
await writeOpenClawJson({
|
||||
plugins: {
|
||||
allow: ['minimax-portal-auth', 'custom-plugin'],
|
||||
entries: {
|
||||
'minimax-portal-auth': { enabled: true },
|
||||
'custom-plugin': { enabled: true },
|
||||
},
|
||||
},
|
||||
models: { providers: {} },
|
||||
});
|
||||
|
||||
const openclawDir = join(testHome, '.openclaw-package-new');
|
||||
await mkdir(join(openclawDir, 'dist', 'extensions', 'minimax'), { recursive: true });
|
||||
await writeFile(
|
||||
join(openclawDir, 'dist', 'extensions', 'minimax', 'openclaw.plugin.json'),
|
||||
JSON.stringify({
|
||||
id: 'minimax',
|
||||
providers: ['minimax', 'minimax-portal'],
|
||||
legacyPluginIds: ['minimax-portal-auth'],
|
||||
}, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
vi.doMock('@electron/utils/paths', async () => {
|
||||
const actual = await vi.importActual<typeof import('@electron/utils/paths')>('@electron/utils/paths');
|
||||
return {
|
||||
...actual,
|
||||
getOpenClawResolvedDir: () => openclawDir,
|
||||
};
|
||||
});
|
||||
|
||||
const { syncProviderConfigToOpenClaw } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await syncProviderConfigToOpenClaw('minimax-portal', 'MiniMax-M2.7', {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
apiKeyEnv: 'minimax-oauth',
|
||||
});
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const plugins = result.plugins as Record<string, unknown>;
|
||||
const allow = plugins.allow as string[];
|
||||
const entries = plugins.entries as Record<string, Record<string, unknown>>;
|
||||
|
||||
expect(allow).toContain('minimax');
|
||||
expect(allow).toContain('custom-plugin');
|
||||
expect(allow).not.toContain('minimax-portal-auth');
|
||||
expect(entries.minimax).toEqual({ enabled: true });
|
||||
expect(entries['minimax-portal-auth']).toBeUndefined();
|
||||
});
|
||||
|
||||
it('writes moonshot web search config to plugin config instead of tools.web.search.kimi', async () => {
|
||||
await writeOpenClawJson({
|
||||
models: {
|
||||
@@ -719,4 +875,104 @@ describe('auth-backed provider discovery', () => {
|
||||
expect(result.providers).toEqual({});
|
||||
await expect(getActiveOpenClawProviders()).resolves.toEqual(new Set());
|
||||
});
|
||||
|
||||
it('removes merged and legacy minimax plugin registrations when deleting the provider', async () => {
|
||||
await writeOpenClawJson({
|
||||
plugins: {
|
||||
allow: ['minimax', 'minimax-portal-auth', 'custom-plugin'],
|
||||
entries: {
|
||||
minimax: { enabled: true },
|
||||
'minimax-portal-auth': { enabled: true },
|
||||
'custom-plugin': { enabled: true },
|
||||
},
|
||||
},
|
||||
models: {
|
||||
providers: {
|
||||
'minimax-portal': {
|
||||
baseUrl: 'https://api.minimax.io/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const openclawDir = join(testHome, '.openclaw-package-new');
|
||||
await mkdir(join(openclawDir, 'dist', 'extensions', 'minimax'), { recursive: true });
|
||||
await writeFile(
|
||||
join(openclawDir, 'dist', 'extensions', 'minimax', 'openclaw.plugin.json'),
|
||||
JSON.stringify({
|
||||
id: 'minimax',
|
||||
providers: ['minimax', 'minimax-portal'],
|
||||
legacyPluginIds: ['minimax-portal-auth'],
|
||||
}, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
vi.doMock('@electron/utils/paths', async () => {
|
||||
const actual = await vi.importActual<typeof import('@electron/utils/paths')>('@electron/utils/paths');
|
||||
return {
|
||||
...actual,
|
||||
getOpenClawResolvedDir: () => openclawDir,
|
||||
};
|
||||
});
|
||||
|
||||
const { removeProviderFromOpenClaw } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await removeProviderFromOpenClaw('minimax-portal');
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const plugins = result.plugins as Record<string, unknown>;
|
||||
const allow = plugins.allow as string[];
|
||||
const entries = plugins.entries as Record<string, Record<string, unknown>>;
|
||||
|
||||
expect(allow).toEqual(['custom-plugin']);
|
||||
expect(entries.minimax).toBeUndefined();
|
||||
expect(entries['minimax-portal-auth']).toBeUndefined();
|
||||
expect(entries['custom-plugin']).toEqual({ enabled: true });
|
||||
});
|
||||
|
||||
it('sanitizes stale minimax-portal-auth entries when merged minimax plugin is installed', async () => {
|
||||
await writeOpenClawJson({
|
||||
plugins: {
|
||||
allow: ['minimax-portal-auth', 'custom-plugin'],
|
||||
entries: {
|
||||
'minimax-portal-auth': { enabled: true },
|
||||
'custom-plugin': { enabled: true },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const openclawDir = join(testHome, '.openclaw-package-new');
|
||||
await mkdir(join(openclawDir, 'dist', 'extensions', 'minimax'), { recursive: true });
|
||||
await writeFile(
|
||||
join(openclawDir, 'dist', 'extensions', 'minimax', 'openclaw.plugin.json'),
|
||||
JSON.stringify({
|
||||
id: 'minimax',
|
||||
providers: ['minimax', 'minimax-portal'],
|
||||
legacyPluginIds: ['minimax-portal-auth'],
|
||||
}, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
vi.doMock('@electron/utils/paths', async () => {
|
||||
const actual = await vi.importActual<typeof import('@electron/utils/paths')>('@electron/utils/paths');
|
||||
return {
|
||||
...actual,
|
||||
getOpenClawResolvedDir: () => openclawDir,
|
||||
};
|
||||
});
|
||||
|
||||
const { sanitizeOpenClawConfig } = await import('@electron/utils/openclaw-auth');
|
||||
|
||||
await sanitizeOpenClawConfig();
|
||||
|
||||
const result = await readOpenClawJson();
|
||||
const plugins = result.plugins as Record<string, unknown>;
|
||||
const allow = plugins.allow as string[];
|
||||
const entries = plugins.entries as Record<string, Record<string, unknown>>;
|
||||
|
||||
expect(allow).toEqual(['custom-plugin']);
|
||||
expect(entries['minimax-portal-auth']).toBeUndefined();
|
||||
expect(entries['custom-plugin']).toEqual({ enabled: true });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -291,6 +291,49 @@ describe('ProviderService.listAccounts (openclaw.json as sole source of truth)',
|
||||
expect(ids).toContain('minimax-portal-cn-uuid');
|
||||
});
|
||||
|
||||
it('seeds a MiniMax CN account when minimax-portal baseUrl points at the CN endpoint', async () => {
|
||||
mocks.listProviderAccounts.mockResolvedValue([]);
|
||||
mocks.getActiveOpenClawProviders.mockResolvedValue(new Set(['minimax-portal']));
|
||||
mocks.getOpenClawProvidersConfig.mockResolvedValue({
|
||||
providers: {
|
||||
'minimax-portal': { baseUrl: 'https://api.minimaxi.com/anthropic' },
|
||||
},
|
||||
defaultModel: undefined,
|
||||
});
|
||||
mocks.getProviderDefinition.mockImplementation((key: string) => {
|
||||
if (key === 'minimax-portal-cn') {
|
||||
return {
|
||||
id: 'minimax-portal-cn',
|
||||
name: 'MiniMax (CN)',
|
||||
defaultAuthMode: 'oauth_device',
|
||||
defaultModelId: 'MiniMax-M2.7',
|
||||
providerConfig: {
|
||||
baseUrl: 'https://api.minimaxi.com/anthropic',
|
||||
api: 'anthropic-messages',
|
||||
},
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
|
||||
const result = await service.listAccounts();
|
||||
|
||||
expect(mocks.saveProviderAccount).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
id: 'minimax-portal',
|
||||
vendorId: 'minimax-portal-cn',
|
||||
label: 'MiniMax (CN)',
|
||||
baseUrl: 'https://api.minimaxi.com/anthropic',
|
||||
}),
|
||||
);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toEqual(expect.objectContaining({
|
||||
id: 'minimax-portal',
|
||||
vendorId: 'minimax-portal-cn',
|
||||
label: 'MiniMax (CN)',
|
||||
}));
|
||||
});
|
||||
|
||||
it('seeds builtin providers discovered from auth profiles without explicit models.providers entries', async () => {
|
||||
mocks.listProviderAccounts.mockResolvedValue([]);
|
||||
mocks.getActiveOpenClawProviders.mockResolvedValue(new Set(['openai', 'anthropic']));
|
||||
|
||||
Reference in New Issue
Block a user