"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.runMcpCommand = runMcpCommand;
// biome-ignore lint/nursery/noExcessiveLinesPerFile: MCP command routing keeps install, verify, create, and catalog behavior in one CLI surface.
const promises_1 = require("node:fs/promises");
const store_1 = require("../auth-profiles/store");
const control_plane_config_1 = require("../config/control-plane-config");
const fs_errors_1 = require("../core/fs-errors");
const requirement_exclusions_1 = require("../catalog/requirement-exclusions");
const catalog_1 = require("../i18n/catalog");
const catalog_2 = require("../mcp/catalog");
const install_1 = require("../mcp/install");
const inventory_1 = require("../mcp/inventory");
const ref_1 = require("../mcp/ref");
const resolution_issue_1 = require("../mcp/resolution-issue");
const search_1 = require("../mcp/search");
const verify_1 = require("../mcp/verify");
const runtime_1 = require("../worker/runtime");
const template_registry_1 = require("../workflows/template-registry");
const resolution_create_draft_1 = require("./resolution-create-draft");
const catalog_search_worker_1 = require("./catalog-search-worker");
const json_output_1 = require("./json-output");
const prompts_1 = require("./prompts");
async function runMcpCommand(cwd, args, deps = {}) {
    switch (args.action) {
        case "list":
            return runMcpListCommand(cwd, args, deps);
        case "search":
            return runMcpSearchCommand(cwd, args, deps);
        case "install":
            return runMcpInstallCommand(cwd, args, deps);
        case "create":
            return runMcpCreateCommand(cwd, args, deps);
        case "verify":
            return runMcpVerifyCommand(cwd, args, deps);
        case "catalog":
            return runMcpCatalogCommand(cwd, args);
    }
}
async function runMcpListCommand(cwd, args, deps = {}) {
    const context = await loadMcpCommandContext(cwd, deps.homeDirectory);
    const workers = args.target === undefined
        ? context.configuredWorkers
        : resolveMcpTargets(args.target, context.configuredWorkers);
    const result = await (0, inventory_1.listAvailableMcpServers)({
        repoRoot: cwd,
        workers,
        configuredWorkers: context.configuredWorkers,
        authProfiles: context.authProfiles,
        ...(deps.homeDirectory === undefined
            ? {}
            : { homeDirectory: deps.homeDirectory }),
        includeExcluded: args.includeExcluded === true,
    });
    (0, json_output_1.printCommandJson)(result);
    return 0;
}
async function runMcpSearchCommand(cwd, args, deps = {}) {
    const context = await loadMcpCommandContext(cwd, deps.homeDirectory);
    const targets = resolveSearchTargets(args.target, context.configuredWorkers);
    const baselineResult = await (0, search_1.searchMcps)({
        repoRoot: cwd,
        query: args.query,
        targets,
        mode: args.mode,
        configuredWorkers: context.configuredWorkers,
        authProfiles: context.authProfiles,
        includeExcluded: args.includeExcluded === true,
        ...(deps.homeDirectory === undefined
            ? {}
            : { homeDirectory: deps.homeDirectory }),
    });
    let result = baselineResult;
    if (args.withWorker === true) {
        const runtime = deps.workerRuntime ??
            (await (0, runtime_1.createConfiguredWorkerRuntime)(context.config, {
                ...(deps.homeDirectory === undefined
                    ? {}
                    : { homeDirectory: deps.homeDirectory }),
            }));
        const workerSelection = await (0, catalog_search_worker_1.runCatalogSearchWorkerSelection)({
            runtime,
            repositoryRoot: cwd,
            query: args.query,
            kind: "mcp",
            mode: args.mode,
            target: (0, catalog_search_worker_1.renderCatalogSearchTarget)({ target: args.target, targets }),
            outputLanguage: context.configLocale,
        });
        if (workerSelection.kind === "selected") {
            const forcedResult = await (0, search_1.searchMcps)({
                repoRoot: cwd,
                query: args.query,
                targets,
                mode: args.mode,
                configuredWorkers: context.configuredWorkers,
                authProfiles: context.authProfiles,
                forcedIds: workerSelection.selected.map((item) => item.id),
                includeExcluded: args.includeExcluded === true,
                ...(deps.homeDirectory === undefined
                    ? {}
                    : { homeDirectory: deps.homeDirectory }),
            });
            const applied = (0, catalog_search_worker_1.applyCatalogSearchWorkerSelection)({
                items: forcedResult.items,
                selected: workerSelection.selected,
                itemSelectionIds: (item) => item.kind === "installed" ? [item.id, item.title] : [item.id],
            });
            result =
                applied.rejected.length > 0
                    ? {
                        ...baselineResult,
                        worker: {
                            requested: true,
                            used: false,
                            selected: [],
                            fallbackReason: "invalid_worker_selection",
                        },
                    }
                    : {
                        ...forcedResult,
                        items: applied.items,
                        worker: {
                            requested: true,
                            used: true,
                            selected: applied.selected,
                            fallbackReason: null,
                        },
                    };
        }
        else {
            result = {
                ...baselineResult,
                worker: {
                    requested: true,
                    used: false,
                    selected: [],
                    fallbackReason: workerSelection.reason,
                },
            };
        }
    }
    (0, json_output_1.printCommandJson)(result);
    return 0;
}
async function runMcpInstallCommand(cwd, args, deps = {}) {
    const context = await loadMcpCommandContext(cwd, deps.homeDirectory);
    const initialResult = await (0, install_1.installMcp)(buildMcpInstallInput({ cwd, args, context, deps }));
    const result = await resolveInteractiveMcpInstall({
        cwd,
        args,
        context,
        deps,
        result: initialResult,
    });
    const output = result.kind === "alpaca.mcpInstallResult"
        ? await attachMcpInstallRequirementExclusion({
            cwd,
            result,
            context,
            deps,
        })
        : result;
    (0, json_output_1.printCommandJson)(output);
    return 0;
}
async function resolveInteractiveMcpInstall(input) {
    const prompter = input.deps.prompter ??
        (0, prompts_1.createCliPrompter)({ locale: input.context.configLocale });
    if (!prompter.interactive) {
        return input.result;
    }
    return ((await resolveMcpInstallSelection({ ...input, prompter })) ?? input.result);
}
async function resolveMcpInstallSelection(input) {
    if (input.result.kind !== "alpaca.mcpInstallSelectionRequired") {
        return null;
    }
    const selected = await input.prompter.select({
        label: input.result.reason === "auth-profile"
            ? "Select MCP auth profile"
            : "Select MCP catalog entry",
        options: input.result.items.map((item) => ({
            value: item,
            label: `${item.title} (${item.origin})`,
        })),
        defaultValue: input.result.items[0],
    });
    const nextOverrides = mergeMcpInstallInteractiveOverrides(input.overrides, input.result.reason === "auth-profile"
        ? { authProfileId: selected.id }
        : { id: selected.id });
    const nextResult = await (0, install_1.installMcp)(buildMcpInstallInput({
        cwd: input.cwd,
        args: input.args,
        context: input.context,
        deps: input.deps,
        ...nextOverrides,
    }));
    return resolveInteractiveMcpInstall({
        ...input,
        result: nextResult,
        overrides: nextOverrides,
    });
}
function mergeMcpInstallInteractiveOverrides(current, patch) {
    if (patch.id !== undefined) {
        const next = { ...current };
        delete next.query;
        return {
            ...next,
            id: patch.id,
            ...(patch.authProfileId === undefined
                ? {}
                : { authProfileId: patch.authProfileId }),
        };
    }
    if (patch.query !== undefined) {
        const next = { ...current };
        delete next.id;
        return {
            ...next,
            query: patch.query,
            ...(patch.authProfileId === undefined
                ? {}
                : { authProfileId: patch.authProfileId }),
        };
    }
    return { ...current, ...patch };
}
function buildMcpInstallInput(input) {
    assertConfiguredMcpTarget(input.args.target, input.context.configuredWorkers);
    const installInput = {
        repoRoot: input.cwd,
        target: input.args.target,
        overwrite: input.args.overwrite,
        authProfiles: input.context.authProfiles,
        includeExcluded: input.args.includeExcluded === true,
    };
    const id = input.id ?? input.args.id;
    const query = input.query ?? (input.id === undefined ? input.args.query : undefined);
    const authProfileId = input.authProfileId ?? input.args.authProfileId;
    if (id !== undefined) {
        installInput.id = id;
    }
    if (query !== undefined) {
        installInput.query = query;
    }
    if (input.args.serverName !== undefined) {
        installInput.serverName = input.args.serverName;
    }
    if (authProfileId !== undefined) {
        if (!(0, control_plane_config_1.isBuiltInWorkerName)(input.args.target)) {
            throw new Error(`Auth profile selection is only supported for built-in MCP targets: ${input.args.target}`);
        }
        installInput.authProfileId = authProfileId;
    }
    if (input.deps.homeDirectory !== undefined) {
        installInput.homeDirectory = input.deps.homeDirectory;
    }
    return installInput;
}
async function runMcpVerifyCommand(cwd, args, deps = {}) {
    const context = await loadMcpCommandContext(cwd, deps.homeDirectory);
    const workers = args.target === undefined
        ? context.configuredWorkers
        : resolveMcpTargets(args.target, context.configuredWorkers);
    const result = await (0, verify_1.verifyMcp)({
        repoRoot: cwd,
        workers,
        configuredWorkers: context.configuredWorkers,
        refs: args.refs,
        authProfiles: context.authProfiles,
        ...(deps.homeDirectory === undefined
            ? {}
            : { homeDirectory: deps.homeDirectory }),
        locale: context.configLocale,
    });
    const registeredRequirementExclusions = await maybeRecordMcpVerifyRequirementExclusions({
        repoRoot: cwd,
        homeDirectory: deps.homeDirectory,
        result,
        refs: args.refs,
    });
    (0, json_output_1.printCommandJson)(registeredRequirementExclusions.length === 0
        ? result
        : { ...result, registeredRequirementExclusions });
    return result.ok ? 0 : 1;
}
async function attachMcpInstallRequirementExclusion(input) {
    const verify = await (0, verify_1.verifyMcp)({
        repoRoot: input.cwd,
        workers: [input.result.target],
        configuredWorkers: input.context.configuredWorkers,
        refs: [input.result.ref],
        authProfiles: input.context.authProfiles,
        ...(input.deps.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.deps.homeDirectory }),
        locale: input.context.configLocale,
    });
    const excludedFromRequires = await findMcpInstallExcludedRequirement({
        cwd: input.cwd,
        homeDirectory: input.deps.homeDirectory,
        result: input.result,
        verify,
    });
    const [registeredRequirementExclusion] = await maybeRecordMcpVerifyRequirementExclusions({
        repoRoot: input.cwd,
        homeDirectory: input.deps.homeDirectory,
        result: verify,
        refs: [input.result.ref],
    });
    return {
        ...input.result,
        verify,
        ...(excludedFromRequires === undefined
            ? {}
            : {
                excludedFromRequires,
                excludeReason: `Installed successfully, but this MCP ref is excluded from workflow requires: ${excludedFromRequires.reason}`,
            }),
        ...(registeredRequirementExclusion === undefined
            ? {}
            : { registeredRequirementExclusion }),
    };
}
async function findMcpInstallExcludedRequirement(input) {
    const item = input.verify.items.find((candidate) => candidate.worker === input.result.target &&
        candidate.serverName === input.result.serverName);
    if (item?.excludedFromRequires !== undefined) {
        return item.excludedFromRequires;
    }
    const exclusions = await (0, requirement_exclusions_1.loadEffectiveRequirementExclusions)({
        repoRoot: input.cwd,
        ...(input.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.homeDirectory }),
    });
    return ((0, requirement_exclusions_1.findRequirementExclusionMatch)(exclusions, "mcp", {
        ref: input.result.ref,
        id: input.result.catalogEntryId,
        source: input.result.source,
    }) ??
        (0, requirement_exclusions_1.findRequirementExclusionMatch)(exclusions, "mcp", {
            ref: input.result.serverName,
            id: input.result.serverName,
            source: input.result.source,
        }) ??
        undefined);
}
async function maybeRecordMcpVerifyRequirementExclusions(input) {
    if (input.result.ok || input.refs.length === 0) {
        return [];
    }
    if (input.result.items.some((item) => item.excludedFromRequires !== undefined)) {
        return [];
    }
    const exclusions = await (0, requirement_exclusions_1.loadEffectiveRequirementExclusions)({
        repoRoot: input.repoRoot,
        ...(input.homeDirectory === undefined
            ? {}
            : { homeDirectory: input.homeDirectory }),
    });
    const refsToRecord = input.refs.filter((ref) => findMcpRefRequirementExclusion(exclusions, ref) === null &&
        mcpVerificationFailureIsStructuredForRef(input.result, ref));
    if (refsToRecord.length === 0) {
        return [];
    }
    if (!shouldAutoExcludeMcpVerificationFailure(input.result)) {
        return [];
    }
    const reason = "MCP ref did not pass the basic requires.mcp verification.";
    const notices = [];
    for (const ref of refsToRecord) {
        const writeResult = await (0, requirement_exclusions_1.addMachineRequirementExclusion)({
            ...(input.homeDirectory === undefined
                ? {}
                : { homeDirectory: input.homeDirectory }),
            target: "mcp",
            entry: {
                ref,
                reason,
                addedAt: new Date().toISOString(),
            },
        });
        notices.push({
            ...writeResult,
            ref,
            reason,
            message: `${ref} was excluded from workflow requires. Remove the entry from ${writeResult.path} after fixing the local setup.`,
        });
    }
    return notices;
}
function findMcpRefRequirementExclusion(exclusions, ref) {
    const parsed = (0, ref_1.parseRequiredMcpRef)(ref);
    return (0, requirement_exclusions_1.findRequirementExclusionMatch)(exclusions, "mcp", {
        ref,
        ...(parsed === null ? {} : { id: parsed.serverName }),
    });
}
function shouldAutoExcludeMcpVerificationFailure(result) {
    const text = [
        result.summary,
        ...result.diagnostics.map((diagnostic) => diagnostic.message),
    ]
        .join("\n")
        .toLowerCase();
    return ![
        "auth",
        "credential",
        "login",
        "oauth",
        "permission",
        "token",
        "environment",
        "env ",
        "network",
        "timeout",
        "rate limit",
    ].some((token) => text.includes(token));
}
function mcpVerificationFailureIsStructuredForRef(result, ref) {
    const parsed = (0, ref_1.parseRequiredMcpRef)(ref);
    if (parsed === null) {
        return false;
    }
    const matchingItems = result.items.filter((item) => item.serverName === parsed.serverName &&
        (parsed.worker === null || item.worker === parsed.worker));
    if (matchingItems.length === 0) {
        return false;
    }
    if (parsed.toolName === null) {
        return matchingItems.every((item) => item.usable === false);
    }
    return matchingItems.some((item) => item.usable &&
        item.toolsKnown &&
        !item.tools.includes(parsed.toolName ?? ""));
}
async function runMcpCreateCommand(cwd, args, deps = {}) {
    if (args.mode === "apply-draft") {
        const { runIssueCreateCommand } = await Promise.resolve().then(() => __importStar(require("./issue")));
        return runIssueCreateCommand(cwd, {
            action: "create",
            mode: "apply-draft",
            applyDraftPath: args.applyDraftPath,
            validateDraft: assertMcpResolutionDraft,
        }, deps);
    }
    const config = await (0, resolution_create_draft_1.loadResolutionCreateConfig)(cwd);
    const configuredWorkers = (0, control_plane_config_1.getConfiguredWorkerProviders)(config);
    assertConfiguredMcpCreateTarget(args.target, configuredWorkers);
    const configLocale = config.i18n.defaultLanguage;
    const workflowOptions = await resolveMcpResolutionWorkflowOptions(cwd, configLocale);
    const draft = await (0, resolution_create_draft_1.draftResolutionIssueFields)({
        config,
        cwd,
        kind: "mcp",
        prompt: args.prompt,
        target: args.target,
        locale: configLocale,
        ...(deps.homeDirectory === undefined
            ? {}
            : { homeDirectory: deps.homeDirectory }),
    });
    const issueArgs = {
        action: "create",
        mode: "manual",
        title: draft.title,
        body: {
            kind: "text",
            text: (0, resolution_issue_1.buildMcpResolutionIssueBody)({
                capability: draft.capability,
                originalRequest: args.prompt,
                target: args.target,
                configuredWorkers,
                locale: configLocale,
                origin: (0, catalog_1.translate)(configLocale, "mcp.resolution.origin.createdByCommand"),
            }),
        },
        assignees: [],
        labels: [],
        disabled: false,
        ...(args.saveDraft === true ? { saveDraft: true } : {}),
        ...(workflowOptions === undefined ? {} : { workflowOptions }),
    };
    const { runIssueCreateCommand } = await Promise.resolve().then(() => __importStar(require("./issue")));
    return runIssueCreateCommand(cwd, issueArgs, deps);
}
async function runMcpCatalogCommand(cwd, args) {
    switch (args.catalogAction) {
        case "list": {
            const result = await (0, catalog_2.listMcpCatalog)({
                repoRoot: cwd,
                includeSystem: args.includeSystem,
            });
            (0, json_output_1.printCommandJson)(result);
            return 0;
        }
        case "add": {
            const entry = JSON.parse(await readEntryInput(args.entryPath));
            const result = await (0, catalog_2.addMcpCatalogEntry)({
                repoRoot: cwd,
                entry,
                overwrite: args.overwrite,
            });
            (0, json_output_1.printCommandJson)(result);
            return 0;
        }
        case "remove": {
            const result = await (0, catalog_2.removeMcpCatalogEntry)({
                repoRoot: cwd,
                id: args.id,
            });
            (0, json_output_1.printCommandJson)(result);
            return 0;
        }
    }
}
function assertMcpResolutionDraft(draftFile) {
    if (!(0, resolution_issue_1.isMcpResolutionIssueDraft)(draftFile)) {
        throw new Error("MCP create --apply-draft must be an alpaca MCP-resolution issue draft bound to __system/mcp-resolution.md.");
    }
}
async function readEntryInput(entryPath) {
    if (entryPath !== "-") {
        return (0, promises_1.readFile)(entryPath, "utf8");
    }
    return new Promise((resolve, reject) => {
        let text = "";
        process.stdin.setEncoding("utf8");
        process.stdin.on("data", (chunk) => {
            text += chunk;
        });
        process.stdin.on("error", reject);
        process.stdin.on("end", () => resolve(text));
    });
}
async function resolveMcpResolutionWorkflowOptions(cwd, configLocale) {
    const catalog = await (0, template_registry_1.loadWorkflowTemplates)((0, template_registry_1.getWorkflowTemplatesDirectoryPath)(cwd), configLocale);
    if (catalog?.templateByPath.has(resolution_issue_1.MCP_RESOLUTION_SYSTEM_WORKFLOW_TEMPLATE) !==
        true) {
        return undefined;
    }
    return {
        workflowSelection: {
            mode: "template",
            templatePath: resolution_issue_1.MCP_RESOLUTION_SYSTEM_WORKFLOW_TEMPLATE,
        },
    };
}
async function loadMcpCommandContext(cwd, homeDirectory) {
    const config = await loadMcpCommandConfig(cwd);
    const configuredWorkers = [...(0, control_plane_config_1.getConfiguredWorkerProviders)(config)];
    const { profiles } = await (0, store_1.loadAuthProfiles)(homeDirectory);
    return {
        config,
        configuredWorkers,
        authProfiles: profiles,
        configLocale: config.i18n.defaultLanguage,
    };
}
async function loadMcpCommandConfig(cwd) {
    try {
        return await (0, control_plane_config_1.loadConfig)(cwd);
    }
    catch (error) {
        if ((0, fs_errors_1.isMissingFileError)(error)) {
            return control_plane_config_1.DEFAULT_CONFIG;
        }
        throw error;
    }
}
function resolveSearchTargets(target, configuredWorkers) {
    return resolveMcpTargets(target, configuredWorkers);
}
function resolveMcpTargets(target, configuredWorkers) {
    if (target === "all") {
        return [...configuredWorkers];
    }
    if (target !== undefined) {
        assertConfiguredMcpTarget(target, configuredWorkers);
        return [target];
    }
    return [...configuredWorkers];
}
function assertConfiguredMcpCreateTarget(target, configuredWorkers) {
    if (target !== "all") {
        assertConfiguredMcpTarget(target, configuredWorkers);
    }
}
function assertConfiguredMcpTarget(target, configuredWorkers) {
    if (!configuredWorkers.includes(target)) {
        throw new Error(`Worker is not configured: ${target}`);
    }
}
