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

View File

@@ -0,0 +1,277 @@
// Minimax provider module implements model/runtime integration.
import {
createProviderHttpError,
formatProviderHttpErrorMessage,
readProviderJsonResponse,
} from "openclaw/plugin-sdk/provider-http";
import {
DEFAULT_SEARCH_COUNT,
buildSearchCacheKey,
formatCliCommand,
mergeScopedSearchConfig,
readCachedSearchPayload,
readConfiguredSecretString,
readPositiveIntegerParam,
readProviderEnvValue,
readStringParam,
MAX_SEARCH_COUNT,
resolveProviderWebSearchPluginConfig,
resolveSearchCacheTtlMs,
resolveSearchCount,
resolveSearchTimeoutSeconds,
resolveSiteName,
withTrustedWebSearchEndpoint,
wrapWebContent,
writeCachedSearchPayload,
type SearchConfigRecord,
} from "openclaw/plugin-sdk/provider-web-search";
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
const MINIMAX_SEARCH_ENDPOINT_GLOBAL = "https://api.minimax.io/v1/coding_plan/search";
const MINIMAX_SEARCH_ENDPOINT_CN = "https://api.minimaxi.com/v1/coding_plan/search";
const MINIMAX_TOKEN_PLAN_ENV_VARS = [
"MINIMAX_CODE_PLAN_KEY",
"MINIMAX_CODING_API_KEY",
"MINIMAX_OAUTH_TOKEN",
] as const;
type MiniMaxSearchResult = {
title?: string;
link?: string;
snippet?: string;
date?: string;
};
type MiniMaxRelatedSearch = {
query?: string;
};
type MiniMaxSearchResponse = {
organic?: MiniMaxSearchResult[];
related_searches?: MiniMaxRelatedSearch[];
base_resp?: {
status_code?: number;
status_msg?: string;
};
};
function resolveMiniMaxApiKey(searchConfig?: SearchConfigRecord): string | undefined {
return (
readConfiguredSecretString(searchConfig?.apiKey, "tools.web.search.apiKey") ??
readProviderEnvValue([...MINIMAX_TOKEN_PLAN_ENV_VARS, "MINIMAX_API_KEY"])
);
}
function isMiniMaxCnHost(value: string | undefined): boolean {
const trimmed = normalizeOptionalString(value);
if (!trimmed) {
return false;
}
try {
return new URL(trimmed).hostname.endsWith("minimaxi.com");
} catch {
return trimmed.includes("minimaxi.com");
}
}
function resolveMiniMaxRegion(
searchConfig?: SearchConfigRecord,
config?: Record<string, unknown>,
): "cn" | "global" {
// 1. Explicit region in search config takes priority
const minimax =
typeof searchConfig?.minimax === "object" &&
searchConfig.minimax !== null &&
!Array.isArray(searchConfig.minimax)
? (searchConfig.minimax as Record<string, unknown>)
: undefined;
const configuredRegion =
typeof minimax?.region === "string" ? normalizeOptionalString(minimax.region) : undefined;
if (configuredRegion) {
return configuredRegion === "cn" ? "cn" : "global";
}
// 2. Infer from the shared MiniMax host override.
if (isMiniMaxCnHost(process.env.MINIMAX_API_HOST)) {
return "cn";
}
// 3. Infer from model provider base URL (set by CN onboarding)
const models = config?.models as Record<string, unknown> | undefined;
const providers = models?.providers as Record<string, unknown> | undefined;
const minimaxProvider = providers?.minimax as Record<string, unknown> | undefined;
const portalProvider = providers?.["minimax-portal"] as Record<string, unknown> | undefined;
const baseUrl = typeof minimaxProvider?.baseUrl === "string" ? minimaxProvider.baseUrl : "";
const portalBaseUrl = typeof portalProvider?.baseUrl === "string" ? portalProvider.baseUrl : "";
if (isMiniMaxCnHost(baseUrl) || isMiniMaxCnHost(portalBaseUrl)) {
return "cn";
}
return "global";
}
function resolveMiniMaxEndpoint(
searchConfig?: SearchConfigRecord,
config?: Record<string, unknown>,
): string {
return resolveMiniMaxRegion(searchConfig, config) === "cn"
? MINIMAX_SEARCH_ENDPOINT_CN
: MINIMAX_SEARCH_ENDPOINT_GLOBAL;
}
async function runMiniMaxSearch(params: {
query: string;
count: number;
apiKey: string;
endpoint: string;
timeoutSeconds: number;
}): Promise<{
results: Array<Record<string, unknown>>;
relatedSearches?: string[];
}> {
return withTrustedWebSearchEndpoint(
{
url: params.endpoint,
timeoutSeconds: params.timeoutSeconds,
init: {
method: "POST",
headers: {
Authorization: `Bearer ${params.apiKey}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ q: params.query }),
},
},
async (res) => {
if (!res.ok) {
throw await createProviderHttpError(res, "MiniMax Search API error");
}
const data = await readProviderJsonResponse<MiniMaxSearchResponse>(
res,
"MiniMax Search API error",
);
if (data.base_resp?.status_code && data.base_resp.status_code !== 0) {
throw new Error(
formatProviderHttpErrorMessage({
label: "MiniMax Search API error",
status: data.base_resp.status_code,
detail: data.base_resp.status_msg || "unknown error",
}),
);
}
const organic = Array.isArray(data.organic) ? data.organic : [];
const results = organic.slice(0, params.count).map((entry) => {
const title = entry.title ?? "";
const url = entry.link ?? "";
const snippet = entry.snippet ?? "";
return {
title: title ? wrapWebContent(title, "web_search") : "",
url,
description: snippet ? wrapWebContent(snippet, "web_search") : "",
published: entry.date || undefined,
siteName: resolveSiteName(url) || undefined,
};
});
const relatedSearches = Array.isArray(data.related_searches)
? data.related_searches
.map((r) => r.query)
.filter((q): q is string => typeof q === "string" && q.length > 0)
.map((q) => wrapWebContent(q, "web_search"))
: undefined;
return { results, relatedSearches };
},
);
}
function missingMiniMaxKeyPayload() {
return {
error: "missing_minimax_api_key",
message: `web_search (minimax) needs a MiniMax Token Plan key or OAuth token. Run \`${formatCliCommand("openclaw configure --section web")}\` to store it, or set MINIMAX_CODE_PLAN_KEY, MINIMAX_CODING_API_KEY, MINIMAX_OAUTH_TOKEN, or MINIMAX_API_KEY in the Gateway environment.`,
docs: "https://docs.openclaw.ai/tools/web",
};
}
export async function executeMiniMaxWebSearchProviderTool(
ctx: { config?: Record<string, unknown>; searchConfig?: SearchConfigRecord },
args: Record<string, unknown>,
): Promise<Record<string, unknown>> {
const searchConfig = mergeScopedSearchConfig(
ctx.searchConfig,
"minimax",
resolveProviderWebSearchPluginConfig(ctx.config, "minimax"),
{ mirrorApiKeyToTopLevel: true },
) as SearchConfigRecord | undefined;
const config = ctx.config;
const apiKey = resolveMiniMaxApiKey(searchConfig);
if (!apiKey) {
return missingMiniMaxKeyPayload();
}
const params = args;
const query = readStringParam(params, "query", { required: true });
const count =
readPositiveIntegerParam(params, "count", {
max: MAX_SEARCH_COUNT,
message: `count must be an integer from 1 to ${MAX_SEARCH_COUNT}.`,
}) ??
searchConfig?.maxResults ??
undefined;
const resolvedCount = resolveSearchCount(count, DEFAULT_SEARCH_COUNT);
const endpoint = resolveMiniMaxEndpoint(searchConfig, config);
const cacheKey = buildSearchCacheKey(["minimax", endpoint, query, resolvedCount]);
const cached = readCachedSearchPayload(cacheKey);
if (cached) {
return cached;
}
const start = Date.now();
const timeoutSeconds = resolveSearchTimeoutSeconds(searchConfig);
const cacheTtlMs = resolveSearchCacheTtlMs(searchConfig);
const { results, relatedSearches } = await runMiniMaxSearch({
query,
count: resolvedCount,
apiKey,
endpoint,
timeoutSeconds,
});
const payload: Record<string, unknown> = {
query,
provider: "minimax",
count: results.length,
tookMs: Date.now() - start,
externalContent: {
untrusted: true,
source: "web_search",
provider: "minimax",
wrapped: true,
},
results,
};
if (relatedSearches && relatedSearches.length > 0) {
payload.relatedSearches = relatedSearches;
}
writeCachedSearchPayload(cacheKey, payload, cacheTtlMs);
return payload;
}
export const testing = {
MINIMAX_SEARCH_ENDPOINT_GLOBAL,
MINIMAX_SEARCH_ENDPOINT_CN,
resolveMiniMaxApiKey,
resolveMiniMaxEndpoint,
resolveMiniMaxRegion,
readMiniMaxSearchJsonResponse: readProviderJsonResponse<MiniMaxSearchResponse>,
} as const;
export { testing as __testing };

View File

@@ -0,0 +1,178 @@
// Minimax tests cover minimax web search provider plugin behavior.
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { minimaxWebSearchTesting } from "../test-api.js";
const {
MINIMAX_SEARCH_ENDPOINT_GLOBAL,
MINIMAX_SEARCH_ENDPOINT_CN,
resolveMiniMaxApiKey,
resolveMiniMaxEndpoint,
resolveMiniMaxRegion,
readMiniMaxSearchJsonResponse,
} = minimaxWebSearchTesting;
function restoreEnvValue(key: string, value: string | undefined) {
if (value === undefined) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
describe("minimax web search provider", () => {
const originalApiHost = process.env.MINIMAX_API_HOST;
const originalCodePlanKey = process.env.MINIMAX_CODE_PLAN_KEY;
const originalCodingApiKey = process.env.MINIMAX_CODING_API_KEY;
const originalOauthToken = process.env.MINIMAX_OAUTH_TOKEN;
const originalApiKey = process.env.MINIMAX_API_KEY;
beforeEach(() => {
delete process.env.MINIMAX_API_HOST;
delete process.env.MINIMAX_CODE_PLAN_KEY;
delete process.env.MINIMAX_CODING_API_KEY;
delete process.env.MINIMAX_OAUTH_TOKEN;
delete process.env.MINIMAX_API_KEY;
});
afterEach(() => {
restoreEnvValue("MINIMAX_API_HOST", originalApiHost);
restoreEnvValue("MINIMAX_CODE_PLAN_KEY", originalCodePlanKey);
restoreEnvValue("MINIMAX_CODING_API_KEY", originalCodingApiKey);
restoreEnvValue("MINIMAX_OAUTH_TOKEN", originalOauthToken);
restoreEnvValue("MINIMAX_API_KEY", originalApiKey);
});
describe("resolveMiniMaxRegion", () => {
it("returns global by default", () => {
expect(resolveMiniMaxRegion()).toBe("global");
expect(resolveMiniMaxRegion({})).toBe("global");
});
it("returns cn when explicit region is cn", () => {
expect(resolveMiniMaxRegion({ minimax: { region: "cn" } })).toBe("cn");
});
it("returns global when explicit region is not cn", () => {
expect(resolveMiniMaxRegion({ minimax: { region: "global" } })).toBe("global");
expect(resolveMiniMaxRegion({ minimax: { region: "us" } })).toBe("global");
});
it("infers cn from MINIMAX_API_HOST", () => {
process.env.MINIMAX_API_HOST = "https://api.minimaxi.com/anthropic";
expect(resolveMiniMaxRegion()).toBe("cn");
});
it("infers cn from model provider base URL", () => {
const cnConfig = {
models: {
providers: {
minimax: { baseUrl: "https://api.minimaxi.com/anthropic" },
},
},
};
expect(resolveMiniMaxRegion({}, cnConfig)).toBe("cn");
});
it("infers cn from minimax-portal base URL (OAuth CN path)", () => {
const cnPortalConfig = {
models: {
providers: {
"minimax-portal": { baseUrl: "https://api.minimaxi.com/anthropic" },
},
},
};
expect(resolveMiniMaxRegion({}, cnPortalConfig)).toBe("cn");
});
it("returns global when model provider base URL is global", () => {
const globalConfig = {
models: {
providers: {
minimax: { baseUrl: "https://api.minimax.io/anthropic" },
},
},
};
expect(resolveMiniMaxRegion({}, globalConfig)).toBe("global");
});
it("explicit search config region takes priority over base URL", () => {
const cnConfig = {
models: {
providers: {
minimax: { baseUrl: "https://api.minimaxi.com/anthropic" },
},
},
};
// Explicit global region overrides CN base URL
expect(resolveMiniMaxRegion({ minimax: { region: "global" } }, cnConfig)).toBe("global");
});
it("handles non-object minimax search config gracefully", () => {
expect(resolveMiniMaxRegion({ minimax: "invalid" })).toBe("global");
expect(resolveMiniMaxRegion({ minimax: null })).toBe("global");
expect(resolveMiniMaxRegion({ minimax: [1, 2] })).toBe("global");
});
});
describe("resolveMiniMaxEndpoint", () => {
it("returns global endpoint by default", () => {
expect(resolveMiniMaxEndpoint()).toBe(MINIMAX_SEARCH_ENDPOINT_GLOBAL);
});
it("returns CN endpoint when region is cn", () => {
expect(resolveMiniMaxEndpoint({ minimax: { region: "cn" } })).toBe(
MINIMAX_SEARCH_ENDPOINT_CN,
);
});
it("returns CN endpoint when inferred from model provider base URL", () => {
const cnConfig = {
models: {
providers: {
minimax: { baseUrl: "https://api.minimaxi.com/anthropic" },
},
},
};
expect(resolveMiniMaxEndpoint({}, cnConfig)).toBe(MINIMAX_SEARCH_ENDPOINT_CN);
});
});
describe("resolveMiniMaxApiKey", () => {
it("prefers configured apiKey over env vars", () => {
process.env.MINIMAX_CODE_PLAN_KEY = "env-key";
expect(resolveMiniMaxApiKey({ apiKey: "configured-key" })).toBe("configured-key");
});
it("accepts MINIMAX_CODING_API_KEY as a token-plan alias", () => {
process.env.MINIMAX_CODING_API_KEY = "coding-key";
expect(resolveMiniMaxApiKey()).toBe("coding-key");
});
it("falls back to MINIMAX_API_KEY last", () => {
process.env.MINIMAX_API_KEY = "plain-key";
expect(resolveMiniMaxApiKey()).toBe("plain-key");
});
it("accepts MINIMAX_OAUTH_TOKEN before the legacy API-key fallback", () => {
process.env.MINIMAX_OAUTH_TOKEN = "oauth-token";
process.env.MINIMAX_API_KEY = "plain-key";
expect(resolveMiniMaxApiKey()).toBe("oauth-token");
});
});
describe("endpoint constants", () => {
it("uses correct global endpoint", () => {
expect(MINIMAX_SEARCH_ENDPOINT_GLOBAL).toBe("https://api.minimax.io/v1/coding_plan/search");
});
it("uses correct CN endpoint", () => {
expect(MINIMAX_SEARCH_ENDPOINT_CN).toBe("https://api.minimaxi.com/v1/coding_plan/search");
});
});
it("reports malformed Search API JSON with a stable provider error", async () => {
await expect(
readMiniMaxSearchJsonResponse(new Response("{ nope"), "MiniMax Search API error"),
).rejects.toThrow("MiniMax Search API error: malformed JSON response");
});
});

View File

@@ -0,0 +1,61 @@
import { createLazyRuntimeModule } from "openclaw/plugin-sdk/lazy-runtime";
// Minimax provider module implements model/runtime integration.
import {
createWebSearchProviderContractFields,
type WebSearchProviderPlugin,
} from "openclaw/plugin-sdk/provider-web-search-config-contract";
const MINIMAX_CREDENTIAL_PATH = "plugins.entries.minimax.config.webSearch.apiKey";
const MINIMAX_TOKEN_PLAN_ENV_VARS = [
"MINIMAX_CODE_PLAN_KEY",
"MINIMAX_CODING_API_KEY",
"MINIMAX_OAUTH_TOKEN",
] as const;
const MINIMAX_WEB_SEARCH_ENV_VARS = [...MINIMAX_TOKEN_PLAN_ENV_VARS, "MINIMAX_API_KEY"] as const;
const loadMiniMaxWebSearchRuntime = createLazyRuntimeModule(
() => import("./minimax-web-search-provider.runtime.js"),
);
const MiniMaxSearchSchema = {
type: "object",
properties: {
query: { type: "string", description: "Search query string." },
count: {
type: "integer",
description: "Number of results to return (1-10).",
minimum: 1,
maximum: 10,
},
},
} satisfies Record<string, unknown>;
export function createMiniMaxWebSearchProvider(): WebSearchProviderPlugin {
return {
id: "minimax",
label: "MiniMax Search",
hint: "Structured results via MiniMax Token Plan search API",
onboardingScopes: ["text-inference"],
credentialLabel: "MiniMax Token Plan key or OAuth token",
envVars: [...MINIMAX_WEB_SEARCH_ENV_VARS],
placeholder: "sk-cp-...",
signupUrl: "https://platform.minimax.io/user-center/basic-information/interface-key",
docsUrl: "https://docs.openclaw.ai/tools/minimax-search",
autoDetectOrder: 15,
credentialPath: MINIMAX_CREDENTIAL_PATH,
...createWebSearchProviderContractFields({
credentialPath: MINIMAX_CREDENTIAL_PATH,
searchCredential: { type: "top-level" },
configuredCredential: { pluginId: "minimax" },
}),
createTool: (ctx) => ({
description:
"Search the web using MiniMax Search API. Returns titles, URLs, snippets, and related search suggestions.",
parameters: MiniMaxSearchSchema,
execute: async (args) => {
const { executeMiniMaxWebSearchProviderTool } = await loadMiniMaxWebSearchRuntime();
return await executeMiniMaxWebSearchProviderTool(ctx, args);
},
}),
};
}