Vendor OpenClaw source as Adolf fork baseline
Some checks failed
ClawSweeper Dispatch / dispatch (push) Has been cancelled
CodeQL / Security High (actions) (push) Has been cancelled
CodeQL / Security High (channel-runtime-boundary) (push) Has been cancelled
CodeQL / Security High (core-auth-secrets) (push) Has been cancelled
CodeQL / Security High (mcp-process-tool-boundary) (push) Has been cancelled
CodeQL / Security High (network-ssrf-boundary) (push) Has been cancelled
CodeQL / Security High (plugin-trust-boundary) (push) Has been cancelled
CodeQL / Security High (process-exec-boundary) (push) Has been cancelled
Docs Sync Publish Repo / sync-publish-repo (push) Has been cancelled
Docs / docs (push) Has been cancelled
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Has been cancelled
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Has been cancelled
Workflow Sanity / no-tabs (push) Has been cancelled
Workflow Sanity / actionlint (push) Has been cancelled
Workflow Sanity / generated-doc-baselines (push) Has been cancelled
CI / runner-admission (push) Has been cancelled
CI / preflight (push) Has been cancelled
CI / security-fast (push) Has been cancelled
CI / pnpm-store-warmup (push) Has been cancelled
CI / build-artifacts (push) Has been cancelled
CI / native-i18n (push) Has been cancelled
CI / ${{ matrix.check_name }} (push) Has been cancelled
CI / ${{ matrix.checkName }} (push) Has been cancelled
CI / checks-node-compat-node22 (push) Has been cancelled
CI / check-bundled-channel-config-metadata (push) Has been cancelled
CI / check-dependencies (push) Has been cancelled
CI / check-guards (push) Has been cancelled
CI / check-lint (push) Has been cancelled
CI / check-prod-types (push) Has been cancelled
CI / check-shrinkwrap (push) Has been cancelled
CI / check-test-types (push) Has been cancelled
CI / check-additional-boundaries-a (push) Has been cancelled
CI / check-additional-boundaries-bcd (push) Has been cancelled
CI / check-additional-extension-bundled (push) Has been cancelled
CI / check-additional-extension-channels (push) Has been cancelled
CI / check-additional-extension-package-boundary (push) Has been cancelled
CI / check-additional-runtime-topology-architecture (push) Has been cancelled
CI / check-session-accessor-boundary (push) Has been cancelled
CI / check-session-transcript-reader-boundary (push) Has been cancelled
CI / check-docs (push) Has been cancelled
CI / skills-python (push) Has been cancelled
CI / macos-swift (push) Has been cancelled
CI / ios-build (push) Has been cancelled
CI / ci-timings-summary (push) Has been cancelled
Native App Locale Refresh / Refresh native fa (push) Has been cancelled
Native App Locale Refresh / Refresh native fr (push) Has been cancelled
Native App Locale Refresh / Refresh native hi (push) Has been cancelled
Native App Locale Refresh / Refresh native id (push) Has been cancelled
Native App Locale Refresh / Refresh native it (push) Has been cancelled
Native App Locale Refresh / Refresh native ja-JP (push) Has been cancelled
Control UI Locale Refresh / plan (push) Has been cancelled
Control UI Locale Refresh / Refresh ${{ matrix.locale }} (push) Has been cancelled
Control UI Locale Refresh / Commit control UI locale refresh (push) Has been cancelled
Live Media Runner Image / Build live media runner image (push) Has been cancelled
Native App Locale Refresh / Refresh native ar (push) Has been cancelled
Native App Locale Refresh / Refresh native de (push) Has been cancelled
Native App Locale Refresh / Refresh native es (push) Has been cancelled
Native App Locale Refresh / Refresh native ko (push) Has been cancelled
Native App Locale Refresh / Refresh native nl (push) Has been cancelled
Native App Locale Refresh / Refresh native pl (push) Has been cancelled
Native App Locale Refresh / Refresh native pt-BR (push) Has been cancelled
Native App Locale Refresh / Refresh native ru (push) Has been cancelled
Native App Locale Refresh / Refresh native sv (push) Has been cancelled
Native App Locale Refresh / Refresh native th (push) Has been cancelled
Native App Locale Refresh / Refresh native tr (push) Has been cancelled
Native App Locale Refresh / Refresh native uk (push) Has been cancelled
Native App Locale Refresh / Refresh native vi (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-CN (push) Has been cancelled
Native App Locale Refresh / Refresh native zh-TW (push) Has been cancelled
Native App Locale Refresh / Commit native locale refresh (push) Has been cancelled
Plugin Init Scaffold Validation / Validate provider scaffold (push) Has been cancelled
Plugin NPM Release / preview_plugins_npm (push) Has been cancelled
Plugin NPM Release / Validate release publish approval (push) Has been cancelled
Plugin NPM Release / preview_plugin_pack (push) Has been cancelled
Plugin NPM Release / publish_plugins_npm (push) Has been cancelled
Sandbox Common Smoke / sandbox-common-smoke (push) Has been cancelled
Website Installer Sync / static (push) Has been cancelled
Website Installer Sync / linux-docker (push) Has been cancelled
Website Installer Sync / macos-installer (push) Has been cancelled
Website Installer Sync / windows-installer (push) Has been cancelled
Website Installer Sync / sync-website (push) Has been cancelled

Adolf is a fork/vendored clone of github.com/openclaw/openclaw (v2026.6.11),
free to diverge. Tree copied sans upstream .git; upstream remote added for
future syncs. Node pinned to 24 (.nvmrc); engines already require >=22.19.
Preserves docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LeqyaxJF2nbRXJtae2kNB2
This commit is contained in:
2026-07-05 09:36:54 +00:00
parent 3216769225
commit bedb527145
21108 changed files with 6010766 additions and 0 deletions

305
extensions/minimax/oauth.ts Normal file
View File

@@ -0,0 +1,305 @@
// Minimax plugin module implements oauth behavior.
import { randomBytes, randomUUID } from "node:crypto";
import {
MAX_DATE_TIMESTAMP_MS,
asSafeIntegerInRange,
resolveExpiresAtMsFromDurationOrEpoch,
resolvePositiveTimerTimeoutMs,
} from "openclaw/plugin-sdk/number-runtime";
import { generatePkceVerifierChallenge, toFormUrlEncoded } from "openclaw/plugin-sdk/provider-auth";
import {
readProviderJsonResponse,
readResponseTextLimited,
} from "openclaw/plugin-sdk/provider-http";
import { ensureGlobalUndiciEnvProxyDispatcher } from "openclaw/plugin-sdk/runtime-env";
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
export type MiniMaxRegion = "cn" | "global";
const MINIMAX_OAUTH_CONFIG = {
cn: {
baseUrl: "https://api.minimaxi.com",
oauthBaseUrl: "https://account.minimaxi.com",
clientId: "78257093-7e40-4613-99e0-527b14b39113",
},
global: {
baseUrl: "https://api.minimax.io",
oauthBaseUrl: "https://account.minimax.io",
clientId: "78257093-7e40-4613-99e0-527b14b39113",
},
} as const;
const MINIMAX_OAUTH_SCOPE = "group_id profile model.completion";
const MINIMAX_OAUTH_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:user_code";
const MINIMAX_RELATIVE_EXPIRY_SECONDS_THRESHOLD = 1_000_000_000;
const MINIMAX_ABSOLUTE_EXPIRY_MS_THRESHOLD = 1_000_000_000_000;
const MINIMAX_OAUTH_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
function getOAuthEndpoints(region: MiniMaxRegion) {
const config = MINIMAX_OAUTH_CONFIG[region];
return {
codeEndpoint: `${config.oauthBaseUrl}/oauth2/device/code`,
tokenEndpoint: `${config.oauthBaseUrl}/oauth2/token`,
clientId: config.clientId,
baseUrl: config.baseUrl,
hostname: new URL(config.oauthBaseUrl).hostname,
};
}
type MiniMaxOAuthAuthorization = {
user_code: string;
verification_uri: string;
expired_in: number;
interval?: number;
state: string;
};
type MiniMaxOAuthToken = {
access: string;
refresh: string;
expires: number;
resourceUrl?: string;
notification_message?: string;
};
type TokenPending = { status: "pending"; message?: string };
type TokenResult =
| { status: "success"; token: MiniMaxOAuthToken }
| TokenPending
| { status: "error"; message: string };
/**
* Normalize MiniMax token endpoint `expired_in` values to the auth-profile
* contract: absolute Unix milliseconds.
*/
export function normalizeOAuthExpires(expiredIn: unknown, now = Date.now()): number | undefined {
return resolveExpiresAtMsFromDurationOrEpoch(expiredIn, {
nowMs: now,
relativeSecondsThreshold: MINIMAX_RELATIVE_EXPIRY_SECONDS_THRESHOLD,
absoluteMillisecondsThreshold: MINIMAX_ABSOLUTE_EXPIRY_MS_THRESHOLD,
});
}
function normalizeOAuthAuthorizationExpires(expiredIn: unknown): number | undefined {
return asSafeIntegerInRange(expiredIn, { min: 1, max: MAX_DATE_TIMESTAMP_MS });
}
function generatePkce(): { verifier: string; challenge: string; state: string } {
const { verifier, challenge } = generatePkceVerifierChallenge();
const state = randomBytes(16).toString("base64url");
return { verifier, challenge, state };
}
async function requestOAuthCode(params: {
challenge: string;
state: string;
region: MiniMaxRegion;
}): Promise<MiniMaxOAuthAuthorization> {
const endpoints = getOAuthEndpoints(params.region);
const { response, release } = await fetchWithSsrFGuard({
url: endpoints.codeEndpoint,
init: {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
"x-request-id": randomUUID(),
},
body: toFormUrlEncoded({
response_type: "code",
client_id: endpoints.clientId,
scope: MINIMAX_OAUTH_SCOPE,
code_challenge: params.challenge,
code_challenge_method: "S256",
state: params.state,
}),
},
policy: { allowedHostnames: [endpoints.hostname] },
auditContext: "minimax.oauth.code",
});
try {
if (!response.ok) {
const text = await readResponseTextLimited(response, MINIMAX_OAUTH_ERROR_BODY_LIMIT_BYTES);
throw new Error(`MiniMax OAuth authorization failed: ${text || response.statusText}`);
}
const payload = (await readProviderJsonResponse(
response,
"minimax.oauth-code",
)) as MiniMaxOAuthAuthorization & { error?: string };
if (!payload.user_code || !payload.verification_uri) {
throw new Error(
payload.error ??
"MiniMax OAuth authorization returned an incomplete payload (missing user_code or verification_uri).",
);
}
if (payload.state !== params.state) {
throw new Error("MiniMax OAuth state mismatch: possible CSRF attack or session corruption.");
}
const expiredIn = normalizeOAuthAuthorizationExpires(payload.expired_in);
if (expiredIn === undefined) {
throw new Error("MiniMax OAuth authorization returned invalid expired_in.");
}
return { ...payload, expired_in: expiredIn };
} finally {
await release();
}
}
async function pollOAuthToken(params: {
userCode: string;
verifier: string;
region: MiniMaxRegion;
}): Promise<TokenResult> {
const endpoints = getOAuthEndpoints(params.region);
const { response, release } = await fetchWithSsrFGuard({
url: endpoints.tokenEndpoint,
init: {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
},
body: toFormUrlEncoded({
grant_type: MINIMAX_OAUTH_GRANT_TYPE,
client_id: endpoints.clientId,
user_code: params.userCode,
code_verifier: params.verifier,
}),
},
policy: { allowedHostnames: [endpoints.hostname] },
auditContext: "minimax.oauth.token",
});
try {
return await parseMiniMaxOAuthTokenResponse(response);
} finally {
await release();
}
}
async function parseMiniMaxOAuthTokenResponse(response: Response): Promise<TokenResult> {
const text = await readResponseTextLimited(response, MINIMAX_OAUTH_ERROR_BODY_LIMIT_BYTES);
let payload:
| {
status?: string;
base_resp?: { status_code?: number; status_msg?: string };
}
| undefined;
if (text) {
try {
payload = JSON.parse(text) as typeof payload;
} catch {
payload = undefined;
}
}
if (!response.ok) {
return {
status: "error",
message:
(payload?.base_resp?.status_msg ?? text) || "MiniMax OAuth failed to parse response.",
};
}
if (!payload) {
return { status: "error", message: "MiniMax OAuth failed to parse response." };
}
const tokenPayload = payload as {
status: string;
access_token?: string | null;
refresh_token?: string | null;
expired_in?: unknown;
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" };
}
if (!tokenPayload.access_token || !tokenPayload.refresh_token || !tokenPayload.expired_in) {
return { status: "error", message: "MiniMax OAuth returned incomplete token payload." };
}
const expires = normalizeOAuthExpires(tokenPayload.expired_in);
if (expires === undefined) {
return { status: "error", message: "MiniMax OAuth returned invalid token expiry." };
}
return {
status: "success",
token: {
access: tokenPayload.access_token,
refresh: tokenPayload.refresh_token,
expires,
resourceUrl: tokenPayload.resource_url,
notification_message: tokenPayload.notification_message,
},
};
}
export async function loginMiniMaxPortalOAuth(params: {
openUrl: (url: string) => Promise<void>;
note: (message: string, title?: string) => Promise<void>;
progress: { update: (message: string) => void; stop: (message?: string) => void };
region?: MiniMaxRegion;
}): Promise<MiniMaxOAuthToken> {
// Ensure env-based proxy dispatcher is active before any outbound fetch calls.
// Without this, HTTP_PROXY/HTTPS_PROXY env vars are silently ignored (#51619).
ensureGlobalUndiciEnvProxyDispatcher();
const region = params.region ?? "global";
const { verifier, challenge, state } = generatePkce();
const oauth = await requestOAuthCode({ challenge, state, region });
const verificationUrl = oauth.verification_uri;
const noteLines = [
`Open ${verificationUrl} to approve access.`,
`If prompted, enter the code ${oauth.user_code}.`,
`Interval: ${oauth.interval ?? "default (2000ms)"}, Expires at: ${new Date(oauth.expired_in).toISOString()}`,
];
await params.note(noteLines.join("\n"), "MiniMax OAuth");
try {
await params.openUrl(verificationUrl);
} catch {
// Fall back to manual copy/paste if browser open fails.
}
let pollIntervalMs = resolvePositiveTimerTimeoutMs(oauth.interval, 2000);
// The authorization endpoint returns an absolute millisecond deadline.
const expireTimeMs = oauth.expired_in;
while (Date.now() < expireTimeMs) {
params.progress.update("Waiting for MiniMax OAuth approval…");
const result = await pollOAuthToken({
userCode: oauth.user_code,
verifier,
region,
});
if (result.status === "success") {
return result.token;
}
if (result.status === "error") {
throw new Error(result.message);
}
const remainingMs = Math.max(0, expireTimeMs - Date.now());
if (remainingMs <= 0) {
break;
}
await new Promise((resolve) => {
setTimeout(resolve, Math.min(pollIntervalMs, remainingMs));
});
pollIntervalMs = Math.max(pollIntervalMs, 2000);
}
throw new Error("MiniMax OAuth timed out before authorization completed.");
}