openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
139 lines (138 loc) • 5.83 kB
JavaScript
import { s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { o as isRecord } from "./record-coerce-DHZ4bFlT.js";
import { c as isRecord$1 } from "./utils-CCC-BEJH.js";
import { r as loadEnabledBundleMcpConfig } from "./bundle-mcp-BeA96PNA.js";
import { a as shouldCreateBundleMcpRuntimeForAttempt } from "./attempt-tool-construction-plan-C32M6pQQ.js";
import crypto from "node:crypto";
//#region src/agents/cli-runner/bundle-mcp-adapter-shared.ts
/**
* Shared normalization helpers for CLI-specific bundle MCP adapters.
*/
function normalizeStringArray(value) {
return Array.isArray(value) && value.every((entry) => typeof entry === "string") ? [...value] : void 0;
}
/** Normalize a string-valued record, dropping non-string entries. */
function normalizeStringRecord(value) {
if (!isRecord(value)) return;
const entries = Object.entries(value).filter((entry) => {
return typeof entry[1] === "string";
});
return entries.length > 0 ? Object.fromEntries(entries) : void 0;
}
/** Decode supported `${ENV}` and `Bearer ${ENV}` header placeholders. */
function decodeHeaderEnvPlaceholder(value) {
const bearerMatch = /^Bearer \${([A-Z0-9_]+)}$/.exec(value);
if (bearerMatch) return {
envVar: bearerMatch[1],
bearer: true
};
const envMatch = /^\${([A-Z0-9_]+)}$/.exec(value);
if (envMatch) return {
envVar: envMatch[1],
bearer: false
};
return null;
}
/** Copy common MCP server config fields into a CLI adapter config object. */
function applyCommonServerConfig(next, server) {
if (typeof server.command === "string") next.command = server.command;
const args = normalizeStringArray(server.args);
if (args) next.args = args;
const env = normalizeStringRecord(server.env);
if (env) next.env = env;
if (typeof server.cwd === "string") next.cwd = server.cwd;
if (typeof server.url === "string") next.url = server.url;
}
//#endregion
//#region src/agents/codex-mcp-config.ts
/**
* Projects enabled bundle MCP servers into Codex app-server thread config.
* The projection keeps loopback approval defaults and header env placeholders
* compatible with Codex's MCP config shape.
*/
function isOpenClawLoopbackMcpServer(name, server) {
return name === "openclaw" && typeof server.url === "string" && /^https?:\/\/(?:127\.0\.0\.1|localhost):\d+\/mcp(?:[?#].*)?$/.test(server.url);
}
const CODEX_MCP_TOOL_APPROVAL_MODES = new Set([
"auto",
"prompt",
"approve"
]);
function readCodexProjectionConfig(server) {
return isRecord$1(server.codex) ? server.codex : {};
}
function normalizeCodexToolApprovalMode(value) {
return typeof value === "string" && CODEX_MCP_TOOL_APPROVAL_MODES.has(value) ? value : void 0;
}
function resolveCodexDefaultToolsApprovalMode(server) {
const codex = readCodexProjectionConfig(server);
return normalizeCodexToolApprovalMode(codex.defaultToolsApprovalMode) ?? normalizeCodexToolApprovalMode(codex.default_tools_approval_mode);
}
/** Normalizes one bundle MCP server into Codex's mcp_servers shape. */
function normalizeCodexMcpServerConfig(name, server) {
const next = {};
applyCommonServerConfig(next, server);
const defaultToolsApprovalMode = resolveCodexDefaultToolsApprovalMode(server);
if (defaultToolsApprovalMode) next.default_tools_approval_mode = defaultToolsApprovalMode;
else if (isOpenClawLoopbackMcpServer(name, server)) next.default_tools_approval_mode = "approve";
const httpHeaders = normalizeStringRecord(server.headers);
if (httpHeaders) {
const staticHeaders = {};
const envHeaders = {};
for (const [nameLocal, value] of Object.entries(httpHeaders)) {
const decoded = decodeHeaderEnvPlaceholder(value);
if (!decoded) {
staticHeaders[nameLocal] = value;
continue;
}
if (decoded.bearer && normalizeOptionalLowercaseString(nameLocal) === "authorization") {
next.bearer_token_env_var = decoded.envVar;
continue;
}
envHeaders[nameLocal] = decoded.envVar;
}
if (Object.keys(staticHeaders).length > 0) next.http_headers = staticHeaders;
if (Object.keys(envHeaders).length > 0) next.env_http_headers = envHeaders;
}
return next;
}
/** Build Codex `mcp_servers` config from normalized bundle MCP config. */
function buildCodexMcpServersConfig(config) {
return Object.fromEntries(Object.entries(config.mcpServers).map(([name, server]) => [name, normalizeCodexMcpServerConfig(name, server)]));
}
function stableJsonValue(value) {
if (Array.isArray(value)) return value.map(stableJsonValue);
if (!value || typeof value !== "object") return value;
return Object.fromEntries(Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, stableJsonValue(child)]));
}
function fingerprintCodexMcpServersConfig(config) {
return crypto.createHash("sha256").update(JSON.stringify(stableJsonValue(config))).digest("hex");
}
/** Load bundle MCP config for one Codex app-server thread. */
function loadCodexBundleMcpThreadConfig(params) {
if (!shouldCreateBundleMcpRuntimeForAttempt({
toolsEnabled: params.toolsEnabled ?? true,
disableTools: params.disableTools,
toolsAllow: params.toolsAllow
})) return {
diagnostics: [],
evaluated: true
};
const bundleMcp = loadEnabledBundleMcpConfig({
workspaceDir: params.workspaceDir,
cfg: params.cfg
});
const mcpServers = buildCodexMcpServersConfig(bundleMcp.config);
if (Object.keys(mcpServers).length === 0) return {
diagnostics: bundleMcp.diagnostics,
evaluated: true
};
return {
configPatch: { mcp_servers: mcpServers },
diagnostics: bundleMcp.diagnostics,
evaluated: true,
fingerprint: fingerprintCodexMcpServersConfig(mcpServers)
};
}
//#endregion
export { decodeHeaderEnvPlaceholder as a, applyCommonServerConfig as i, loadCodexBundleMcpThreadConfig as n, normalizeStringRecord as o, normalizeCodexMcpServerConfig as r, buildCodexMcpServersConfig as t };