Files
openclaw/extensions/acpx/src/runtime-internals/mcp-agent-command.ts
Rodrigo Uroz ff2e7a2945 fix(acp): strip provider auth env for child ACP processes (openclaw#42250)
Verified:
- pnpm build
- pnpm check
- pnpm test:macmini

Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-10 16:50:10 -05:00

122 lines
3.3 KiB
TypeScript

import path from "node:path";
import { fileURLToPath } from "node:url";
import { spawnAndCollect, type SpawnCommandOptions } from "./process.js";
const ACPX_BUILTIN_AGENT_COMMANDS: Record<string, string> = {
codex: "npx @zed-industries/codex-acp",
claude: "npx -y @zed-industries/claude-agent-acp",
gemini: "gemini",
opencode: "npx -y opencode-ai acp",
pi: "npx pi-acp",
};
const MCP_PROXY_PATH = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "mcp-proxy.mjs");
type AcpxConfigDisplay = {
agents?: Record<string, { command?: unknown }>;
};
type AcpMcpServer = {
name: string;
command: string;
args: string[];
env: Array<{ name: string; value: string }>;
};
function normalizeAgentName(value: string): string {
return value.trim().toLowerCase();
}
function quoteCommandPart(value: string): string {
if (value === "") {
return '""';
}
if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) {
return value;
}
return `"${value.replace(/["\\]/g, "\\$&")}"`;
}
export const __testing = {
quoteCommandPart,
};
function toCommandLine(parts: string[]): string {
return parts.map(quoteCommandPart).join(" ");
}
function readConfiguredAgentOverrides(value: unknown): Record<string, string> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
return {};
}
const overrides: Record<string, string> = {};
for (const [name, entry] of Object.entries(value)) {
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
continue;
}
const command = (entry as { command?: unknown }).command;
if (typeof command !== "string" || command.trim() === "") {
continue;
}
overrides[normalizeAgentName(name)] = command.trim();
}
return overrides;
}
async function loadAgentOverrides(params: {
acpxCommand: string;
cwd: string;
stripProviderAuthEnvVars?: boolean;
spawnOptions?: SpawnCommandOptions;
}): Promise<Record<string, string>> {
const result = await spawnAndCollect(
{
command: params.acpxCommand,
args: ["--cwd", params.cwd, "config", "show"],
cwd: params.cwd,
stripProviderAuthEnvVars: params.stripProviderAuthEnvVars,
},
params.spawnOptions,
);
if (result.error || (result.code ?? 0) !== 0) {
return {};
}
try {
const parsed = JSON.parse(result.stdout) as AcpxConfigDisplay;
return readConfiguredAgentOverrides(parsed.agents);
} catch {
return {};
}
}
export async function resolveAcpxAgentCommand(params: {
acpxCommand: string;
cwd: string;
agent: string;
stripProviderAuthEnvVars?: boolean;
spawnOptions?: SpawnCommandOptions;
}): Promise<string> {
const normalizedAgent = normalizeAgentName(params.agent);
const overrides = await loadAgentOverrides({
acpxCommand: params.acpxCommand,
cwd: params.cwd,
stripProviderAuthEnvVars: params.stripProviderAuthEnvVars,
spawnOptions: params.spawnOptions,
});
return overrides[normalizedAgent] ?? ACPX_BUILTIN_AGENT_COMMANDS[normalizedAgent] ?? params.agent;
}
export function buildMcpProxyAgentCommand(params: {
targetCommand: string;
mcpServers: AcpMcpServer[];
}): string {
const payload = Buffer.from(
JSON.stringify({
targetCommand: params.targetCommand,
mcpServers: params.mcpServers,
}),
"utf8",
).toString("base64url");
return toCommandLine([process.execPath, MCP_PROXY_PATH, "--payload", payload]);
}