refactor: merge minimax bundled plugins
This commit is contained in:
parent
6513749ef6
commit
47a9c1a893
@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898.
|
- Docs/Zalo: clarify the Marketplace-bot support matrix and config guidance so the Zalo channel docs match current Bot Creator behavior more closely. (#47552) Thanks @No898.
|
||||||
- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs.
|
- Install/update: allow package-manager installs from GitHub `main` via `openclaw update --tag main`, installer `--version main`, or direct npm/pnpm git specs.
|
||||||
- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy.
|
- Plugins/providers: move OpenRouter, GitHub Copilot, and OpenAI Codex provider/runtime logic into bundled plugins, including dynamic model fallback, runtime auth exchange, stream wrappers, capability hints, and cache-TTL policy.
|
||||||
|
- Plugins/MiniMax: merge the bundled MiniMax API and MiniMax OAuth plugin surfaces into a single default-on `minimax` plugin, while keeping legacy `minimax-portal-auth` config ids aliased for compatibility.
|
||||||
- Plugins/bundles: add compatible Codex, Claude, and Cursor bundle discovery/install support, map bundle skills into OpenClaw skills, and apply Claude bundle `settings.json` defaults to embedded Pi with shell overrides sanitized.
|
- Plugins/bundles: add compatible Codex, Claude, and Cursor bundle discovery/install support, map bundle skills into OpenClaw skills, and apply Claude bundle `settings.json` defaults to embedded Pi with shell overrides sanitized.
|
||||||
- Plugins/agent integrations: broaden the plugin surface for app-server integrations with channel-aware commands, interactive callbacks, inbound claims, and Discord/Telegram conversation binding support. (#45318) Thanks @huntharo and @vincentkoc.
|
- Plugins/agent integrations: broaden the plugin surface for app-server integrations with channel-aware commands, interactive callbacks, inbound claims, and Discord/Telegram conversation binding support. (#45318) Thanks @huntharo and @vincentkoc.
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ MiniMax highlights these improvements in M2.5:
|
|||||||
Enable the bundled OAuth plugin and authenticate:
|
Enable the bundled OAuth plugin and authenticate:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
openclaw plugins enable minimax-portal-auth # skip if already loaded.
|
openclaw plugins enable minimax # skip if already loaded.
|
||||||
openclaw gateway restart # restart if gateway is already running
|
openclaw gateway restart # restart if gateway is already running
|
||||||
openclaw onboard --auth-choice minimax-portal
|
openclaw onboard --auth-choice minimax-portal
|
||||||
```
|
```
|
||||||
@ -52,7 +52,7 @@ You will be prompted to select an endpoint:
|
|||||||
- **Global** - International users (`api.minimax.io`)
|
- **Global** - International users (`api.minimax.io`)
|
||||||
- **CN** - Users in China (`api.minimaxi.com`)
|
- **CN** - Users in China (`api.minimaxi.com`)
|
||||||
|
|
||||||
See [MiniMax OAuth plugin README](https://github.com/openclaw/openclaw/tree/main/extensions/minimax-portal-auth) for details.
|
See [MiniMax plugin README](https://github.com/openclaw/openclaw/tree/main/extensions/minimax) for details.
|
||||||
|
|
||||||
### MiniMax M2.5 (API key)
|
### MiniMax M2.5 (API key)
|
||||||
|
|
||||||
|
|||||||
@ -172,8 +172,7 @@ Important trust note:
|
|||||||
- Hugging Face provider catalog — bundled as `huggingface` (enabled by default)
|
- Hugging Face provider catalog — bundled as `huggingface` (enabled by default)
|
||||||
- Kilo Gateway provider runtime — bundled as `kilocode` (enabled by default)
|
- Kilo Gateway provider runtime — bundled as `kilocode` (enabled by default)
|
||||||
- Kimi Coding provider catalog — bundled as `kimi-coding` (enabled by default)
|
- Kimi Coding provider catalog — bundled as `kimi-coding` (enabled by default)
|
||||||
- MiniMax provider catalog + usage — bundled as `minimax` (enabled by default)
|
- MiniMax provider catalog + usage + OAuth — bundled as `minimax` (enabled by default; owns `minimax` and `minimax-portal`)
|
||||||
- MiniMax OAuth (provider auth + catalog) — bundled as `minimax-portal-auth` (enabled by default)
|
|
||||||
- Mistral provider capabilities — bundled as `mistral` (enabled by default)
|
- Mistral provider capabilities — bundled as `mistral` (enabled by default)
|
||||||
- Model Studio provider catalog — bundled as `modelstudio` (enabled by default)
|
- Model Studio provider catalog — bundled as `modelstudio` (enabled by default)
|
||||||
- Moonshot provider runtime — bundled as `moonshot` (enabled by default)
|
- Moonshot provider runtime — bundled as `moonshot` (enabled by default)
|
||||||
@ -664,7 +663,7 @@ Default-on bundled plugin examples:
|
|||||||
- `kilocode`
|
- `kilocode`
|
||||||
- `kimi-coding`
|
- `kimi-coding`
|
||||||
- `minimax`
|
- `minimax`
|
||||||
- `minimax-portal-auth`
|
- `minimax`
|
||||||
- `modelstudio`
|
- `modelstudio`
|
||||||
- `moonshot`
|
- `moonshot`
|
||||||
- `nvidia`
|
- `nvidia`
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
# MiniMax OAuth (OpenClaw plugin)
|
|
||||||
|
|
||||||
OAuth provider plugin for **MiniMax** (OAuth).
|
|
||||||
|
|
||||||
## Enable
|
|
||||||
|
|
||||||
Bundled plugins are disabled by default. Enable this one:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw plugins enable minimax-portal-auth
|
|
||||||
```
|
|
||||||
|
|
||||||
Restart the Gateway after enabling.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw gateway restart
|
|
||||||
```
|
|
||||||
|
|
||||||
## Authenticate
|
|
||||||
|
|
||||||
```bash
|
|
||||||
openclaw models auth login --provider minimax-portal --set-default
|
|
||||||
```
|
|
||||||
|
|
||||||
You will be prompted to select an endpoint:
|
|
||||||
|
|
||||||
- **Global** - International users, optimized for overseas access (`api.minimax.io`)
|
|
||||||
- **China** - Optimized for users in China (`api.minimaxi.com`)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- MiniMax OAuth uses a user-code login flow.
|
|
||||||
- Currently, OAuth login is supported only for the Coding plan
|
|
||||||
@ -1,163 +0,0 @@
|
|||||||
import {
|
|
||||||
buildOauthProviderAuthResult,
|
|
||||||
emptyPluginConfigSchema,
|
|
||||||
type OpenClawPluginApi,
|
|
||||||
type ProviderAuthContext,
|
|
||||||
type ProviderAuthResult,
|
|
||||||
type ProviderCatalogContext,
|
|
||||||
} from "openclaw/plugin-sdk/minimax-portal-auth";
|
|
||||||
import { ensureAuthProfileStore, listProfilesForProvider } from "../../src/agents/auth-profiles.js";
|
|
||||||
import { MINIMAX_OAUTH_MARKER } from "../../src/agents/model-auth-markers.js";
|
|
||||||
import { buildMinimaxPortalProvider } from "../../src/agents/models-config.providers.static.js";
|
|
||||||
import { loginMiniMaxPortalOAuth, type MiniMaxRegion } from "./oauth.js";
|
|
||||||
|
|
||||||
const PROVIDER_ID = "minimax-portal";
|
|
||||||
const PROVIDER_LABEL = "MiniMax";
|
|
||||||
const DEFAULT_MODEL = "MiniMax-M2.5";
|
|
||||||
const DEFAULT_BASE_URL_CN = "https://api.minimaxi.com/anthropic";
|
|
||||||
const DEFAULT_BASE_URL_GLOBAL = "https://api.minimax.io/anthropic";
|
|
||||||
|
|
||||||
function getDefaultBaseUrl(region: MiniMaxRegion): string {
|
|
||||||
return region === "cn" ? DEFAULT_BASE_URL_CN : DEFAULT_BASE_URL_GLOBAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
function modelRef(modelId: string): string {
|
|
||||||
return `${PROVIDER_ID}/${modelId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildProviderCatalog(params: { baseUrl: string; apiKey: string }) {
|
|
||||||
return {
|
|
||||||
...buildMinimaxPortalProvider(),
|
|
||||||
baseUrl: params.baseUrl,
|
|
||||||
apiKey: params.apiKey,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveCatalog(ctx: ProviderCatalogContext) {
|
|
||||||
const explicitProvider = ctx.config.models?.providers?.[PROVIDER_ID];
|
|
||||||
const envApiKey = ctx.resolveProviderApiKey(PROVIDER_ID).apiKey;
|
|
||||||
const authStore = ensureAuthProfileStore(ctx.agentDir, {
|
|
||||||
allowKeychainPrompt: false,
|
|
||||||
});
|
|
||||||
const hasProfiles = listProfilesForProvider(authStore, PROVIDER_ID).length > 0;
|
|
||||||
const explicitApiKey =
|
|
||||||
typeof explicitProvider?.apiKey === "string" ? explicitProvider.apiKey.trim() : undefined;
|
|
||||||
const apiKey = envApiKey ?? explicitApiKey ?? (hasProfiles ? MINIMAX_OAUTH_MARKER : undefined);
|
|
||||||
if (!apiKey) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const explicitBaseUrl =
|
|
||||||
typeof explicitProvider?.baseUrl === "string" ? explicitProvider.baseUrl.trim() : undefined;
|
|
||||||
|
|
||||||
return {
|
|
||||||
provider: buildProviderCatalog({
|
|
||||||
baseUrl: explicitBaseUrl || DEFAULT_BASE_URL_GLOBAL,
|
|
||||||
apiKey,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function createOAuthHandler(region: MiniMaxRegion) {
|
|
||||||
const defaultBaseUrl = getDefaultBaseUrl(region);
|
|
||||||
const regionLabel = region === "cn" ? "CN" : "Global";
|
|
||||||
|
|
||||||
return async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {
|
|
||||||
const progress = ctx.prompter.progress(`Starting MiniMax OAuth (${regionLabel})…`);
|
|
||||||
try {
|
|
||||||
const result = await loginMiniMaxPortalOAuth({
|
|
||||||
openUrl: ctx.openUrl,
|
|
||||||
note: ctx.prompter.note,
|
|
||||||
progress,
|
|
||||||
region,
|
|
||||||
});
|
|
||||||
|
|
||||||
progress.stop("MiniMax OAuth complete");
|
|
||||||
|
|
||||||
if (result.notification_message) {
|
|
||||||
await ctx.prompter.note(result.notification_message, "MiniMax OAuth");
|
|
||||||
}
|
|
||||||
|
|
||||||
const baseUrl = result.resourceUrl || defaultBaseUrl;
|
|
||||||
|
|
||||||
return buildOauthProviderAuthResult({
|
|
||||||
providerId: PROVIDER_ID,
|
|
||||||
defaultModel: modelRef(DEFAULT_MODEL),
|
|
||||||
access: result.access,
|
|
||||||
refresh: result.refresh,
|
|
||||||
expires: result.expires,
|
|
||||||
configPatch: {
|
|
||||||
models: {
|
|
||||||
providers: {
|
|
||||||
[PROVIDER_ID]: {
|
|
||||||
baseUrl,
|
|
||||||
models: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
agents: {
|
|
||||||
defaults: {
|
|
||||||
models: {
|
|
||||||
[modelRef("MiniMax-M2.5")]: { alias: "minimax-m2.5" },
|
|
||||||
[modelRef("MiniMax-M2.5-highspeed")]: {
|
|
||||||
alias: "minimax-m2.5-highspeed",
|
|
||||||
},
|
|
||||||
[modelRef("MiniMax-M2.5-Lightning")]: {
|
|
||||||
alias: "minimax-m2.5-lightning",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
notes: [
|
|
||||||
"MiniMax OAuth tokens auto-refresh. Re-run login if refresh fails or access is revoked.",
|
|
||||||
`Base URL defaults to ${defaultBaseUrl}. Override models.providers.${PROVIDER_ID}.baseUrl if needed.`,
|
|
||||||
...(result.notification_message ? [result.notification_message] : []),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
||||||
progress.stop(`MiniMax OAuth failed: ${errorMsg}`);
|
|
||||||
await ctx.prompter.note(
|
|
||||||
"If OAuth fails, verify your MiniMax account has portal access and try again.",
|
|
||||||
"MiniMax OAuth",
|
|
||||||
);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const minimaxPortalPlugin = {
|
|
||||||
id: "minimax-portal-auth",
|
|
||||||
name: "MiniMax OAuth",
|
|
||||||
description: "OAuth flow for MiniMax models",
|
|
||||||
configSchema: emptyPluginConfigSchema(),
|
|
||||||
register(api: OpenClawPluginApi) {
|
|
||||||
api.registerProvider({
|
|
||||||
id: PROVIDER_ID,
|
|
||||||
label: PROVIDER_LABEL,
|
|
||||||
docsPath: "/providers/minimax",
|
|
||||||
catalog: {
|
|
||||||
run: async (ctx: ProviderCatalogContext) => resolveCatalog(ctx),
|
|
||||||
},
|
|
||||||
auth: [
|
|
||||||
{
|
|
||||||
id: "oauth",
|
|
||||||
label: "MiniMax OAuth (Global)",
|
|
||||||
hint: "Global endpoint - api.minimax.io",
|
|
||||||
kind: "device_code",
|
|
||||||
run: createOAuthHandler("global"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "oauth-cn",
|
|
||||||
label: "MiniMax OAuth (CN)",
|
|
||||||
hint: "CN endpoint - api.minimaxi.com",
|
|
||||||
kind: "device_code",
|
|
||||||
run: createOAuthHandler("cn"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default minimaxPortalPlugin;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "minimax-portal-auth",
|
|
||||||
"providers": ["minimax-portal"],
|
|
||||||
"configSchema": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": false,
|
|
||||||
"properties": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@openclaw/minimax-portal-auth",
|
|
||||||
"version": "2026.3.14",
|
|
||||||
"private": true,
|
|
||||||
"description": "OpenClaw MiniMax Portal OAuth provider plugin",
|
|
||||||
"type": "module",
|
|
||||||
"openclaw": {
|
|
||||||
"extensions": [
|
|
||||||
"./index.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
37
extensions/minimax/README.md
Normal file
37
extensions/minimax/README.md
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# MiniMax (OpenClaw plugin)
|
||||||
|
|
||||||
|
Bundled MiniMax plugin for both:
|
||||||
|
|
||||||
|
- API-key provider setup (`minimax`)
|
||||||
|
- Coding Plan OAuth setup (`minimax-portal`)
|
||||||
|
|
||||||
|
## Enable
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw plugins enable minimax
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart the Gateway after enabling.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw gateway restart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authenticate
|
||||||
|
|
||||||
|
OAuth:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw models auth login --provider minimax-portal --set-default
|
||||||
|
```
|
||||||
|
|
||||||
|
API key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw onboard --auth-choice minimax-global-api
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- MiniMax OAuth uses a user-code login flow.
|
||||||
|
- OAuth currently targets the Coding Plan path.
|
||||||
@ -1,35 +1,165 @@
|
|||||||
import { emptyPluginConfigSchema, type OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
import {
|
||||||
import { buildMinimaxProvider } from "../../src/agents/models-config.providers.static.js";
|
buildOauthProviderAuthResult,
|
||||||
|
emptyPluginConfigSchema,
|
||||||
|
type OpenClawPluginApi,
|
||||||
|
type ProviderAuthContext,
|
||||||
|
type ProviderAuthResult,
|
||||||
|
type ProviderCatalogContext,
|
||||||
|
} from "openclaw/plugin-sdk/minimax-portal-auth";
|
||||||
|
import { ensureAuthProfileStore, listProfilesForProvider } from "../../src/agents/auth-profiles.js";
|
||||||
|
import { MINIMAX_OAUTH_MARKER } from "../../src/agents/model-auth-markers.js";
|
||||||
|
import {
|
||||||
|
buildMinimaxPortalProvider,
|
||||||
|
buildMinimaxProvider,
|
||||||
|
} from "../../src/agents/models-config.providers.static.js";
|
||||||
import { fetchMinimaxUsage } from "../../src/infra/provider-usage.fetch.js";
|
import { fetchMinimaxUsage } from "../../src/infra/provider-usage.fetch.js";
|
||||||
|
import { loginMiniMaxPortalOAuth, type MiniMaxRegion } from "./oauth.js";
|
||||||
|
|
||||||
const PROVIDER_ID = "minimax";
|
const API_PROVIDER_ID = "minimax";
|
||||||
|
const PORTAL_PROVIDER_ID = "minimax-portal";
|
||||||
|
const PROVIDER_LABEL = "MiniMax";
|
||||||
|
const DEFAULT_MODEL = "MiniMax-M2.5";
|
||||||
|
const DEFAULT_BASE_URL_CN = "https://api.minimaxi.com/anthropic";
|
||||||
|
const DEFAULT_BASE_URL_GLOBAL = "https://api.minimax.io/anthropic";
|
||||||
|
|
||||||
|
function getDefaultBaseUrl(region: MiniMaxRegion): string {
|
||||||
|
return region === "cn" ? DEFAULT_BASE_URL_CN : DEFAULT_BASE_URL_GLOBAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function modelRef(modelId: string): string {
|
||||||
|
return `${PORTAL_PROVIDER_ID}/${modelId}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPortalProviderCatalog(params: { baseUrl: string; apiKey: string }) {
|
||||||
|
return {
|
||||||
|
...buildMinimaxPortalProvider(),
|
||||||
|
baseUrl: params.baseUrl,
|
||||||
|
apiKey: params.apiKey,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveApiCatalog(ctx: ProviderCatalogContext) {
|
||||||
|
const apiKey = ctx.resolveProviderApiKey(API_PROVIDER_ID).apiKey;
|
||||||
|
if (!apiKey) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
provider: {
|
||||||
|
...buildMinimaxProvider(),
|
||||||
|
apiKey,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 =
|
||||||
|
typeof explicitProvider?.apiKey === "string" ? explicitProvider.apiKey.trim() : undefined;
|
||||||
|
const apiKey = envApiKey ?? explicitApiKey ?? (hasProfiles ? MINIMAX_OAUTH_MARKER : undefined);
|
||||||
|
if (!apiKey) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const explicitBaseUrl =
|
||||||
|
typeof explicitProvider?.baseUrl === "string" ? explicitProvider.baseUrl.trim() : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
provider: buildPortalProviderCatalog({
|
||||||
|
baseUrl: explicitBaseUrl || DEFAULT_BASE_URL_GLOBAL,
|
||||||
|
apiKey,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOAuthHandler(region: MiniMaxRegion) {
|
||||||
|
const defaultBaseUrl = getDefaultBaseUrl(region);
|
||||||
|
const regionLabel = region === "cn" ? "CN" : "Global";
|
||||||
|
|
||||||
|
return async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {
|
||||||
|
const progress = ctx.prompter.progress(`Starting MiniMax OAuth (${regionLabel})…`);
|
||||||
|
try {
|
||||||
|
const result = await loginMiniMaxPortalOAuth({
|
||||||
|
openUrl: ctx.openUrl,
|
||||||
|
note: ctx.prompter.note,
|
||||||
|
progress,
|
||||||
|
region,
|
||||||
|
});
|
||||||
|
|
||||||
|
progress.stop("MiniMax OAuth complete");
|
||||||
|
|
||||||
|
if (result.notification_message) {
|
||||||
|
await ctx.prompter.note(result.notification_message, "MiniMax OAuth");
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseUrl = result.resourceUrl || defaultBaseUrl;
|
||||||
|
|
||||||
|
return buildOauthProviderAuthResult({
|
||||||
|
providerId: PORTAL_PROVIDER_ID,
|
||||||
|
defaultModel: modelRef(DEFAULT_MODEL),
|
||||||
|
access: result.access,
|
||||||
|
refresh: result.refresh,
|
||||||
|
expires: result.expires,
|
||||||
|
configPatch: {
|
||||||
|
models: {
|
||||||
|
providers: {
|
||||||
|
[PORTAL_PROVIDER_ID]: {
|
||||||
|
baseUrl,
|
||||||
|
models: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
models: {
|
||||||
|
[modelRef("MiniMax-M2.5")]: { alias: "minimax-m2.5" },
|
||||||
|
[modelRef("MiniMax-M2.5-highspeed")]: {
|
||||||
|
alias: "minimax-m2.5-highspeed",
|
||||||
|
},
|
||||||
|
[modelRef("MiniMax-M2.5-Lightning")]: {
|
||||||
|
alias: "minimax-m2.5-lightning",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
notes: [
|
||||||
|
"MiniMax OAuth tokens auto-refresh. Re-run login if refresh fails or access is revoked.",
|
||||||
|
`Base URL defaults to ${defaultBaseUrl}. Override models.providers.${PORTAL_PROVIDER_ID}.baseUrl if needed.`,
|
||||||
|
...(result.notification_message ? [result.notification_message] : []),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
||||||
|
progress.stop(`MiniMax OAuth failed: ${errorMsg}`);
|
||||||
|
await ctx.prompter.note(
|
||||||
|
"If OAuth fails, verify your MiniMax account has portal access and try again.",
|
||||||
|
"MiniMax OAuth",
|
||||||
|
);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const minimaxPlugin = {
|
const minimaxPlugin = {
|
||||||
id: PROVIDER_ID,
|
id: API_PROVIDER_ID,
|
||||||
name: "MiniMax Provider",
|
name: "MiniMax",
|
||||||
description: "Bundled MiniMax provider plugin",
|
description: "Bundled MiniMax API-key and OAuth provider plugin",
|
||||||
configSchema: emptyPluginConfigSchema(),
|
configSchema: emptyPluginConfigSchema(),
|
||||||
register(api: OpenClawPluginApi) {
|
register(api: OpenClawPluginApi) {
|
||||||
api.registerProvider({
|
api.registerProvider({
|
||||||
id: PROVIDER_ID,
|
id: API_PROVIDER_ID,
|
||||||
label: "MiniMax",
|
label: PROVIDER_LABEL,
|
||||||
docsPath: "/providers/minimax",
|
docsPath: "/providers/minimax",
|
||||||
envVars: ["MINIMAX_API_KEY"],
|
envVars: ["MINIMAX_API_KEY"],
|
||||||
auth: [],
|
auth: [],
|
||||||
catalog: {
|
catalog: {
|
||||||
order: "simple",
|
order: "simple",
|
||||||
run: async (ctx) => {
|
run: async (ctx) => resolveApiCatalog(ctx),
|
||||||
const apiKey = ctx.resolveProviderApiKey(PROVIDER_ID).apiKey;
|
|
||||||
if (!apiKey) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
provider: {
|
|
||||||
...buildMinimaxProvider(),
|
|
||||||
apiKey,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
resolveUsageAuth: async (ctx) => {
|
resolveUsageAuth: async (ctx) => {
|
||||||
const apiKey = ctx.resolveApiKeyFromConfigAndStore({
|
const apiKey = ctx.resolveApiKeyFromConfigAndStore({
|
||||||
@ -40,6 +170,31 @@ const minimaxPlugin = {
|
|||||||
fetchUsageSnapshot: async (ctx) =>
|
fetchUsageSnapshot: async (ctx) =>
|
||||||
await fetchMinimaxUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
|
await fetchMinimaxUsage(ctx.token, ctx.timeoutMs, ctx.fetchFn),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
api.registerProvider({
|
||||||
|
id: PORTAL_PROVIDER_ID,
|
||||||
|
label: PROVIDER_LABEL,
|
||||||
|
docsPath: "/providers/minimax",
|
||||||
|
catalog: {
|
||||||
|
run: async (ctx) => resolvePortalCatalog(ctx),
|
||||||
|
},
|
||||||
|
auth: [
|
||||||
|
{
|
||||||
|
id: "oauth",
|
||||||
|
label: "MiniMax OAuth (Global)",
|
||||||
|
hint: "Global endpoint - api.minimax.io",
|
||||||
|
kind: "device_code",
|
||||||
|
run: createOAuthHandler("global"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "oauth-cn",
|
||||||
|
label: "MiniMax OAuth (CN)",
|
||||||
|
hint: "CN endpoint - api.minimaxi.com",
|
||||||
|
kind: "device_code",
|
||||||
|
run: createOAuthHandler("cn"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -161,7 +161,7 @@ async function pollOAuthToken(params: {
|
|||||||
return { status: "error", message: "An error occurred. Please try again later" };
|
return { status: "error", message: "An error occurred. Please try again later" };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokenPayload.status != "success") {
|
if (tokenPayload.status !== "success") {
|
||||||
return { status: "pending", message: "current user code is not authorized" };
|
return { status: "pending", message: "current user code is not authorized" };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,29 +216,17 @@ export async function loginMiniMaxPortalOAuth(params: {
|
|||||||
region,
|
region,
|
||||||
});
|
});
|
||||||
|
|
||||||
// // Debug: print poll result
|
|
||||||
// await params.note(
|
|
||||||
// `status: ${result.status}` +
|
|
||||||
// (result.status === "success" ? `\ntoken: ${JSON.stringify(result.token, null, 2)}` : "") +
|
|
||||||
// (result.status === "error" ? `\nmessage: ${result.message}` : "") +
|
|
||||||
// (result.status === "pending" && result.message ? `\nmessage: ${result.message}` : ""),
|
|
||||||
// "MiniMax OAuth Poll Result",
|
|
||||||
// );
|
|
||||||
|
|
||||||
if (result.status === "success") {
|
if (result.status === "success") {
|
||||||
return result.token;
|
return result.token;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.status === "error") {
|
if (result.status === "error") {
|
||||||
throw new Error(`MiniMax OAuth failed: ${result.message}`);
|
throw new Error(result.message);
|
||||||
}
|
|
||||||
|
|
||||||
if (result.status === "pending") {
|
|
||||||
pollIntervalMs = Math.min(pollIntervalMs * 1.5, 10000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
||||||
|
pollIntervalMs = Math.max(pollIntervalMs, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error("MiniMax OAuth timed out waiting for authorization.");
|
throw new Error("MiniMax OAuth timed out before authorization completed.");
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "minimax",
|
"id": "minimax",
|
||||||
"providers": ["minimax"],
|
"providers": ["minimax", "minimax-portal"],
|
||||||
"configSchema": {
|
"configSchema": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"name": "@openclaw/minimax-provider",
|
"name": "@openclaw/minimax-provider",
|
||||||
"version": "2026.3.14",
|
"version": "2026.3.14",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "OpenClaw MiniMax provider plugin",
|
"description": "OpenClaw MiniMax provider and OAuth plugin",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"openclaw": {
|
"openclaw": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
|
|||||||
@ -24,8 +24,8 @@ const allowedRawFetchCallsites = new Set([
|
|||||||
"extensions/mattermost/src/mattermost/client.ts:211",
|
"extensions/mattermost/src/mattermost/client.ts:211",
|
||||||
"extensions/mattermost/src/mattermost/monitor.ts:230",
|
"extensions/mattermost/src/mattermost/monitor.ts:230",
|
||||||
"extensions/mattermost/src/mattermost/probe.ts:27",
|
"extensions/mattermost/src/mattermost/probe.ts:27",
|
||||||
"extensions/minimax-portal-auth/oauth.ts:71",
|
"extensions/minimax/oauth.ts:62",
|
||||||
"extensions/minimax-portal-auth/oauth.ts:112",
|
"extensions/minimax/oauth.ts:93",
|
||||||
"extensions/msteams/src/graph.ts:39",
|
"extensions/msteams/src/graph.ts:39",
|
||||||
"extensions/nextcloud-talk/src/room-info.ts:92",
|
"extensions/nextcloud-talk/src/room-info.ts:92",
|
||||||
"extensions/nextcloud-talk/src/send.ts:107",
|
"extensions/nextcloud-talk/src/send.ts:107",
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export async function applyAuthChoiceMiniMax(
|
|||||||
if (params.authChoice === "minimax-global-oauth") {
|
if (params.authChoice === "minimax-global-oauth") {
|
||||||
return await applyAuthChoicePluginProvider(params, {
|
return await applyAuthChoicePluginProvider(params, {
|
||||||
authChoice: "minimax-global-oauth",
|
authChoice: "minimax-global-oauth",
|
||||||
pluginId: "minimax-portal-auth",
|
pluginId: "minimax",
|
||||||
providerId: "minimax-portal",
|
providerId: "minimax-portal",
|
||||||
methodId: "oauth",
|
methodId: "oauth",
|
||||||
label: "MiniMax",
|
label: "MiniMax",
|
||||||
@ -32,7 +32,7 @@ export async function applyAuthChoiceMiniMax(
|
|||||||
if (params.authChoice === "minimax-cn-oauth") {
|
if (params.authChoice === "minimax-cn-oauth") {
|
||||||
return await applyAuthChoicePluginProvider(params, {
|
return await applyAuthChoicePluginProvider(params, {
|
||||||
authChoice: "minimax-cn-oauth",
|
authChoice: "minimax-cn-oauth",
|
||||||
pluginId: "minimax-portal-auth",
|
pluginId: "minimax",
|
||||||
providerId: "minimax-portal",
|
providerId: "minimax-portal",
|
||||||
methodId: "oauth-cn",
|
methodId: "oauth-cn",
|
||||||
label: "MiniMax CN",
|
label: "MiniMax CN",
|
||||||
|
|||||||
@ -310,6 +310,25 @@ describe("applyPluginAutoEnable", () => {
|
|||||||
expect(result.config.plugins?.entries?.google?.enabled).toBe(true);
|
expect(result.config.plugins?.entries?.google?.enabled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("auto-enables minimax when minimax-portal profiles exist", () => {
|
||||||
|
const result = applyPluginAutoEnable({
|
||||||
|
config: {
|
||||||
|
auth: {
|
||||||
|
profiles: {
|
||||||
|
"minimax-portal:default": {
|
||||||
|
provider: "minimax-portal",
|
||||||
|
mode: "oauth",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
env: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.config.plugins?.entries?.minimax?.enabled).toBe(true);
|
||||||
|
expect(result.config.plugins?.entries?.["minimax-portal-auth"]).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
it("auto-enables acpx plugin when ACP is configured", () => {
|
it("auto-enables acpx plugin when ACP is configured", () => {
|
||||||
const result = applyPluginAutoEnable({
|
const result = applyPluginAutoEnable({
|
||||||
config: {
|
config: {
|
||||||
|
|||||||
@ -31,7 +31,7 @@ const PROVIDER_PLUGIN_IDS: Array<{ pluginId: string; providerId: string }> = [
|
|||||||
{ pluginId: "google", providerId: "google-gemini-cli" },
|
{ pluginId: "google", providerId: "google-gemini-cli" },
|
||||||
{ pluginId: "qwen-portal-auth", providerId: "qwen-portal" },
|
{ pluginId: "qwen-portal-auth", providerId: "qwen-portal" },
|
||||||
{ pluginId: "copilot-proxy", providerId: "copilot-proxy" },
|
{ pluginId: "copilot-proxy", providerId: "copilot-proxy" },
|
||||||
{ pluginId: "minimax-portal-auth", providerId: "minimax-portal" },
|
{ pluginId: "minimax", providerId: "minimax-portal" },
|
||||||
];
|
];
|
||||||
|
|
||||||
function hasNonEmptyString(value: unknown): boolean {
|
function hasNonEmptyString(value: unknown): boolean {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// Narrow plugin-sdk surface for the bundled minimax-portal-auth plugin.
|
// Narrow plugin-sdk surface for MiniMax OAuth helpers used by the bundled minimax plugin.
|
||||||
// Keep this list additive and scoped to symbols used under extensions/minimax-portal-auth.
|
// Keep this list additive and scoped to MiniMax OAuth support code.
|
||||||
|
|
||||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||||
export { buildOauthProviderAuthResult } from "./provider-auth-result.js";
|
export { buildOauthProviderAuthResult } from "./provider-auth-result.js";
|
||||||
|
|||||||
@ -80,18 +80,22 @@ describe("normalizePluginsConfig", () => {
|
|||||||
|
|
||||||
it("normalizes legacy plugin ids to their merged bundled plugin id", () => {
|
it("normalizes legacy plugin ids to their merged bundled plugin id", () => {
|
||||||
const result = normalizePluginsConfig({
|
const result = normalizePluginsConfig({
|
||||||
allow: ["openai-codex"],
|
allow: ["openai-codex", "minimax-portal-auth"],
|
||||||
deny: ["openai-codex"],
|
deny: ["openai-codex", "minimax-portal-auth"],
|
||||||
entries: {
|
entries: {
|
||||||
"openai-codex": {
|
"openai-codex": {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
|
"minimax-portal-auth": {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.allow).toEqual(["openai"]);
|
expect(result.allow).toEqual(["openai", "minimax"]);
|
||||||
expect(result.deny).toEqual(["openai"]);
|
expect(result.deny).toEqual(["openai", "minimax"]);
|
||||||
expect(result.entries.openai?.enabled).toBe(true);
|
expect(result.entries.openai?.enabled).toBe(true);
|
||||||
|
expect(result.entries.minimax?.enabled).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,6 @@ export const BUNDLED_ENABLED_BY_DEFAULT = new Set<string>([
|
|||||||
"kilocode",
|
"kilocode",
|
||||||
"kimi-coding",
|
"kimi-coding",
|
||||||
"minimax",
|
"minimax",
|
||||||
"minimax-portal-auth",
|
|
||||||
"mistral",
|
"mistral",
|
||||||
"modelstudio",
|
"modelstudio",
|
||||||
"moonshot",
|
"moonshot",
|
||||||
@ -60,6 +59,7 @@ export const BUNDLED_ENABLED_BY_DEFAULT = new Set<string>([
|
|||||||
|
|
||||||
const PLUGIN_ID_ALIASES: Readonly<Record<string, string>> = {
|
const PLUGIN_ID_ALIASES: Readonly<Record<string, string>> = {
|
||||||
"openai-codex": "openai",
|
"openai-codex": "openai",
|
||||||
|
"minimax-portal-auth": "minimax",
|
||||||
};
|
};
|
||||||
|
|
||||||
function normalizePluginId(id: string): string {
|
function normalizePluginId(id: string): string {
|
||||||
|
|||||||
@ -16,7 +16,6 @@ const BUNDLED_PROVIDER_ALLOWLIST_COMPAT_PLUGIN_IDS = [
|
|||||||
"kilocode",
|
"kilocode",
|
||||||
"kimi-coding",
|
"kimi-coding",
|
||||||
"minimax",
|
"minimax",
|
||||||
"minimax-portal-auth",
|
|
||||||
"mistral",
|
"mistral",
|
||||||
"modelstudio",
|
"modelstudio",
|
||||||
"moonshot",
|
"moonshot",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user