import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
import { isKeepAliveServer } from '../lifecycle.js';
export function createKeepAliveRuntime(base, options) {
    if (!options.daemonClient || options.keepAliveServers.size === 0) {
        return base;
    }
    return new KeepAliveRuntime(base, options.daemonClient, options.keepAliveServers);
}
class KeepAliveRuntime {
    base;
    daemon;
    keepAliveServers;
    constructor(base, daemon, keepAliveServers) {
        this.base = base;
        this.daemon = daemon;
        this.keepAliveServers = keepAliveServers;
    }
    listServers() {
        return this.base.listServers();
    }
    getDefinitions() {
        return this.base.getDefinitions();
    }
    getDefinition(server) {
        return this.base.getDefinition(server);
    }
    registerDefinition(definition, options) {
        this.base.registerDefinition(definition, options);
        if (isKeepAliveServer(definition)) {
            this.keepAliveServers.add(definition.name);
        }
        else {
            this.keepAliveServers.delete(definition.name);
        }
    }
    async listTools(server, options) {
        if (this.shouldUseDaemon(server)) {
            return (await this.invokeWithRestart(server, 'listTools', () => this.daemon.listTools({
                server,
                includeSchema: options?.includeSchema,
                autoAuthorize: options?.autoAuthorize,
            })));
        }
        return this.base.listTools(server, options);
    }
    async callTool(server, toolName, options) {
        if (this.shouldUseDaemon(server)) {
            return this.invokeWithRestart(server, 'callTool', () => this.daemon.callTool({
                server,
                tool: toolName,
                args: options?.args,
                timeoutMs: options?.timeoutMs,
            }));
        }
        return this.base.callTool(server, toolName, options);
    }
    async listResources(server, options) {
        if (this.shouldUseDaemon(server)) {
            return this.invokeWithRestart(server, 'listResources', () => this.daemon.listResources({ server, params: options ?? {} }));
        }
        return this.base.listResources(server, options);
    }
    async connect(server) {
        return this.base.connect(server);
    }
    async close(server) {
        if (!server) {
            await this.base.close();
            return;
        }
        if (this.shouldUseDaemon(server)) {
            await this.daemon.closeServer({ server }).catch(() => { });
            return;
        }
        await this.base.close(server);
    }
    shouldUseDaemon(server) {
        return this.keepAliveServers.has(server);
    }
    async invokeWithRestart(server, operation, action) {
        try {
            return await action();
        }
        catch (error) {
            if (!shouldRestartDaemonServer(error)) {
                throw error;
            }
            // The daemon keeps STDIO transports warm; if a call fails due to a fatal error,
            // force-close the cached server so the retry launches a fresh Chrome instance.
            logDaemonRetry(server, operation, error);
            await this.daemon.closeServer({ server }).catch(() => { });
            return action();
        }
    }
}
const NON_FATAL_CODES = new Set([ErrorCode.InvalidRequest, ErrorCode.MethodNotFound, ErrorCode.InvalidParams]);
function shouldRestartDaemonServer(error) {
    if (!error) {
        return false;
    }
    if (error instanceof McpError) {
        return !NON_FATAL_CODES.has(error.code);
    }
    return true;
}
function logDaemonRetry(server, operation, error) {
    const reason = error instanceof Error ? error.message : String(error);
    console.log(`[mcporter] Restarting '${server}' before retrying ${operation}: ${reason}`);
}
//# sourceMappingURL=runtime-wrapper.js.map