import { createHash } from "node:crypto";
import { McpHost, } from "../../hub/index.js";
import { getErrorMessage } from "../../hub/utils.js";
const MAX_RESOURCE_TOKEN_LENGTH = 24;
function shortHash(value) {
    return createHash("sha256").update(value).digest("hex").slice(0, 6);
}
function safeResourceToken(value) {
    const safe = value.replace(/[^a-zA-Z0-9_-]+/g, "_").replace(/^_+|_+$/g, "");
    if (safe.length === 0) {
        return `x_${shortHash(value)}`;
    }
    if (safe === value && safe.length <= MAX_RESOURCE_TOKEN_LENGTH) {
        return safe;
    }
    return `${safe.slice(0, MAX_RESOURCE_TOKEN_LENGTH)}_${shortHash(value)}`;
}
function remoteMcpResourceToken(peerId, resourceId) {
    return safeResourceToken(`${peerId}:${resourceId}`);
}
function remoteMcpToolNameFromLocal(peerId, localMcpToolName) {
    const match = /^mcp__([^_][^_]*)__([^_][\s\S]*)$/.exec(localMcpToolName);
    if (!match) {
        return undefined;
    }
    return `mcp__${remoteMcpResourceToken(peerId, match[1])}__${match[2]}`;
}
function serversFromMcpRoot(root) {
    if (Array.isArray(root)) {
        return root;
    }
    if (root && typeof root === "object" && Array.isArray(root.servers)) {
        const { servers, timeoutMs } = root;
        if (timeoutMs === undefined) {
            return servers;
        }
        return servers.map((server) => {
            if (!server || typeof server !== "object" || Array.isArray(server)) {
                return server;
            }
            if (server.timeoutMs !== undefined) {
                return server;
            }
            return { ...server, timeoutMs };
        });
    }
    return [];
}
export function mergePeerMcpConfig(snapshot) {
    return {
        servers: [...serversFromMcpRoot(snapshot.global?.mcp), ...serversFromMcpRoot(snapshot.cwdLayer?.mcp)],
    };
}
export class PeerMcpRuntime {
    options;
    host;
    constructor(options) {
        this.options = options;
        this.host = options.host;
    }
    async start() {
        await this.requireHost().start();
    }
    async stop() {
        await this.requireHost().stop();
    }
    getSnapshot() {
        const host = this.requireHost();
        const configError = host.getConfigError();
        return {
            servers: host.getStatuses(),
            ...(configError && { configError }),
        };
    }
    getRemoteToolNames(peerId) {
        const names = [];
        for (const tool of this.requireHost().getSharedCustomToolsArray()) {
            const remoteName = remoteMcpToolNameFromLocal(peerId, tool.name);
            if (remoteName) {
                names.push(remoteName);
            }
        }
        return names;
    }
    async executeToolRequest(peerId, payload, socket) {
        const localTool = this.findLocalTool(peerId, payload.toolName);
        if (!localTool) {
            return false;
        }
        const abortController = new AbortController();
        const timeoutId = setTimeout(() => {
            abortController.abort();
        }, payload.timeoutMs);
        try {
            socket.emit("tool:call_ack", { toolCallId: payload.toolCallId });
            const result = await localTool.execute(payload.toolCallId, payload.args, abortController.signal, (partialResult) => {
                socket.emit("tool:call_update", {
                    toolCallId: payload.toolCallId,
                    partialResult,
                });
            }, {});
            socket.emit("tool:call_result", {
                toolCallId: payload.toolCallId,
                result,
            });
        }
        catch (error) {
            socket.emit("tool:call_error", {
                toolCallId: payload.toolCallId,
                message: getErrorMessage(error),
            });
        }
        finally {
            clearTimeout(timeoutId);
        }
        return true;
    }
    findLocalTool(peerId, remoteToolName) {
        for (const tool of this.requireHost().getSharedCustomToolsArray()) {
            if (remoteMcpToolNameFromLocal(peerId, tool.name) === remoteToolName) {
                return tool;
            }
        }
        return undefined;
    }
    requireHost() {
        this.host ??= new McpHost({
            cwd: this.options.cwd,
            customTools: [],
            configRoot: () => mergePeerMcpConfig(this.options.snapshot),
        });
        return this.host;
    }
}
//# sourceMappingURL=peer-mcp-runtime.js.map