"use strict";
/**
 * WebSearch MCP installer and ~/.claude.json provisioning.
 */
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 (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ensureWebSearchMcpOrThrow = exports.uninstallWebSearchMcp = exports.removeWebSearchMcpConfig = exports.uninstallWebSearchMcpServer = exports.syncWebSearchMcpToConfigDir = exports.ensureWebSearchMcp = exports.ensureWebSearchMcpConfig = exports.installWebSearchMcpServer = exports.getWebSearchMcpServerPath = exports.getWebSearchMcpServerName = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const config_manager_1 = require("../config-manager");
const claude_config_path_1 = require("../claude-config-path");
const ui_1 = require("../ui");
const instance_manager_1 = require("../../management/instance-manager");
const hook_installer_1 = require("./hook-installer");
const trace_1 = require("./trace");
const config_loader_facade_1 = require("../../config/config-loader-facade");
const WEBSEARCH_MCP_SERVER = 'ccs-websearch-server.cjs';
const WEBSEARCH_MCP_SERVER_NAME = 'ccs-websearch';
function getCcsMcpDir() {
    return path.join((0, config_manager_1.getCcsDir)(), 'mcp');
}
function getWebSearchMcpServerName() {
    return WEBSEARCH_MCP_SERVER_NAME;
}
exports.getWebSearchMcpServerName = getWebSearchMcpServerName;
function getWebSearchMcpServerPath() {
    return path.join(getCcsMcpDir(), WEBSEARCH_MCP_SERVER);
}
exports.getWebSearchMcpServerPath = getWebSearchMcpServerPath;
function hasMatchingContents(sourcePath, destinationPath) {
    if (!fs.existsSync(destinationPath)) {
        return false;
    }
    const source = fs.readFileSync(sourcePath);
    try {
        const destination = fs.readFileSync(destinationPath);
        return source.equals(destination);
    }
    catch (error) {
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Existing WebSearch MCP server is unreadable: ${error.message}`));
        }
        return false;
    }
}
function getTempPath(targetPath) {
    const suffix = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
    return `${targetPath}.${suffix}.tmp`;
}
function resolveBundledServerSourcePath() {
    const possiblePaths = [
        path.join(__dirname, '..', '..', '..', 'lib', 'mcp', WEBSEARCH_MCP_SERVER),
        path.join(__dirname, '..', '..', 'lib', 'mcp', WEBSEARCH_MCP_SERVER),
        path.join(__dirname, '..', 'lib', 'mcp', WEBSEARCH_MCP_SERVER),
    ];
    for (const candidate of possiblePaths) {
        if (fs.existsSync(candidate)) {
            return candidate;
        }
    }
    return null;
}
function readClaudeUserConfig(configPath) {
    if (!fs.existsSync(configPath)) {
        return {};
    }
    try {
        const raw = fs.readFileSync(configPath, 'utf8');
        const parsed = JSON.parse(raw);
        if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
            return null;
        }
        return parsed;
    }
    catch {
        return null;
    }
}
function writeClaudeUserConfig(configPath, config) {
    const tempPath = getTempPath(configPath);
    const fileMode = fs.existsSync(configPath) ? fs.statSync(configPath).mode & 0o777 : 0o600;
    try {
        fs.writeFileSync(tempPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
        fs.chmodSync(tempPath, fileMode);
        fs.renameSync(tempPath, configPath);
        return true;
    }
    finally {
        if (fs.existsSync(tempPath)) {
            fs.unlinkSync(tempPath);
        }
    }
}
function removeManagedServerConfig(configPath) {
    if (!fs.existsSync(configPath)) {
        return false;
    }
    const config = readClaudeUserConfig(configPath);
    if (config === null) {
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Malformed Claude config prevents MCP cleanup: ${configPath}`));
        }
        return false;
    }
    const existingServers = config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers)
        ? { ...config.mcpServers }
        : {};
    if (!(WEBSEARCH_MCP_SERVER_NAME in existingServers)) {
        return false;
    }
    delete existingServers[WEBSEARCH_MCP_SERVER_NAME];
    const nextConfig = { ...config };
    if (Object.keys(existingServers).length === 0) {
        delete nextConfig.mcpServers;
    }
    else {
        nextConfig.mcpServers = existingServers;
    }
    try {
        writeClaudeUserConfig(configPath, nextConfig);
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.info)(`Removed WebSearch MCP config from ${configPath}`));
        }
        return true;
    }
    catch (error) {
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Failed to remove WebSearch MCP config from ${configPath}: ${error.message}`));
        }
        return false;
    }
}
function installWebSearchMcpServer() {
    const wsConfig = (0, config_loader_facade_1.getWebSearchConfig)();
    if (!wsConfig.enabled) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_install_skipped', { reason: 'disabled' });
        return false;
    }
    if (!(0, hook_installer_1.installWebSearchHook)()) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_install_failed', { reason: 'hook_unavailable' });
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)('WebSearch MCP server install skipped because hook runtime is unavailable'));
        }
        return false;
    }
    const sourcePath = resolveBundledServerSourcePath();
    if (!sourcePath) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_install_failed', { reason: 'source_missing' });
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`WebSearch MCP server source not found: ${WEBSEARCH_MCP_SERVER}`));
        }
        return false;
    }
    const mcpDir = getCcsMcpDir();
    if (!fs.existsSync(mcpDir)) {
        fs.mkdirSync(mcpDir, { recursive: true, mode: 0o700 });
    }
    const serverPath = getWebSearchMcpServerPath();
    if (hasMatchingContents(sourcePath, serverPath)) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_install_ready', { serverPath });
        return true;
    }
    const tempPath = getTempPath(serverPath);
    try {
        fs.copyFileSync(sourcePath, tempPath);
        fs.chmodSync(tempPath, 0o755);
        try {
            fs.renameSync(tempPath, serverPath);
        }
        catch (renameError) {
            const errorCode = renameError.code;
            if (errorCode !== 'EEXIST' && errorCode !== 'EPERM') {
                throw renameError;
            }
            if (!hasMatchingContents(sourcePath, serverPath)) {
                fs.copyFileSync(tempPath, serverPath);
                fs.chmodSync(serverPath, 0o755);
            }
        }
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_install_ready', { serverPath });
        return true;
    }
    catch (error) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_install_failed', {
            reason: 'copy_failed',
            error: error.message,
        });
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Failed to install WebSearch MCP server: ${error.message}`));
        }
        return false;
    }
    finally {
        if (fs.existsSync(tempPath)) {
            fs.unlinkSync(tempPath);
        }
    }
}
exports.installWebSearchMcpServer = installWebSearchMcpServer;
function ensureWebSearchMcpConfig() {
    const wsConfig = (0, config_loader_facade_1.getWebSearchConfig)();
    if (!wsConfig.enabled) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_config_skipped', { reason: 'disabled' });
        return false;
    }
    const claudeUserConfigPath = (0, claude_config_path_1.getClaudeUserConfigPath)();
    const claudeUserConfigDir = path.dirname(claudeUserConfigPath);
    const config = readClaudeUserConfig(claudeUserConfigPath);
    if (config === null) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_config_failed', { reason: 'malformed_user_config' });
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)('Malformed ~/.claude.json prevents WebSearch MCP provisioning'));
        }
        return false;
    }
    if (!fs.existsSync(claudeUserConfigDir)) {
        fs.mkdirSync(claudeUserConfigDir, { recursive: true, mode: 0o700 });
    }
    const existingServers = config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers)
        ? config.mcpServers
        : {};
    const desiredServerConfig = {
        type: 'stdio',
        command: 'node',
        args: [getWebSearchMcpServerPath()],
        env: {},
    };
    const currentConfig = existingServers[WEBSEARCH_MCP_SERVER_NAME];
    if (typeof currentConfig === 'object' &&
        currentConfig !== null &&
        JSON.stringify(currentConfig) === JSON.stringify(desiredServerConfig)) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_config_ready', { configPath: claudeUserConfigPath });
        return true;
    }
    const nextConfig = {
        ...config,
        mcpServers: {
            ...existingServers,
            [WEBSEARCH_MCP_SERVER_NAME]: desiredServerConfig,
        },
    };
    try {
        writeClaudeUserConfig(claudeUserConfigPath, nextConfig);
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_config_ready', { configPath: claudeUserConfigPath });
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.info)(`Ensured WebSearch MCP config in ${claudeUserConfigPath}`));
        }
        return true;
    }
    catch (error) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_config_failed', {
            reason: 'write_failed',
            configPath: claudeUserConfigPath,
            error: error.message,
        });
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Failed to update ~/.claude.json: ${error.message}`));
        }
        return false;
    }
}
exports.ensureWebSearchMcpConfig = ensureWebSearchMcpConfig;
function ensureWebSearchMcp() {
    const wsConfig = (0, config_loader_facade_1.getWebSearchConfig)();
    if (!wsConfig.enabled) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_ensure_skipped', { reason: 'disabled' });
        return false;
    }
    const installed = installWebSearchMcpServer();
    const configured = installed && ensureWebSearchMcpConfig();
    (0, trace_1.appendWebSearchTrace)('websearch_mcp_ensure_result', { installed, configured });
    return installed && configured;
}
exports.ensureWebSearchMcp = ensureWebSearchMcp;
function syncWebSearchMcpToConfigDir(claudeConfigDir) {
    if (!claudeConfigDir) {
        (0, trace_1.appendWebSearchTrace)('websearch_mcp_sync_skipped', { reason: 'missing_config_dir' });
        return false;
    }
    const synced = new instance_manager_1.InstanceManager().syncMcpServers(claudeConfigDir);
    (0, trace_1.appendWebSearchTrace)('websearch_mcp_sync_result', { claudeConfigDir, synced });
    return synced;
}
exports.syncWebSearchMcpToConfigDir = syncWebSearchMcpToConfigDir;
function uninstallWebSearchMcpServer() {
    const serverPath = getWebSearchMcpServerPath();
    if (!fs.existsSync(serverPath)) {
        return false;
    }
    try {
        fs.unlinkSync(serverPath);
        return true;
    }
    catch (error) {
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Failed to remove WebSearch MCP server: ${error.message}`));
        }
        return false;
    }
}
exports.uninstallWebSearchMcpServer = uninstallWebSearchMcpServer;
function removeWebSearchMcpConfig() {
    let removed = removeManagedServerConfig((0, claude_config_path_1.getClaudeUserConfigPath)());
    const instanceManager = new instance_manager_1.InstanceManager();
    for (const instanceName of instanceManager.listInstances()) {
        const instancePath = instanceManager.getInstancePath(instanceName);
        const instanceClaudeConfigPath = path.join(instancePath, '.claude.json');
        removed = removeManagedServerConfig(instanceClaudeConfigPath) || removed;
    }
    return removed;
}
exports.removeWebSearchMcpConfig = removeWebSearchMcpConfig;
function uninstallWebSearchMcp() {
    const removedConfig = removeWebSearchMcpConfig();
    const removedServer = uninstallWebSearchMcpServer();
    return removedConfig || removedServer;
}
exports.uninstallWebSearchMcp = uninstallWebSearchMcp;
function ensureWebSearchMcpOrThrow() {
    const wsConfig = (0, config_loader_facade_1.getWebSearchConfig)();
    if (!wsConfig.enabled) {
        return;
    }
    if (!ensureWebSearchMcp()) {
        throw new Error('WebSearch is enabled, but CCS could not prepare the local WebSearch tool.');
    }
}
exports.ensureWebSearchMcpOrThrow = ensureWebSearchMcpOrThrow;
//# sourceMappingURL=mcp-installer.js.map