/**
 * CodexExecutor - OpenAI Codex CLI 执行器
 * 参考: https://shipyard.build/blog/codex-cli-cheat-sheet/
 */
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { parse as parseToml } from 'smol-toml';
import { AgentType } from '../types/index.js';
import { which } from '../utils/index.js';
import { BaseExecutor, AgentCapability, } from './base.executor.js';
import { CommandBuilder, applyOverrides } from './command-builder.js';
import { extractImagePaths } from './image-utils.js';
/**
 * 将嵌套对象展平为 dotted path 键值对（递归到标量叶子）
 * { model_providers: { azure: { name: "A", query_params: { "api-version": "v1" } } } }
 * → [
 *     ["model_providers.azure.name", "A"],
 *     ["model_providers.azure.query_params.api-version", "v1"],
 *   ]
 */
function flattenObject(obj, prefix = '') {
    const entries = [];
    for (const [key, value] of Object.entries(obj)) {
        const fullPath = prefix ? `${prefix}.${key}` : key;
        if (value !== null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)) {
            entries.push(...flattenObject(value, fullPath));
        }
        else {
            entries.push([fullPath, value]);
        }
    }
    return entries;
}
/**
 * 将 JS 值转为 TOML 字面量字符串（用于 -c 参数）
 * 对象转为 TOML inline table: { key = "value", key2 = 123 }
 */
function toTomlLiteral(value) {
    if (typeof value === 'string')
        return `"${value}"`;
    if (typeof value === 'boolean' || typeof value === 'number')
        return String(value);
    if (Array.isArray(value)) {
        return `[${value.map(v => toTomlLiteral(v)).join(', ')}]`;
    }
    if (value !== null && typeof value === 'object') {
        // TOML inline table: { key = "value", key2 = 123 }
        const pairs = Object.entries(value)
            .map(([k, v]) => `${k} = ${toTomlLiteral(v)}`)
            .join(', ');
        return `{${pairs}}`;
    }
    return String(value);
}
/**
 * 获取基础命令
 */
function getBaseCommand() {
    return 'codex';
}
export class CodexExecutor extends BaseExecutor {
    agentType = AgentType.CODEX;
    displayName = 'Codex';
    config;
    constructor(config = {}) {
        super();
        this.config = config;
        this.cmdOverrides = config.cmd;
    }
    /**
     * 获取可用性信息
     */
    async getAvailabilityInfo() {
        // 检查 codex 命令是否存在
        const codexPath = await which('codex');
        if (!codexPath) {
            return { type: 'NOT_FOUND', error: 'Codex CLI not installed' };
        }
        // 检查配置文件是否存在
        const configPath = path.join(os.homedir(), '.codex', 'config.toml');
        const authPath = path.join(os.homedir(), '.codex', 'auth.json');
        // 检查认证文件
        if (fs.existsSync(authPath)) {
            try {
                const stats = fs.statSync(authPath);
                const timestamp = Math.floor(stats.mtimeMs / 1000);
                return {
                    type: 'LOGIN_DETECTED',
                    lastAuthTimestamp: timestamp,
                };
            }
            catch {
                return { type: 'INSTALLATION_FOUND' };
            }
        }
        // 仅当 provider 显式依赖 profile 时，才要求本地 config.toml 存在。
        // 其他场景下，Codex 也可能通过 provider 的 settings/env 在运行时注入配置。
        if (this.config.profile && !fs.existsSync(configPath)) {
            return {
                type: 'NOT_FOUND',
                error: 'Codex profile requires ~/.codex/config.toml',
            };
        }
        return { type: 'INSTALLATION_FOUND' };
    }
    /**
     * 获取 Agent 能力
     */
    getCapabilities() {
        return [AgentCapability.SESSION_FORK];
    }
    /**
     * 获取默认 MCP 配置路径
     */
    getDefaultMcpConfigPath() {
        return path.join(os.homedir(), '.codex', 'config.toml');
    }
    /**
     * 构建 TOML 配置覆盖参数（--profile 和 -c）
     */
    buildConfigOverrides() {
        const args = [];
        // --profile
        if (this.config.profile) {
            args.push('--profile', this.config.profile);
        }
        // settings (TOML) → -c 参数
        if (this.config.settings) {
            const parsed = parseToml(this.config.settings);
            for (const [key, value] of flattenObject(parsed)) {
                args.push('-c', `${key}=${toTomlLiteral(value)}`);
            }
        }
        return args;
    }
    /**
     * 构建命令
     */
    buildCommandBuilder() {
        let builder = CommandBuilder.new(getBaseCommand());
        // 配置覆盖（--profile、-c 参数）— 放在最前面
        const configArgs = this.buildConfigOverrides();
        if (configArgs.length > 0) {
            builder.extendParams(configArgs);
        }
        // 模型选择
        if (this.config.model) {
            builder.extendParams(['--model', this.config.model]);
        }
        // 跳过所有确认提示并在无沙箱环境下执行命令（危险模式，类似 Claude Code 的 --dangerously-skip-permissions）
        if (this.config.dangerouslyBypassApprovalsAndSandbox) {
            builder.extendParams(['--dangerously-bypass-approvals-and-sandbox']);
        }
        // 实时搜索
        if (this.config.liveSearch) {
            builder.extendParams(['--search', 'live']);
        }
        // 应用覆盖
        return applyOverrides(builder, this.cmdOverrides);
    }
    /**
     * 启动新会话
     */
    async spawn(config) {
        const commandBuilder = this.buildCommandBuilder();
        // Codex 使用 exec 子命令进行非交互式执行
        commandBuilder.extendParams(['exec', '--json', '--skip-git-repo-check']);
        // 组合 prompt
        const prompt = this.combinePrompt(config.prompt);
        // 检测并提取图片路径（-i <FILE>... 是 variadic 参数，会贪婪消费后续参数，
        // 必须用 -- 分隔，防止 prompt 被当成文件路径）
        const { textPrompt, imagePaths } = await extractImagePaths(prompt);
        for (const imgPath of imagePaths) {
            commandBuilder.extendParams(['-i', imgPath]);
        }
        if (imagePaths.length > 0) {
            commandBuilder.extendParams(['--']);
        }
        const commandParts = commandBuilder.buildInitial();
        const newConfig = { ...config, prompt: textPrompt };
        return this.spawnInternal(newConfig, commandParts);
    }
    /**
     * 继续现有会话
     */
    async spawnFollowUp(config, sessionId, resetToMessageId) {
        const commandBuilder = this.buildCommandBuilder();
        // Codex 使用 exec resume 命令继续会话（支持 JSON 输出）
        const additionalArgs = ['exec', 'resume', '--json', '--skip-git-repo-check'];
        if (sessionId) {
            additionalArgs.push(sessionId);
        }
        // 注意：Codex 的 exec resume 不支持 --from 参数
        // resetToMessageId 参数暂时忽略
        // 组合 prompt
        const prompt = this.combinePrompt(config.prompt);
        // 检测并提取图片路径（-i <FILE>... 是 variadic 参数，会贪婪消费后续参数，
        // 必须用 -- 分隔，防止 prompt 被当成文件路径）
        const { textPrompt, imagePaths } = await extractImagePaths(prompt);
        for (const imgPath of imagePaths) {
            additionalArgs.push('-i', imgPath);
        }
        if (imagePaths.length > 0) {
            additionalArgs.push('--');
        }
        const commandParts = commandBuilder.buildFollowUp(additionalArgs);
        const newConfig = { ...config, prompt: textPrompt };
        return this.spawnInternal(newConfig, commandParts);
    }
    /**
     * 组合 prompt
     */
    combinePrompt(prompt) {
        if (this.config.appendPrompt) {
            return `${prompt}${this.config.appendPrompt}`;
        }
        return prompt;
    }
}
//# sourceMappingURL=codex.executor.js.map