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>
122 lines
3.3 KiB
TypeScript
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]);
|
|
}
|