"use strict";
/**
 * Copilot Executor
 *
 * Main execution flow for running Claude Code with copilot-api proxy.
 * Uses local installation from ~/.ccs/copilot/ (managed by copilot-package-manager).
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeCopilotProfile = exports.resolveCopilotImageAnalysisEnv = exports.generateCopilotEnv = exports.getCopilotStatus = void 0;
const child_process_1 = require("child_process");
const cliproxy_1 = require("../cliproxy");
const auth_token_manager_1 = require("../cliproxy/auth/auth-token-manager");
const port_manager_1 = require("../cliproxy/config/port-manager");
const copilot_auth_1 = require("./copilot-auth");
const copilot_daemon_1 = require("./copilot-daemon");
const copilot_package_manager_1 = require("./copilot-package-manager");
const copilot_model_normalizer_1 = require("./copilot-model-normalizer");
const ui_1 = require("../utils/ui");
const websearch_manager_1 = require("../utils/websearch-manager");
const image_analysis_1 = require("../utils/image-analysis");
const hooks_1 = require("../utils/hooks");
const shell_executor_1 = require("../utils/shell-executor");
const logging_1 = require("../services/logging");
const config_loader_facade_1 = require("../config/config-loader-facade");
const logger = (0, logging_1.createLogger)('copilot:executor');
/**
 * Get full copilot status (auth + daemon).
 */
async function getCopilotStatus(config) {
    const normalizedConfig = (0, copilot_model_normalizer_1.normalizeCopilotConfigWithWarnings)(config).config;
    const [auth, daemonRunning] = await Promise.all([
        (0, copilot_auth_1.checkAuthStatus)(),
        (0, copilot_daemon_1.isDaemonRunning)(normalizedConfig.port),
    ]);
    return {
        auth,
        daemon: {
            running: daemonRunning,
            port: normalizedConfig.port,
        },
    };
}
exports.getCopilotStatus = getCopilotStatus;
/**
 * Generate environment variables for Claude Code to use copilot-api.
 * Uses model mapping for opus/sonnet/haiku tiers if configured.
 */
function generateCopilotEnv(config, claudeConfigDir) {
    const normalizedConfig = (0, copilot_model_normalizer_1.normalizeCopilotConfigWithWarnings)(config).config;
    // Use mapped models if configured, otherwise fall back to default model
    const opusModel = normalizedConfig.opus_model || normalizedConfig.model;
    const sonnetModel = normalizedConfig.sonnet_model || normalizedConfig.model;
    const haikuModel = normalizedConfig.haiku_model || normalizedConfig.model;
    // Use 127.0.0.1 instead of localhost for more reliable local connections
    // (bypasses DNS resolution and potential IPv6 issues)
    return {
        ANTHROPIC_BASE_URL: `http://127.0.0.1:${normalizedConfig.port}`,
        ANTHROPIC_AUTH_TOKEN: 'dummy', // copilot-api handles auth internally
        ANTHROPIC_MODEL: normalizedConfig.model,
        // Model tier mapping - allows different models for opus/sonnet/haiku
        ANTHROPIC_DEFAULT_OPUS_MODEL: opusModel,
        ANTHROPIC_DEFAULT_SONNET_MODEL: sonnetModel,
        ANTHROPIC_SMALL_FAST_MODEL: haikuModel,
        ANTHROPIC_DEFAULT_HAIKU_MODEL: haikuModel,
        // Disable non-essential traffic to avoid rate limiting
        DISABLE_NON_ESSENTIAL_MODEL_CALLS: '1',
        CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
        ...(claudeConfigDir ? { CLAUDE_CONFIG_DIR: claudeConfigDir } : {}),
    };
}
exports.generateCopilotEnv = generateCopilotEnv;
async function resolveCopilotImageAnalysisEnv(verbose = false, deps = {}) {
    const resolvedDeps = {
        ensureCliproxyService: cliproxy_1.ensureCliproxyService,
        getImageAnalysisHookEnv: hooks_1.getImageAnalysisHookEnv,
        resolveImageAnalysisRuntimeStatus: hooks_1.resolveImageAnalysisRuntimeStatus,
        getLocalRuntimeApiKey: auth_token_manager_1.getEffectiveApiKey,
        ...deps,
    };
    const env = resolvedDeps.getImageAnalysisHookEnv({
        profileName: 'copilot',
        profileType: 'copilot',
    });
    const provider = env['CCS_CURRENT_PROVIDER'];
    if (env['CCS_IMAGE_ANALYSIS_SKIP'] === '1' || !provider) {
        return { env, warning: null };
    }
    const status = await resolvedDeps.resolveImageAnalysisRuntimeStatus({
        profileName: 'copilot',
        profileType: 'copilot',
    });
    if (status.effectiveRuntimeMode === 'native-read') {
        return {
            env: {
                ...env,
                CCS_CURRENT_PROVIDER: '',
                CCS_IMAGE_ANALYSIS_SKIP: '1',
            },
            warning: `${status.effectiveRuntimeReason || `Image analysis via ${provider} is unavailable.`} This session will use native Read.`,
        };
    }
    if (status.proxyReadiness === 'stopped') {
        const ensureServiceResult = await resolvedDeps.ensureCliproxyService((0, port_manager_1.resolveLifecyclePort)(), verbose);
        if (!ensureServiceResult.started) {
            return {
                env: {
                    ...env,
                    CCS_CURRENT_PROVIDER: '',
                    CCS_IMAGE_ANALYSIS_SKIP: '1',
                },
                warning: `Image analysis via ${provider} is unavailable because CCS could not start the local CLIProxy service. This session will use native Read.`,
            };
        }
    }
    const runtimeConnection = (0, hooks_1.resolveImageAnalysisRuntimeConnection)();
    return {
        env: (0, hooks_1.applyImageAnalysisRuntimeOverrides)(env, {
            backendId: status.backendId,
            model: status.model,
            runtimePath: status.runtimePath,
            baseUrl: runtimeConnection.baseUrl,
            apiKey: runtimeConnection.proxyTarget.isRemote
                ? runtimeConnection.apiKey
                : resolvedDeps.getLocalRuntimeApiKey(),
            allowSelfSigned: runtimeConnection.allowSelfSigned,
        }),
        warning: null,
    };
}
exports.resolveCopilotImageAnalysisEnv = resolveCopilotImageAnalysisEnv;
/**
 * Execute Claude Code with copilot-api proxy.
 *
 * @param config Copilot configuration
 * @param claudeArgs Arguments to pass to Claude CLI
 * @returns Exit code
 */
async function executeCopilotProfile(config, claudeArgs, claudeConfigDir, claudeCliPath = 'claude') {
    const { config: normalizedConfig, warnings } = (0, copilot_model_normalizer_1.normalizeCopilotConfigWithWarnings)(config);
    logger.stage('intake', 'copilot.execute.start', 'Starting Copilot profile execution', {
        provider: 'copilot',
        model: normalizedConfig.model,
        port: normalizedConfig.port,
    });
    if (warnings.length > 0) {
        warnings.forEach(({ message }) => console.log((0, ui_1.warn)(message)));
        console.log((0, ui_1.warn)('Run `ccs config` and save the Copilot section to persist these replacements.'));
        console.log('');
    }
    // Ensure copilot-api is installed (auto-install if missing, auto-update if outdated)
    try {
        await (0, copilot_package_manager_1.ensureCopilotApi)();
    }
    catch (error) {
        console.error((0, ui_1.fail)('Failed to install copilot-api.'));
        console.error('');
        console.error(`Error: ${error.message}`);
        console.error('');
        console.error('Try installing manually:');
        console.error('  npm install -g copilot-api');
        return 1;
    }
    // Check if copilot-api is installed (should be after ensureCopilotApi)
    if (!(0, copilot_auth_1.isCopilotApiInstalled)()) {
        console.error((0, ui_1.fail)('copilot-api is not installed.'));
        console.error('');
        console.error('Install/repair by running: ccs copilot start');
        return 1;
    }
    // Check authentication
    const authStatus = await (0, copilot_auth_1.checkAuthStatus)();
    logger.stage('auth', 'copilot.execute.auth_check', 'Checked Copilot auth status', {
        provider: 'copilot',
        authenticated: authStatus.authenticated,
    });
    if (!authStatus.authenticated) {
        console.error((0, ui_1.fail)('Not authenticated with GitHub.'));
        console.error('');
        console.error('Run: npx copilot-api auth');
        console.error('Or:  ccs copilot auth');
        return 1;
    }
    // Check if daemon is running or needs to be started
    let daemonRunning = await (0, copilot_daemon_1.isDaemonRunning)(normalizedConfig.port);
    if (!daemonRunning) {
        if (normalizedConfig.auto_start) {
            console.log((0, ui_1.info)('Starting copilot-api daemon...'));
            const result = await (0, copilot_daemon_1.startDaemon)(normalizedConfig);
            if (!result.success) {
                console.error((0, ui_1.fail)(`Failed to start daemon: ${result.error}`));
                return 1;
            }
            console.log((0, ui_1.ok)(`Daemon started on port ${normalizedConfig.port}`));
            daemonRunning = true;
        }
        else {
            console.error((0, ui_1.fail)('copilot-api daemon is not running.'));
            console.error('');
            console.error('Start the daemon:');
            console.error('  ccs copilot start');
            console.error('Fallback manual command:');
            console.error(`  npx copilot-api start --port ${normalizedConfig.port}`);
            console.error('');
            console.error('Or enable auto_start in config:');
            console.error('  ccs config  (then enable auto_start in Copilot section)');
            return 1;
        }
    }
    // Generate environment for Claude
    const copilotEnv = generateCopilotEnv(normalizedConfig, claudeConfigDir);
    // Get global env vars (DISABLE_TELEMETRY, etc.) for third-party profiles
    const globalEnvConfig = (0, config_loader_facade_1.getGlobalEnvConfig)();
    const globalEnv = globalEnvConfig.enabled ? globalEnvConfig.env : {};
    const imageAnalysisMcpReady = (0, image_analysis_1.ensureImageAnalysisMcpOrThrow)();
    (0, websearch_manager_1.syncWebSearchMcpToConfigDir)(claudeConfigDir);
    (0, image_analysis_1.syncImageAnalysisMcpToConfigDir)(claudeConfigDir);
    // Merge with current environment (global env first, copilot overrides, then hook env vars)
    const webSearchEnv = (0, websearch_manager_1.getWebSearchHookEnv)();
    const imageAnalysisResolution = await resolveCopilotImageAnalysisEnv();
    const imageAnalysisProvisioningFailed = !imageAnalysisMcpReady && imageAnalysisResolution.env.CCS_IMAGE_ANALYSIS_ENABLED === '1';
    const imageAnalysisWarning = imageAnalysisProvisioningFailed
        ? 'ImageAnalysis MCP provisioning failed. This session will use compatibility fallback when available.'
        : imageAnalysisResolution.warning;
    const imageAnalysisEnv = {
        ...imageAnalysisResolution.env,
        CCS_IMAGE_ANALYSIS_SKIP_HOOK: imageAnalysisMcpReady ? '1' : '0',
    };
    const env = (0, shell_executor_1.stripClaudeCodeEnv)({
        ...process.env,
        ...globalEnv,
        ...copilotEnv,
        ...webSearchEnv,
        ...imageAnalysisEnv,
        CCS_PROFILE_TYPE: 'copilot',
    });
    console.log((0, ui_1.info)(`Using GitHub Copilot proxy (model: ${normalizedConfig.model})`));
    if (imageAnalysisWarning) {
        console.log((0, ui_1.info)(imageAnalysisWarning));
    }
    console.log('');
    // Spawn Claude CLI
    const spawnStartedAt = Date.now();
    return new Promise((resolve) => {
        const imageAnalysisArgs = imageAnalysisMcpReady
            ? (0, image_analysis_1.appendThirdPartyImageAnalysisToolArgs)(claudeArgs)
            : claudeArgs;
        const launchArgs = (0, websearch_manager_1.appendThirdPartyWebSearchToolArgs)(imageAnalysisArgs);
        const traceEnv = (0, websearch_manager_1.createWebSearchTraceContext)({
            launcher: 'copilot.executor',
            args: launchArgs,
            profile: 'copilot',
            profileType: 'copilot',
            claudeConfigDir,
        });
        logger.stage('dispatch', 'copilot.execute.spawn', 'Spawning Claude via Copilot proxy', {
            provider: 'copilot',
            argCount: launchArgs.length,
        });
        const proc = (0, child_process_1.spawn)(claudeCliPath, launchArgs, {
            stdio: 'inherit',
            env: { ...env, ...traceEnv },
            shell: process.platform === 'win32',
        });
        proc.on('close', (code) => {
            logger.stage('respond', 'copilot.execute.exit', 'Claude process exited (Copilot)', { provider: 'copilot', exitCode: code }, { latencyMs: Date.now() - spawnStartedAt });
            resolve(code ?? 0);
        });
        proc.on('error', (err) => {
            logger.stage('cleanup', 'copilot.execute.error', 'Failed to spawn Claude (Copilot)', { provider: 'copilot' }, {
                level: 'error',
                latencyMs: Date.now() - spawnStartedAt,
                error: { name: err.name, message: err.message },
            });
            console.error((0, ui_1.fail)(`Failed to start Claude: ${err.message}`));
            resolve(1);
        });
    });
}
exports.executeCopilotProfile = executeCopilotProfile;
//# sourceMappingURL=copilot-executor.js.map