"use strict";
/**
 * Image Analysis 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.ensureImageAnalysisMcpOrThrow = exports.uninstallImageAnalysisMcp = exports.removeImageAnalysisMcpConfig = exports.uninstallImageAnalysisMcpServer = exports.syncImageAnalysisMcpToConfigDir = exports.ensureImageAnalysisMcp = exports.ensureImageAnalysisMcpConfig = exports.installImageAnalysisMcpServer = exports.hasImageAnalysisMcpReady = exports.hasImageAnalysisMcpConfig = exports.hasImageAnalysisMcpServerInstalled = exports.getImageAnalysisMcpRuntimePath = exports.getImageAnalysisMcpServerPath = exports.getImageAnalysisMcpServerName = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const lockfile = __importStar(require("proper-lockfile"));
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 config_loader_facade_1 = require("../../config/config-loader-facade");
const IMAGE_ANALYSIS_MCP_SERVER = 'ccs-image-analysis-server.cjs';
const IMAGE_ANALYSIS_MCP_RUNTIME = 'image-analysis-runtime.cjs';
const IMAGE_ANALYSIS_MCP_SERVER_NAME = 'ccs-image-analysis';
function getCcsMcpDir() {
    return path.join((0, config_manager_1.getCcsDir)(), 'mcp');
}
function getImageAnalysisMcpServerName() {
    return IMAGE_ANALYSIS_MCP_SERVER_NAME;
}
exports.getImageAnalysisMcpServerName = getImageAnalysisMcpServerName;
function getImageAnalysisMcpServerPath() {
    return path.join(getCcsMcpDir(), IMAGE_ANALYSIS_MCP_SERVER);
}
exports.getImageAnalysisMcpServerPath = getImageAnalysisMcpServerPath;
function getImageAnalysisMcpRuntimePath() {
    return path.join(getCcsMcpDir(), IMAGE_ANALYSIS_MCP_RUNTIME);
}
exports.getImageAnalysisMcpRuntimePath = getImageAnalysisMcpRuntimePath;
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 Image Analysis 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 resolveBundledArtifactSourcePath(fileName) {
    const possiblePaths = [
        path.join(__dirname, '..', '..', '..', 'lib', 'mcp', fileName),
        path.join(__dirname, '..', '..', 'lib', 'mcp', fileName),
        path.join(__dirname, '..', 'lib', 'mcp', fileName),
        path.join(__dirname, '..', '..', '..', 'lib', 'hooks', fileName),
        path.join(__dirname, '..', '..', 'lib', 'hooks', fileName),
        path.join(__dirname, '..', 'lib', 'hooks', fileName),
    ];
    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 withClaudeUserConfigLock(configPath, callback) {
    const configDir = path.dirname(configPath);
    const lockTarget = path.join(configDir, `${path.basename(configPath)}.ccs-lock`);
    let release;
    if (!fs.existsSync(configDir)) {
        fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
    }
    if (!fs.existsSync(lockTarget)) {
        fs.writeFileSync(lockTarget, '', { encoding: 'utf8', mode: 0o600 });
    }
    try {
        release = lockfile.lockSync(lockTarget, { stale: 10000 });
        return callback();
    }
    finally {
        if (release) {
            try {
                release();
            }
            catch {
                // Best-effort release.
            }
        }
    }
}
function isLockUnavailableError(error) {
    const code = error?.code;
    return code === 'ELOCKED' || code === 'ENOTACQUIRED';
}
function hasImageAnalysisMcpServerInstalled() {
    return (fs.existsSync(getImageAnalysisMcpServerPath()) &&
        fs.existsSync(getImageAnalysisMcpRuntimePath()));
}
exports.hasImageAnalysisMcpServerInstalled = hasImageAnalysisMcpServerInstalled;
function hasImageAnalysisMcpConfig(configPath = (0, claude_config_path_1.getClaudeUserConfigPath)()) {
    const config = readClaudeUserConfig(configPath);
    if (config === null) {
        return false;
    }
    const existingServers = config.mcpServers && typeof config.mcpServers === 'object' && !Array.isArray(config.mcpServers)
        ? config.mcpServers
        : {};
    const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME];
    return (typeof currentConfig === 'object' &&
        currentConfig !== null &&
        JSON.stringify(currentConfig) ===
            JSON.stringify({
                type: 'stdio',
                command: 'node',
                args: [getImageAnalysisMcpServerPath()],
                env: {},
            }));
}
exports.hasImageAnalysisMcpConfig = hasImageAnalysisMcpConfig;
function hasImageAnalysisMcpReady(configPath = (0, claude_config_path_1.getClaudeUserConfigPath)()) {
    return hasImageAnalysisMcpServerInstalled() && hasImageAnalysisMcpConfig(configPath);
}
exports.hasImageAnalysisMcpReady = hasImageAnalysisMcpReady;
function removeManagedServerConfig(configPath) {
    if (!fs.existsSync(configPath)) {
        return false;
    }
    try {
        return withClaudeUserConfigLock(configPath, () => {
            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 (!(IMAGE_ANALYSIS_MCP_SERVER_NAME in existingServers)) {
                return false;
            }
            delete existingServers[IMAGE_ANALYSIS_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 Image Analysis MCP config from ${configPath}`));
                }
                return true;
            }
            catch (error) {
                if (process.env.CCS_DEBUG) {
                    console.error((0, ui_1.warn)(`Failed to remove Image Analysis MCP config from ${configPath}: ${error.message}`));
                }
                return false;
            }
        });
    }
    catch (error) {
        if (isLockUnavailableError(error)) {
            if (process.env.CCS_DEBUG) {
                console.error((0, ui_1.warn)(`Image Analysis MCP cleanup skipped because ${configPath} is locked by another process`));
            }
            return false;
        }
        throw error;
    }
}
function installImageAnalysisMcpServer() {
    const config = (0, config_loader_facade_1.getImageAnalysisConfig)();
    if (!config.enabled) {
        return false;
    }
    const artifacts = [
        {
            fileName: IMAGE_ANALYSIS_MCP_SERVER,
            sourcePath: resolveBundledArtifactSourcePath(IMAGE_ANALYSIS_MCP_SERVER),
            destinationPath: getImageAnalysisMcpServerPath(),
        },
        {
            fileName: IMAGE_ANALYSIS_MCP_RUNTIME,
            sourcePath: resolveBundledArtifactSourcePath(IMAGE_ANALYSIS_MCP_RUNTIME),
            destinationPath: getImageAnalysisMcpRuntimePath(),
        },
    ];
    const missingArtifact = artifacts.find((artifact) => !artifact.sourcePath);
    if (missingArtifact) {
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Image Analysis MCP runtime source not found: ${missingArtifact.fileName}`));
        }
        return false;
    }
    const mcpDir = getCcsMcpDir();
    if (!fs.existsSync(mcpDir)) {
        fs.mkdirSync(mcpDir, { recursive: true, mode: 0o700 });
    }
    try {
        for (const artifact of artifacts) {
            const sourcePath = artifact.sourcePath;
            if (!sourcePath) {
                continue;
            }
            if (hasMatchingContents(sourcePath, artifact.destinationPath)) {
                continue;
            }
            const tempPath = getTempPath(artifact.destinationPath);
            try {
                fs.copyFileSync(sourcePath, tempPath);
                fs.chmodSync(tempPath, 0o755);
                try {
                    fs.renameSync(tempPath, artifact.destinationPath);
                }
                catch (renameError) {
                    const errorCode = renameError.code;
                    if (errorCode !== 'EEXIST' && errorCode !== 'EPERM') {
                        throw renameError;
                    }
                    if (!hasMatchingContents(sourcePath, artifact.destinationPath)) {
                        fs.copyFileSync(tempPath, artifact.destinationPath);
                        fs.chmodSync(artifact.destinationPath, 0o755);
                    }
                }
            }
            finally {
                if (fs.existsSync(tempPath)) {
                    fs.unlinkSync(tempPath);
                }
            }
        }
        (0, hook_installer_1.installImageAnalysisPrompts)();
        return true;
    }
    catch (error) {
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Failed to install Image Analysis MCP server: ${error.message}`));
        }
        return false;
    }
}
exports.installImageAnalysisMcpServer = installImageAnalysisMcpServer;
function ensureImageAnalysisMcpConfig() {
    const imageConfig = (0, config_loader_facade_1.getImageAnalysisConfig)();
    if (!imageConfig.enabled) {
        return false;
    }
    const claudeUserConfigPath = (0, claude_config_path_1.getClaudeUserConfigPath)();
    const claudeUserConfigDir = path.dirname(claudeUserConfigPath);
    if (!fs.existsSync(claudeUserConfigDir)) {
        fs.mkdirSync(claudeUserConfigDir, { recursive: true, mode: 0o700 });
    }
    const desiredServerConfig = {
        type: 'stdio',
        command: 'node',
        args: [getImageAnalysisMcpServerPath()],
        env: {},
    };
    try {
        return withClaudeUserConfigLock(claudeUserConfigPath, () => {
            const config = readClaudeUserConfig(claudeUserConfigPath);
            if (config === null) {
                if (process.env.CCS_DEBUG) {
                    console.error((0, ui_1.warn)('Malformed ~/.claude.json prevents Image Analysis MCP provisioning'));
                }
                return false;
            }
            const existingServers = config.mcpServers &&
                typeof config.mcpServers === 'object' &&
                !Array.isArray(config.mcpServers)
                ? config.mcpServers
                : {};
            const currentConfig = existingServers[IMAGE_ANALYSIS_MCP_SERVER_NAME];
            if (typeof currentConfig === 'object' &&
                currentConfig !== null &&
                JSON.stringify(currentConfig) === JSON.stringify(desiredServerConfig)) {
                return true;
            }
            const nextConfig = {
                ...config,
                mcpServers: {
                    ...existingServers,
                    [IMAGE_ANALYSIS_MCP_SERVER_NAME]: desiredServerConfig,
                },
            };
            try {
                writeClaudeUserConfig(claudeUserConfigPath, nextConfig);
                if (process.env.CCS_DEBUG) {
                    console.error((0, ui_1.info)(`Ensured Image Analysis MCP config in ${claudeUserConfigPath}`));
                }
                return true;
            }
            catch (error) {
                if (process.env.CCS_DEBUG) {
                    console.error((0, ui_1.warn)(`Failed to update ~/.claude.json: ${error.message}`));
                }
                return false;
            }
        });
    }
    catch (error) {
        if (isLockUnavailableError(error)) {
            if (process.env.CCS_DEBUG) {
                console.error((0, ui_1.warn)(`Image Analysis MCP provisioning skipped because ${claudeUserConfigPath} is locked by another process`));
            }
            return false;
        }
        throw error;
    }
}
exports.ensureImageAnalysisMcpConfig = ensureImageAnalysisMcpConfig;
function ensureImageAnalysisMcp() {
    const imageConfig = (0, config_loader_facade_1.getImageAnalysisConfig)();
    if (!imageConfig.enabled) {
        return false;
    }
    const installed = installImageAnalysisMcpServer();
    const configured = installed && ensureImageAnalysisMcpConfig();
    return installed && configured;
}
exports.ensureImageAnalysisMcp = ensureImageAnalysisMcp;
function syncImageAnalysisMcpToConfigDir(claudeConfigDir) {
    if (!claudeConfigDir) {
        return false;
    }
    return new instance_manager_1.InstanceManager().syncMcpServers(claudeConfigDir);
}
exports.syncImageAnalysisMcpToConfigDir = syncImageAnalysisMcpToConfigDir;
function uninstallImageAnalysisMcpServer() {
    const artifactPaths = [getImageAnalysisMcpServerPath(), getImageAnalysisMcpRuntimePath()];
    if (!artifactPaths.some((artifactPath) => fs.existsSync(artifactPath))) {
        return false;
    }
    try {
        let removed = false;
        for (const artifactPath of artifactPaths) {
            if (!fs.existsSync(artifactPath)) {
                continue;
            }
            fs.unlinkSync(artifactPath);
            removed = true;
        }
        return removed;
    }
    catch (error) {
        if (process.env.CCS_DEBUG) {
            console.error((0, ui_1.warn)(`Failed to remove Image Analysis MCP server: ${error.message}`));
        }
        return false;
    }
}
exports.uninstallImageAnalysisMcpServer = uninstallImageAnalysisMcpServer;
function removeImageAnalysisMcpConfig() {
    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.removeImageAnalysisMcpConfig = removeImageAnalysisMcpConfig;
function uninstallImageAnalysisMcp() {
    const removedConfig = removeImageAnalysisMcpConfig();
    const removedServer = uninstallImageAnalysisMcpServer();
    return removedConfig || removedServer;
}
exports.uninstallImageAnalysisMcp = uninstallImageAnalysisMcp;
function ensureImageAnalysisMcpOrThrow() {
    const imageConfig = (0, config_loader_facade_1.getImageAnalysisConfig)();
    if (!imageConfig.enabled) {
        return false;
    }
    const ready = ensureImageAnalysisMcp();
    if (!ready) {
        console.error((0, ui_1.warn)('Image Analysis is enabled, but CCS could not prepare the local ImageAnalysis tool. This session will fall back to native Read.'));
    }
    return ready;
}
exports.ensureImageAnalysisMcpOrThrow = ensureImageAnalysisMcpOrThrow;
//# sourceMappingURL=mcp-installer.js.map