import { homedir } from "node:os";
import { checkProviderLaunchAvailable, resolveProviderLaunch } from "../provider-launch-config.js";
import { ACPAgentClient, deriveModelDefinitionsFromACP, deriveModesFromACP, } from "./acp-agent.js";
import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, buildBinaryDiagnosticRows, toDiagnosticErrorMessage, } from "./diagnostic-utils.js";
const ACP_DIAGNOSTIC_INITIALIZE_TIMEOUT_MS = 8000;
const ACP_DIAGNOSTIC_SESSION_TIMEOUT_MS = 8000;
export class GenericACPAgentClient extends ACPAgentClient {
    constructor(options) {
        super({
            provider: "acp",
            logger: options.logger,
            runtimeSettings: {
                env: options.env,
            },
            defaultCommand: options.command,
            waitForInitialCommands: options.waitForInitialCommands,
            initialCommandsWaitTimeoutMs: options.initialCommandsWaitTimeoutMs,
        });
        this.command = options.command;
        this.providerId = options.providerId;
        this.label = options.label;
    }
    async resolveLaunchCommand() {
        return {
            command: this.command[0],
            args: this.command.slice(1),
        };
    }
    async isAvailable() {
        const launch = await this.resolveConfiguredLaunch();
        const availability = await checkProviderLaunchAvailable(launch);
        return availability.available;
    }
    async getDiagnostic() {
        const providerName = formatProviderName(this.label, this.providerId);
        try {
            const launch = await this.resolveConfiguredLaunch();
            const availability = await checkProviderLaunchAvailable(launch);
            const available = availability.available;
            const versionProbe = buildVersionProbeCommand(this.command);
            const probeResult = available
                ? await this.runDiagnosticACPProbe()
                : {
                    status: formatDiagnosticStatus(false),
                    initialize: "Not checked",
                    session: "Not checked",
                    models: "Not checked",
                    modes: "Not checked",
                };
            return {
                diagnostic: formatProviderDiagnostic(providerName, [
                    { label: "Provider ID", value: this.providerId ?? "unknown" },
                    { label: "Configured command", value: this.command.join(" ") },
                    ...(await buildBinaryDiagnosticRows(launch, availability, {
                        binaryLabel: "Launcher binary",
                        versionCommand: {
                            command: versionProbe.command,
                            args: versionProbe.args,
                            env: this.runtimeSettings?.env,
                        },
                    })),
                    {
                        label: "Version command",
                        value: formatCommand(versionProbe.command, versionProbe.args),
                    },
                    { label: "ACP initialize", value: probeResult.initialize },
                    { label: "ACP session/new", value: probeResult.session },
                    { label: "Models", value: probeResult.models },
                    { label: "Modes", value: probeResult.modes },
                    { label: "Status", value: probeResult.status },
                ]),
            };
        }
        catch (error) {
            return {
                diagnostic: formatProviderDiagnosticError(providerName, error),
            };
        }
    }
    async resolveConfiguredLaunch() {
        return resolveProviderLaunch({
            commandConfig: { mode: "replace", argv: this.command },
            defaultBinary: this.command[0],
        });
    }
    async runDiagnosticACPProbe() {
        let initializeValue = "Not checked";
        let sessionValue = "Not checked";
        try {
            const probe = await this.spawnProcess({
                NO_BROWSER: "true",
                NO_OPEN_BROWSER: "1",
                GEMINI_CLI_NO_BROWSER: "true",
                CI: "1",
            }, {
                initializeTimeoutMs: ACP_DIAGNOSTIC_INITIALIZE_TIMEOUT_MS,
            });
            try {
                initializeValue = formatInitializeResult(probe.initialize);
                const response = await withTimeout(probe.connection.newSession({
                    cwd: homedir(),
                    mcpServers: [],
                }), ACP_DIAGNOSTIC_SESSION_TIMEOUT_MS, "ACP session/new");
                sessionValue = response.sessionId ? `ok (${response.sessionId})` : "ok";
                const transformed = this.transformSessionResponse(response);
                return {
                    status: formatDiagnosticStatus(true),
                    initialize: initializeValue,
                    session: sessionValue,
                    ...summarizeSessionState(this.provider, transformed),
                };
            }
            finally {
                await this.closeProbe(probe);
            }
        }
        catch (error) {
            return {
                status: formatDiagnosticStatus(true, {
                    source: "ACP probe",
                    cause: error,
                }),
                initialize: formatProbeError(initializeValue, error),
                session: initializeValue === "Not checked" ? "Not checked" : formatProbeError(sessionValue, error),
                models: "Not checked",
                modes: "Not checked",
            };
        }
    }
}
function formatProviderName(label, providerId) {
    if (label) {
        return `${label} (ACP)`;
    }
    if (providerId) {
        return `${providerId} (ACP)`;
    }
    return "Custom ACP";
}
function formatCommand(command, args) {
    return [command, ...args].join(" ");
}
export function buildVersionProbeCommand(command) {
    const [launcher, ...args] = command;
    if (isPackageRunner(launcher)) {
        return {
            command: launcher,
            args: [...takePackageRunnerPrefix(args), "--version"],
        };
    }
    return {
        command: launcher,
        args: ["--version"],
    };
}
function isPackageRunner(command) {
    return ["npx", "bunx", "pnpm", "uvx"].includes(command);
}
function takePackageRunnerPrefix(args) {
    if (args.length === 0) {
        return [];
    }
    if (args[0] === "dlx") {
        return ["dlx", ...takePackageSpecPrefix(args.slice(1))];
    }
    return takePackageSpecPrefix(args);
}
function takePackageSpecPrefix(args) {
    const prefix = [];
    for (let index = 0; index < args.length; index += 1) {
        const arg = args[index];
        prefix.push(arg);
        if (arg === "--package" || arg === "-p") {
            if (args[index + 1]) {
                prefix.push(args[index + 1]);
                index += 1;
            }
            continue;
        }
        if (!arg.startsWith("-")) {
            break;
        }
    }
    return prefix;
}
function formatInitializeResult(initialize) {
    const agentInfo = isAgentInfo(initialize.agentInfo)
        ? `${initialize.agentInfo.name}${initialize.agentInfo.version ? ` ${initialize.agentInfo.version}` : ""}`
        : "ok";
    return `ok (protocol ${initialize.protocolVersion}, ${agentInfo})`;
}
function isAgentInfo(value) {
    return (typeof value === "object" &&
        value !== null &&
        "name" in value &&
        typeof Reflect.get(value, "name") === "string");
}
function summarizeSessionState(provider, response) {
    const models = deriveModelDefinitionsFromACP(provider, response.models, response.configOptions);
    const { modes } = deriveModesFromACP([], response.modes, response.configOptions);
    return {
        models: `${models.length}`,
        modes: modes.length > 0 ? modes.map((mode) => mode.label || mode.id).join(", ") : "none reported",
    };
}
function formatProbeError(currentValue, error) {
    if (currentValue !== "Not checked") {
        return currentValue;
    }
    return `Error - ${toDiagnosticErrorMessage(error)}`;
}
async function withTimeout(promise, timeoutMs, label) {
    let timeout = null;
    try {
        return await Promise.race([
            promise,
            new Promise((_, reject) => {
                timeout = setTimeout(() => {
                    reject(new Error(`${label} timed out after ${timeoutMs}ms`));
                }, timeoutMs);
            }),
        ]);
    }
    finally {
        if (timeout) {
            clearTimeout(timeout);
        }
    }
}
//# sourceMappingURL=generic-acp-agent.js.map