"use strict";
/**
 * Droid Adapter
 *
 * TargetAdapter implementation for Factory Droid CLI.
 * Writes credentials + active model to ~/.factory/settings.json and spawns `droid`.
 */
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.DroidAdapter = void 0;
const child_process_1 = require("child_process");
const fs = __importStar(require("fs"));
const droid_detector_1 = require("./droid-detector");
const droid_config_manager_1 = require("./droid-config-manager");
const droid_provider_1 = require("./droid-provider");
const shell_executor_1 = require("../utils/shell-executor");
const signal_forwarder_1 = require("../utils/signal-forwarder");
const errors_1 = require("../errors");
const logging_1 = require("../services/logging");
const adapterLogger = (0, logging_1.createLogger)('targets:droid');
class DroidAdapter {
    constructor() {
        this.type = 'droid';
        this.displayName = 'Factory Droid';
    }
    validateCredentials(creds) {
        if (!creds.baseUrl?.trim()) {
            throw new Error('Droid target requires ANTHROPIC_BASE_URL');
        }
        if (!creds.apiKey?.trim()) {
            throw new Error('Droid target requires ANTHROPIC_AUTH_TOKEN');
        }
    }
    detectBinary() {
        const info = (0, droid_detector_1.getDroidBinaryInfo)();
        if (!info)
            return null;
        // Version compatibility check (non-blocking warning)
        (0, droid_detector_1.checkDroidVersion)(info.path);
        return info;
    }
    /**
     * Write CCS credentials to ~/.factory/settings.json as a custom model entry.
     * This is the key difference from Claude — Droid reads config files, not env vars.
     */
    async prepareCredentials(creds) {
        this.validateCredentials(creds);
        const provider = (0, droid_provider_1.resolveDroidProvider)({
            provider: creds.provider,
            baseUrl: creds.baseUrl,
            model: creds.model,
        });
        const modelRef = await (0, droid_config_manager_1.upsertCcsModel)(creds.profile, {
            model: creds.model || 'claude-opus-4-6',
            displayName: `CCS ${creds.profile}`,
            baseUrl: creds.baseUrl,
            apiKey: creds.apiKey,
            provider,
            reasoningOverride: creds.reasoningOverride,
        });
        if (!modelRef.selector) {
            throw new Error(`Failed to resolve Droid model selector for profile "${creds.profile}"`);
        }
    }
    buildArgs(profile, userArgs) {
        if (!/^[a-zA-Z0-9._-]+$/.test(profile)) {
            throw new Error(`Invalid profile name "${profile}" for Droid target: only alphanumeric, dot, underscore, hyphen allowed`);
        }
        // Droid interactive mode treats unknown argv as queued prompt text.
        // Model selection must be persisted in settings.json (`model`) instead of `-m`.
        return [...userArgs];
    }
    /**
     * Droid uses config file for credentials — keep parent env, but strip stale
     * ANTHROPIC_* values so prior CCS/CLIProxy sessions do not leak into Droid.
     */
    buildEnv(_creds, _profileType) {
        return { ...(0, shell_executor_1.stripAnthropicEnv)(process.env) };
    }
    exec(args, env, options) {
        const exitWithCleanup = (code) => {
            try {
                (0, errors_1.runCleanup)();
            }
            catch {
                // Cleanup should be best-effort on launch errors.
            }
            process.exit(code);
        };
        const droidPath = options?.binaryInfo?.path || (0, droid_detector_1.detectDroidCli)();
        if (!droidPath) {
            console.error('[X] Droid CLI not found. Install: npm i -g @factory/cli');
            return exitWithCleanup(1);
        }
        try {
            const stat = fs.statSync(droidPath);
            if (!stat.isFile()) {
                console.error(`[X] Droid CLI path is not a file: ${droidPath}`);
                return exitWithCleanup(1);
            }
        }
        catch (err) {
            const error = err;
            console.error(`[X] Droid CLI path is not accessible (${error.code || 'unknown'}): ${droidPath}`);
            return exitWithCleanup(1);
        }
        const isWindows = process.platform === 'win32';
        const isPowerShellScript = isWindows && /\.ps1$/i.test(droidPath);
        const needsShell = isWindows && /\.(cmd|bat)$/i.test(droidPath);
        const spawnStartedAt = Date.now();
        adapterLogger.stage('dispatch', 'target.spawn', 'Spawning Droid CLI child process', {
            target: 'droid',
            droidPath,
            argCount: args.length,
        });
        let child;
        if (isPowerShellScript) {
            child = (0, child_process_1.spawn)('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', droidPath, ...args], {
                stdio: 'inherit',
                windowsHide: true,
                env,
            });
        }
        else if (needsShell) {
            const cmdString = [droidPath, ...args].map(shell_executor_1.escapeShellArg).join(' ');
            child = (0, child_process_1.spawn)(cmdString, {
                stdio: 'inherit',
                windowsHide: true,
                shell: (0, shell_executor_1.getWindowsEscapedCommandShell)(),
                env,
            });
        }
        else {
            child = (0, child_process_1.spawn)(droidPath, args, {
                stdio: 'inherit',
                windowsHide: true,
                env,
            });
        }
        child.on('exit', (code, signal) => {
            adapterLogger.stage('respond', 'target.exit', 'Droid CLI child process exited', { target: 'droid', exitCode: code, signal }, { latencyMs: Date.now() - spawnStartedAt });
        });
        (0, signal_forwarder_1.wireChildProcessSignals)(child, (err) => {
            if (err.code === 'EACCES') {
                console.error(`[X] Droid CLI is not executable: ${droidPath}`);
                console.error('    Check file permissions and executable bit.');
            }
            else if (err.code === 'ENOENT') {
                if (isPowerShellScript) {
                    console.error('[X] PowerShell executable not found (required for .ps1 wrapper launch).');
                    console.error('    Ensure powershell.exe is available in PATH.');
                }
                else if (needsShell) {
                    console.error('[X] Windows command shell not found for Droid wrapper launch.');
                    console.error('    Ensure cmd.exe is available and accessible.');
                }
                else {
                    console.error(`[X] Droid CLI not found: ${droidPath}`);
                    console.error('    Install: npm i -g @factory/cli');
                }
            }
            else {
                console.error(`[X] Failed to start Droid CLI (${droidPath}):`, err.message);
            }
            return exitWithCleanup(1);
        });
    }
    /** Droid supports settings/default and CLIProxy-executed profile flows. */
    supportsProfileType(profileType) {
        return profileType === 'settings' || profileType === 'default' || profileType === 'cliproxy';
    }
}
exports.DroidAdapter = DroidAdapter;
//# sourceMappingURL=droid-adapter.js.map