feat(minimax-portal-auth): add regional endpoint selection and improve OAuth flow
- Support Global (api.minimax.io) and CN (api.minimaxi.com) endpoints - Add endpoint selection prompt in onboard flow - Add notification_message display after successful login - Update documentation with Coding Plan details - Fix API response field parsing (expired_in, base_resp.status_msg) - Add debug logging for OAuth response and poll results
This commit is contained in:
parent
cdd03f465d
commit
2219152ccf
@ -35,7 +35,24 @@ MiniMax highlights these improvements in M2.1:
|
||||
|
||||
## Choose a setup
|
||||
|
||||
### MiniMax M2.1 — recommended
|
||||
### MiniMax OAuth (Coding Plan) — recommended
|
||||
|
||||
**Best for:** quick setup with MiniMax Coding Plan via OAuth, no API key required.
|
||||
|
||||
Enable the bundled OAuth plugin and authenticate:
|
||||
|
||||
```bash
|
||||
moltbot plugins enable minimax-portal-auth
|
||||
moltbot gateway restart
|
||||
moltbot onboard --auth-choice minimax-portal
|
||||
```
|
||||
You will be prompted to select an endpoint:
|
||||
- **Global** - International users (`api.minimax.io`)
|
||||
- **CN** - Users in China (`api.minimaxi.com`)
|
||||
|
||||
See [MiniMax OAuth plugin README](https://github.com/moltbot/moltbot/tree/main/extensions/minimax-portal-auth) for details.
|
||||
|
||||
### MiniMax M2.1 (API key)
|
||||
|
||||
**Best for:** hosted MiniMax with Anthropic-compatible API.
|
||||
|
||||
@ -143,6 +160,7 @@ Use the interactive config wizard to set MiniMax without editing JSON:
|
||||
3) Choose **MiniMax M2.1**.
|
||||
4) Pick your default model when prompted.
|
||||
|
||||
|
||||
## Configuration options
|
||||
|
||||
- `models.providers.minimax.baseUrl`: prefer `https://api.minimax.io/anthropic` (Anthropic-compatible); `https://api.minimax.io/v1` is optional for OpenAI-compatible payloads.
|
||||
|
||||
@ -29,5 +29,5 @@ You will be prompted to select an endpoint:
|
||||
|
||||
## Notes
|
||||
|
||||
- MiniMax OAuth uses a device-code login flow.
|
||||
- Tokens auto-refresh; re-run login if refresh fails or access is revoked.
|
||||
- MiniMax OAuth uses a user-code login flow.
|
||||
- Currently, OAuth login is supported only for the Coding plan
|
||||
@ -44,6 +44,10 @@ function createOAuthHandler(region: MiniMaxRegion) {
|
||||
|
||||
progress.stop("MiniMax OAuth complete");
|
||||
|
||||
if (result.notification_message) {
|
||||
await ctx.prompter.note(result.notification_message, "MiniMax OAuth");
|
||||
}
|
||||
|
||||
const profileId = `${PROVIDER_ID}:default`;
|
||||
const baseUrl = result.resourceUrl || defaultBaseUrl;
|
||||
|
||||
@ -85,7 +89,8 @@ function createOAuthHandler(region: MiniMaxRegion) {
|
||||
agents: {
|
||||
defaults: {
|
||||
models: {
|
||||
"MiniMax-M2.1": { alias: "minimax" },
|
||||
"MiniMax-M2.1": { alias: "minimax-m2.1" },
|
||||
"MiniMax-M2.1-lightning": { alias: "minimax-m2.1-lightning" },
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -94,10 +99,12 @@ function createOAuthHandler(region: MiniMaxRegion) {
|
||||
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) {
|
||||
progress.stop("MiniMax OAuth failed");
|
||||
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",
|
||||
|
||||
@ -29,10 +29,8 @@ function getOAuthEndpoints(region: MiniMaxRegion) {
|
||||
export type MiniMaxOAuthAuthorization = {
|
||||
user_code: string;
|
||||
verification_uri: string;
|
||||
expires_in: number;
|
||||
expired_in: number;
|
||||
interval?: number;
|
||||
has_benefit: boolean;
|
||||
benefit_message: string;
|
||||
state: string;
|
||||
};
|
||||
|
||||
@ -41,6 +39,7 @@ export type MiniMaxOAuthToken = {
|
||||
refresh: string;
|
||||
expires: number;
|
||||
resourceUrl?: string;
|
||||
notification_message?: string;
|
||||
};
|
||||
|
||||
type TokenPending = { status: "pending"; message?: string };
|
||||
@ -127,6 +126,7 @@ async function pollOAuthToken(params: {
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
let payload: {
|
||||
status?: string;
|
||||
base_resp?: { status_code?: number; status_msg?: string };
|
||||
@ -134,11 +134,11 @@ async function pollOAuthToken(params: {
|
||||
try {
|
||||
payload = (await response.json()) as typeof payload;
|
||||
} catch {
|
||||
return { status: "error", message: response.statusText };
|
||||
return { status: "error", message: text || "MiniMax OAuth failed to parse response.",};
|
||||
}
|
||||
return {
|
||||
status: "error",
|
||||
message: payload?.base_resp?.status_msg ?? response.statusText,
|
||||
message: text || "MiniMax OAuth failed to parse response.",
|
||||
};
|
||||
}
|
||||
|
||||
@ -149,7 +149,13 @@ async function pollOAuthToken(params: {
|
||||
expired_in?: number | null;
|
||||
token_type?: string;
|
||||
resource_url?: string;
|
||||
notification_message?: string;
|
||||
};
|
||||
|
||||
if (tokenPayload.status === "error") {
|
||||
return { status: "error", message: "An error occurred. Please try again later"};
|
||||
}
|
||||
|
||||
if (tokenPayload.status != "success") {
|
||||
return { status: "pending", message: "current user code is not authorized" };
|
||||
}
|
||||
@ -163,8 +169,9 @@ async function pollOAuthToken(params: {
|
||||
token: {
|
||||
access: tokenPayload.access_token,
|
||||
refresh: tokenPayload.refresh_token,
|
||||
expires: Date.now() + tokenPayload.expired_in * 1000,
|
||||
expires: tokenPayload.expired_in,
|
||||
resourceUrl: tokenPayload.resource_url,
|
||||
notification_message: tokenPayload.notification_message,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -183,10 +190,8 @@ export async function loginMiniMaxPortalOAuth(params: {
|
||||
const noteLines = [
|
||||
`Open ${verificationUrl} to approve access.`,
|
||||
`If prompted, enter the code ${oauth.user_code}.`,
|
||||
`Interval: ${oauth.interval ?? "default (2000ms)"}, Expires in: ${oauth.expired_in}ms`,
|
||||
];
|
||||
if (oauth.has_benefit && oauth.benefit_message) {
|
||||
noteLines.push("", oauth.benefit_message);
|
||||
}
|
||||
await params.note(noteLines.join("\n"), "MiniMax OAuth");
|
||||
|
||||
try {
|
||||
@ -195,11 +200,11 @@ export async function loginMiniMaxPortalOAuth(params: {
|
||||
// Fall back to manual copy/paste if browser open fails.
|
||||
}
|
||||
|
||||
const start = Date.now();
|
||||
let pollIntervalMs = oauth.interval ? oauth.interval * 1000 : 2000;
|
||||
const timeoutMs = oauth.expires_in * 1000;
|
||||
let pollIntervalMs = oauth.interval ? oauth.interval : 2000;
|
||||
const expireTimeMs = oauth.expired_in;
|
||||
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
|
||||
while (Date.now() < expireTimeMs) {
|
||||
params.progress.update("Waiting for MiniMax OAuth approval…");
|
||||
const result = await pollOAuthToken({
|
||||
userCode: oauth.user_code,
|
||||
@ -207,6 +212,15 @@ export async function loginMiniMaxPortalOAuth(params: {
|
||||
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") {
|
||||
return result.token;
|
||||
}
|
||||
|
||||
@ -29,11 +29,20 @@ export async function applyAuthChoiceMiniMax(
|
||||
);
|
||||
};
|
||||
if (params.authChoice === "minimax-portal") {
|
||||
// Let user choose between Global/CN endpoints
|
||||
const endpoint = await params.prompter.select({
|
||||
message: "Select MiniMax endpoint",
|
||||
options: [
|
||||
{ value: "oauth", label: "Global", hint: "OAuth for international users" },
|
||||
{ value: "oauth-cn", label: "CN", hint: "OAuth for users in China" },
|
||||
],
|
||||
});
|
||||
|
||||
return await applyAuthChoicePluginProvider(params, {
|
||||
authChoice: "minimax-portal",
|
||||
pluginId: "minimax-portal-auth",
|
||||
providerId: "minimax-portal",
|
||||
methodId: "device",
|
||||
methodId: endpoint as string,
|
||||
label: "MiniMax",
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user